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 "xedittags.h"
211 #include "engineoutput.h"
220 #define usleep(t) _sleep2(((t)+500)/1000)
224 # define _(s) gettext (s)
225 # define N_(s) gettext_noop (s)
231 int main P((int argc, char **argv));
232 RETSIGTYPE CmailSigHandler P((int sig));
233 RETSIGTYPE IntSigHandler P((int sig));
234 RETSIGTYPE TermSizeSigHandler P((int sig));
235 static void CreateGCs P((int redo));
236 static void CreateAnyPieces P((void));
237 void CreateXIMPieces P((void));
238 void CreateXPMPieces P((void));
239 void CreateXPMBoard P((char *s, int n));
240 void CreatePieces P((void));
241 Widget CreateMenuBar P((Menu *mb, int boardWidth));
243 char *InsertPxlSize P((char *pattern, int targetPxlSize));
244 XFontSet CreateFontSet P((char *base_fnt_lst));
246 char *FindFont P((char *pattern, int targetPxlSize));
248 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
249 u_int wreq, u_int hreq));
250 void CreateGrid P((void));
251 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
252 void DelayedDrag P((void));
253 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
254 void HandlePV P((Widget w, XEvent * event,
255 String * params, Cardinal * nParams));
256 void DrawPositionProc P((Widget w, XEvent *event,
257 String *prms, Cardinal *nprms));
258 void CommentClick P((Widget w, XEvent * event,
259 String * params, Cardinal * nParams));
260 void ICSInputBoxPopUp P((void));
261 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
262 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
263 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
264 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
265 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
266 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
267 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
268 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
269 Boolean TempBackwardActive = False;
270 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
271 void DisplayMove P((int moveNumber));
272 void ICSInitScript P((void));
273 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
274 void update_ics_width P(());
275 int CopyMemoProc P(());
278 * XBoard depends on Xt R4 or higher
280 int xtVersion = XtSpecificationRelease;
285 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
286 highlightSquareColor, premoveHighlightColor, dialogColor, buttonColor;
287 Pixel lowTimeWarningColor;
288 GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
289 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
291 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
292 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
293 Option *optList; // contains all widgets of main window
294 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
296 XFontSet fontSet, clockFontSet;
299 XFontStruct *clockFontStruct;
301 Font coordFontID, countFontID;
302 XFontStruct *coordFontStruct, *countFontStruct;
303 XtAppContext appContext;
306 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
308 Position commentX = -1, commentY = -1;
309 Dimension commentW, commentH;
310 typedef unsigned int BoardSize;
312 Boolean chessProgram;
314 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
315 int smallLayout = 0, tinyLayout = 0,
316 marginW, marginH, // [HGM] for run-time resizing
317 fromX = -1, fromY = -1, toX, toY, commentUp = False,
318 errorExitStatus = -1, defaultLineGap;
319 Dimension textHeight;
320 Pixel timerForegroundPixel, timerBackgroundPixel;
321 Pixel buttonForegroundPixel, buttonBackgroundPixel;
322 char *chessDir, *programName, *programVersion;
323 Boolean alwaysOnTop = False;
324 char *icsTextMenuString;
326 char *firstChessProgramNames;
327 char *secondChessProgramNames;
329 WindowPlacement wpMain;
330 WindowPlacement wpConsole;
331 WindowPlacement wpComment;
332 WindowPlacement wpMoveHistory;
333 WindowPlacement wpEvalGraph;
334 WindowPlacement wpEngineOutput;
335 WindowPlacement wpGameList;
336 WindowPlacement wpTags;
341 Pixmap pieceBitmap[2][(int)BlackPawn];
342 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
343 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
344 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
345 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
346 Pixmap xpmBoardBitmap[2];
347 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
348 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
349 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
350 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
351 XImage *ximLightSquare, *ximDarkSquare;
354 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
355 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
357 #define White(piece) ((int)(piece) < (int)BlackPawn)
359 /* Bitmaps for use as masks when drawing XPM pieces.
360 Need one for each black and white piece. */
361 static Pixmap xpmMask[BlackKing + 1];
363 /* This magic number is the number of intermediate frames used
364 in each half of the animation. For short moves it's reduced
365 by 1. The total number of frames will be factor * 2 + 1. */
368 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
375 DropMenuEnables dmEnables[] = {
392 XtResource clientResources[] = {
393 { "flashCount", "flashCount", XtRInt, sizeof(int),
394 XtOffset(AppDataPtr, flashCount), XtRImmediate,
395 (XtPointer) FLASH_COUNT },
398 XrmOptionDescRec shellOptions[] = {
399 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
400 { "-flash", "flashCount", XrmoptionNoArg, "3" },
401 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
404 XtActionsRec boardActions[] = {
405 { "DrawPosition", DrawPositionProc },
406 { "HandlePV", HandlePV },
407 { "SelectPV", SelectPV },
408 { "StopPV", StopPV },
409 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
410 { "QuitProc", QuitWrapper },
411 { "ManProc", ManInner },
412 { "TempBackwardProc", TempBackwardProc },
413 { "TempForwardProc", TempForwardProc },
414 { "CommentClick", (XtActionProc) CommentClick },
415 { "GenericPopDown", (XtActionProc) GenericPopDown },
416 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
417 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
418 { "SelectMove", (XtActionProc) SelectMove },
419 { "LoadSelectedProc", LoadSelectedProc },
420 { "SetFilterProc", SetFilterProc },
421 { "TypeInProc", TypeInProc },
422 { "EnterKeyProc", EnterKeyProc },
423 { "UpKeyProc", UpKeyProc },
424 { "DownKeyProc", DownKeyProc },
425 { "WheelProc", WheelProc },
426 { "TabProc", TabProc },
429 char globalTranslations[] =
430 ":<Key>F9: MenuItem(Actions.Resign) \n \
431 :Ctrl<Key>n: MenuItem(File.NewGame) \n \
432 :Meta<Key>V: MenuItem(File.NewVariant) \n \
433 :Ctrl<Key>o: MenuItem(File.LoadGame) \n \
434 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
435 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
436 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
437 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
438 :Ctrl<Key>s: MenuItem(File.SaveGame) \n \
439 :Ctrl<Key>c: MenuItem(Edit.CopyGame) \n \
440 :Ctrl<Key>v: MenuItem(Edit.PasteGame) \n \
441 :Ctrl<Key>O: MenuItem(File.LoadPosition) \n \
442 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
443 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
444 :Ctrl<Key>S: MenuItem(File.SavePosition) \n \
445 :Ctrl<Key>C: MenuItem(Edit.CopyPosition) \n \
446 :Ctrl<Key>V: MenuItem(Edit.PastePosition) \n \
447 :Ctrl<Key>q: MenuItem(File.Quit) \n \
448 :Ctrl<Key>w: MenuItem(Mode.MachineWhite) \n \
449 :Ctrl<Key>b: MenuItem(Mode.MachineBlack) \n \
450 :Ctrl<Key>t: MenuItem(Mode.TwoMachines) \n \
451 :Ctrl<Key>a: MenuItem(Mode.AnalysisMode) \n \
452 :Ctrl<Key>g: MenuItem(Mode.AnalyzeFile) \n \
453 :Ctrl<Key>e: MenuItem(Mode.EditGame) \n \
454 :Ctrl<Key>E: MenuItem(Mode.EditPosition) \n \
455 :Meta<Key>O: MenuItem(View.EngineOutput) \n \
456 :Meta<Key>E: MenuItem(View.EvaluationGraph) \n \
457 :Meta<Key>G: MenuItem(View.GameList) \n \
458 :Meta<Key>H: MenuItem(View.MoveHistory) \n \
459 :<Key>Pause: MenuItem(Mode.Pause) \n \
460 :<Key>F3: MenuItem(Action.Accept) \n \
461 :<Key>F4: MenuItem(Action.Decline) \n \
462 :<Key>F12: MenuItem(Action.Rematch) \n \
463 :<Key>F5: MenuItem(Action.CallFlag) \n \
464 :<Key>F6: MenuItem(Action.Draw) \n \
465 :<Key>F7: MenuItem(Action.Adjourn) \n \
466 :<Key>F8: MenuItem(Action.Abort) \n \
467 :<Key>F10: MenuItem(Action.StopObserving) \n \
468 :<Key>F11: MenuItem(Action.StopExamining) \n \
469 :Ctrl<Key>d: MenuItem(DebugProc) \n \
470 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
471 :Meta<Key>End: MenuItem(Edit.ForwardtoEnd) \n \
472 :Meta<Key>Right: MenuItem(Edit.Forward) \n \
473 :Meta<Key>Home: MenuItem(Edit.BacktoStart) \n \
474 :Meta<Key>Left: MenuItem(Edit.Backward) \n \
475 :<Key>Left: MenuItem(Edit.Backward) \n \
476 :<Key>Right: MenuItem(Edit.Forward) \n \
477 :<Key>Home: MenuItem(Edit.Revert) \n \
478 :<Key>End: MenuItem(Edit.TruncateGame) \n \
479 :Ctrl<Key>m: MenuItem(Engine.MoveNow) \n \
480 :Ctrl<Key>x: MenuItem(Engine.RetractMove) \n \
481 :Meta<Key>J: MenuItem(Options.Adjudications) \n \
482 :Meta<Key>U: MenuItem(Options.CommonEngine) \n \
483 :Meta<Key>T: MenuItem(Options.TimeControl) \n \
484 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
485 #ifndef OPTIONSDIALOG
487 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
488 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
489 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
490 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
491 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
494 :<Key>F1: MenuItem(Help.ManXBoard) \n \
495 :<Key>F2: MenuItem(View.FlipView) \n \
496 :<KeyDown>Return: TempBackwardProc() \n \
497 :<KeyUp>Return: TempForwardProc() \n";
499 char ICSInputTranslations[] =
500 "<Key>Up: UpKeyProc() \n "
501 "<Key>Down: DownKeyProc() \n "
502 "<Key>Return: EnterKeyProc() \n";
504 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
505 // as the widget is destroyed before the up-click can call extend-end
506 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
508 String xboardResources[] = {
509 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
514 /* Max possible square size */
515 #define MAXSQSIZE 256
517 static int xpm_avail[MAXSQSIZE];
519 #ifdef HAVE_DIR_STRUCT
521 /* Extract piece size from filename */
523 xpm_getsize (char *name, int len, char *ext)
531 if ((p=strchr(name, '.')) == NULL ||
532 StrCaseCmp(p+1, ext) != 0)
538 while (*p && isdigit(*p))
545 /* Setup xpm_avail */
547 xpm_getavail (char *dirname, char *ext)
553 for (i=0; i<MAXSQSIZE; ++i)
556 if (appData.debugMode)
557 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
559 dir = opendir(dirname);
562 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
563 programName, dirname);
567 while ((ent=readdir(dir)) != NULL) {
568 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
569 if (i > 0 && i < MAXSQSIZE)
579 xpm_print_avail (FILE *fp, char *ext)
583 fprintf(fp, _("Available `%s' sizes:\n"), ext);
584 for (i=1; i<MAXSQSIZE; ++i) {
590 /* Return XPM piecesize closest to size */
592 xpm_closest_to (char *dirname, int size, char *ext)
595 int sm_diff = MAXSQSIZE;
599 xpm_getavail(dirname, ext);
601 if (appData.debugMode)
602 xpm_print_avail(stderr, ext);
604 for (i=1; i<MAXSQSIZE; ++i) {
607 diff = (diff<0) ? -diff : diff;
608 if (diff < sm_diff) {
616 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
622 #else /* !HAVE_DIR_STRUCT */
623 /* If we are on a system without a DIR struct, we can't
624 read the directory, so we can't collect a list of
625 filenames, etc., so we can't do any size-fitting. */
627 xpm_closest_to (char *dirname, int size, char *ext)
630 Warning: No DIR structure found on this system --\n\
631 Unable to autosize for XPM/XIM pieces.\n\
632 Please report this error to %s.\n\
633 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
636 #endif /* HAVE_DIR_STRUCT */
639 /* Arrange to catch delete-window events */
640 Atom wm_delete_window;
642 CatchDeleteWindow (Widget w, String procname)
645 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
646 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
647 XtAugmentTranslations(w, XtParseTranslationTable(buf));
654 XtSetArg(args[0], XtNiconic, False);
655 XtSetValues(shellWidget, args, 1);
657 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
660 //---------------------------------------------------------------------------------------------------------
661 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
664 #define CW_USEDEFAULT (1<<31)
665 #define ICS_TEXT_MENU_SIZE 90
666 #define DEBUG_FILE "xboard.debug"
667 #define SetCurrentDirectory chdir
668 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
672 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
675 // front-end part of option handling
677 // [HGM] This platform-dependent table provides the location for storing the color info
678 extern char *crWhite, * crBlack;
682 &appData.whitePieceColor,
683 &appData.blackPieceColor,
684 &appData.lightSquareColor,
685 &appData.darkSquareColor,
686 &appData.highlightSquareColor,
687 &appData.premoveHighlightColor,
688 &appData.lowTimeWarningColor,
699 // [HGM] font: keep a font for each square size, even non-stndard ones
702 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
703 char *fontTable[NUM_FONTS][MAX_SIZE];
706 ParseFont (char *name, int number)
707 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
709 if(sscanf(name, "size%d:", &size)) {
710 // [HGM] font: font is meant for specific boardSize (likely from settings file);
711 // defer processing it until we know if it matches our board size
712 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
713 fontTable[number][size] = strdup(strchr(name, ':')+1);
714 fontValid[number][size] = True;
719 case 0: // CLOCK_FONT
720 appData.clockFont = strdup(name);
722 case 1: // MESSAGE_FONT
723 appData.font = strdup(name);
725 case 2: // COORD_FONT
726 appData.coordFont = strdup(name);
731 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
736 { // only 2 fonts currently
737 appData.clockFont = CLOCK_FONT_NAME;
738 appData.coordFont = COORD_FONT_NAME;
739 appData.font = DEFAULT_FONT_NAME;
744 { // no-op, until we identify the code for this already in XBoard and move it here
748 ParseColor (int n, char *name)
749 { // in XBoard, just copy the color-name string
750 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
754 ParseTextAttribs (ColorClass cc, char *s)
756 (&appData.colorShout)[cc] = strdup(s);
760 ParseBoardSize (void *addr, char *name)
762 appData.boardSize = strdup(name);
767 { // In XBoard the sound-playing program takes care of obtaining the actual sound
771 SetCommPortDefaults ()
772 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
775 // [HGM] args: these three cases taken out to stay in front-end
777 SaveFontArg (FILE *f, ArgDescriptor *ad)
780 int i, n = (int)(intptr_t)ad->argLoc;
782 case 0: // CLOCK_FONT
783 name = appData.clockFont;
785 case 1: // MESSAGE_FONT
788 case 2: // COORD_FONT
789 name = appData.coordFont;
794 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
795 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
796 fontTable[n][squareSize] = strdup(name);
797 fontValid[n][squareSize] = True;
800 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
801 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
806 { // nothing to do, as the sounds are at all times represented by their text-string names already
810 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
811 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
812 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
816 SaveColor (FILE *f, ArgDescriptor *ad)
817 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
818 if(colorVariable[(int)(intptr_t)ad->argLoc])
819 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
823 SaveBoardSize (FILE *f, char *name, void *addr)
824 { // wrapper to shield back-end from BoardSize & sizeInfo
825 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
829 ParseCommPortSettings (char *s)
830 { // no such option in XBoard (yet)
836 GetActualPlacement (Widget wg, WindowPlacement *wp)
838 XWindowAttributes winAt;
845 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
846 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
847 wp->x = rx - winAt.x;
848 wp->y = ry - winAt.y;
849 wp->height = winAt.height;
850 wp->width = winAt.width;
851 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
856 { // wrapper to shield use of window handles from back-end (make addressible by number?)
857 // In XBoard this will have to wait until awareness of window parameters is implemented
858 GetActualPlacement(shellWidget, &wpMain);
859 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
860 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
861 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
862 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
863 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
864 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
868 PrintCommPortSettings (FILE *f, char *name)
869 { // This option does not exist in XBoard
873 EnsureOnScreen (int *x, int *y, int minX, int minY)
880 { // [HGM] args: allows testing if main window is realized from back-end
881 return xBoardWindow != 0;
887 extern Option dualOptions[];
889 Window tmp = xBoardWindow;
890 if(!dual) dual = XtWindow(dualOptions[3].handle); // must be first call
891 xBoardWindow = dual; // swap them
896 PopUpStartupDialog ()
897 { // start menu not implemented in XBoard
901 ConvertToLine (int argc, char **argv)
903 static char line[128*1024], buf[1024];
907 for(i=1; i<argc; i++)
909 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
910 && argv[i][0] != '{' )
911 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
913 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
914 strncat(line, buf, 128*1024 - strlen(line) - 1 );
917 line[strlen(line)-1] = NULLCHAR;
921 //--------------------------------------------------------------------------------------------
923 #define BoardSize int
925 InitDrawingSizes (BoardSize boardSize, int flags)
926 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
927 Dimension boardWidth, boardHeight, w, h;
929 static Dimension oldWidth, oldHeight;
930 static VariantClass oldVariant;
931 static int oldMono = -1, oldTwoBoards = 0;
933 if(!formWidget) return;
935 if(oldTwoBoards && !twoBoards) PopDown(DummyDlg);
936 oldTwoBoards = twoBoards;
938 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
939 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
940 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
942 if(boardWidth != oldWidth || boardHeight != oldHeight) { // do resizing stuff only if size actually changed
944 oldWidth = boardWidth; oldHeight = boardHeight;
948 * Inhibit shell resizing.
950 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW ;
951 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
952 shellArgs[4].value = shellArgs[2].value = w;
953 shellArgs[5].value = shellArgs[3].value = h;
954 XtSetValues(shellWidget, &shellArgs[0], 6);
956 XSync(xDisplay, False);
960 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
963 if(gameInfo.variant != oldVariant) { // and only if variant changed
968 for(p=0; p<=(int)WhiteKing; p++)
969 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
970 if(gameInfo.variant == VariantShogi) {
971 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
972 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
973 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
974 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
975 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
978 if(gameInfo.variant == VariantGothic) {
979 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
982 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
983 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
984 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
987 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
988 for(p=0; p<=(int)WhiteKing; p++)
989 ximMaskPm[p] = ximMaskPm2[p]; // defaults
990 if(gameInfo.variant == VariantShogi) {
991 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
992 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
993 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
994 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
995 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
998 if(gameInfo.variant == VariantGothic) {
999 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1002 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1003 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1004 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1009 for(i=0; i<2; i++) {
1011 for(p=0; p<=(int)WhiteKing; p++)
1012 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1013 if(gameInfo.variant == VariantShogi) {
1014 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1015 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1016 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1017 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1018 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1021 if(gameInfo.variant == VariantGothic) {
1022 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1025 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1026 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1027 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1031 oldMono = -10; // kludge to force recreation of animation masks
1032 oldVariant = gameInfo.variant;
1035 if(appData.monoMode != oldMono)
1038 oldMono = appData.monoMode;
1042 MakeOneColor (char *name, Pixel *color)
1044 XrmValue vFrom, vTo;
1045 if (!appData.monoMode) {
1046 vFrom.addr = (caddr_t) name;
1047 vFrom.size = strlen(name);
1048 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1049 if (vTo.addr == NULL) {
1050 appData.monoMode = True;
1053 *color = *(Pixel *) vTo.addr;
1061 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1062 int forceMono = False;
1064 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1065 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1066 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1067 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1068 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1069 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1070 if (appData.lowTimeWarning)
1071 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
1072 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1073 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1080 { // [HGM] taken out of main
1082 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1083 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1084 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1086 if (appData.bitmapDirectory[0] != NULLCHAR) {
1090 CreateXPMBoard(appData.liteBackTextureFile, 1);
1091 CreateXPMBoard(appData.darkBackTextureFile, 0);
1095 /* Create regular pieces */
1096 if (!useImages) CreatePieces();
1101 InitDrawingParams ()
1103 MakeColors(); CreateGCs(True);
1108 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
1109 { // detervtomine what fonts to use, and create them
1113 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1114 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1115 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1116 appData.font = fontTable[MESSAGE_FONT][squareSize];
1117 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1118 appData.coordFont = fontTable[COORD_FONT][squareSize];
1121 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1122 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1123 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1124 fontSet = CreateFontSet(appData.font);
1125 clockFontSet = CreateFontSet(appData.clockFont);
1127 /* For the coordFont, use the 0th font of the fontset. */
1128 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1129 XFontStruct **font_struct_list;
1130 XFontSetExtents *fontSize;
1131 char **font_name_list;
1132 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1133 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1134 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1135 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1136 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1139 appData.font = FindFont(appData.font, fontPxlSize);
1140 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1141 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1142 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1143 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1144 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1145 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1146 // textHeight in !NLS mode!
1148 countFontID = coordFontID; // [HGM] holdings
1149 countFontStruct = coordFontStruct;
1151 xdb = XtDatabase(xDisplay);
1153 XrmPutLineResource(&xdb, "*international: True");
1154 vTo.size = sizeof(XFontSet);
1155 vTo.addr = (XtPointer) &fontSet;
1156 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1158 XrmPutStringResource(&xdb, "*font", appData.font);
1163 PrintArg (ArgType t)
1168 case ArgInt: p = " N"; break;
1169 case ArgString: p = " STR"; break;
1170 case ArgBoolean: p = " TF"; break;
1171 case ArgSettingsFilename:
1172 case ArgFilename: p = " FILE"; break;
1173 case ArgX: p = " Nx"; break;
1174 case ArgY: p = " Ny"; break;
1175 case ArgAttribs: p = " TEXTCOL"; break;
1176 case ArgColor: p = " COL"; break;
1177 case ArgFont: p = " FONT"; break;
1178 case ArgBoardSize: p = " SIZE"; break;
1179 case ArgFloat: p = " FLOAT"; break;
1184 case ArgCommSettings:
1195 ArgDescriptor *q, *p = argDescriptors+5;
1196 printf("\nXBoard accepts the following options:\n"
1197 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
1198 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
1199 " SIZE = board-size spec(s)\n"
1200 " Within parentheses are short forms, or options to set to true or false.\n"
1201 " Persistent options (saved in the settings file) are marked with *)\n\n");
1203 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
1204 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
1205 if(p->save) strcat(buf+len, "*");
1206 for(q=p+1; q->argLoc == p->argLoc; q++) {
1207 if(q->argName[0] == '-') continue;
1208 strcat(buf+len, q == p+1 ? " (" : " ");
1209 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
1211 if(q != p+1) strcat(buf+len, ")");
1213 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
1216 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
1220 main (int argc, char **argv)
1222 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1223 XSetWindowAttributes window_attributes;
1225 Dimension boardWidth, boardHeight, w, h;
1227 int forceMono = False;
1229 srandom(time(0)); // [HGM] book: make random truly random
1231 setbuf(stdout, NULL);
1232 setbuf(stderr, NULL);
1235 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1236 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1240 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1245 programName = strrchr(argv[0], '/');
1246 if (programName == NULL)
1247 programName = argv[0];
1252 XtSetLanguageProc(NULL, NULL, NULL);
1253 if (appData.debugMode) {
1254 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1257 bindtextdomain(PACKAGE, LOCALEDIR);
1258 textdomain(PACKAGE);
1261 appData.boardSize = "";
1262 InitAppData(ConvertToLine(argc, argv));
1264 if (p == NULL) p = "/tmp";
1265 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1266 gameCopyFilename = (char*) malloc(i);
1267 gamePasteFilename = (char*) malloc(i);
1268 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1269 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1271 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1272 static char buf[MSG_SIZ];
1273 EscapeExpand(buf, appData.firstInitString);
1274 appData.firstInitString = strdup(buf);
1275 EscapeExpand(buf, appData.secondInitString);
1276 appData.secondInitString = strdup(buf);
1277 EscapeExpand(buf, appData.firstComputerString);
1278 appData.firstComputerString = strdup(buf);
1279 EscapeExpand(buf, appData.secondComputerString);
1280 appData.secondComputerString = strdup(buf);
1283 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1286 if (chdir(chessDir) != 0) {
1287 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1293 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1294 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1295 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1296 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1299 setbuf(debugFP, NULL);
1302 /* [HGM,HR] make sure board size is acceptable */
1303 if(appData.NrFiles > BOARD_FILES ||
1304 appData.NrRanks > BOARD_RANKS )
1305 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1308 /* This feature does not work; animation needs a rewrite */
1309 appData.highlightDragging = FALSE;
1313 gameInfo.variant = StringToVariant(appData.variant);
1314 InitPosition(FALSE);
1317 XtAppInitialize(&appContext, "XBoard", shellOptions,
1318 XtNumber(shellOptions),
1319 &argc, argv, xboardResources, NULL, 0);
1321 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1322 clientResources, XtNumber(clientResources),
1325 xDisplay = XtDisplay(shellWidget);
1326 xScreen = DefaultScreen(xDisplay);
1327 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1330 * determine size, based on supplied or remembered -size, or screen size
1332 if (isdigit(appData.boardSize[0])) {
1333 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1334 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1335 &fontPxlSize, &smallLayout, &tinyLayout);
1337 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1338 programName, appData.boardSize);
1342 /* Find some defaults; use the nearest known size */
1343 SizeDefaults *szd, *nearest;
1344 int distance = 99999;
1345 nearest = szd = sizeDefaults;
1346 while (szd->name != NULL) {
1347 if (abs(szd->squareSize - squareSize) < distance) {
1349 distance = abs(szd->squareSize - squareSize);
1350 if (distance == 0) break;
1354 if (i < 2) lineGap = nearest->lineGap;
1355 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1356 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1357 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1358 if (i < 6) smallLayout = nearest->smallLayout;
1359 if (i < 7) tinyLayout = nearest->tinyLayout;
1362 SizeDefaults *szd = sizeDefaults;
1363 if (*appData.boardSize == NULLCHAR) {
1364 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1365 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1368 if (szd->name == NULL) szd--;
1369 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1371 while (szd->name != NULL &&
1372 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1373 if (szd->name == NULL) {
1374 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1375 programName, appData.boardSize);
1379 squareSize = szd->squareSize;
1380 lineGap = szd->lineGap;
1381 clockFontPxlSize = szd->clockFontPxlSize;
1382 coordFontPxlSize = szd->coordFontPxlSize;
1383 fontPxlSize = szd->fontPxlSize;
1384 smallLayout = szd->smallLayout;
1385 tinyLayout = szd->tinyLayout;
1386 // [HGM] font: use defaults from settings file if available and not overruled
1389 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1390 if (strlen(appData.pixmapDirectory) > 0) {
1391 p = ExpandPathName(appData.pixmapDirectory);
1393 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1394 appData.pixmapDirectory);
1397 if (appData.debugMode) {
1398 fprintf(stderr, _("\
1399 XBoard square size (hint): %d\n\
1400 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1402 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1403 if (appData.debugMode) {
1404 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1407 defaultLineGap = lineGap;
1408 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1410 /* [HR] height treated separately (hacked) */
1411 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1412 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1415 * Determine what fonts to use.
1417 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1420 * Detect if there are not enough colors available and adapt.
1422 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1423 appData.monoMode = True;
1426 forceMono = MakeColors();
1429 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1431 appData.monoMode = True;
1434 if (appData.monoMode && appData.debugMode) {
1435 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1436 (unsigned long) XWhitePixel(xDisplay, xScreen),
1437 (unsigned long) XBlackPixel(xDisplay, xScreen));
1440 ParseIcsTextColors();
1442 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1448 layoutName = "tinyLayout";
1449 } else if (smallLayout) {
1450 layoutName = "smallLayout";
1452 layoutName = "normalLayout";
1455 optList = BoardPopUp(squareSize, lineGap, (void*)
1461 boardWidget = optList[W_BOARD].handle;
1462 menuBarWidget = optList[W_MENU].handle;
1463 dropMenu = optList[W_DROP].handle;
1464 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1465 formWidget = XtParent(boardWidget);
1466 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1467 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1468 XtGetValues(optList[W_WHITE].handle, args, 2);
1469 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1470 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1471 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1472 XtGetValues(optList[W_PAUSE].handle, args, 2);
1474 AppendEnginesToMenu(appData.recentEngineList);
1476 xBoardWindow = XtWindow(boardWidget);
1478 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1479 // not need to go into InitDrawingSizes().
1482 * Create X checkmark bitmap and initialize option menu checks.
1484 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1485 checkmark_bits, checkmark_width, checkmark_height);
1491 ReadBitmap(&wIconPixmap, "icon_white.bm",
1492 icon_white_bits, icon_white_width, icon_white_height);
1493 ReadBitmap(&bIconPixmap, "icon_black.bm",
1494 icon_black_bits, icon_black_width, icon_black_height);
1495 iconPixmap = wIconPixmap;
1497 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1498 XtSetValues(shellWidget, args, i);
1501 * Create a cursor for the board widget.
1503 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1504 XChangeWindowAttributes(xDisplay, xBoardWindow,
1505 CWCursor, &window_attributes);
1508 * Inhibit shell resizing.
1510 shellArgs[0].value = (XtArgVal) &w;
1511 shellArgs[1].value = (XtArgVal) &h;
1512 XtGetValues(shellWidget, shellArgs, 2);
1513 shellArgs[4].value = shellArgs[2].value = w;
1514 shellArgs[5].value = shellArgs[3].value = h;
1515 XtSetValues(shellWidget, &shellArgs[2], 4);
1516 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1517 marginH = h - boardHeight;
1519 CatchDeleteWindow(shellWidget, "QuitProc");
1525 if(appData.logoSize)
1526 { // locate and read user logo
1528 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1529 ASSIGN(userLogo, buf);
1532 if (appData.animate || appData.animateDragging)
1535 XtAugmentTranslations(formWidget,
1536 XtParseTranslationTable(globalTranslations));
1538 XtAddEventHandler(formWidget, KeyPressMask, False,
1539 (XtEventHandler) MoveTypeInProc, NULL);
1540 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1541 (XtEventHandler) EventProc, NULL);
1543 /* [AS] Restore layout */
1544 if( wpMoveHistory.visible ) {
1548 if( wpEvalGraph.visible )
1553 if( wpEngineOutput.visible ) {
1554 EngineOutputPopUp();
1559 if (errorExitStatus == -1) {
1560 if (appData.icsActive) {
1561 /* We now wait until we see "login:" from the ICS before
1562 sending the logon script (problems with timestamp otherwise) */
1563 /*ICSInitScript();*/
1564 if (appData.icsInputBox) ICSInputBoxPopUp();
1568 signal(SIGWINCH, TermSizeSigHandler);
1570 signal(SIGINT, IntSigHandler);
1571 signal(SIGTERM, IntSigHandler);
1572 if (*appData.cmailGameName != NULLCHAR) {
1573 signal(SIGUSR1, CmailSigHandler);
1577 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1580 // XtSetKeyboardFocus(shellWidget, formWidget);
1581 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1583 XtAppMainLoop(appContext);
1584 if (appData.debugMode) fclose(debugFP); // [DM] debug
1589 TermSizeSigHandler (int sig)
1595 IntSigHandler (int sig)
1601 CmailSigHandler (int sig)
1606 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1608 /* Activate call-back function CmailSigHandlerCallBack() */
1609 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1611 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1615 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1618 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1620 /**** end signal code ****/
1623 #define Abs(n) ((n)<0 ? -(n) : (n))
1627 InsertPxlSize (char *pattern, int targetPxlSize)
1629 char *base_fnt_lst, strInt[12], *p, *q;
1630 int alternatives, i, len, strIntLen;
1633 * Replace the "*" (if present) in the pixel-size slot of each
1634 * alternative with the targetPxlSize.
1638 while ((p = strchr(p, ',')) != NULL) {
1642 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1643 strIntLen = strlen(strInt);
1644 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1648 while (alternatives--) {
1649 char *comma = strchr(p, ',');
1650 for (i=0; i<14; i++) {
1651 char *hyphen = strchr(p, '-');
1653 if (comma && hyphen > comma) break;
1654 len = hyphen + 1 - p;
1655 if (i == 7 && *p == '*' && len == 2) {
1657 memcpy(q, strInt, strIntLen);
1667 len = comma + 1 - p;
1674 return base_fnt_lst;
1678 CreateFontSet (char *base_fnt_lst)
1681 char **missing_list;
1685 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1686 &missing_list, &missing_count, &def_string);
1687 if (appData.debugMode) {
1689 XFontStruct **font_struct_list;
1690 char **font_name_list;
1691 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1693 fprintf(debugFP, " got list %s, locale %s\n",
1694 XBaseFontNameListOfFontSet(fntSet),
1695 XLocaleOfFontSet(fntSet));
1696 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1697 for (i = 0; i < count; i++) {
1698 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1701 for (i = 0; i < missing_count; i++) {
1702 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1705 if (fntSet == NULL) {
1706 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1711 #else // not ENABLE_NLS
1713 * Find a font that matches "pattern" that is as close as
1714 * possible to the targetPxlSize. Prefer fonts that are k
1715 * pixels smaller to fonts that are k pixels larger. The
1716 * pattern must be in the X Consortium standard format,
1717 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1718 * The return value should be freed with XtFree when no
1722 FindFont (char *pattern, int targetPxlSize)
1724 char **fonts, *p, *best, *scalable, *scalableTail;
1725 int i, j, nfonts, minerr, err, pxlSize;
1727 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1729 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1730 programName, pattern);
1737 for (i=0; i<nfonts; i++) {
1740 if (*p != '-') continue;
1742 if (*p == NULLCHAR) break;
1743 if (*p++ == '-') j++;
1745 if (j < 7) continue;
1748 scalable = fonts[i];
1751 err = pxlSize - targetPxlSize;
1752 if (Abs(err) < Abs(minerr) ||
1753 (minerr > 0 && err < 0 && -err == minerr)) {
1759 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1760 /* If the error is too big and there is a scalable font,
1761 use the scalable font. */
1762 int headlen = scalableTail - scalable;
1763 p = (char *) XtMalloc(strlen(scalable) + 10);
1764 while (isdigit(*scalableTail)) scalableTail++;
1765 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1767 p = (char *) XtMalloc(strlen(best) + 2);
1768 safeStrCpy(p, best, strlen(best)+1 );
1770 if (appData.debugMode) {
1771 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1772 pattern, targetPxlSize, p);
1774 XFreeFontNames(fonts);
1781 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
1782 // must be called before all non-first callse to CreateGCs()
1783 XtReleaseGC(shellWidget, highlineGC);
1784 XtReleaseGC(shellWidget, lightSquareGC);
1785 XtReleaseGC(shellWidget, darkSquareGC);
1786 XtReleaseGC(shellWidget, lineGC);
1787 if (appData.monoMode) {
1788 if (DefaultDepth(xDisplay, xScreen) == 1) {
1789 XtReleaseGC(shellWidget, wbPieceGC);
1791 XtReleaseGC(shellWidget, bwPieceGC);
1794 XtReleaseGC(shellWidget, prelineGC);
1795 XtReleaseGC(shellWidget, wdPieceGC);
1796 XtReleaseGC(shellWidget, wlPieceGC);
1797 XtReleaseGC(shellWidget, bdPieceGC);
1798 XtReleaseGC(shellWidget, blPieceGC);
1803 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
1805 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1806 | GCBackground | GCFunction | GCPlaneMask;
1807 gc_values->foreground = foreground;
1808 gc_values->background = background;
1809 return XtGetGC(shellWidget, value_mask, gc_values);
1813 CreateGCs (int redo)
1815 XGCValues gc_values;
1817 Pixel white = XWhitePixel(xDisplay, xScreen);
1818 Pixel black = XBlackPixel(xDisplay, xScreen);
1820 gc_values.plane_mask = AllPlanes;
1821 gc_values.line_width = lineGap;
1822 gc_values.line_style = LineSolid;
1823 gc_values.function = GXcopy;
1826 DeleteGCs(); // called a second time; clean up old GCs first
1827 } else { // [HGM] grid and font GCs created on first call only
1828 coordGC = CreateOneGC(&gc_values, black, white);
1829 XSetFont(xDisplay, coordGC, coordFontID);
1831 // [HGM] make font for holdings counts (white on black)
1832 countGC = CreateOneGC(&gc_values, white, black);
1833 XSetFont(xDisplay, countGC, countFontID);
1835 lineGC = CreateOneGC(&gc_values, black, black);
1837 if (appData.monoMode) {
1839 highlineGC = CreateOneGC(&gc_values, white, white);
1840 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
1841 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
1843 if (DefaultDepth(xDisplay, xScreen) == 1) {
1844 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
1845 gc_values.function = GXcopyInverted;
1846 copyInvertedGC = CreateOneGC(&gc_values, black, white);
1847 gc_values.function = GXcopy;
1848 if (XBlackPixel(xDisplay, xScreen) == 1) {
1849 bwPieceGC = darkSquareGC;
1850 wbPieceGC = copyInvertedGC;
1852 bwPieceGC = copyInvertedGC;
1853 wbPieceGC = lightSquareGC;
1858 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
1859 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
1860 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
1861 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
1862 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
1863 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
1864 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
1865 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
1870 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
1878 fp = fopen(filename, "rb");
1880 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
1887 for (y=0; y<h; ++y) {
1888 for (x=0; x<h; ++x) {
1893 XPutPixel(xim, x, y, blackPieceColor);
1895 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1898 XPutPixel(xim, x, y, darkSquareColor);
1900 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1903 XPutPixel(xim, x, y, whitePieceColor);
1905 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1908 XPutPixel(xim, x, y, lightSquareColor);
1910 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1918 /* create Pixmap of piece */
1919 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1921 XPutImage(xDisplay, *dest, lightSquareGC, xim,
1924 /* create Pixmap of clipmask
1925 Note: We assume the white/black pieces have the same
1926 outline, so we make only 6 masks. This is okay
1927 since the XPM clipmask routines do the same. */
1929 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1931 XPutImage(xDisplay, temp, lightSquareGC, xmask,
1934 /* now create the 1-bit version */
1935 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1938 values.foreground = 1;
1939 values.background = 0;
1941 /* Don't use XtGetGC, not read only */
1942 maskGC = XCreateGC(xDisplay, *mask,
1943 GCForeground | GCBackground, &values);
1944 XCopyPlane(xDisplay, temp, *mask, maskGC,
1945 0, 0, squareSize, squareSize, 0, 0, 1);
1946 XFreePixmap(xDisplay, temp);
1951 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
1959 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
1964 /* The XSynchronize calls were copied from CreatePieces.
1965 Not sure if needed, but can't hurt */
1966 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
1969 /* temp needed by loadXIM() */
1970 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1971 0, 0, ss, ss, AllPlanes, XYPixmap);
1973 if (strlen(appData.pixmapDirectory) == 0) {
1977 if (appData.monoMode) {
1978 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
1982 fprintf(stderr, _("\nLoading XIMs...\n"));
1984 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
1985 fprintf(stderr, "%d", piece+1);
1986 for (kind=0; kind<4; kind++) {
1987 fprintf(stderr, ".");
1988 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
1989 ExpandPathName(appData.pixmapDirectory),
1990 piece <= (int) WhiteKing ? "" : "w",
1991 pieceBitmapNames[piece],
1993 ximPieceBitmap[kind][piece] =
1994 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1995 0, 0, ss, ss, AllPlanes, XYPixmap);
1996 if (appData.debugMode)
1997 fprintf(stderr, _("(File:%s:) "), buf);
1998 loadXIM(ximPieceBitmap[kind][piece],
2000 &(xpmPieceBitmap2[kind][piece]),
2001 &(ximMaskPm2[piece]));
2002 if(piece <= (int)WhiteKing)
2003 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2005 fprintf(stderr," ");
2007 /* Load light and dark squares */
2008 /* If the LSQ and DSQ pieces don't exist, we will
2009 draw them with solid squares. */
2010 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2011 if (access(buf, 0) != 0) {
2015 fprintf(stderr, _("light square "));
2017 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2018 0, 0, ss, ss, AllPlanes, XYPixmap);
2019 if (appData.debugMode)
2020 fprintf(stderr, _("(File:%s:) "), buf);
2022 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2023 fprintf(stderr, _("dark square "));
2024 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2025 ExpandPathName(appData.pixmapDirectory), ss);
2026 if (appData.debugMode)
2027 fprintf(stderr, _("(File:%s:) "), buf);
2029 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2030 0, 0, ss, ss, AllPlanes, XYPixmap);
2031 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2032 xpmJailSquare = xpmLightSquare;
2034 fprintf(stderr, _("Done.\n"));
2036 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2039 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2043 CreateXPMBoard (char *s, int kind)
2047 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2048 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2049 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2055 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2056 // thisroutine has to be called t free the old piece pixmaps
2058 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2059 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2061 XFreePixmap(xDisplay, xpmLightSquare);
2062 XFreePixmap(xDisplay, xpmDarkSquare);
2071 u_int ss = squareSize;
2073 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2074 XpmColorSymbol symbols[4];
2075 static int redo = False;
2077 if(redo) FreeXPMPieces(); else redo = 1;
2079 /* The XSynchronize calls were copied from CreatePieces.
2080 Not sure if needed, but can't hurt */
2081 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2083 /* Setup translations so piece colors match square colors */
2084 symbols[0].name = "light_piece";
2085 symbols[0].value = appData.whitePieceColor;
2086 symbols[1].name = "dark_piece";
2087 symbols[1].value = appData.blackPieceColor;
2088 symbols[2].name = "light_square";
2089 symbols[2].value = appData.lightSquareColor;
2090 symbols[3].name = "dark_square";
2091 symbols[3].value = appData.darkSquareColor;
2093 attr.valuemask = XpmColorSymbols;
2094 attr.colorsymbols = symbols;
2095 attr.numsymbols = 4;
2097 if (appData.monoMode) {
2098 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2102 if (strlen(appData.pixmapDirectory) == 0) {
2103 XpmPieces* pieces = builtInXpms;
2106 while (pieces->size != squareSize && pieces->size) pieces++;
2107 if (!pieces->size) {
2108 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2111 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2112 for (kind=0; kind<4; kind++) {
2114 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2115 pieces->xpm[piece][kind],
2116 &(xpmPieceBitmap2[kind][piece]),
2117 NULL, &attr)) != 0) {
2118 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2122 if(piece <= (int) WhiteKing)
2123 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2127 xpmJailSquare = xpmLightSquare;
2131 fprintf(stderr, _("\nLoading XPMs...\n"));
2134 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2135 fprintf(stderr, "%d ", piece+1);
2136 for (kind=0; kind<4; kind++) {
2137 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2138 ExpandPathName(appData.pixmapDirectory),
2139 piece > (int) WhiteKing ? "w" : "",
2140 pieceBitmapNames[piece],
2142 if (appData.debugMode) {
2143 fprintf(stderr, _("(File:%s:) "), buf);
2145 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2146 &(xpmPieceBitmap2[kind][piece]),
2147 NULL, &attr)) != 0) {
2148 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2149 // [HGM] missing: read of unorthodox piece failed; substitute King.
2150 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2151 ExpandPathName(appData.pixmapDirectory),
2153 if (appData.debugMode) {
2154 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2156 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2157 &(xpmPieceBitmap2[kind][piece]),
2161 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2166 if(piece <= (int) WhiteKing)
2167 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2170 /* Load light and dark squares */
2171 /* If the LSQ and DSQ pieces don't exist, we will
2172 draw them with solid squares. */
2173 fprintf(stderr, _("light square "));
2174 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2175 if (access(buf, 0) != 0) {
2179 if (appData.debugMode)
2180 fprintf(stderr, _("(File:%s:) "), buf);
2182 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2183 &xpmLightSquare, NULL, &attr)) != 0) {
2184 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2187 fprintf(stderr, _("dark square "));
2188 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2189 ExpandPathName(appData.pixmapDirectory), ss);
2190 if (appData.debugMode) {
2191 fprintf(stderr, _("(File:%s:) "), buf);
2193 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2194 &xpmDarkSquare, NULL, &attr)) != 0) {
2195 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2199 xpmJailSquare = xpmLightSquare;
2200 fprintf(stderr, _("Done.\n"));
2202 oldVariant = -1; // kludge to force re-makig of animation masks
2203 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2206 #endif /* HAVE_LIBXPM */
2209 /* No built-in bitmaps */
2214 u_int ss = squareSize;
2216 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2219 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2220 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2221 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2222 pieceBitmapNames[piece],
2223 ss, kind == SOLID ? 's' : 'o');
2224 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2225 if(piece <= (int)WhiteKing)
2226 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2230 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2234 /* With built-in bitmaps */
2238 BuiltInBits* bib = builtInBits;
2241 u_int ss = squareSize;
2243 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2246 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2248 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2249 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2250 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2251 pieceBitmapNames[piece],
2252 ss, kind == SOLID ? 's' : 'o');
2253 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2254 bib->bits[kind][piece], ss, ss);
2255 if(piece <= (int)WhiteKing)
2256 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2260 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2266 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2271 char msg[MSG_SIZ], fullname[MSG_SIZ];
2273 if (*appData.bitmapDirectory != NULLCHAR) {
2274 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2275 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2276 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2277 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2278 &w, &h, pm, &x_hot, &y_hot);
2279 fprintf(stderr, "load %s\n", name);
2280 if (errcode != BitmapSuccess) {
2282 case BitmapOpenFailed:
2283 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2285 case BitmapFileInvalid:
2286 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2288 case BitmapNoMemory:
2289 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2293 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2297 fprintf(stderr, _("%s: %s...using built-in\n"),
2299 } else if (w != wreq || h != hreq) {
2301 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2302 programName, fullname, w, h, wreq, hreq);
2308 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2318 if (lineGap == 0) return;
2320 /* [HR] Split this into 2 loops for non-square boards. */
2322 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2323 gridSegments[i].x1 = 0;
2324 gridSegments[i].x2 =
2325 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2326 gridSegments[i].y1 = gridSegments[i].y2
2327 = lineGap / 2 + (i * (squareSize + lineGap));
2330 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2331 gridSegments[j + i].y1 = 0;
2332 gridSegments[j + i].y2 =
2333 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2334 gridSegments[j + i].x1 = gridSegments[j + i].x2
2335 = lineGap / 2 + (j * (squareSize + lineGap));
2340 MarkMenuItem (char *menuRef, int state)
2342 MenuItem *item = MenuNameToItem(menuRef);
2346 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2347 XtSetValues(item->handle, args, 1);
2352 EnableNamedMenuItem (char *menuRef, int state)
2354 MenuItem *item = MenuNameToItem(menuRef);
2356 if(item) XtSetSensitive(item->handle, state);
2360 EnableButtonBar (int state)
2362 XtSetSensitive(optList[W_BUTTON].handle, state);
2367 SetMenuEnables (Enables *enab)
2369 while (enab->name != NULL) {
2370 EnableNamedMenuItem(enab->name, enab->value);
2376 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2377 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2379 if(*nprms == 0) return;
2380 item = MenuNameToItem(prms[0]);
2381 if(item) ((MenuProc *) item->proc) ();
2385 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2387 RecentEngineEvent((int) (intptr_t) addr);
2391 AppendMenuItem (char *msg, int n)
2393 CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
2405 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2406 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2407 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2408 dmEnables[i].piece);
2409 XtSetSensitive(entry, p != NULL || !appData.testLegality
2410 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2411 && !appData.icsActive));
2413 while (p && *p++ == dmEnables[i].piece) count++;
2414 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2416 XtSetArg(args[j], XtNlabel, label); j++;
2417 XtSetValues(entry, args, j);
2423 do_flash_delay (unsigned long msec)
2429 DrawBorder (int x, int y, int type)
2433 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
2435 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
2436 squareSize+lineGap, squareSize+lineGap);
2440 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
2442 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
2443 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
2445 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
2446 if(textureW[kind] < W*squareSize)
2447 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
2449 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
2450 if(textureH[kind] < H*squareSize)
2451 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
2453 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
2458 DrawLogo (void *handle, void *logo)
2460 cairo_surface_t *img, *cs;
2464 if(!logo || !handle) return;
2465 cs = cairo_xlib_surface_create(xDisplay, XtWindow(handle), DefaultVisual(xDisplay, 0), appData.logoSize, appData.logoSize/2);
2466 img = cairo_image_surface_create_from_png (logo);
2467 w = cairo_image_surface_get_width (img);
2468 h = cairo_image_surface_get_height (img);
2469 cr = cairo_create(cs);
2470 cairo_scale(cr, (float)appData.logoSize/w, appData.logoSize/(2.*h));
2471 cairo_set_source_surface (cr, img, 0, 0);
2474 cairo_surface_destroy (img);
2475 cairo_surface_destroy (cs);
2479 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
2480 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
2482 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
2483 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
2484 squareSize, squareSize, x*fac, y*fac);
2486 if (useImages && useImageSqs) {
2490 pm = xpmLightSquare;
2495 case 2: /* neutral */
2497 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
2500 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
2501 squareSize, squareSize, x*fac, y*fac);
2511 case 2: /* neutral */
2516 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
2521 I split out the routines to draw a piece so that I could
2522 make a generic flash routine.
2525 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2527 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2528 switch (square_color) {
2530 case 2: /* neutral */
2532 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2533 ? *pieceToOutline(piece)
2534 : *pieceToSolid(piece),
2535 dest, bwPieceGC, 0, 0,
2536 squareSize, squareSize, x, y);
2539 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2540 ? *pieceToSolid(piece)
2541 : *pieceToOutline(piece),
2542 dest, wbPieceGC, 0, 0,
2543 squareSize, squareSize, x, y);
2549 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2551 switch (square_color) {
2553 case 2: /* neutral */
2555 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2556 ? *pieceToOutline(piece)
2557 : *pieceToSolid(piece),
2558 dest, bwPieceGC, 0, 0,
2559 squareSize, squareSize, x, y, 1);
2562 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2563 ? *pieceToSolid(piece)
2564 : *pieceToOutline(piece),
2565 dest, wbPieceGC, 0, 0,
2566 squareSize, squareSize, x, y, 1);
2572 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2574 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
2575 switch (square_color) {
2577 XCopyPlane(xDisplay, *pieceToSolid(piece),
2578 dest, (int) piece < (int) BlackPawn
2579 ? wlPieceGC : blPieceGC, 0, 0,
2580 squareSize, squareSize, x, y, 1);
2583 XCopyPlane(xDisplay, *pieceToSolid(piece),
2584 dest, (int) piece < (int) BlackPawn
2585 ? wdPieceGC : bdPieceGC, 0, 0,
2586 squareSize, squareSize, x, y, 1);
2588 case 2: /* neutral */
2590 break; // should never contain pieces
2595 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2597 int kind, p = piece;
2599 switch (square_color) {
2601 case 2: /* neutral */
2603 if ((int)piece < (int) BlackPawn) {
2611 if ((int)piece < (int) BlackPawn) {
2619 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2620 if(useTexture & square_color+1) {
2621 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2622 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
2623 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
2624 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
2625 XSetClipMask(xDisplay, wlPieceGC, None);
2626 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
2628 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
2629 dest, wlPieceGC, 0, 0,
2630 squareSize, squareSize, x, y);
2633 typedef void (*DrawFunc)();
2638 if (appData.monoMode) {
2639 if (DefaultDepth(xDisplay, xScreen) == 1) {
2640 return monoDrawPiece_1bit;
2642 return monoDrawPiece;
2646 return colorDrawPieceImage;
2648 return colorDrawPiece;
2653 DrawDot (int marker, int x, int y, int r)
2655 if(appData.monoMode) {
2656 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
2657 x, y, r, r, 0, 64*360);
2658 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
2659 x, y, r, r, 0, 64*360);
2661 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
2662 x, y, r, r, 0, 64*360);
2666 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2667 { // basic front-end board-draw function: takes care of everything that can be in square:
2668 // piece, background, coordinate/count, marker dot
2669 int direction, font_ascent, font_descent;
2670 XCharStruct overall;
2673 if (piece == EmptySquare) {
2674 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2676 drawfunc = ChooseDrawFunc();
2677 drawfunc(piece, square_color, x, y, xBoardWindow);
2680 if(align) { // square carries inscription (coord or piece count)
2682 GC hGC = align < 3 ? coordGC : countGC;
2683 // first calculate where it goes
2684 XTextExtents(countFontStruct, string, 1, &direction,
2685 &font_ascent, &font_descent, &overall);
2687 xx += squareSize - overall.width - 2;
2688 yy += squareSize - font_descent - 1;
2689 } else if (align == 2) {
2690 xx += 2, yy += font_ascent + 1;
2691 } else if (align == 3) {
2692 xx += squareSize - overall.width - 2;
2693 yy += font_ascent + 1;
2694 } else if (align == 4) {
2695 xx += 2, yy += font_ascent + 1;
2698 if (appData.monoMode) {
2699 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2701 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2705 if(marker) { // print fat marker dot, if requested
2706 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2711 FlashDelay (int flash_delay)
2713 XSync(xDisplay, False);
2714 if(flash_delay) do_flash_delay(flash_delay);
2718 Fraction (int x, int start, int stop)
2720 double f = ((double) x - start)/(stop - start);
2721 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2725 static WindowPlacement wpNew;
2728 CoDrag (Widget sh, WindowPlacement *wp)
2731 int j=0, touch=0, fudge = 2;
2732 GetActualPlacement(sh, wp);
2733 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
2734 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
2735 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2736 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
2737 if(!touch ) return; // only windows that touch co-move
2738 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2739 int heightInc = wpNew.height - wpMain.height;
2740 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2741 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2742 wp->y += fracTop * heightInc;
2743 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2744 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2745 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2746 int widthInc = wpNew.width - wpMain.width;
2747 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2748 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2749 wp->y += fracLeft * widthInc;
2750 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2751 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2753 wp->x += wpNew.x - wpMain.x;
2754 wp->y += wpNew.y - wpMain.y;
2755 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2756 if(touch == 3) wp->y += wpNew.height - wpMain.height;
2757 XtSetArg(args[j], XtNx, wp->x); j++;
2758 XtSetArg(args[j], XtNy, wp->y); j++;
2759 XtSetValues(sh, args, j);
2762 static XtIntervalId delayedDragID = 0;
2767 GetActualPlacement(shellWidget, &wpNew);
2768 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
2769 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
2770 return; // false alarm
2771 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
2772 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
2773 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
2774 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
2776 DrawPosition(True, NULL);
2777 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
2784 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
2786 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
2790 EventProc (Widget widget, caddr_t unused, XEvent *event)
2792 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
2793 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
2796 // [HGM] seekgraph: some low-level drawing routines (by JC, mostly)
2798 static cairo_surface_t *cs; // to keep out of back-end :-(
2801 Color (char *col, int n)
2804 sscanf(col, "#%x", &c);
2810 SetPen (cairo_t *cr, float w, char *col, int dash)
2812 static const double dotted[] = {4.0, 4.0};
2813 static int len = sizeof(dotted) / sizeof(dotted[0]);
2814 cairo_set_line_width (cr, w);
2815 cairo_set_source_rgba (cr, Color(col, 4), Color(col, 2), Color(col, 0), 1.0);
2816 if(dash) cairo_set_dash (cr, dotted, len, 0.0);
2819 void DrawSeekAxis( int x, int y, int xTo, int yTo )
2824 cr = cairo_create (cs);
2826 cairo_move_to (cr, x, y);
2827 cairo_line_to(cr, xTo, yTo );
2829 SetPen(cr, 2, "#000000", 0);
2836 void DrawSeekBackground( int left, int top, int right, int bottom )
2838 cairo_t *cr = cairo_create (cs);
2840 cairo_rectangle (cr, left, top, right-left, bottom-top);
2842 cairo_set_source_rgba(cr, 0.8, 0.8, 0.4,1.0);
2849 void DrawSeekText(char *buf, int x, int y)
2851 cairo_t *cr = cairo_create (cs);
2853 cairo_select_font_face (cr, "Sans",
2854 CAIRO_FONT_SLANT_NORMAL,
2855 CAIRO_FONT_WEIGHT_NORMAL);
2857 cairo_set_font_size (cr, 12.0);
2859 cairo_move_to (cr, x, y+4);
2860 cairo_show_text( cr, buf);
2862 cairo_set_source_rgba(cr, 0, 0, 0,1.0);
2869 void DrawSeekDot(int x, int y, int colorNr)
2871 cairo_t *cr = cairo_create (cs);
2872 int square = colorNr & 0x80;
2876 cairo_rectangle (cr, x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
2878 cairo_arc(cr, x, y, squareSize/8, 0.0, 2*M_PI);
2880 SetPen(cr, 2, "#000000", 0);
2881 cairo_stroke_preserve(cr);
2883 case 0: cairo_set_source_rgba(cr, 1.0, 0, 0,1.0); break;
2884 case 1: cairo_set_source_rgba (cr, 0.0, 0.7, 0.2, 1.0); break;
2885 default: cairo_set_source_rgba (cr, 1.0, 1.0, 0.0, 1.0); break;
2896 int boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2897 int boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2898 cs = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), boardWidth, boardHeight);
2904 cairo_surface_destroy(cs);
2910 /* draws a grid starting around Nx, Ny squares starting at x,y */
2916 cr = cairo_create (cs);
2918 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
2919 SetPen(cr, lineGap, "#000000", 0);
2922 for (i = 0; i < BOARD_WIDTH + BOARD_HEIGHT + 2; i++)
2924 cairo_move_to (cr, gridSegments[i].x1, gridSegments[i].y1);
2925 cairo_line_to (cr, gridSegments[i].x2, gridSegments[i].y2);
2937 * event handler for redrawing the board
2940 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2942 DrawPosition(True, NULL);
2947 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
2948 { // [HGM] pv: walk PV
2949 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
2952 static int savedIndex; /* gross that this is global */
2955 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
2958 XawTextPosition index, dummy;
2961 XawTextGetSelectionPos(w, &index, &dummy);
2962 XtSetArg(arg, XtNstring, &val);
2963 XtGetValues(w, &arg, 1);
2964 ReplaceComment(savedIndex, val);
2965 if(savedIndex != currentMove) ToNrEvent(savedIndex);
2966 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
2970 EditCommentPopUp (int index, char *title, char *text)
2973 if (text == NULL) text = "";
2974 NewCommentPopup(title, text, index);
2978 CommentPopUp (char *title, char *text)
2980 savedIndex = currentMove; // [HGM] vari
2981 NewCommentPopup(title, text, currentMove);
2987 PopDown(CommentDlg);
2991 /* Disable all user input other than deleting the window */
2992 static int frozen = 0;
2998 /* Grab by a widget that doesn't accept input */
2999 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
3003 /* Undo a FreezeUI */
3007 if (!frozen) return;
3008 XtRemoveGrab(optList[W_MESSG].handle);
3016 static int oldPausing = FALSE;
3017 static GameMode oldmode = (GameMode) -1;
3020 if (!boardWidget || !XtIsRealized(boardWidget)) return;
3022 if (pausing != oldPausing) {
3023 oldPausing = pausing;
3024 MarkMenuItem("Mode.Pause", pausing);
3026 if (appData.showButtonBar) {
3027 /* Always toggle, don't set. Previous code messes up when
3028 invoked while the button is pressed, as releasing it
3029 toggles the state again. */
3032 XtSetArg(args[0], XtNbackground, &oldbg);
3033 XtSetArg(args[1], XtNforeground, &oldfg);
3034 XtGetValues(optList[W_PAUSE].handle,
3036 XtSetArg(args[0], XtNbackground, oldfg);
3037 XtSetArg(args[1], XtNforeground, oldbg);
3039 XtSetValues(optList[W_PAUSE].handle, args, 2);
3043 wname = ModeToWidgetName(oldmode);
3044 if (wname != NULL) {
3045 MarkMenuItem(wname, False);
3047 wname = ModeToWidgetName(gameMode);
3048 if (wname != NULL) {
3049 MarkMenuItem(wname, True);
3052 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
3054 /* Maybe all the enables should be handled here, not just this one */
3055 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
3057 DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);
3062 * Button/menu procedures
3065 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3066 char *selected_fen_position=NULL;
3069 SendPositionSelection (Widget w, Atom *selection, Atom *target,
3070 Atom *type_return, XtPointer *value_return,
3071 unsigned long *length_return, int *format_return)
3073 char *selection_tmp;
3075 // if (!selected_fen_position) return False; /* should never happen */
3076 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3077 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
3078 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
3081 if (f == NULL) return False;
3085 selection_tmp = XtMalloc(len + 1);
3086 count = fread(selection_tmp, 1, len, f);
3089 XtFree(selection_tmp);
3092 selection_tmp[len] = NULLCHAR;
3094 /* note: since no XtSelectionDoneProc was registered, Xt will
3095 * automatically call XtFree on the value returned. So have to
3096 * make a copy of it allocated with XtMalloc */
3097 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3098 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
3101 *value_return=selection_tmp;
3102 *length_return=strlen(selection_tmp);
3103 *type_return=*target;
3104 *format_return = 8; /* bits per byte */
3106 } else if (*target == XA_TARGETS(xDisplay)) {
3107 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3108 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3109 targets_tmp[1] = XA_STRING;
3110 *value_return = targets_tmp;
3111 *type_return = XA_ATOM;
3114 // This code leads to a read of value_return out of bounds on 64-bit systems.
3115 // Other code which I have seen always sets *format_return to 32 independent of
3116 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
3117 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
3118 *format_return = 8 * sizeof(Atom);
3119 if (*format_return > 32) {
3120 *length_return *= *format_return / 32;
3121 *format_return = 32;
3124 *format_return = 32;
3132 /* note: when called from menu all parameters are NULL, so no clue what the
3133 * Widget which was clicked on was, or what the click event was
3136 CopySomething (char *src)
3138 selected_fen_position = src;
3140 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3141 * have a notion of a position that is selected but not copied.
3142 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3144 XtOwnSelection(menuBarWidget, XA_PRIMARY,
3146 SendPositionSelection,
3147 NULL/* lose_ownership_proc */ ,
3148 NULL/* transfer_done_proc */);
3149 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3151 SendPositionSelection,
3152 NULL/* lose_ownership_proc */ ,
3153 NULL/* transfer_done_proc */);
3156 /* function called when the data to Paste is ready */
3158 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3159 Atom *type, XtPointer value, unsigned long *len, int *format)
3162 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3163 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3164 EditPositionPasteFEN(fenstr);
3168 /* called when Paste Position button is pressed,
3169 * all parameters will be NULL */
3171 PastePositionProc ()
3173 XtGetSelectionValue(menuBarWidget,
3174 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3175 /* (XtSelectionCallbackProc) */ PastePositionCB,
3176 NULL, /* client_data passed to PastePositionCB */
3178 /* better to use the time field from the event that triggered the
3179 * call to this function, but that isn't trivial to get
3186 /* note: when called from menu all parameters are NULL, so no clue what the
3187 * Widget which was clicked on was, or what the click event was
3189 /* function called when the data to Paste is ready */
3191 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3192 Atom *type, XtPointer value, unsigned long *len, int *format)
3195 if (value == NULL || *len == 0) {
3196 return; /* nothing had been selected to copy */
3198 f = fopen(gamePasteFilename, "w");
3200 DisplayError(_("Can't open temp file"), errno);
3203 fwrite(value, 1, *len, f);
3206 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3209 /* called when Paste Game button is pressed,
3210 * all parameters will be NULL */
3214 XtGetSelectionValue(menuBarWidget,
3215 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3216 /* (XtSelectionCallbackProc) */ PasteGameCB,
3217 NULL, /* client_data passed to PasteGameCB */
3219 /* better to use the time field from the event that triggered the
3220 * call to this function, but that isn't trivial to get
3229 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3236 { // bassic primitive for determining if modifier keys are pressed
3237 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3240 XQueryKeymap(xDisplay,keys);
3241 for(i=0; i<6; i++) {
3243 j = XKeysymToKeycode(xDisplay, codes[i]);
3244 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3250 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3254 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3255 if ( n == 1 && *buf >= 32 // printable
3256 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3257 ) BoxAutoPopUp (buf);
3261 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3262 { // [HGM] input: let up-arrow recall previous line from history
3267 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3268 { // [HGM] input: let down-arrow recall next line from history
3273 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3279 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3281 if (!TempBackwardActive) {
3282 TempBackwardActive = True;
3288 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3290 /* Check to see if triggered by a key release event for a repeating key.
3291 * If so the next queued event will be a key press of the same key at the same time */
3292 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3294 XPeekEvent(xDisplay, &next);
3295 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3296 next.xkey.keycode == event->xkey.keycode)
3300 TempBackwardActive = False;
3304 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3305 { // called as key binding
3308 if (nprms && *nprms > 0)
3312 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3318 { // called from menu
3319 ManInner(NULL, NULL, NULL, NULL);
3323 SetWindowTitle (char *text, char *title, char *icon)
3327 if (appData.titleInWindow) {
3329 XtSetArg(args[i], XtNlabel, text); i++;
3330 XtSetValues(titleWidget, args, i);
3333 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3334 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3335 XtSetValues(shellWidget, args, i);
3336 XSync(xDisplay, False);
3341 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3347 DisplayIcsInteractionTitle (String message)
3349 if (oldICSInteractionTitle == NULL) {
3350 /* Magic to find the old window title, adapted from vim */
3351 char *wina = getenv("WINDOWID");
3353 Window win = (Window) atoi(wina);
3354 Window root, parent, *children;
3355 unsigned int nchildren;
3356 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3358 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3359 if (!XQueryTree(xDisplay, win, &root, &parent,
3360 &children, &nchildren)) break;
3361 if (children) XFree((void *)children);
3362 if (parent == root || parent == 0) break;
3365 XSetErrorHandler(oldHandler);
3367 if (oldICSInteractionTitle == NULL) {
3368 oldICSInteractionTitle = "xterm";
3371 printf("\033]0;%s\007", message);
3376 XtIntervalId delayedEventTimerXID = 0;
3377 DelayedEventCallback delayedEventCallback = 0;
3382 delayedEventTimerXID = 0;
3383 delayedEventCallback();
3387 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3389 if(delayedEventTimerXID && delayedEventCallback == cb)
3390 // [HGM] alive: replace, rather than add or flush identical event
3391 XtRemoveTimeOut(delayedEventTimerXID);
3392 delayedEventCallback = cb;
3393 delayedEventTimerXID =
3394 XtAppAddTimeOut(appContext, millisec,
3395 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3398 DelayedEventCallback
3401 if (delayedEventTimerXID) {
3402 return delayedEventCallback;
3409 CancelDelayedEvent ()
3411 if (delayedEventTimerXID) {
3412 XtRemoveTimeOut(delayedEventTimerXID);
3413 delayedEventTimerXID = 0;
3417 XtIntervalId loadGameTimerXID = 0;
3420 LoadGameTimerRunning ()
3422 return loadGameTimerXID != 0;
3426 StopLoadGameTimer ()
3428 if (loadGameTimerXID != 0) {
3429 XtRemoveTimeOut(loadGameTimerXID);
3430 loadGameTimerXID = 0;
3438 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3440 loadGameTimerXID = 0;
3445 StartLoadGameTimer (long millisec)
3448 XtAppAddTimeOut(appContext, millisec,
3449 (XtTimerCallbackProc) LoadGameTimerCallback,
3453 XtIntervalId analysisClockXID = 0;
3456 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3458 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3459 || appData.icsEngineAnalyze) { // [DM]
3460 AnalysisPeriodicEvent(0);
3461 StartAnalysisClock();
3466 StartAnalysisClock ()
3469 XtAppAddTimeOut(appContext, 2000,
3470 (XtTimerCallbackProc) AnalysisClockCallback,
3474 XtIntervalId clockTimerXID = 0;
3477 ClockTimerRunning ()
3479 return clockTimerXID != 0;
3485 if (clockTimerXID != 0) {
3486 XtRemoveTimeOut(clockTimerXID);
3495 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3502 StartClockTimer (long millisec)
3505 XtAppAddTimeOut(appContext, millisec,
3506 (XtTimerCallbackProc) ClockTimerCallback,
3511 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
3515 Widget w = (Widget) opt->handle;
3517 /* check for low time warning */
3518 Pixel foregroundOrWarningColor = timerForegroundPixel;
3521 appData.lowTimeWarning &&
3522 (timer / 1000) < appData.icsAlarmTime)
3523 foregroundOrWarningColor = lowTimeWarningColor;
3525 if (appData.clockMode) {
3526 snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
3527 XtSetArg(args[0], XtNlabel, buf);
3529 snprintf(buf, MSG_SIZ, "%s ", color);
3530 XtSetArg(args[0], XtNlabel, buf);
3535 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3536 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3538 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3539 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3542 XtSetValues(w, args, 3);
3545 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3548 SetClockIcon (int color)
3551 Pixmap pm = *clockIcons[color];
3552 if (iconPixmap != pm) {
3554 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3555 XtSetValues(shellWidget, args, 1);
3560 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3562 InputSource *is = (InputSource *) closure;
3567 if (is->lineByLine) {
3568 count = read(is->fd, is->unused,
3569 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3571 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3574 is->unused += count;
3576 while (p < is->unused) {
3577 q = memchr(p, '\n', is->unused - p);
3578 if (q == NULL) break;
3580 (is->func)(is, is->closure, p, q - p, 0);
3584 while (p < is->unused) {
3589 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3594 (is->func)(is, is->closure, is->buf, count, error);
3599 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3602 ChildProc *cp = (ChildProc *) pr;
3604 is = (InputSource *) calloc(1, sizeof(InputSource));
3605 is->lineByLine = lineByLine;
3609 is->fd = fileno(stdin);
3611 is->kind = cp->kind;
3612 is->fd = cp->fdFrom;
3615 is->unused = is->buf;
3618 is->xid = XtAppAddInput(appContext, is->fd,
3619 (XtPointer) (XtInputReadMask),
3620 (XtInputCallbackProc) DoInputCallback,
3622 is->closure = closure;
3623 return (InputSourceRef) is;
3627 RemoveInputSource (InputSourceRef isr)
3629 InputSource *is = (InputSource *) isr;
3631 if (is->xid == 0) return;
3632 XtRemoveInput(is->xid);
3636 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
3638 /* Masks for XPM pieces. Black and white pieces can have
3639 different shapes, but in the interest of retaining my
3640 sanity pieces must have the same outline on both light
3641 and dark squares, and all pieces must use the same
3642 background square colors/images. */
3644 static int xpmDone = 0;
3645 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
3646 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
3649 CreateAnimMasks (int pieceDepth)
3655 unsigned long plane;
3658 /* Need a bitmap just to get a GC with right depth */
3659 buf = XCreatePixmap(xDisplay, xBoardWindow,
3661 values.foreground = 1;
3662 values.background = 0;
3663 /* Don't use XtGetGC, not read only */
3664 maskGC = XCreateGC(xDisplay, buf,
3665 GCForeground | GCBackground, &values);
3666 XFreePixmap(xDisplay, buf);
3668 buf = XCreatePixmap(xDisplay, xBoardWindow,
3669 squareSize, squareSize, pieceDepth);
3670 values.foreground = XBlackPixel(xDisplay, xScreen);
3671 values.background = XWhitePixel(xDisplay, xScreen);
3672 bufGC = XCreateGC(xDisplay, buf,
3673 GCForeground | GCBackground, &values);
3675 for (piece = WhitePawn; piece <= BlackKing; piece++) {
3676 /* Begin with empty mask */
3677 if(!xpmDone) // [HGM] pieces: keep using existing
3678 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
3679 squareSize, squareSize, 1);
3680 XSetFunction(xDisplay, maskGC, GXclear);
3681 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
3682 0, 0, squareSize, squareSize);
3684 /* Take a copy of the piece */
3689 XSetFunction(xDisplay, bufGC, GXcopy);
3690 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
3692 0, 0, squareSize, squareSize, 0, 0);
3694 /* XOR the background (light) over the piece */
3695 XSetFunction(xDisplay, bufGC, GXxor);
3697 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
3698 0, 0, squareSize, squareSize, 0, 0);
3700 XSetForeground(xDisplay, bufGC, lightSquareColor);
3701 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
3704 /* We now have an inverted piece image with the background
3705 erased. Construct mask by just selecting all the non-zero
3706 pixels - no need to reconstruct the original image. */
3707 XSetFunction(xDisplay, maskGC, GXor);
3709 /* Might be quicker to download an XImage and create bitmap
3710 data from it rather than this N copies per piece, but it
3711 only takes a fraction of a second and there is a much
3712 longer delay for loading the pieces. */
3713 for (n = 0; n < pieceDepth; n ++) {
3714 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
3715 0, 0, squareSize, squareSize,
3721 XFreePixmap(xDisplay, buf);
3722 XFreeGC(xDisplay, bufGC);
3723 XFreeGC(xDisplay, maskGC);
3727 InitAnimState (AnimNr anr, XWindowAttributes *info)
3732 /* Each buffer is square size, same depth as window */
3733 animBufs[anr+4] = xBoardWindow;
3734 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
3735 squareSize, squareSize, info->depth);
3736 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
3737 squareSize, squareSize, info->depth);
3739 /* Create a plain GC for blitting */
3740 mask = GCForeground | GCBackground | GCFunction |
3741 GCPlaneMask | GCGraphicsExposures;
3742 values.foreground = XBlackPixel(xDisplay, xScreen);
3743 values.background = XWhitePixel(xDisplay, xScreen);
3744 values.function = GXcopy;
3745 values.plane_mask = AllPlanes;
3746 values.graphics_exposures = False;
3747 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
3749 /* Piece will be copied from an existing context at
3750 the start of each new animation/drag. */
3751 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
3753 /* Outline will be a read-only copy of an existing */
3754 animGCs[anr+4] = None;
3760 XWindowAttributes info;
3762 if (xpmDone && gameInfo.variant == oldVariant) return;
3763 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
3764 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
3766 InitAnimState(Game, &info);
3767 InitAnimState(Player, &info);
3769 /* For XPM pieces, we need bitmaps to use as masks. */
3771 CreateAnimMasks(info.depth), xpmDone = 1;
3776 static Boolean frameWaiting;
3779 FrameAlarm (int sig)
3781 frameWaiting = False;
3782 /* In case System-V style signals. Needed?? */
3783 signal(SIGALRM, FrameAlarm);
3787 FrameDelay (int time)
3789 struct itimerval delay;
3791 XSync(xDisplay, False);
3794 frameWaiting = True;
3795 signal(SIGALRM, FrameAlarm);
3796 delay.it_interval.tv_sec =
3797 delay.it_value.tv_sec = time / 1000;
3798 delay.it_interval.tv_usec =
3799 delay.it_value.tv_usec = (time % 1000) * 1000;
3800 setitimer(ITIMER_REAL, &delay, NULL);
3801 while (frameWaiting) pause();
3802 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
3803 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
3804 setitimer(ITIMER_REAL, &delay, NULL);
3811 FrameDelay (int time)
3813 XSync(xDisplay, False);
3815 usleep(time * 1000);
3821 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
3825 /* Bitmap for piece being moved. */
3826 if (appData.monoMode) {
3827 *mask = *pieceToSolid(piece);
3828 } else if (useImages) {
3830 *mask = xpmMask[piece];
3832 *mask = ximMaskPm[piece];
3835 *mask = *pieceToSolid(piece);
3838 /* GC for piece being moved. Square color doesn't matter, but
3839 since it gets modified we make a copy of the original. */
3841 if (appData.monoMode)
3846 if (appData.monoMode)
3851 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
3853 /* Outline only used in mono mode and is not modified */
3855 *outline = bwPieceGC;
3857 *outline = wbPieceGC;
3861 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
3866 /* Draw solid rectangle which will be clipped to shape of piece */
3867 XFillRectangle(xDisplay, dest, clip,
3868 0, 0, squareSize, squareSize);
3869 if (appData.monoMode)
3870 /* Also draw outline in contrasting color for black
3871 on black / white on white cases */
3872 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
3873 0, 0, squareSize, squareSize, 0, 0, 1);
3875 /* Copy the piece */
3880 if(appData.upsideDown && flipView) kind ^= 2;
3881 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3883 0, 0, squareSize, squareSize,
3889 InsertPiece (AnimNr anr, ChessSquare piece)
3891 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
3895 DrawBlank (AnimNr anr, int x, int y, int startColor)
3897 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
3900 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
3901 int srcX, int srcY, int width, int height, int destX, int destY)
3903 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
3904 srcX, srcY, width, height, destX, destY);
3908 SetDragPiece (AnimNr anr, ChessSquare piece)
3911 /* The piece will be drawn using its own bitmap as a matte */
3912 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
3913 XSetClipMask(xDisplay, animGCs[anr+2], mask);
3916 /* [AS] Arrow highlighting support */
3918 void DrawPolygon(Pnt arrow[], int nr)
3919 { // for now on own surface; eventually this should become a global that is only destroyed on resize
3920 cairo_surface_t *boardSurface;
3923 int w = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3924 int h = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3925 boardSurface = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), w, h);
3926 cr = cairo_create (boardSurface);
3927 cairo_move_to (cr, arrow[nr-1].x, arrow[nr-1].y);
3928 for (i=0;i<nr;i++) {
3929 cairo_line_to(cr, arrow[i].x, arrow[i].y);
3931 if(appData.monoMode) { // should we always outline arrow?
3932 cairo_line_to(cr, arrow[0].x, arrow[0].y);
3933 SetPen(cr, 2, "#000000", 0);
3934 cairo_stroke_preserve(cr);
3936 SetPen(cr, 2, appData.highlightSquareColor, 0);
3941 cairo_surface_destroy (boardSurface);
3945 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
3947 char buf[MSG_SIZ], *logoName = buf;
3948 if(appData.logo[n][0]) {
3949 logoName = appData.logo[n];
3950 } else if(appData.autoLogo) {
3951 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
3952 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
3953 } else if(appData.directory[n] && appData.directory[n][0]) {
3954 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
3958 { ASSIGN(cps->programLogo, logoName); }
3962 UpdateLogos (int displ)
3964 if(optList[W_WHITE-1].handle == NULL) return;
3965 LoadLogo(&first, 0, 0);
3966 LoadLogo(&second, 1, appData.icsActive);
3967 if(displ) DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);