2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
64 #include <cairo/cairo.h>
65 #include <cairo/cairo-xlib.h>
68 # if HAVE_SYS_SOCKET_H
69 # include <sys/socket.h>
70 # include <netinet/in.h>
72 # else /* not HAVE_SYS_SOCKET_H */
73 # if HAVE_LAN_SOCKET_H
74 # include <lan/socket.h>
76 # include <lan/netdb.h>
77 # else /* not HAVE_LAN_SOCKET_H */
78 # define OMIT_SOCKETS 1
79 # endif /* not HAVE_LAN_SOCKET_H */
80 # endif /* not HAVE_SYS_SOCKET_H */
81 #endif /* !OMIT_SOCKETS */
86 #else /* not STDC_HEADERS */
87 extern char *getenv();
90 # else /* not HAVE_STRING_H */
92 # endif /* not HAVE_STRING_H */
93 #endif /* not STDC_HEADERS */
96 # include <sys/fcntl.h>
97 #else /* not HAVE_SYS_FCNTL_H */
100 # endif /* HAVE_FCNTL_H */
101 #endif /* not HAVE_SYS_FCNTL_H */
103 #if HAVE_SYS_SYSTEMINFO_H
104 # include <sys/systeminfo.h>
105 #endif /* HAVE_SYS_SYSTEMINFO_H */
107 #if TIME_WITH_SYS_TIME
108 # include <sys/time.h>
112 # include <sys/time.h>
123 # include <sys/wait.h>
128 # define NAMLEN(dirent) strlen((dirent)->d_name)
129 # define HAVE_DIR_STRUCT
131 # define dirent direct
132 # define NAMLEN(dirent) (dirent)->d_namlen
134 # include <sys/ndir.h>
135 # define HAVE_DIR_STRUCT
138 # include <sys/dir.h>
139 # define HAVE_DIR_STRUCT
143 # define HAVE_DIR_STRUCT
151 #include <X11/Intrinsic.h>
152 #include <X11/StringDefs.h>
153 #include <X11/Shell.h>
154 #include <X11/cursorfont.h>
155 #include <X11/Xatom.h>
156 #include <X11/Xmu/Atoms.h>
158 #include <X11/Xaw3d/Dialog.h>
159 #include <X11/Xaw3d/Form.h>
160 #include <X11/Xaw3d/List.h>
161 #include <X11/Xaw3d/Label.h>
162 #include <X11/Xaw3d/SimpleMenu.h>
163 #include <X11/Xaw3d/SmeBSB.h>
164 #include <X11/Xaw3d/SmeLine.h>
165 #include <X11/Xaw3d/Box.h>
166 #include <X11/Xaw3d/MenuButton.h>
167 #include <X11/Xaw3d/Text.h>
168 #include <X11/Xaw3d/AsciiText.h>
170 #include <X11/Xaw/Dialog.h>
171 #include <X11/Xaw/Form.h>
172 #include <X11/Xaw/List.h>
173 #include <X11/Xaw/Label.h>
174 #include <X11/Xaw/SimpleMenu.h>
175 #include <X11/Xaw/SmeBSB.h>
176 #include <X11/Xaw/SmeLine.h>
177 #include <X11/Xaw/Box.h>
178 #include <X11/Xaw/MenuButton.h>
179 #include <X11/Xaw/Text.h>
180 #include <X11/Xaw/AsciiText.h>
183 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
188 #include "pixmaps/pixmaps.h"
189 #define IMAGE_EXT "xpm"
191 #define IMAGE_EXT "xim"
192 #include "bitmaps/bitmaps.h"
195 #include "bitmaps/icon_white.bm"
196 #include "bitmaps/icon_black.bm"
197 #include "bitmaps/checkmark.bm"
199 #include "frontend.h"
201 #include "backendz.h"
205 #include "xgamelist.h"
206 #include "xhistory.h"
207 #include "xevalgraph.h"
208 #include "xedittags.h"
212 #include "engineoutput.h"
221 #define usleep(t) _sleep2(((t)+500)/1000)
225 # define _(s) gettext (s)
226 # define N_(s) gettext_noop (s)
232 int main P((int argc, char **argv));
233 RETSIGTYPE CmailSigHandler P((int sig));
234 RETSIGTYPE IntSigHandler P((int sig));
235 RETSIGTYPE TermSizeSigHandler P((int sig));
236 static void CreateGCs P((int redo));
237 static void CreateAnyPieces P((void));
238 void CreateXIMPieces P((void));
239 void CreateXPMPieces P((void));
240 void CreateXPMBoard P((char *s, int n));
241 void CreatePieces P((void));
242 Widget CreateMenuBar P((Menu *mb, int boardWidth));
244 char *InsertPxlSize P((char *pattern, int targetPxlSize));
245 XFontSet CreateFontSet P((char *base_fnt_lst));
247 char *FindFont P((char *pattern, int targetPxlSize));
249 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
250 u_int wreq, u_int hreq));
251 void CreateGrid P((void));
252 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
253 void DelayedDrag P((void));
254 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
255 void HandlePV P((Widget w, XEvent * event,
256 String * params, Cardinal * nParams));
257 void DrawPositionProc P((Widget w, XEvent *event,
258 String *prms, Cardinal *nprms));
259 void CommentClick P((Widget w, XEvent * event,
260 String * params, Cardinal * nParams));
261 void ICSInputBoxPopUp P((void));
262 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
263 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
264 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
265 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
266 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
267 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
268 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
269 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
270 Boolean TempBackwardActive = False;
271 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
272 void DisplayMove P((int moveNumber));
273 void ICSInitScript P((void));
274 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
275 void update_ics_width P(());
276 int CopyMemoProc P(());
279 * XBoard depends on Xt R4 or higher
281 int xtVersion = XtSpecificationRelease;
286 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
287 highlightSquareColor, premoveHighlightColor, dialogColor, buttonColor;
288 Pixel lowTimeWarningColor;
289 GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
290 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
292 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
293 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
294 Option *optList; // contains all widgets of main window
295 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
297 XFontSet fontSet, clockFontSet;
300 XFontStruct *clockFontStruct;
302 Font coordFontID, countFontID;
303 XFontStruct *coordFontStruct, *countFontStruct;
304 XtAppContext appContext;
307 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
309 Position commentX = -1, commentY = -1;
310 Dimension commentW, commentH;
311 typedef unsigned int BoardSize;
313 Boolean chessProgram;
315 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
316 int smallLayout = 0, tinyLayout = 0,
317 marginW, marginH, // [HGM] for run-time resizing
318 fromX = -1, fromY = -1, toX, toY, commentUp = False,
319 errorExitStatus = -1, defaultLineGap;
320 Dimension textHeight;
321 Pixel timerForegroundPixel, timerBackgroundPixel;
322 Pixel buttonForegroundPixel, buttonBackgroundPixel;
323 char *chessDir, *programName, *programVersion;
324 Boolean alwaysOnTop = False;
325 char *icsTextMenuString;
327 char *firstChessProgramNames;
328 char *secondChessProgramNames;
330 WindowPlacement wpMain;
331 WindowPlacement wpConsole;
332 WindowPlacement wpComment;
333 WindowPlacement wpMoveHistory;
334 WindowPlacement wpEvalGraph;
335 WindowPlacement wpEngineOutput;
336 WindowPlacement wpGameList;
337 WindowPlacement wpTags;
342 Pixmap pieceBitmap[2][(int)BlackPawn];
343 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
344 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
345 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
346 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
347 Pixmap xpmBoardBitmap[2];
348 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
349 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
350 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
351 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
352 XImage *ximLightSquare, *ximDarkSquare;
355 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
356 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
358 #define White(piece) ((int)(piece) < (int)BlackPawn)
360 /* Bitmaps for use as masks when drawing XPM pieces.
361 Need one for each black and white piece. */
362 static Pixmap xpmMask[BlackKing + 1];
364 /* This magic number is the number of intermediate frames used
365 in each half of the animation. For short moves it's reduced
366 by 1. The total number of frames will be factor * 2 + 1. */
369 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
376 DropMenuEnables dmEnables[] = {
393 XtResource clientResources[] = {
394 { "flashCount", "flashCount", XtRInt, sizeof(int),
395 XtOffset(AppDataPtr, flashCount), XtRImmediate,
396 (XtPointer) FLASH_COUNT },
399 XrmOptionDescRec shellOptions[] = {
400 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
401 { "-flash", "flashCount", XrmoptionNoArg, "3" },
402 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
405 XtActionsRec boardActions[] = {
406 { "DrawPosition", DrawPositionProc },
407 { "HandlePV", HandlePV },
408 { "SelectPV", SelectPV },
409 { "StopPV", StopPV },
410 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
411 { "QuitProc", QuitWrapper },
412 { "ManProc", ManInner },
413 { "TempBackwardProc", TempBackwardProc },
414 { "TempForwardProc", TempForwardProc },
415 { "CommentClick", (XtActionProc) CommentClick },
416 { "GenericPopDown", (XtActionProc) GenericPopDown },
417 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
418 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
419 { "SelectMove", (XtActionProc) SelectMove },
420 { "LoadSelectedProc", LoadSelectedProc },
421 { "SetFilterProc", SetFilterProc },
422 { "TypeInProc", TypeInProc },
423 { "EnterKeyProc", EnterKeyProc },
424 { "UpKeyProc", UpKeyProc },
425 { "DownKeyProc", DownKeyProc },
426 { "WheelProc", WheelProc },
427 { "TabProc", TabProc },
430 char globalTranslations[] =
431 ":<Key>F9: MenuItem(Actions.Resign) \n \
432 :Ctrl<Key>n: MenuItem(File.NewGame) \n \
433 :Meta<Key>V: MenuItem(File.NewVariant) \n \
434 :Ctrl<Key>o: MenuItem(File.LoadGame) \n \
435 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
436 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
437 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
438 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
439 :Ctrl<Key>s: MenuItem(File.SaveGame) \n \
440 :Ctrl<Key>c: MenuItem(Edit.CopyGame) \n \
441 :Ctrl<Key>v: MenuItem(Edit.PasteGame) \n \
442 :Ctrl<Key>O: MenuItem(File.LoadPosition) \n \
443 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
444 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
445 :Ctrl<Key>S: MenuItem(File.SavePosition) \n \
446 :Ctrl<Key>C: MenuItem(Edit.CopyPosition) \n \
447 :Ctrl<Key>V: MenuItem(Edit.PastePosition) \n \
448 :Ctrl<Key>q: MenuItem(File.Quit) \n \
449 :Ctrl<Key>w: MenuItem(Mode.MachineWhite) \n \
450 :Ctrl<Key>b: MenuItem(Mode.MachineBlack) \n \
451 :Ctrl<Key>t: MenuItem(Mode.TwoMachines) \n \
452 :Ctrl<Key>a: MenuItem(Mode.AnalysisMode) \n \
453 :Ctrl<Key>g: MenuItem(Mode.AnalyzeFile) \n \
454 :Ctrl<Key>e: MenuItem(Mode.EditGame) \n \
455 :Ctrl<Key>E: MenuItem(Mode.EditPosition) \n \
456 :Meta<Key>O: MenuItem(View.EngineOutput) \n \
457 :Meta<Key>E: MenuItem(View.EvaluationGraph) \n \
458 :Meta<Key>G: MenuItem(View.GameList) \n \
459 :Meta<Key>H: MenuItem(View.MoveHistory) \n \
460 :<Key>Pause: MenuItem(Mode.Pause) \n \
461 :<Key>F3: MenuItem(Action.Accept) \n \
462 :<Key>F4: MenuItem(Action.Decline) \n \
463 :<Key>F12: MenuItem(Action.Rematch) \n \
464 :<Key>F5: MenuItem(Action.CallFlag) \n \
465 :<Key>F6: MenuItem(Action.Draw) \n \
466 :<Key>F7: MenuItem(Action.Adjourn) \n \
467 :<Key>F8: MenuItem(Action.Abort) \n \
468 :<Key>F10: MenuItem(Action.StopObserving) \n \
469 :<Key>F11: MenuItem(Action.StopExamining) \n \
470 :Ctrl<Key>d: MenuItem(DebugProc) \n \
471 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
472 :Meta<Key>End: MenuItem(Edit.ForwardtoEnd) \n \
473 :Meta<Key>Right: MenuItem(Edit.Forward) \n \
474 :Meta<Key>Home: MenuItem(Edit.BacktoStart) \n \
475 :Meta<Key>Left: MenuItem(Edit.Backward) \n \
476 :<Key>Left: MenuItem(Edit.Backward) \n \
477 :<Key>Right: MenuItem(Edit.Forward) \n \
478 :<Key>Home: MenuItem(Edit.Revert) \n \
479 :<Key>End: MenuItem(Edit.TruncateGame) \n \
480 :Ctrl<Key>m: MenuItem(Engine.MoveNow) \n \
481 :Ctrl<Key>x: MenuItem(Engine.RetractMove) \n \
482 :Meta<Key>J: MenuItem(Options.Adjudications) \n \
483 :Meta<Key>U: MenuItem(Options.CommonEngine) \n \
484 :Meta<Key>T: MenuItem(Options.TimeControl) \n \
485 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
486 #ifndef OPTIONSDIALOG
488 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
489 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
490 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
491 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
492 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
495 :<Key>F1: MenuItem(Help.ManXBoard) \n \
496 :<Key>F2: MenuItem(View.FlipView) \n \
497 :<KeyDown>Return: TempBackwardProc() \n \
498 :<KeyUp>Return: TempForwardProc() \n";
500 char ICSInputTranslations[] =
501 "<Key>Up: UpKeyProc() \n "
502 "<Key>Down: DownKeyProc() \n "
503 "<Key>Return: EnterKeyProc() \n";
505 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
506 // as the widget is destroyed before the up-click can call extend-end
507 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
509 String xboardResources[] = {
510 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
515 /* Max possible square size */
516 #define MAXSQSIZE 256
518 static int xpm_avail[MAXSQSIZE];
520 #ifdef HAVE_DIR_STRUCT
522 /* Extract piece size from filename */
524 xpm_getsize (char *name, int len, char *ext)
532 if ((p=strchr(name, '.')) == NULL ||
533 StrCaseCmp(p+1, ext) != 0)
539 while (*p && isdigit(*p))
546 /* Setup xpm_avail */
548 xpm_getavail (char *dirname, char *ext)
554 for (i=0; i<MAXSQSIZE; ++i)
557 if (appData.debugMode)
558 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
560 dir = opendir(dirname);
563 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
564 programName, dirname);
568 while ((ent=readdir(dir)) != NULL) {
569 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
570 if (i > 0 && i < MAXSQSIZE)
580 xpm_print_avail (FILE *fp, char *ext)
584 fprintf(fp, _("Available `%s' sizes:\n"), ext);
585 for (i=1; i<MAXSQSIZE; ++i) {
591 /* Return XPM piecesize closest to size */
593 xpm_closest_to (char *dirname, int size, char *ext)
596 int sm_diff = MAXSQSIZE;
600 xpm_getavail(dirname, ext);
602 if (appData.debugMode)
603 xpm_print_avail(stderr, ext);
605 for (i=1; i<MAXSQSIZE; ++i) {
608 diff = (diff<0) ? -diff : diff;
609 if (diff < sm_diff) {
617 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
623 #else /* !HAVE_DIR_STRUCT */
624 /* If we are on a system without a DIR struct, we can't
625 read the directory, so we can't collect a list of
626 filenames, etc., so we can't do any size-fitting. */
628 xpm_closest_to (char *dirname, int size, char *ext)
631 Warning: No DIR structure found on this system --\n\
632 Unable to autosize for XPM/XIM pieces.\n\
633 Please report this error to %s.\n\
634 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
637 #endif /* HAVE_DIR_STRUCT */
640 /* Arrange to catch delete-window events */
641 Atom wm_delete_window;
643 CatchDeleteWindow (Widget w, String procname)
646 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
647 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
648 XtAugmentTranslations(w, XtParseTranslationTable(buf));
655 XtSetArg(args[0], XtNiconic, False);
656 XtSetValues(shellWidget, args, 1);
658 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
661 //---------------------------------------------------------------------------------------------------------
662 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
665 #define CW_USEDEFAULT (1<<31)
666 #define ICS_TEXT_MENU_SIZE 90
667 #define DEBUG_FILE "xboard.debug"
668 #define SetCurrentDirectory chdir
669 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
673 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
676 // front-end part of option handling
678 // [HGM] This platform-dependent table provides the location for storing the color info
679 extern char *crWhite, * crBlack;
683 &appData.whitePieceColor,
684 &appData.blackPieceColor,
685 &appData.lightSquareColor,
686 &appData.darkSquareColor,
687 &appData.highlightSquareColor,
688 &appData.premoveHighlightColor,
689 &appData.lowTimeWarningColor,
700 // [HGM] font: keep a font for each square size, even non-stndard ones
703 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
704 char *fontTable[NUM_FONTS][MAX_SIZE];
707 ParseFont (char *name, int number)
708 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
710 if(sscanf(name, "size%d:", &size)) {
711 // [HGM] font: font is meant for specific boardSize (likely from settings file);
712 // defer processing it until we know if it matches our board size
713 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
714 fontTable[number][size] = strdup(strchr(name, ':')+1);
715 fontValid[number][size] = True;
720 case 0: // CLOCK_FONT
721 appData.clockFont = strdup(name);
723 case 1: // MESSAGE_FONT
724 appData.font = strdup(name);
726 case 2: // COORD_FONT
727 appData.coordFont = strdup(name);
732 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
737 { // only 2 fonts currently
738 appData.clockFont = CLOCK_FONT_NAME;
739 appData.coordFont = COORD_FONT_NAME;
740 appData.font = DEFAULT_FONT_NAME;
745 { // no-op, until we identify the code for this already in XBoard and move it here
749 ParseColor (int n, char *name)
750 { // in XBoard, just copy the color-name string
751 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
755 ParseTextAttribs (ColorClass cc, char *s)
757 (&appData.colorShout)[cc] = strdup(s);
761 ParseBoardSize (void *addr, char *name)
763 appData.boardSize = strdup(name);
768 { // In XBoard the sound-playing program takes care of obtaining the actual sound
772 SetCommPortDefaults ()
773 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
776 // [HGM] args: these three cases taken out to stay in front-end
778 SaveFontArg (FILE *f, ArgDescriptor *ad)
781 int i, n = (int)(intptr_t)ad->argLoc;
783 case 0: // CLOCK_FONT
784 name = appData.clockFont;
786 case 1: // MESSAGE_FONT
789 case 2: // COORD_FONT
790 name = appData.coordFont;
795 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
796 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
797 fontTable[n][squareSize] = strdup(name);
798 fontValid[n][squareSize] = True;
801 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
802 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
807 { // nothing to do, as the sounds are at all times represented by their text-string names already
811 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
812 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
813 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
817 SaveColor (FILE *f, ArgDescriptor *ad)
818 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
819 if(colorVariable[(int)(intptr_t)ad->argLoc])
820 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
824 SaveBoardSize (FILE *f, char *name, void *addr)
825 { // wrapper to shield back-end from BoardSize & sizeInfo
826 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
830 ParseCommPortSettings (char *s)
831 { // no such option in XBoard (yet)
837 GetActualPlacement (Widget wg, WindowPlacement *wp)
839 XWindowAttributes winAt;
846 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
847 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
848 wp->x = rx - winAt.x;
849 wp->y = ry - winAt.y;
850 wp->height = winAt.height;
851 wp->width = winAt.width;
852 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
857 { // wrapper to shield use of window handles from back-end (make addressible by number?)
858 // In XBoard this will have to wait until awareness of window parameters is implemented
859 GetActualPlacement(shellWidget, &wpMain);
860 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
861 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
862 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
863 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
864 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
865 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
869 PrintCommPortSettings (FILE *f, char *name)
870 { // This option does not exist in XBoard
874 EnsureOnScreen (int *x, int *y, int minX, int minY)
881 { // [HGM] args: allows testing if main window is realized from back-end
882 return xBoardWindow != 0;
888 extern Option dualOptions[];
890 Window tmp = xBoardWindow;
891 if(!dual) dual = XtWindow(dualOptions[3].handle); // must be first call
892 xBoardWindow = dual; // swap them
897 PopUpStartupDialog ()
898 { // start menu not implemented in XBoard
902 ConvertToLine (int argc, char **argv)
904 static char line[128*1024], buf[1024];
908 for(i=1; i<argc; i++)
910 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
911 && argv[i][0] != '{' )
912 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
914 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
915 strncat(line, buf, 128*1024 - strlen(line) - 1 );
918 line[strlen(line)-1] = NULLCHAR;
922 //--------------------------------------------------------------------------------------------
924 #define BoardSize int
926 InitDrawingSizes (BoardSize boardSize, int flags)
927 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
928 Dimension boardWidth, boardHeight, w, h;
930 static Dimension oldWidth, oldHeight;
931 static VariantClass oldVariant;
932 static int oldMono = -1, oldTwoBoards = 0;
934 if(!formWidget) return;
936 if(oldTwoBoards && !twoBoards) PopDown(DummyDlg);
937 oldTwoBoards = twoBoards;
939 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
940 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
941 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
943 if(boardWidth != oldWidth || boardHeight != oldHeight) { // do resizing stuff only if size actually changed
945 oldWidth = boardWidth; oldHeight = boardHeight;
949 * Inhibit shell resizing.
951 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW ;
952 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
953 shellArgs[4].value = shellArgs[2].value = w;
954 shellArgs[5].value = shellArgs[3].value = h;
955 XtSetValues(shellWidget, &shellArgs[0], 6);
957 XSync(xDisplay, False);
961 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
964 if(gameInfo.variant != oldVariant) { // and only if variant changed
969 for(p=0; p<=(int)WhiteKing; p++)
970 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
971 if(gameInfo.variant == VariantShogi) {
972 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
973 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
974 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
975 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
976 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
979 if(gameInfo.variant == VariantGothic) {
980 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
983 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
984 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
985 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
988 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
989 for(p=0; p<=(int)WhiteKing; p++)
990 ximMaskPm[p] = ximMaskPm2[p]; // defaults
991 if(gameInfo.variant == VariantShogi) {
992 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
993 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
994 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
995 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
996 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
999 if(gameInfo.variant == VariantGothic) {
1000 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1003 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1004 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1005 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1010 for(i=0; i<2; i++) {
1012 for(p=0; p<=(int)WhiteKing; p++)
1013 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1014 if(gameInfo.variant == VariantShogi) {
1015 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1016 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1017 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1018 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1019 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1022 if(gameInfo.variant == VariantGothic) {
1023 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1026 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1027 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1028 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1032 oldMono = -10; // kludge to force recreation of animation masks
1033 oldVariant = gameInfo.variant;
1036 if(appData.monoMode != oldMono)
1039 oldMono = appData.monoMode;
1043 MakeOneColor (char *name, Pixel *color)
1045 XrmValue vFrom, vTo;
1046 if (!appData.monoMode) {
1047 vFrom.addr = (caddr_t) name;
1048 vFrom.size = strlen(name);
1049 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1050 if (vTo.addr == NULL) {
1051 appData.monoMode = True;
1054 *color = *(Pixel *) vTo.addr;
1062 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1063 int forceMono = False;
1065 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1066 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1067 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1068 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1069 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1070 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1071 if (appData.lowTimeWarning)
1072 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
1073 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1074 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1081 { // [HGM] taken out of main
1083 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1084 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1085 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1087 if (appData.bitmapDirectory[0] != NULLCHAR) {
1091 CreateXPMBoard(appData.liteBackTextureFile, 1);
1092 CreateXPMBoard(appData.darkBackTextureFile, 0);
1096 /* Create regular pieces */
1097 if (!useImages) CreatePieces();
1102 InitDrawingParams ()
1104 MakeColors(); CreateGCs(True);
1109 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
1110 { // detervtomine what fonts to use, and create them
1114 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1115 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1116 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1117 appData.font = fontTable[MESSAGE_FONT][squareSize];
1118 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1119 appData.coordFont = fontTable[COORD_FONT][squareSize];
1122 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1123 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1124 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1125 fontSet = CreateFontSet(appData.font);
1126 clockFontSet = CreateFontSet(appData.clockFont);
1128 /* For the coordFont, use the 0th font of the fontset. */
1129 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1130 XFontStruct **font_struct_list;
1131 XFontSetExtents *fontSize;
1132 char **font_name_list;
1133 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1134 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1135 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1136 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1137 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1140 appData.font = FindFont(appData.font, fontPxlSize);
1141 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1142 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1143 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1144 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1145 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1146 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1147 // textHeight in !NLS mode!
1149 countFontID = coordFontID; // [HGM] holdings
1150 countFontStruct = coordFontStruct;
1152 xdb = XtDatabase(xDisplay);
1154 XrmPutLineResource(&xdb, "*international: True");
1155 vTo.size = sizeof(XFontSet);
1156 vTo.addr = (XtPointer) &fontSet;
1157 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1159 XrmPutStringResource(&xdb, "*font", appData.font);
1164 PrintArg (ArgType t)
1169 case ArgInt: p = " N"; break;
1170 case ArgString: p = " STR"; break;
1171 case ArgBoolean: p = " TF"; break;
1172 case ArgSettingsFilename:
1173 case ArgFilename: p = " FILE"; break;
1174 case ArgX: p = " Nx"; break;
1175 case ArgY: p = " Ny"; break;
1176 case ArgAttribs: p = " TEXTCOL"; break;
1177 case ArgColor: p = " COL"; break;
1178 case ArgFont: p = " FONT"; break;
1179 case ArgBoardSize: p = " SIZE"; break;
1180 case ArgFloat: p = " FLOAT"; break;
1185 case ArgCommSettings:
1196 ArgDescriptor *q, *p = argDescriptors+5;
1197 printf("\nXBoard accepts the following options:\n"
1198 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
1199 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
1200 " SIZE = board-size spec(s)\n"
1201 " Within parentheses are short forms, or options to set to true or false.\n"
1202 " Persistent options (saved in the settings file) are marked with *)\n\n");
1204 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
1205 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
1206 if(p->save) strcat(buf+len, "*");
1207 for(q=p+1; q->argLoc == p->argLoc; q++) {
1208 if(q->argName[0] == '-') continue;
1209 strcat(buf+len, q == p+1 ? " (" : " ");
1210 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
1212 if(q != p+1) strcat(buf+len, ")");
1214 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
1217 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
1221 main (int argc, char **argv)
1223 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1224 XSetWindowAttributes window_attributes;
1226 Dimension boardWidth, boardHeight, w, h;
1228 int forceMono = False;
1230 srandom(time(0)); // [HGM] book: make random truly random
1232 setbuf(stdout, NULL);
1233 setbuf(stderr, NULL);
1236 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1237 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1241 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1246 programName = strrchr(argv[0], '/');
1247 if (programName == NULL)
1248 programName = argv[0];
1253 XtSetLanguageProc(NULL, NULL, NULL);
1254 if (appData.debugMode) {
1255 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1258 bindtextdomain(PACKAGE, LOCALEDIR);
1259 textdomain(PACKAGE);
1262 appData.boardSize = "";
1263 InitAppData(ConvertToLine(argc, argv));
1265 if (p == NULL) p = "/tmp";
1266 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1267 gameCopyFilename = (char*) malloc(i);
1268 gamePasteFilename = (char*) malloc(i);
1269 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1270 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1272 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1273 static char buf[MSG_SIZ];
1274 EscapeExpand(buf, appData.firstInitString);
1275 appData.firstInitString = strdup(buf);
1276 EscapeExpand(buf, appData.secondInitString);
1277 appData.secondInitString = strdup(buf);
1278 EscapeExpand(buf, appData.firstComputerString);
1279 appData.firstComputerString = strdup(buf);
1280 EscapeExpand(buf, appData.secondComputerString);
1281 appData.secondComputerString = strdup(buf);
1284 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1287 if (chdir(chessDir) != 0) {
1288 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1294 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1295 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1296 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1297 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1300 setbuf(debugFP, NULL);
1303 /* [HGM,HR] make sure board size is acceptable */
1304 if(appData.NrFiles > BOARD_FILES ||
1305 appData.NrRanks > BOARD_RANKS )
1306 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1309 /* This feature does not work; animation needs a rewrite */
1310 appData.highlightDragging = FALSE;
1314 gameInfo.variant = StringToVariant(appData.variant);
1315 InitPosition(FALSE);
1318 XtAppInitialize(&appContext, "XBoard", shellOptions,
1319 XtNumber(shellOptions),
1320 &argc, argv, xboardResources, NULL, 0);
1322 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1323 clientResources, XtNumber(clientResources),
1326 xDisplay = XtDisplay(shellWidget);
1327 xScreen = DefaultScreen(xDisplay);
1328 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1331 * determine size, based on supplied or remembered -size, or screen size
1333 if (isdigit(appData.boardSize[0])) {
1334 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1335 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1336 &fontPxlSize, &smallLayout, &tinyLayout);
1338 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1339 programName, appData.boardSize);
1343 /* Find some defaults; use the nearest known size */
1344 SizeDefaults *szd, *nearest;
1345 int distance = 99999;
1346 nearest = szd = sizeDefaults;
1347 while (szd->name != NULL) {
1348 if (abs(szd->squareSize - squareSize) < distance) {
1350 distance = abs(szd->squareSize - squareSize);
1351 if (distance == 0) break;
1355 if (i < 2) lineGap = nearest->lineGap;
1356 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1357 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1358 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1359 if (i < 6) smallLayout = nearest->smallLayout;
1360 if (i < 7) tinyLayout = nearest->tinyLayout;
1363 SizeDefaults *szd = sizeDefaults;
1364 if (*appData.boardSize == NULLCHAR) {
1365 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1366 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1369 if (szd->name == NULL) szd--;
1370 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1372 while (szd->name != NULL &&
1373 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1374 if (szd->name == NULL) {
1375 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1376 programName, appData.boardSize);
1380 squareSize = szd->squareSize;
1381 lineGap = szd->lineGap;
1382 clockFontPxlSize = szd->clockFontPxlSize;
1383 coordFontPxlSize = szd->coordFontPxlSize;
1384 fontPxlSize = szd->fontPxlSize;
1385 smallLayout = szd->smallLayout;
1386 tinyLayout = szd->tinyLayout;
1387 // [HGM] font: use defaults from settings file if available and not overruled
1390 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1391 if (strlen(appData.pixmapDirectory) > 0) {
1392 p = ExpandPathName(appData.pixmapDirectory);
1394 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1395 appData.pixmapDirectory);
1398 if (appData.debugMode) {
1399 fprintf(stderr, _("\
1400 XBoard square size (hint): %d\n\
1401 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1403 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1404 if (appData.debugMode) {
1405 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1408 defaultLineGap = lineGap;
1409 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1411 /* [HR] height treated separately (hacked) */
1412 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1413 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1416 * Determine what fonts to use.
1418 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1421 * Detect if there are not enough colors available and adapt.
1423 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1424 appData.monoMode = True;
1427 forceMono = MakeColors();
1430 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1432 appData.monoMode = True;
1435 if (appData.monoMode && appData.debugMode) {
1436 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1437 (unsigned long) XWhitePixel(xDisplay, xScreen),
1438 (unsigned long) XBlackPixel(xDisplay, xScreen));
1441 ParseIcsTextColors();
1443 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1449 layoutName = "tinyLayout";
1450 } else if (smallLayout) {
1451 layoutName = "smallLayout";
1453 layoutName = "normalLayout";
1456 optList = BoardPopUp(squareSize, lineGap, (void*)
1462 boardWidget = optList[W_BOARD].handle;
1463 menuBarWidget = optList[W_MENU].handle;
1464 dropMenu = optList[W_DROP].handle;
1465 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1466 formWidget = XtParent(boardWidget);
1467 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1468 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1469 XtGetValues(optList[W_WHITE].handle, args, 2);
1470 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1471 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1472 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1473 XtGetValues(optList[W_PAUSE].handle, args, 2);
1475 AppendEnginesToMenu(appData.recentEngineList);
1477 xBoardWindow = XtWindow(boardWidget);
1479 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1480 // not need to go into InitDrawingSizes().
1483 * Create X checkmark bitmap and initialize option menu checks.
1485 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1486 checkmark_bits, checkmark_width, checkmark_height);
1492 ReadBitmap(&wIconPixmap, "icon_white.bm",
1493 icon_white_bits, icon_white_width, icon_white_height);
1494 ReadBitmap(&bIconPixmap, "icon_black.bm",
1495 icon_black_bits, icon_black_width, icon_black_height);
1496 iconPixmap = wIconPixmap;
1498 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1499 XtSetValues(shellWidget, args, i);
1502 * Create a cursor for the board widget.
1504 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1505 XChangeWindowAttributes(xDisplay, xBoardWindow,
1506 CWCursor, &window_attributes);
1509 * Inhibit shell resizing.
1511 shellArgs[0].value = (XtArgVal) &w;
1512 shellArgs[1].value = (XtArgVal) &h;
1513 XtGetValues(shellWidget, shellArgs, 2);
1514 shellArgs[4].value = shellArgs[2].value = w;
1515 shellArgs[5].value = shellArgs[3].value = h;
1516 XtSetValues(shellWidget, &shellArgs[2], 4);
1517 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1518 marginH = h - boardHeight;
1520 CatchDeleteWindow(shellWidget, "QuitProc");
1526 if(appData.logoSize)
1527 { // locate and read user logo
1529 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1530 ASSIGN(userLogo, buf);
1533 if (appData.animate || appData.animateDragging)
1536 XtAugmentTranslations(formWidget,
1537 XtParseTranslationTable(globalTranslations));
1539 XtAddEventHandler(formWidget, KeyPressMask, False,
1540 (XtEventHandler) MoveTypeInProc, NULL);
1541 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1542 (XtEventHandler) EventProc, NULL);
1544 /* [AS] Restore layout */
1545 if( wpMoveHistory.visible ) {
1549 if( wpEvalGraph.visible )
1554 if( wpEngineOutput.visible ) {
1555 EngineOutputPopUp();
1560 if (errorExitStatus == -1) {
1561 if (appData.icsActive) {
1562 /* We now wait until we see "login:" from the ICS before
1563 sending the logon script (problems with timestamp otherwise) */
1564 /*ICSInitScript();*/
1565 if (appData.icsInputBox) ICSInputBoxPopUp();
1569 signal(SIGWINCH, TermSizeSigHandler);
1571 signal(SIGINT, IntSigHandler);
1572 signal(SIGTERM, IntSigHandler);
1573 if (*appData.cmailGameName != NULLCHAR) {
1574 signal(SIGUSR1, CmailSigHandler);
1578 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1581 // XtSetKeyboardFocus(shellWidget, formWidget);
1582 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1584 XtAppMainLoop(appContext);
1585 if (appData.debugMode) fclose(debugFP); // [DM] debug
1590 TermSizeSigHandler (int sig)
1596 IntSigHandler (int sig)
1602 CmailSigHandler (int sig)
1607 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1609 /* Activate call-back function CmailSigHandlerCallBack() */
1610 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1612 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1616 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1619 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1621 /**** end signal code ****/
1624 #define Abs(n) ((n)<0 ? -(n) : (n))
1628 InsertPxlSize (char *pattern, int targetPxlSize)
1630 char *base_fnt_lst, strInt[12], *p, *q;
1631 int alternatives, i, len, strIntLen;
1634 * Replace the "*" (if present) in the pixel-size slot of each
1635 * alternative with the targetPxlSize.
1639 while ((p = strchr(p, ',')) != NULL) {
1643 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1644 strIntLen = strlen(strInt);
1645 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1649 while (alternatives--) {
1650 char *comma = strchr(p, ',');
1651 for (i=0; i<14; i++) {
1652 char *hyphen = strchr(p, '-');
1654 if (comma && hyphen > comma) break;
1655 len = hyphen + 1 - p;
1656 if (i == 7 && *p == '*' && len == 2) {
1658 memcpy(q, strInt, strIntLen);
1668 len = comma + 1 - p;
1675 return base_fnt_lst;
1679 CreateFontSet (char *base_fnt_lst)
1682 char **missing_list;
1686 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1687 &missing_list, &missing_count, &def_string);
1688 if (appData.debugMode) {
1690 XFontStruct **font_struct_list;
1691 char **font_name_list;
1692 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1694 fprintf(debugFP, " got list %s, locale %s\n",
1695 XBaseFontNameListOfFontSet(fntSet),
1696 XLocaleOfFontSet(fntSet));
1697 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1698 for (i = 0; i < count; i++) {
1699 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1702 for (i = 0; i < missing_count; i++) {
1703 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1706 if (fntSet == NULL) {
1707 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1712 #else // not ENABLE_NLS
1714 * Find a font that matches "pattern" that is as close as
1715 * possible to the targetPxlSize. Prefer fonts that are k
1716 * pixels smaller to fonts that are k pixels larger. The
1717 * pattern must be in the X Consortium standard format,
1718 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1719 * The return value should be freed with XtFree when no
1723 FindFont (char *pattern, int targetPxlSize)
1725 char **fonts, *p, *best, *scalable, *scalableTail;
1726 int i, j, nfonts, minerr, err, pxlSize;
1728 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1730 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1731 programName, pattern);
1738 for (i=0; i<nfonts; i++) {
1741 if (*p != '-') continue;
1743 if (*p == NULLCHAR) break;
1744 if (*p++ == '-') j++;
1746 if (j < 7) continue;
1749 scalable = fonts[i];
1752 err = pxlSize - targetPxlSize;
1753 if (Abs(err) < Abs(minerr) ||
1754 (minerr > 0 && err < 0 && -err == minerr)) {
1760 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1761 /* If the error is too big and there is a scalable font,
1762 use the scalable font. */
1763 int headlen = scalableTail - scalable;
1764 p = (char *) XtMalloc(strlen(scalable) + 10);
1765 while (isdigit(*scalableTail)) scalableTail++;
1766 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1768 p = (char *) XtMalloc(strlen(best) + 2);
1769 safeStrCpy(p, best, strlen(best)+1 );
1771 if (appData.debugMode) {
1772 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1773 pattern, targetPxlSize, p);
1775 XFreeFontNames(fonts);
1782 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
1783 // must be called before all non-first callse to CreateGCs()
1784 XtReleaseGC(shellWidget, highlineGC);
1785 XtReleaseGC(shellWidget, lightSquareGC);
1786 XtReleaseGC(shellWidget, darkSquareGC);
1787 XtReleaseGC(shellWidget, lineGC);
1788 if (appData.monoMode) {
1789 if (DefaultDepth(xDisplay, xScreen) == 1) {
1790 XtReleaseGC(shellWidget, wbPieceGC);
1792 XtReleaseGC(shellWidget, bwPieceGC);
1795 XtReleaseGC(shellWidget, prelineGC);
1796 XtReleaseGC(shellWidget, wdPieceGC);
1797 XtReleaseGC(shellWidget, wlPieceGC);
1798 XtReleaseGC(shellWidget, bdPieceGC);
1799 XtReleaseGC(shellWidget, blPieceGC);
1804 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
1806 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1807 | GCBackground | GCFunction | GCPlaneMask;
1808 gc_values->foreground = foreground;
1809 gc_values->background = background;
1810 return XtGetGC(shellWidget, value_mask, gc_values);
1814 CreateGCs (int redo)
1816 XGCValues gc_values;
1818 Pixel white = XWhitePixel(xDisplay, xScreen);
1819 Pixel black = XBlackPixel(xDisplay, xScreen);
1821 gc_values.plane_mask = AllPlanes;
1822 gc_values.line_width = lineGap;
1823 gc_values.line_style = LineSolid;
1824 gc_values.function = GXcopy;
1827 DeleteGCs(); // called a second time; clean up old GCs first
1828 } else { // [HGM] grid and font GCs created on first call only
1829 coordGC = CreateOneGC(&gc_values, black, white);
1830 XSetFont(xDisplay, coordGC, coordFontID);
1832 // [HGM] make font for holdings counts (white on black)
1833 countGC = CreateOneGC(&gc_values, white, black);
1834 XSetFont(xDisplay, countGC, countFontID);
1836 lineGC = CreateOneGC(&gc_values, black, black);
1838 if (appData.monoMode) {
1840 highlineGC = CreateOneGC(&gc_values, white, white);
1841 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
1842 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
1844 if (DefaultDepth(xDisplay, xScreen) == 1) {
1845 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
1846 gc_values.function = GXcopyInverted;
1847 copyInvertedGC = CreateOneGC(&gc_values, black, white);
1848 gc_values.function = GXcopy;
1849 if (XBlackPixel(xDisplay, xScreen) == 1) {
1850 bwPieceGC = darkSquareGC;
1851 wbPieceGC = copyInvertedGC;
1853 bwPieceGC = copyInvertedGC;
1854 wbPieceGC = lightSquareGC;
1859 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
1860 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
1861 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
1862 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
1863 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
1864 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
1865 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
1866 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
1871 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
1879 fp = fopen(filename, "rb");
1881 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
1888 for (y=0; y<h; ++y) {
1889 for (x=0; x<h; ++x) {
1894 XPutPixel(xim, x, y, blackPieceColor);
1896 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1899 XPutPixel(xim, x, y, darkSquareColor);
1901 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1904 XPutPixel(xim, x, y, whitePieceColor);
1906 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1909 XPutPixel(xim, x, y, lightSquareColor);
1911 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1919 /* create Pixmap of piece */
1920 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1922 XPutImage(xDisplay, *dest, lightSquareGC, xim,
1925 /* create Pixmap of clipmask
1926 Note: We assume the white/black pieces have the same
1927 outline, so we make only 6 masks. This is okay
1928 since the XPM clipmask routines do the same. */
1930 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1932 XPutImage(xDisplay, temp, lightSquareGC, xmask,
1935 /* now create the 1-bit version */
1936 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1939 values.foreground = 1;
1940 values.background = 0;
1942 /* Don't use XtGetGC, not read only */
1943 maskGC = XCreateGC(xDisplay, *mask,
1944 GCForeground | GCBackground, &values);
1945 XCopyPlane(xDisplay, temp, *mask, maskGC,
1946 0, 0, squareSize, squareSize, 0, 0, 1);
1947 XFreePixmap(xDisplay, temp);
1952 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
1960 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
1965 /* The XSynchronize calls were copied from CreatePieces.
1966 Not sure if needed, but can't hurt */
1967 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
1970 /* temp needed by loadXIM() */
1971 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1972 0, 0, ss, ss, AllPlanes, XYPixmap);
1974 if (strlen(appData.pixmapDirectory) == 0) {
1978 if (appData.monoMode) {
1979 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
1983 fprintf(stderr, _("\nLoading XIMs...\n"));
1985 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
1986 fprintf(stderr, "%d", piece+1);
1987 for (kind=0; kind<4; kind++) {
1988 fprintf(stderr, ".");
1989 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
1990 ExpandPathName(appData.pixmapDirectory),
1991 piece <= (int) WhiteKing ? "" : "w",
1992 pieceBitmapNames[piece],
1994 ximPieceBitmap[kind][piece] =
1995 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1996 0, 0, ss, ss, AllPlanes, XYPixmap);
1997 if (appData.debugMode)
1998 fprintf(stderr, _("(File:%s:) "), buf);
1999 loadXIM(ximPieceBitmap[kind][piece],
2001 &(xpmPieceBitmap2[kind][piece]),
2002 &(ximMaskPm2[piece]));
2003 if(piece <= (int)WhiteKing)
2004 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2006 fprintf(stderr," ");
2008 /* Load light and dark squares */
2009 /* If the LSQ and DSQ pieces don't exist, we will
2010 draw them with solid squares. */
2011 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2012 if (access(buf, 0) != 0) {
2016 fprintf(stderr, _("light square "));
2018 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2019 0, 0, ss, ss, AllPlanes, XYPixmap);
2020 if (appData.debugMode)
2021 fprintf(stderr, _("(File:%s:) "), buf);
2023 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2024 fprintf(stderr, _("dark square "));
2025 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2026 ExpandPathName(appData.pixmapDirectory), ss);
2027 if (appData.debugMode)
2028 fprintf(stderr, _("(File:%s:) "), buf);
2030 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2031 0, 0, ss, ss, AllPlanes, XYPixmap);
2032 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2033 xpmJailSquare = xpmLightSquare;
2035 fprintf(stderr, _("Done.\n"));
2037 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2040 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2044 CreateXPMBoard (char *s, int kind)
2048 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2049 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2050 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2056 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2057 // thisroutine has to be called t free the old piece pixmaps
2059 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2060 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2062 XFreePixmap(xDisplay, xpmLightSquare);
2063 XFreePixmap(xDisplay, xpmDarkSquare);
2072 u_int ss = squareSize;
2074 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2075 XpmColorSymbol symbols[4];
2076 static int redo = False;
2078 if(redo) FreeXPMPieces(); else redo = 1;
2080 /* The XSynchronize calls were copied from CreatePieces.
2081 Not sure if needed, but can't hurt */
2082 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2084 /* Setup translations so piece colors match square colors */
2085 symbols[0].name = "light_piece";
2086 symbols[0].value = appData.whitePieceColor;
2087 symbols[1].name = "dark_piece";
2088 symbols[1].value = appData.blackPieceColor;
2089 symbols[2].name = "light_square";
2090 symbols[2].value = appData.lightSquareColor;
2091 symbols[3].name = "dark_square";
2092 symbols[3].value = appData.darkSquareColor;
2094 attr.valuemask = XpmColorSymbols;
2095 attr.colorsymbols = symbols;
2096 attr.numsymbols = 4;
2098 if (appData.monoMode) {
2099 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2103 if (strlen(appData.pixmapDirectory) == 0) {
2104 XpmPieces* pieces = builtInXpms;
2107 while (pieces->size != squareSize && pieces->size) pieces++;
2108 if (!pieces->size) {
2109 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2112 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2113 for (kind=0; kind<4; kind++) {
2115 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2116 pieces->xpm[piece][kind],
2117 &(xpmPieceBitmap2[kind][piece]),
2118 NULL, &attr)) != 0) {
2119 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2123 if(piece <= (int) WhiteKing)
2124 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2128 xpmJailSquare = xpmLightSquare;
2132 fprintf(stderr, _("\nLoading XPMs...\n"));
2135 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2136 fprintf(stderr, "%d ", piece+1);
2137 for (kind=0; kind<4; kind++) {
2138 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2139 ExpandPathName(appData.pixmapDirectory),
2140 piece > (int) WhiteKing ? "w" : "",
2141 pieceBitmapNames[piece],
2143 if (appData.debugMode) {
2144 fprintf(stderr, _("(File:%s:) "), buf);
2146 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2147 &(xpmPieceBitmap2[kind][piece]),
2148 NULL, &attr)) != 0) {
2149 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2150 // [HGM] missing: read of unorthodox piece failed; substitute King.
2151 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2152 ExpandPathName(appData.pixmapDirectory),
2154 if (appData.debugMode) {
2155 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2157 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2158 &(xpmPieceBitmap2[kind][piece]),
2162 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2167 if(piece <= (int) WhiteKing)
2168 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2171 /* Load light and dark squares */
2172 /* If the LSQ and DSQ pieces don't exist, we will
2173 draw them with solid squares. */
2174 fprintf(stderr, _("light square "));
2175 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2176 if (access(buf, 0) != 0) {
2180 if (appData.debugMode)
2181 fprintf(stderr, _("(File:%s:) "), buf);
2183 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2184 &xpmLightSquare, NULL, &attr)) != 0) {
2185 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2188 fprintf(stderr, _("dark square "));
2189 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2190 ExpandPathName(appData.pixmapDirectory), ss);
2191 if (appData.debugMode) {
2192 fprintf(stderr, _("(File:%s:) "), buf);
2194 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2195 &xpmDarkSquare, NULL, &attr)) != 0) {
2196 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2200 xpmJailSquare = xpmLightSquare;
2201 fprintf(stderr, _("Done.\n"));
2203 oldVariant = -1; // kludge to force re-makig of animation masks
2204 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2207 #endif /* HAVE_LIBXPM */
2210 /* No built-in bitmaps */
2215 u_int ss = squareSize;
2217 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2220 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2221 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2222 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2223 pieceBitmapNames[piece],
2224 ss, kind == SOLID ? 's' : 'o');
2225 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2226 if(piece <= (int)WhiteKing)
2227 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2231 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2235 /* With built-in bitmaps */
2239 BuiltInBits* bib = builtInBits;
2242 u_int ss = squareSize;
2244 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2247 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2249 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2250 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2251 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2252 pieceBitmapNames[piece],
2253 ss, kind == SOLID ? 's' : 'o');
2254 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2255 bib->bits[kind][piece], ss, ss);
2256 if(piece <= (int)WhiteKing)
2257 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2261 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2267 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2272 char msg[MSG_SIZ], fullname[MSG_SIZ];
2274 if (*appData.bitmapDirectory != NULLCHAR) {
2275 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2276 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2277 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2278 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2279 &w, &h, pm, &x_hot, &y_hot);
2280 fprintf(stderr, "load %s\n", name);
2281 if (errcode != BitmapSuccess) {
2283 case BitmapOpenFailed:
2284 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2286 case BitmapFileInvalid:
2287 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2289 case BitmapNoMemory:
2290 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2294 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2298 fprintf(stderr, _("%s: %s...using built-in\n"),
2300 } else if (w != wreq || h != hreq) {
2302 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2303 programName, fullname, w, h, wreq, hreq);
2309 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2319 if (lineGap == 0) return;
2321 /* [HR] Split this into 2 loops for non-square boards. */
2323 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2324 gridSegments[i].x1 = 0;
2325 gridSegments[i].x2 =
2326 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2327 gridSegments[i].y1 = gridSegments[i].y2
2328 = lineGap / 2 + (i * (squareSize + lineGap));
2331 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2332 gridSegments[j + i].y1 = 0;
2333 gridSegments[j + i].y2 =
2334 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2335 gridSegments[j + i].x1 = gridSegments[j + i].x2
2336 = lineGap / 2 + (j * (squareSize + lineGap));
2341 MarkMenuItem (char *menuRef, int state)
2343 MenuItem *item = MenuNameToItem(menuRef);
2347 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2348 XtSetValues(item->handle, args, 1);
2353 EnableNamedMenuItem (char *menuRef, int state)
2355 MenuItem *item = MenuNameToItem(menuRef);
2357 if(item) XtSetSensitive(item->handle, state);
2361 EnableButtonBar (int state)
2363 XtSetSensitive(optList[W_BUTTON].handle, state);
2368 SetMenuEnables (Enables *enab)
2370 while (enab->name != NULL) {
2371 EnableNamedMenuItem(enab->name, enab->value);
2377 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2378 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2380 if(*nprms == 0) return;
2381 item = MenuNameToItem(prms[0]);
2382 if(item) ((MenuProc *) item->proc) ();
2386 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2388 RecentEngineEvent((int) (intptr_t) addr);
2392 AppendMenuItem (char *msg, int n)
2394 CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
2406 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2407 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2408 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2409 dmEnables[i].piece);
2410 XtSetSensitive(entry, p != NULL || !appData.testLegality
2411 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2412 && !appData.icsActive));
2414 while (p && *p++ == dmEnables[i].piece) count++;
2415 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2417 XtSetArg(args[j], XtNlabel, label); j++;
2418 XtSetValues(entry, args, j);
2424 do_flash_delay (unsigned long msec)
2429 static cairo_surface_t *cs; // to keep out of back-end :-(
2432 DrawBorder (int x, int y, int type)
2437 cr = cairo_create(cs);
2438 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
2439 cairo_rectangle(cr, x, y, squareSize+lineGap, squareSize+lineGap);
2440 SetPen(cr, lineGap, type == 1 ? appData.highlightSquareColor : appData.premoveHighlightColor, 0);
2447 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
2449 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
2450 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
2452 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
2453 if(textureW[kind] < W*squareSize)
2454 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
2456 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
2457 if(textureH[kind] < H*squareSize)
2458 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
2460 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
2465 DrawLogo (void *handle, void *logo)
2467 cairo_surface_t *img, *cs;
2471 if(!logo || !handle) return;
2472 cs = cairo_xlib_surface_create(xDisplay, XtWindow(handle), DefaultVisual(xDisplay, 0), appData.logoSize, appData.logoSize/2);
2473 img = cairo_image_surface_create_from_png (logo);
2474 w = cairo_image_surface_get_width (img);
2475 h = cairo_image_surface_get_height (img);
2476 cr = cairo_create(cs);
2477 cairo_scale(cr, (float)appData.logoSize/w, appData.logoSize/(2.*h));
2478 cairo_set_source_surface (cr, img, 0, 0);
2481 cairo_surface_destroy (img);
2482 cairo_surface_destroy (cs);
2486 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
2487 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
2489 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
2490 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
2491 squareSize, squareSize, x*fac, y*fac);
2493 if (useImages && useImageSqs) {
2497 pm = xpmLightSquare;
2502 case 2: /* neutral */
2504 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
2507 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
2508 squareSize, squareSize, x*fac, y*fac);
2518 case 2: /* neutral */
2523 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
2528 I split out the routines to draw a piece so that I could
2529 make a generic flash routine.
2532 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2534 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2535 switch (square_color) {
2537 case 2: /* neutral */
2539 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2540 ? *pieceToOutline(piece)
2541 : *pieceToSolid(piece),
2542 dest, bwPieceGC, 0, 0,
2543 squareSize, squareSize, x, y);
2546 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2547 ? *pieceToSolid(piece)
2548 : *pieceToOutline(piece),
2549 dest, wbPieceGC, 0, 0,
2550 squareSize, squareSize, x, y);
2556 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2558 switch (square_color) {
2560 case 2: /* neutral */
2562 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2563 ? *pieceToOutline(piece)
2564 : *pieceToSolid(piece),
2565 dest, bwPieceGC, 0, 0,
2566 squareSize, squareSize, x, y, 1);
2569 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2570 ? *pieceToSolid(piece)
2571 : *pieceToOutline(piece),
2572 dest, wbPieceGC, 0, 0,
2573 squareSize, squareSize, x, y, 1);
2579 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2581 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
2582 switch (square_color) {
2584 XCopyPlane(xDisplay, *pieceToSolid(piece),
2585 dest, (int) piece < (int) BlackPawn
2586 ? wlPieceGC : blPieceGC, 0, 0,
2587 squareSize, squareSize, x, y, 1);
2590 XCopyPlane(xDisplay, *pieceToSolid(piece),
2591 dest, (int) piece < (int) BlackPawn
2592 ? wdPieceGC : bdPieceGC, 0, 0,
2593 squareSize, squareSize, x, y, 1);
2595 case 2: /* neutral */
2597 break; // should never contain pieces
2602 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2604 int kind, p = piece;
2606 switch (square_color) {
2608 case 2: /* neutral */
2610 if ((int)piece < (int) BlackPawn) {
2618 if ((int)piece < (int) BlackPawn) {
2626 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2627 if(useTexture & square_color+1) {
2628 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2629 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
2630 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
2631 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
2632 XSetClipMask(xDisplay, wlPieceGC, None);
2633 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
2635 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
2636 dest, wlPieceGC, 0, 0,
2637 squareSize, squareSize, x, y);
2640 typedef void (*DrawFunc)();
2645 if (appData.monoMode) {
2646 if (DefaultDepth(xDisplay, xScreen) == 1) {
2647 return monoDrawPiece_1bit;
2649 return monoDrawPiece;
2653 return colorDrawPieceImage;
2655 return colorDrawPiece;
2660 DrawDot (int marker, int x, int y, int r)
2664 cr = cairo_create(cs);
2665 cairo_arc(cr, x+r/2, y+r/2, r/2, 0.0, 2*M_PI);
2666 if(appData.monoMode) {
2667 SetPen(cr, 2, marker == 2 ? "#000000" : "#FFFFFF", 0);
2668 cairo_stroke_preserve(cr);
2669 SetPen(cr, 2, marker == 2 ? "#FFFFFF" : "#000000", 0);
2671 SetPen(cr, 2, marker == 2 ? "#FF0000" : "#FFFF00", 0);
2681 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2682 { // basic front-end board-draw function: takes care of everything that can be in square:
2683 // piece, background, coordinate/count, marker dot
2684 int direction, font_ascent, font_descent;
2685 XCharStruct overall;
2688 if (piece == EmptySquare) {
2689 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2691 drawfunc = ChooseDrawFunc();
2692 drawfunc(piece, square_color, x, y, xBoardWindow);
2695 if(align) { // square carries inscription (coord or piece count)
2697 GC hGC = align < 3 ? coordGC : countGC;
2698 // first calculate where it goes
2699 XTextExtents(countFontStruct, string, 1, &direction,
2700 &font_ascent, &font_descent, &overall);
2702 xx += squareSize - overall.width - 2;
2703 yy += squareSize - font_descent - 1;
2704 } else if (align == 2) {
2705 xx += 2, yy += font_ascent + 1;
2706 } else if (align == 3) {
2707 xx += squareSize - overall.width - 2;
2708 yy += font_ascent + 1;
2709 } else if (align == 4) {
2710 xx += 2, yy += font_ascent + 1;
2713 if (appData.monoMode) {
2714 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2716 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2720 if(marker) { // print fat marker dot, if requested
2721 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2726 FlashDelay (int flash_delay)
2728 XSync(xDisplay, False);
2729 if(flash_delay) do_flash_delay(flash_delay);
2733 Fraction (int x, int start, int stop)
2735 double f = ((double) x - start)/(stop - start);
2736 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2740 static WindowPlacement wpNew;
2743 CoDrag (Widget sh, WindowPlacement *wp)
2746 int j=0, touch=0, fudge = 2;
2747 GetActualPlacement(sh, wp);
2748 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
2749 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
2750 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2751 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
2752 if(!touch ) return; // only windows that touch co-move
2753 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2754 int heightInc = wpNew.height - wpMain.height;
2755 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2756 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2757 wp->y += fracTop * heightInc;
2758 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2759 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2760 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2761 int widthInc = wpNew.width - wpMain.width;
2762 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2763 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2764 wp->y += fracLeft * widthInc;
2765 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2766 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2768 wp->x += wpNew.x - wpMain.x;
2769 wp->y += wpNew.y - wpMain.y;
2770 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2771 if(touch == 3) wp->y += wpNew.height - wpMain.height;
2772 XtSetArg(args[j], XtNx, wp->x); j++;
2773 XtSetArg(args[j], XtNy, wp->y); j++;
2774 XtSetValues(sh, args, j);
2777 static XtIntervalId delayedDragID = 0;
2782 GetActualPlacement(shellWidget, &wpNew);
2783 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
2784 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
2785 return; // false alarm
2786 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
2787 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
2788 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
2789 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
2791 DrawPosition(True, NULL);
2792 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
2799 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
2801 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
2805 EventProc (Widget widget, caddr_t unused, XEvent *event)
2807 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
2808 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
2811 // [HGM] seekgraph: some low-level drawing routines (by JC, mostly)
2814 Color (char *col, int n)
2817 sscanf(col, "#%x", &c);
2823 SetPen (cairo_t *cr, float w, char *col, int dash)
2825 static const double dotted[] = {4.0, 4.0};
2826 static int len = sizeof(dotted) / sizeof(dotted[0]);
2827 cairo_set_line_width (cr, w);
2828 cairo_set_source_rgba (cr, Color(col, 4), Color(col, 2), Color(col, 0), 1.0);
2829 if(dash) cairo_set_dash (cr, dotted, len, 0.0);
2832 void DrawSeekAxis( int x, int y, int xTo, int yTo )
2837 cr = cairo_create (cs);
2839 cairo_move_to (cr, x, y);
2840 cairo_line_to(cr, xTo, yTo );
2842 SetPen(cr, 2, "#000000", 0);
2849 void DrawSeekBackground( int left, int top, int right, int bottom )
2851 cairo_t *cr = cairo_create (cs);
2853 cairo_rectangle (cr, left, top, right-left, bottom-top);
2855 cairo_set_source_rgba(cr, 0.8, 0.8, 0.4,1.0);
2862 void DrawSeekText(char *buf, int x, int y)
2864 cairo_t *cr = cairo_create (cs);
2866 cairo_select_font_face (cr, "Sans",
2867 CAIRO_FONT_SLANT_NORMAL,
2868 CAIRO_FONT_WEIGHT_NORMAL);
2870 cairo_set_font_size (cr, 12.0);
2872 cairo_move_to (cr, x, y+4);
2873 cairo_show_text( cr, buf);
2875 cairo_set_source_rgba(cr, 0, 0, 0,1.0);
2882 void DrawSeekDot(int x, int y, int colorNr)
2884 cairo_t *cr = cairo_create (cs);
2885 int square = colorNr & 0x80;
2889 cairo_rectangle (cr, x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
2891 cairo_arc(cr, x, y, squareSize/8, 0.0, 2*M_PI);
2893 SetPen(cr, 2, "#000000", 0);
2894 cairo_stroke_preserve(cr);
2896 case 0: cairo_set_source_rgba(cr, 1.0, 0, 0,1.0); break;
2897 case 1: cairo_set_source_rgba (cr, 0.0, 0.7, 0.2, 1.0); break;
2898 default: cairo_set_source_rgba (cr, 1.0, 1.0, 0.0, 1.0); break;
2909 int boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2910 int boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2911 cs = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), boardWidth, boardHeight);
2917 cairo_surface_destroy(cs);
2923 /* draws a grid starting around Nx, Ny squares starting at x,y */
2929 cr = cairo_create (cs);
2931 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
2932 SetPen(cr, lineGap, "#000000", 0);
2935 for (i = 0; i < BOARD_WIDTH + BOARD_HEIGHT + 2; i++)
2937 cairo_move_to (cr, gridSegments[i].x1, gridSegments[i].y1);
2938 cairo_line_to (cr, gridSegments[i].x2, gridSegments[i].y2);
2950 * event handler for redrawing the board
2953 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2955 DrawPosition(True, NULL);
2960 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
2961 { // [HGM] pv: walk PV
2962 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
2965 static int savedIndex; /* gross that this is global */
2968 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
2971 XawTextPosition index, dummy;
2974 XawTextGetSelectionPos(w, &index, &dummy);
2975 XtSetArg(arg, XtNstring, &val);
2976 XtGetValues(w, &arg, 1);
2977 ReplaceComment(savedIndex, val);
2978 if(savedIndex != currentMove) ToNrEvent(savedIndex);
2979 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
2983 EditCommentPopUp (int index, char *title, char *text)
2986 if (text == NULL) text = "";
2987 NewCommentPopup(title, text, index);
2991 CommentPopUp (char *title, char *text)
2993 savedIndex = currentMove; // [HGM] vari
2994 NewCommentPopup(title, text, currentMove);
3000 PopDown(CommentDlg);
3004 /* Disable all user input other than deleting the window */
3005 static int frozen = 0;
3011 /* Grab by a widget that doesn't accept input */
3012 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
3016 /* Undo a FreezeUI */
3020 if (!frozen) return;
3021 XtRemoveGrab(optList[W_MESSG].handle);
3029 static int oldPausing = FALSE;
3030 static GameMode oldmode = (GameMode) -1;
3033 if (!boardWidget || !XtIsRealized(boardWidget)) return;
3035 if (pausing != oldPausing) {
3036 oldPausing = pausing;
3037 MarkMenuItem("Mode.Pause", pausing);
3039 if (appData.showButtonBar) {
3040 /* Always toggle, don't set. Previous code messes up when
3041 invoked while the button is pressed, as releasing it
3042 toggles the state again. */
3045 XtSetArg(args[0], XtNbackground, &oldbg);
3046 XtSetArg(args[1], XtNforeground, &oldfg);
3047 XtGetValues(optList[W_PAUSE].handle,
3049 XtSetArg(args[0], XtNbackground, oldfg);
3050 XtSetArg(args[1], XtNforeground, oldbg);
3052 XtSetValues(optList[W_PAUSE].handle, args, 2);
3056 wname = ModeToWidgetName(oldmode);
3057 if (wname != NULL) {
3058 MarkMenuItem(wname, False);
3060 wname = ModeToWidgetName(gameMode);
3061 if (wname != NULL) {
3062 MarkMenuItem(wname, True);
3065 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
3067 /* Maybe all the enables should be handled here, not just this one */
3068 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
3070 DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);
3075 * Button/menu procedures
3078 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3079 char *selected_fen_position=NULL;
3082 SendPositionSelection (Widget w, Atom *selection, Atom *target,
3083 Atom *type_return, XtPointer *value_return,
3084 unsigned long *length_return, int *format_return)
3086 char *selection_tmp;
3088 // if (!selected_fen_position) return False; /* should never happen */
3089 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3090 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
3091 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
3094 if (f == NULL) return False;
3098 selection_tmp = XtMalloc(len + 1);
3099 count = fread(selection_tmp, 1, len, f);
3102 XtFree(selection_tmp);
3105 selection_tmp[len] = NULLCHAR;
3107 /* note: since no XtSelectionDoneProc was registered, Xt will
3108 * automatically call XtFree on the value returned. So have to
3109 * make a copy of it allocated with XtMalloc */
3110 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3111 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
3114 *value_return=selection_tmp;
3115 *length_return=strlen(selection_tmp);
3116 *type_return=*target;
3117 *format_return = 8; /* bits per byte */
3119 } else if (*target == XA_TARGETS(xDisplay)) {
3120 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3121 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3122 targets_tmp[1] = XA_STRING;
3123 *value_return = targets_tmp;
3124 *type_return = XA_ATOM;
3127 // This code leads to a read of value_return out of bounds on 64-bit systems.
3128 // Other code which I have seen always sets *format_return to 32 independent of
3129 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
3130 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
3131 *format_return = 8 * sizeof(Atom);
3132 if (*format_return > 32) {
3133 *length_return *= *format_return / 32;
3134 *format_return = 32;
3137 *format_return = 32;
3145 /* note: when called from menu all parameters are NULL, so no clue what the
3146 * Widget which was clicked on was, or what the click event was
3149 CopySomething (char *src)
3151 selected_fen_position = src;
3153 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3154 * have a notion of a position that is selected but not copied.
3155 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3157 XtOwnSelection(menuBarWidget, XA_PRIMARY,
3159 SendPositionSelection,
3160 NULL/* lose_ownership_proc */ ,
3161 NULL/* transfer_done_proc */);
3162 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3164 SendPositionSelection,
3165 NULL/* lose_ownership_proc */ ,
3166 NULL/* transfer_done_proc */);
3169 /* function called when the data to Paste is ready */
3171 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3172 Atom *type, XtPointer value, unsigned long *len, int *format)
3175 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3176 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3177 EditPositionPasteFEN(fenstr);
3181 /* called when Paste Position button is pressed,
3182 * all parameters will be NULL */
3184 PastePositionProc ()
3186 XtGetSelectionValue(menuBarWidget,
3187 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3188 /* (XtSelectionCallbackProc) */ PastePositionCB,
3189 NULL, /* client_data passed to PastePositionCB */
3191 /* better to use the time field from the event that triggered the
3192 * call to this function, but that isn't trivial to get
3199 /* note: when called from menu all parameters are NULL, so no clue what the
3200 * Widget which was clicked on was, or what the click event was
3202 /* function called when the data to Paste is ready */
3204 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3205 Atom *type, XtPointer value, unsigned long *len, int *format)
3208 if (value == NULL || *len == 0) {
3209 return; /* nothing had been selected to copy */
3211 f = fopen(gamePasteFilename, "w");
3213 DisplayError(_("Can't open temp file"), errno);
3216 fwrite(value, 1, *len, f);
3219 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3222 /* called when Paste Game button is pressed,
3223 * all parameters will be NULL */
3227 XtGetSelectionValue(menuBarWidget,
3228 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3229 /* (XtSelectionCallbackProc) */ PasteGameCB,
3230 NULL, /* client_data passed to PasteGameCB */
3232 /* better to use the time field from the event that triggered the
3233 * call to this function, but that isn't trivial to get
3242 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3249 { // bassic primitive for determining if modifier keys are pressed
3250 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3253 XQueryKeymap(xDisplay,keys);
3254 for(i=0; i<6; i++) {
3256 j = XKeysymToKeycode(xDisplay, codes[i]);
3257 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3263 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3267 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3268 if ( n == 1 && *buf >= 32 // printable
3269 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3270 ) BoxAutoPopUp (buf);
3274 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3275 { // [HGM] input: let up-arrow recall previous line from history
3280 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3281 { // [HGM] input: let down-arrow recall next line from history
3286 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3292 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3294 if (!TempBackwardActive) {
3295 TempBackwardActive = True;
3301 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3303 /* Check to see if triggered by a key release event for a repeating key.
3304 * If so the next queued event will be a key press of the same key at the same time */
3305 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3307 XPeekEvent(xDisplay, &next);
3308 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3309 next.xkey.keycode == event->xkey.keycode)
3313 TempBackwardActive = False;
3317 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3318 { // called as key binding
3321 if (nprms && *nprms > 0)
3325 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3331 { // called from menu
3332 ManInner(NULL, NULL, NULL, NULL);
3336 SetWindowTitle (char *text, char *title, char *icon)
3340 if (appData.titleInWindow) {
3342 XtSetArg(args[i], XtNlabel, text); i++;
3343 XtSetValues(titleWidget, args, i);
3346 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3347 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3348 XtSetValues(shellWidget, args, i);
3349 XSync(xDisplay, False);
3354 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3360 DisplayIcsInteractionTitle (String message)
3362 if (oldICSInteractionTitle == NULL) {
3363 /* Magic to find the old window title, adapted from vim */
3364 char *wina = getenv("WINDOWID");
3366 Window win = (Window) atoi(wina);
3367 Window root, parent, *children;
3368 unsigned int nchildren;
3369 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3371 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3372 if (!XQueryTree(xDisplay, win, &root, &parent,
3373 &children, &nchildren)) break;
3374 if (children) XFree((void *)children);
3375 if (parent == root || parent == 0) break;
3378 XSetErrorHandler(oldHandler);
3380 if (oldICSInteractionTitle == NULL) {
3381 oldICSInteractionTitle = "xterm";
3384 printf("\033]0;%s\007", message);
3389 XtIntervalId delayedEventTimerXID = 0;
3390 DelayedEventCallback delayedEventCallback = 0;
3395 delayedEventTimerXID = 0;
3396 delayedEventCallback();
3400 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3402 if(delayedEventTimerXID && delayedEventCallback == cb)
3403 // [HGM] alive: replace, rather than add or flush identical event
3404 XtRemoveTimeOut(delayedEventTimerXID);
3405 delayedEventCallback = cb;
3406 delayedEventTimerXID =
3407 XtAppAddTimeOut(appContext, millisec,
3408 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3411 DelayedEventCallback
3414 if (delayedEventTimerXID) {
3415 return delayedEventCallback;
3422 CancelDelayedEvent ()
3424 if (delayedEventTimerXID) {
3425 XtRemoveTimeOut(delayedEventTimerXID);
3426 delayedEventTimerXID = 0;
3430 XtIntervalId loadGameTimerXID = 0;
3433 LoadGameTimerRunning ()
3435 return loadGameTimerXID != 0;
3439 StopLoadGameTimer ()
3441 if (loadGameTimerXID != 0) {
3442 XtRemoveTimeOut(loadGameTimerXID);
3443 loadGameTimerXID = 0;
3451 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3453 loadGameTimerXID = 0;
3458 StartLoadGameTimer (long millisec)
3461 XtAppAddTimeOut(appContext, millisec,
3462 (XtTimerCallbackProc) LoadGameTimerCallback,
3466 XtIntervalId analysisClockXID = 0;
3469 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3471 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3472 || appData.icsEngineAnalyze) { // [DM]
3473 AnalysisPeriodicEvent(0);
3474 StartAnalysisClock();
3479 StartAnalysisClock ()
3482 XtAppAddTimeOut(appContext, 2000,
3483 (XtTimerCallbackProc) AnalysisClockCallback,
3487 XtIntervalId clockTimerXID = 0;
3490 ClockTimerRunning ()
3492 return clockTimerXID != 0;
3498 if (clockTimerXID != 0) {
3499 XtRemoveTimeOut(clockTimerXID);
3508 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3515 StartClockTimer (long millisec)
3518 XtAppAddTimeOut(appContext, millisec,
3519 (XtTimerCallbackProc) ClockTimerCallback,
3524 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
3528 Widget w = (Widget) opt->handle;
3530 /* check for low time warning */
3531 Pixel foregroundOrWarningColor = timerForegroundPixel;
3534 appData.lowTimeWarning &&
3535 (timer / 1000) < appData.icsAlarmTime)
3536 foregroundOrWarningColor = lowTimeWarningColor;
3538 if (appData.clockMode) {
3539 snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
3540 XtSetArg(args[0], XtNlabel, buf);
3542 snprintf(buf, MSG_SIZ, "%s ", color);
3543 XtSetArg(args[0], XtNlabel, buf);
3548 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3549 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3551 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3552 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3555 XtSetValues(w, args, 3);
3558 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3561 SetClockIcon (int color)
3564 Pixmap pm = *clockIcons[color];
3565 if (iconPixmap != pm) {
3567 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3568 XtSetValues(shellWidget, args, 1);
3573 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3575 InputSource *is = (InputSource *) closure;
3580 if (is->lineByLine) {
3581 count = read(is->fd, is->unused,
3582 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3584 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3587 is->unused += count;
3589 while (p < is->unused) {
3590 q = memchr(p, '\n', is->unused - p);
3591 if (q == NULL) break;
3593 (is->func)(is, is->closure, p, q - p, 0);
3597 while (p < is->unused) {
3602 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3607 (is->func)(is, is->closure, is->buf, count, error);
3612 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3615 ChildProc *cp = (ChildProc *) pr;
3617 is = (InputSource *) calloc(1, sizeof(InputSource));
3618 is->lineByLine = lineByLine;
3622 is->fd = fileno(stdin);
3624 is->kind = cp->kind;
3625 is->fd = cp->fdFrom;
3628 is->unused = is->buf;
3631 is->xid = XtAppAddInput(appContext, is->fd,
3632 (XtPointer) (XtInputReadMask),
3633 (XtInputCallbackProc) DoInputCallback,
3635 is->closure = closure;
3636 return (InputSourceRef) is;
3640 RemoveInputSource (InputSourceRef isr)
3642 InputSource *is = (InputSource *) isr;
3644 if (is->xid == 0) return;
3645 XtRemoveInput(is->xid);
3649 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
3651 /* Masks for XPM pieces. Black and white pieces can have
3652 different shapes, but in the interest of retaining my
3653 sanity pieces must have the same outline on both light
3654 and dark squares, and all pieces must use the same
3655 background square colors/images. */
3657 static int xpmDone = 0;
3658 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
3659 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
3662 CreateAnimMasks (int pieceDepth)
3668 unsigned long plane;
3671 /* Need a bitmap just to get a GC with right depth */
3672 buf = XCreatePixmap(xDisplay, xBoardWindow,
3674 values.foreground = 1;
3675 values.background = 0;
3676 /* Don't use XtGetGC, not read only */
3677 maskGC = XCreateGC(xDisplay, buf,
3678 GCForeground | GCBackground, &values);
3679 XFreePixmap(xDisplay, buf);
3681 buf = XCreatePixmap(xDisplay, xBoardWindow,
3682 squareSize, squareSize, pieceDepth);
3683 values.foreground = XBlackPixel(xDisplay, xScreen);
3684 values.background = XWhitePixel(xDisplay, xScreen);
3685 bufGC = XCreateGC(xDisplay, buf,
3686 GCForeground | GCBackground, &values);
3688 for (piece = WhitePawn; piece <= BlackKing; piece++) {
3689 /* Begin with empty mask */
3690 if(!xpmDone) // [HGM] pieces: keep using existing
3691 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
3692 squareSize, squareSize, 1);
3693 XSetFunction(xDisplay, maskGC, GXclear);
3694 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
3695 0, 0, squareSize, squareSize);
3697 /* Take a copy of the piece */
3702 XSetFunction(xDisplay, bufGC, GXcopy);
3703 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
3705 0, 0, squareSize, squareSize, 0, 0);
3707 /* XOR the background (light) over the piece */
3708 XSetFunction(xDisplay, bufGC, GXxor);
3710 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
3711 0, 0, squareSize, squareSize, 0, 0);
3713 XSetForeground(xDisplay, bufGC, lightSquareColor);
3714 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
3717 /* We now have an inverted piece image with the background
3718 erased. Construct mask by just selecting all the non-zero
3719 pixels - no need to reconstruct the original image. */
3720 XSetFunction(xDisplay, maskGC, GXor);
3722 /* Might be quicker to download an XImage and create bitmap
3723 data from it rather than this N copies per piece, but it
3724 only takes a fraction of a second and there is a much
3725 longer delay for loading the pieces. */
3726 for (n = 0; n < pieceDepth; n ++) {
3727 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
3728 0, 0, squareSize, squareSize,
3734 XFreePixmap(xDisplay, buf);
3735 XFreeGC(xDisplay, bufGC);
3736 XFreeGC(xDisplay, maskGC);
3740 InitAnimState (AnimNr anr, XWindowAttributes *info)
3745 /* Each buffer is square size, same depth as window */
3746 animBufs[anr+4] = xBoardWindow;
3747 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
3748 squareSize, squareSize, info->depth);
3749 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
3750 squareSize, squareSize, info->depth);
3752 /* Create a plain GC for blitting */
3753 mask = GCForeground | GCBackground | GCFunction |
3754 GCPlaneMask | GCGraphicsExposures;
3755 values.foreground = XBlackPixel(xDisplay, xScreen);
3756 values.background = XWhitePixel(xDisplay, xScreen);
3757 values.function = GXcopy;
3758 values.plane_mask = AllPlanes;
3759 values.graphics_exposures = False;
3760 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
3762 /* Piece will be copied from an existing context at
3763 the start of each new animation/drag. */
3764 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
3766 /* Outline will be a read-only copy of an existing */
3767 animGCs[anr+4] = None;
3773 XWindowAttributes info;
3775 if (xpmDone && gameInfo.variant == oldVariant) return;
3776 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
3777 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
3779 InitAnimState(Game, &info);
3780 InitAnimState(Player, &info);
3782 /* For XPM pieces, we need bitmaps to use as masks. */
3784 CreateAnimMasks(info.depth), xpmDone = 1;
3789 static Boolean frameWaiting;
3792 FrameAlarm (int sig)
3794 frameWaiting = False;
3795 /* In case System-V style signals. Needed?? */
3796 signal(SIGALRM, FrameAlarm);
3800 FrameDelay (int time)
3802 struct itimerval delay;
3804 XSync(xDisplay, False);
3807 frameWaiting = True;
3808 signal(SIGALRM, FrameAlarm);
3809 delay.it_interval.tv_sec =
3810 delay.it_value.tv_sec = time / 1000;
3811 delay.it_interval.tv_usec =
3812 delay.it_value.tv_usec = (time % 1000) * 1000;
3813 setitimer(ITIMER_REAL, &delay, NULL);
3814 while (frameWaiting) pause();
3815 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
3816 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
3817 setitimer(ITIMER_REAL, &delay, NULL);
3824 FrameDelay (int time)
3826 XSync(xDisplay, False);
3828 usleep(time * 1000);
3834 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
3838 /* Bitmap for piece being moved. */
3839 if (appData.monoMode) {
3840 *mask = *pieceToSolid(piece);
3841 } else if (useImages) {
3843 *mask = xpmMask[piece];
3845 *mask = ximMaskPm[piece];
3848 *mask = *pieceToSolid(piece);
3851 /* GC for piece being moved. Square color doesn't matter, but
3852 since it gets modified we make a copy of the original. */
3854 if (appData.monoMode)
3859 if (appData.monoMode)
3864 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
3866 /* Outline only used in mono mode and is not modified */
3868 *outline = bwPieceGC;
3870 *outline = wbPieceGC;
3874 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
3879 /* Draw solid rectangle which will be clipped to shape of piece */
3880 XFillRectangle(xDisplay, dest, clip,
3881 0, 0, squareSize, squareSize);
3882 if (appData.monoMode)
3883 /* Also draw outline in contrasting color for black
3884 on black / white on white cases */
3885 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
3886 0, 0, squareSize, squareSize, 0, 0, 1);
3888 /* Copy the piece */
3893 if(appData.upsideDown && flipView) kind ^= 2;
3894 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3896 0, 0, squareSize, squareSize,
3902 InsertPiece (AnimNr anr, ChessSquare piece)
3904 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
3908 DrawBlank (AnimNr anr, int x, int y, int startColor)
3910 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
3913 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
3914 int srcX, int srcY, int width, int height, int destX, int destY)
3916 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
3917 srcX, srcY, width, height, destX, destY);
3921 SetDragPiece (AnimNr anr, ChessSquare piece)
3924 /* The piece will be drawn using its own bitmap as a matte */
3925 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
3926 XSetClipMask(xDisplay, animGCs[anr+2], mask);
3929 /* [AS] Arrow highlighting support */
3931 void DrawPolygon(Pnt arrow[], int nr)
3932 { // for now on own surface; eventually this should become a global that is only destroyed on resize
3933 cairo_surface_t *boardSurface;
3936 int w = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3937 int h = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3938 boardSurface = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), w, h);
3939 cr = cairo_create (boardSurface);
3940 cairo_move_to (cr, arrow[nr-1].x, arrow[nr-1].y);
3941 for (i=0;i<nr;i++) {
3942 cairo_line_to(cr, arrow[i].x, arrow[i].y);
3944 if(appData.monoMode) { // should we always outline arrow?
3945 cairo_line_to(cr, arrow[0].x, arrow[0].y);
3946 SetPen(cr, 2, "#000000", 0);
3947 cairo_stroke_preserve(cr);
3949 SetPen(cr, 2, appData.highlightSquareColor, 0);
3954 cairo_surface_destroy (boardSurface);
3958 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
3960 char buf[MSG_SIZ], *logoName = buf;
3961 if(appData.logo[n][0]) {
3962 logoName = appData.logo[n];
3963 } else if(appData.autoLogo) {
3964 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
3965 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
3966 } else if(appData.directory[n] && appData.directory[n][0]) {
3967 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
3971 { ASSIGN(cps->programLogo, logoName); }
3975 UpdateLogos (int displ)
3977 if(optList[W_WHITE-1].handle == NULL) return;
3978 LoadLogo(&first, 0, 0);
3979 LoadLogo(&second, 1, appData.icsActive);
3980 if(displ) DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);