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)
2662 if(appData.monoMode) {
2663 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
2664 x, y, r, r, 0, 64*360);
2665 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
2666 x, y, r, r, 0, 64*360);
2668 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
2669 x, y, r, r, 0, 64*360);
2673 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2674 { // basic front-end board-draw function: takes care of everything that can be in square:
2675 // piece, background, coordinate/count, marker dot
2676 int direction, font_ascent, font_descent;
2677 XCharStruct overall;
2680 if (piece == EmptySquare) {
2681 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2683 drawfunc = ChooseDrawFunc();
2684 drawfunc(piece, square_color, x, y, xBoardWindow);
2687 if(align) { // square carries inscription (coord or piece count)
2689 GC hGC = align < 3 ? coordGC : countGC;
2690 // first calculate where it goes
2691 XTextExtents(countFontStruct, string, 1, &direction,
2692 &font_ascent, &font_descent, &overall);
2694 xx += squareSize - overall.width - 2;
2695 yy += squareSize - font_descent - 1;
2696 } else if (align == 2) {
2697 xx += 2, yy += font_ascent + 1;
2698 } else if (align == 3) {
2699 xx += squareSize - overall.width - 2;
2700 yy += font_ascent + 1;
2701 } else if (align == 4) {
2702 xx += 2, yy += font_ascent + 1;
2705 if (appData.monoMode) {
2706 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2708 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2712 if(marker) { // print fat marker dot, if requested
2713 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2718 FlashDelay (int flash_delay)
2720 XSync(xDisplay, False);
2721 if(flash_delay) do_flash_delay(flash_delay);
2725 Fraction (int x, int start, int stop)
2727 double f = ((double) x - start)/(stop - start);
2728 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2732 static WindowPlacement wpNew;
2735 CoDrag (Widget sh, WindowPlacement *wp)
2738 int j=0, touch=0, fudge = 2;
2739 GetActualPlacement(sh, wp);
2740 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
2741 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
2742 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2743 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
2744 if(!touch ) return; // only windows that touch co-move
2745 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2746 int heightInc = wpNew.height - wpMain.height;
2747 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2748 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2749 wp->y += fracTop * heightInc;
2750 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2751 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2752 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2753 int widthInc = wpNew.width - wpMain.width;
2754 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2755 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2756 wp->y += fracLeft * widthInc;
2757 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2758 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2760 wp->x += wpNew.x - wpMain.x;
2761 wp->y += wpNew.y - wpMain.y;
2762 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2763 if(touch == 3) wp->y += wpNew.height - wpMain.height;
2764 XtSetArg(args[j], XtNx, wp->x); j++;
2765 XtSetArg(args[j], XtNy, wp->y); j++;
2766 XtSetValues(sh, args, j);
2769 static XtIntervalId delayedDragID = 0;
2774 GetActualPlacement(shellWidget, &wpNew);
2775 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
2776 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
2777 return; // false alarm
2778 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
2779 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
2780 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
2781 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
2783 DrawPosition(True, NULL);
2784 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
2791 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
2793 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
2797 EventProc (Widget widget, caddr_t unused, XEvent *event)
2799 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
2800 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
2803 // [HGM] seekgraph: some low-level drawing routines (by JC, mostly)
2806 Color (char *col, int n)
2809 sscanf(col, "#%x", &c);
2815 SetPen (cairo_t *cr, float w, char *col, int dash)
2817 static const double dotted[] = {4.0, 4.0};
2818 static int len = sizeof(dotted) / sizeof(dotted[0]);
2819 cairo_set_line_width (cr, w);
2820 cairo_set_source_rgba (cr, Color(col, 4), Color(col, 2), Color(col, 0), 1.0);
2821 if(dash) cairo_set_dash (cr, dotted, len, 0.0);
2824 void DrawSeekAxis( int x, int y, int xTo, int yTo )
2829 cr = cairo_create (cs);
2831 cairo_move_to (cr, x, y);
2832 cairo_line_to(cr, xTo, yTo );
2834 SetPen(cr, 2, "#000000", 0);
2841 void DrawSeekBackground( int left, int top, int right, int bottom )
2843 cairo_t *cr = cairo_create (cs);
2845 cairo_rectangle (cr, left, top, right-left, bottom-top);
2847 cairo_set_source_rgba(cr, 0.8, 0.8, 0.4,1.0);
2854 void DrawSeekText(char *buf, int x, int y)
2856 cairo_t *cr = cairo_create (cs);
2858 cairo_select_font_face (cr, "Sans",
2859 CAIRO_FONT_SLANT_NORMAL,
2860 CAIRO_FONT_WEIGHT_NORMAL);
2862 cairo_set_font_size (cr, 12.0);
2864 cairo_move_to (cr, x, y+4);
2865 cairo_show_text( cr, buf);
2867 cairo_set_source_rgba(cr, 0, 0, 0,1.0);
2874 void DrawSeekDot(int x, int y, int colorNr)
2876 cairo_t *cr = cairo_create (cs);
2877 int square = colorNr & 0x80;
2881 cairo_rectangle (cr, x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
2883 cairo_arc(cr, x, y, squareSize/8, 0.0, 2*M_PI);
2885 SetPen(cr, 2, "#000000", 0);
2886 cairo_stroke_preserve(cr);
2888 case 0: cairo_set_source_rgba(cr, 1.0, 0, 0,1.0); break;
2889 case 1: cairo_set_source_rgba (cr, 0.0, 0.7, 0.2, 1.0); break;
2890 default: cairo_set_source_rgba (cr, 1.0, 1.0, 0.0, 1.0); break;
2901 int boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2902 int boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2903 cs = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), boardWidth, boardHeight);
2909 cairo_surface_destroy(cs);
2915 /* draws a grid starting around Nx, Ny squares starting at x,y */
2921 cr = cairo_create (cs);
2923 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
2924 SetPen(cr, lineGap, "#000000", 0);
2927 for (i = 0; i < BOARD_WIDTH + BOARD_HEIGHT + 2; i++)
2929 cairo_move_to (cr, gridSegments[i].x1, gridSegments[i].y1);
2930 cairo_line_to (cr, gridSegments[i].x2, gridSegments[i].y2);
2942 * event handler for redrawing the board
2945 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2947 DrawPosition(True, NULL);
2952 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
2953 { // [HGM] pv: walk PV
2954 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
2957 static int savedIndex; /* gross that this is global */
2960 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
2963 XawTextPosition index, dummy;
2966 XawTextGetSelectionPos(w, &index, &dummy);
2967 XtSetArg(arg, XtNstring, &val);
2968 XtGetValues(w, &arg, 1);
2969 ReplaceComment(savedIndex, val);
2970 if(savedIndex != currentMove) ToNrEvent(savedIndex);
2971 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
2975 EditCommentPopUp (int index, char *title, char *text)
2978 if (text == NULL) text = "";
2979 NewCommentPopup(title, text, index);
2983 CommentPopUp (char *title, char *text)
2985 savedIndex = currentMove; // [HGM] vari
2986 NewCommentPopup(title, text, currentMove);
2992 PopDown(CommentDlg);
2996 /* Disable all user input other than deleting the window */
2997 static int frozen = 0;
3003 /* Grab by a widget that doesn't accept input */
3004 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
3008 /* Undo a FreezeUI */
3012 if (!frozen) return;
3013 XtRemoveGrab(optList[W_MESSG].handle);
3021 static int oldPausing = FALSE;
3022 static GameMode oldmode = (GameMode) -1;
3025 if (!boardWidget || !XtIsRealized(boardWidget)) return;
3027 if (pausing != oldPausing) {
3028 oldPausing = pausing;
3029 MarkMenuItem("Mode.Pause", pausing);
3031 if (appData.showButtonBar) {
3032 /* Always toggle, don't set. Previous code messes up when
3033 invoked while the button is pressed, as releasing it
3034 toggles the state again. */
3037 XtSetArg(args[0], XtNbackground, &oldbg);
3038 XtSetArg(args[1], XtNforeground, &oldfg);
3039 XtGetValues(optList[W_PAUSE].handle,
3041 XtSetArg(args[0], XtNbackground, oldfg);
3042 XtSetArg(args[1], XtNforeground, oldbg);
3044 XtSetValues(optList[W_PAUSE].handle, args, 2);
3048 wname = ModeToWidgetName(oldmode);
3049 if (wname != NULL) {
3050 MarkMenuItem(wname, False);
3052 wname = ModeToWidgetName(gameMode);
3053 if (wname != NULL) {
3054 MarkMenuItem(wname, True);
3057 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
3059 /* Maybe all the enables should be handled here, not just this one */
3060 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
3062 DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);
3067 * Button/menu procedures
3070 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3071 char *selected_fen_position=NULL;
3074 SendPositionSelection (Widget w, Atom *selection, Atom *target,
3075 Atom *type_return, XtPointer *value_return,
3076 unsigned long *length_return, int *format_return)
3078 char *selection_tmp;
3080 // if (!selected_fen_position) return False; /* should never happen */
3081 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3082 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
3083 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
3086 if (f == NULL) return False;
3090 selection_tmp = XtMalloc(len + 1);
3091 count = fread(selection_tmp, 1, len, f);
3094 XtFree(selection_tmp);
3097 selection_tmp[len] = NULLCHAR;
3099 /* note: since no XtSelectionDoneProc was registered, Xt will
3100 * automatically call XtFree on the value returned. So have to
3101 * make a copy of it allocated with XtMalloc */
3102 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3103 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
3106 *value_return=selection_tmp;
3107 *length_return=strlen(selection_tmp);
3108 *type_return=*target;
3109 *format_return = 8; /* bits per byte */
3111 } else if (*target == XA_TARGETS(xDisplay)) {
3112 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3113 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3114 targets_tmp[1] = XA_STRING;
3115 *value_return = targets_tmp;
3116 *type_return = XA_ATOM;
3119 // This code leads to a read of value_return out of bounds on 64-bit systems.
3120 // Other code which I have seen always sets *format_return to 32 independent of
3121 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
3122 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
3123 *format_return = 8 * sizeof(Atom);
3124 if (*format_return > 32) {
3125 *length_return *= *format_return / 32;
3126 *format_return = 32;
3129 *format_return = 32;
3137 /* note: when called from menu all parameters are NULL, so no clue what the
3138 * Widget which was clicked on was, or what the click event was
3141 CopySomething (char *src)
3143 selected_fen_position = src;
3145 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3146 * have a notion of a position that is selected but not copied.
3147 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3149 XtOwnSelection(menuBarWidget, XA_PRIMARY,
3151 SendPositionSelection,
3152 NULL/* lose_ownership_proc */ ,
3153 NULL/* transfer_done_proc */);
3154 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3156 SendPositionSelection,
3157 NULL/* lose_ownership_proc */ ,
3158 NULL/* transfer_done_proc */);
3161 /* function called when the data to Paste is ready */
3163 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3164 Atom *type, XtPointer value, unsigned long *len, int *format)
3167 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3168 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3169 EditPositionPasteFEN(fenstr);
3173 /* called when Paste Position button is pressed,
3174 * all parameters will be NULL */
3176 PastePositionProc ()
3178 XtGetSelectionValue(menuBarWidget,
3179 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3180 /* (XtSelectionCallbackProc) */ PastePositionCB,
3181 NULL, /* client_data passed to PastePositionCB */
3183 /* better to use the time field from the event that triggered the
3184 * call to this function, but that isn't trivial to get
3191 /* note: when called from menu all parameters are NULL, so no clue what the
3192 * Widget which was clicked on was, or what the click event was
3194 /* function called when the data to Paste is ready */
3196 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3197 Atom *type, XtPointer value, unsigned long *len, int *format)
3200 if (value == NULL || *len == 0) {
3201 return; /* nothing had been selected to copy */
3203 f = fopen(gamePasteFilename, "w");
3205 DisplayError(_("Can't open temp file"), errno);
3208 fwrite(value, 1, *len, f);
3211 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3214 /* called when Paste Game button is pressed,
3215 * all parameters will be NULL */
3219 XtGetSelectionValue(menuBarWidget,
3220 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3221 /* (XtSelectionCallbackProc) */ PasteGameCB,
3222 NULL, /* client_data passed to PasteGameCB */
3224 /* better to use the time field from the event that triggered the
3225 * call to this function, but that isn't trivial to get
3234 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3241 { // bassic primitive for determining if modifier keys are pressed
3242 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3245 XQueryKeymap(xDisplay,keys);
3246 for(i=0; i<6; i++) {
3248 j = XKeysymToKeycode(xDisplay, codes[i]);
3249 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3255 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3259 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3260 if ( n == 1 && *buf >= 32 // printable
3261 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3262 ) BoxAutoPopUp (buf);
3266 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3267 { // [HGM] input: let up-arrow recall previous line from history
3272 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3273 { // [HGM] input: let down-arrow recall next line from history
3278 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3284 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3286 if (!TempBackwardActive) {
3287 TempBackwardActive = True;
3293 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3295 /* Check to see if triggered by a key release event for a repeating key.
3296 * If so the next queued event will be a key press of the same key at the same time */
3297 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3299 XPeekEvent(xDisplay, &next);
3300 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3301 next.xkey.keycode == event->xkey.keycode)
3305 TempBackwardActive = False;
3309 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3310 { // called as key binding
3313 if (nprms && *nprms > 0)
3317 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3323 { // called from menu
3324 ManInner(NULL, NULL, NULL, NULL);
3328 SetWindowTitle (char *text, char *title, char *icon)
3332 if (appData.titleInWindow) {
3334 XtSetArg(args[i], XtNlabel, text); i++;
3335 XtSetValues(titleWidget, args, i);
3338 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3339 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3340 XtSetValues(shellWidget, args, i);
3341 XSync(xDisplay, False);
3346 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3352 DisplayIcsInteractionTitle (String message)
3354 if (oldICSInteractionTitle == NULL) {
3355 /* Magic to find the old window title, adapted from vim */
3356 char *wina = getenv("WINDOWID");
3358 Window win = (Window) atoi(wina);
3359 Window root, parent, *children;
3360 unsigned int nchildren;
3361 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3363 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3364 if (!XQueryTree(xDisplay, win, &root, &parent,
3365 &children, &nchildren)) break;
3366 if (children) XFree((void *)children);
3367 if (parent == root || parent == 0) break;
3370 XSetErrorHandler(oldHandler);
3372 if (oldICSInteractionTitle == NULL) {
3373 oldICSInteractionTitle = "xterm";
3376 printf("\033]0;%s\007", message);
3381 XtIntervalId delayedEventTimerXID = 0;
3382 DelayedEventCallback delayedEventCallback = 0;
3387 delayedEventTimerXID = 0;
3388 delayedEventCallback();
3392 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3394 if(delayedEventTimerXID && delayedEventCallback == cb)
3395 // [HGM] alive: replace, rather than add or flush identical event
3396 XtRemoveTimeOut(delayedEventTimerXID);
3397 delayedEventCallback = cb;
3398 delayedEventTimerXID =
3399 XtAppAddTimeOut(appContext, millisec,
3400 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3403 DelayedEventCallback
3406 if (delayedEventTimerXID) {
3407 return delayedEventCallback;
3414 CancelDelayedEvent ()
3416 if (delayedEventTimerXID) {
3417 XtRemoveTimeOut(delayedEventTimerXID);
3418 delayedEventTimerXID = 0;
3422 XtIntervalId loadGameTimerXID = 0;
3425 LoadGameTimerRunning ()
3427 return loadGameTimerXID != 0;
3431 StopLoadGameTimer ()
3433 if (loadGameTimerXID != 0) {
3434 XtRemoveTimeOut(loadGameTimerXID);
3435 loadGameTimerXID = 0;
3443 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3445 loadGameTimerXID = 0;
3450 StartLoadGameTimer (long millisec)
3453 XtAppAddTimeOut(appContext, millisec,
3454 (XtTimerCallbackProc) LoadGameTimerCallback,
3458 XtIntervalId analysisClockXID = 0;
3461 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3463 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3464 || appData.icsEngineAnalyze) { // [DM]
3465 AnalysisPeriodicEvent(0);
3466 StartAnalysisClock();
3471 StartAnalysisClock ()
3474 XtAppAddTimeOut(appContext, 2000,
3475 (XtTimerCallbackProc) AnalysisClockCallback,
3479 XtIntervalId clockTimerXID = 0;
3482 ClockTimerRunning ()
3484 return clockTimerXID != 0;
3490 if (clockTimerXID != 0) {
3491 XtRemoveTimeOut(clockTimerXID);
3500 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3507 StartClockTimer (long millisec)
3510 XtAppAddTimeOut(appContext, millisec,
3511 (XtTimerCallbackProc) ClockTimerCallback,
3516 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
3520 Widget w = (Widget) opt->handle;
3522 /* check for low time warning */
3523 Pixel foregroundOrWarningColor = timerForegroundPixel;
3526 appData.lowTimeWarning &&
3527 (timer / 1000) < appData.icsAlarmTime)
3528 foregroundOrWarningColor = lowTimeWarningColor;
3530 if (appData.clockMode) {
3531 snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
3532 XtSetArg(args[0], XtNlabel, buf);
3534 snprintf(buf, MSG_SIZ, "%s ", color);
3535 XtSetArg(args[0], XtNlabel, buf);
3540 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3541 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3543 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3544 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3547 XtSetValues(w, args, 3);
3550 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3553 SetClockIcon (int color)
3556 Pixmap pm = *clockIcons[color];
3557 if (iconPixmap != pm) {
3559 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3560 XtSetValues(shellWidget, args, 1);
3565 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3567 InputSource *is = (InputSource *) closure;
3572 if (is->lineByLine) {
3573 count = read(is->fd, is->unused,
3574 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3576 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3579 is->unused += count;
3581 while (p < is->unused) {
3582 q = memchr(p, '\n', is->unused - p);
3583 if (q == NULL) break;
3585 (is->func)(is, is->closure, p, q - p, 0);
3589 while (p < is->unused) {
3594 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3599 (is->func)(is, is->closure, is->buf, count, error);
3604 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3607 ChildProc *cp = (ChildProc *) pr;
3609 is = (InputSource *) calloc(1, sizeof(InputSource));
3610 is->lineByLine = lineByLine;
3614 is->fd = fileno(stdin);
3616 is->kind = cp->kind;
3617 is->fd = cp->fdFrom;
3620 is->unused = is->buf;
3623 is->xid = XtAppAddInput(appContext, is->fd,
3624 (XtPointer) (XtInputReadMask),
3625 (XtInputCallbackProc) DoInputCallback,
3627 is->closure = closure;
3628 return (InputSourceRef) is;
3632 RemoveInputSource (InputSourceRef isr)
3634 InputSource *is = (InputSource *) isr;
3636 if (is->xid == 0) return;
3637 XtRemoveInput(is->xid);
3641 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
3643 /* Masks for XPM pieces. Black and white pieces can have
3644 different shapes, but in the interest of retaining my
3645 sanity pieces must have the same outline on both light
3646 and dark squares, and all pieces must use the same
3647 background square colors/images. */
3649 static int xpmDone = 0;
3650 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
3651 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
3654 CreateAnimMasks (int pieceDepth)
3660 unsigned long plane;
3663 /* Need a bitmap just to get a GC with right depth */
3664 buf = XCreatePixmap(xDisplay, xBoardWindow,
3666 values.foreground = 1;
3667 values.background = 0;
3668 /* Don't use XtGetGC, not read only */
3669 maskGC = XCreateGC(xDisplay, buf,
3670 GCForeground | GCBackground, &values);
3671 XFreePixmap(xDisplay, buf);
3673 buf = XCreatePixmap(xDisplay, xBoardWindow,
3674 squareSize, squareSize, pieceDepth);
3675 values.foreground = XBlackPixel(xDisplay, xScreen);
3676 values.background = XWhitePixel(xDisplay, xScreen);
3677 bufGC = XCreateGC(xDisplay, buf,
3678 GCForeground | GCBackground, &values);
3680 for (piece = WhitePawn; piece <= BlackKing; piece++) {
3681 /* Begin with empty mask */
3682 if(!xpmDone) // [HGM] pieces: keep using existing
3683 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
3684 squareSize, squareSize, 1);
3685 XSetFunction(xDisplay, maskGC, GXclear);
3686 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
3687 0, 0, squareSize, squareSize);
3689 /* Take a copy of the piece */
3694 XSetFunction(xDisplay, bufGC, GXcopy);
3695 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
3697 0, 0, squareSize, squareSize, 0, 0);
3699 /* XOR the background (light) over the piece */
3700 XSetFunction(xDisplay, bufGC, GXxor);
3702 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
3703 0, 0, squareSize, squareSize, 0, 0);
3705 XSetForeground(xDisplay, bufGC, lightSquareColor);
3706 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
3709 /* We now have an inverted piece image with the background
3710 erased. Construct mask by just selecting all the non-zero
3711 pixels - no need to reconstruct the original image. */
3712 XSetFunction(xDisplay, maskGC, GXor);
3714 /* Might be quicker to download an XImage and create bitmap
3715 data from it rather than this N copies per piece, but it
3716 only takes a fraction of a second and there is a much
3717 longer delay for loading the pieces. */
3718 for (n = 0; n < pieceDepth; n ++) {
3719 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
3720 0, 0, squareSize, squareSize,
3726 XFreePixmap(xDisplay, buf);
3727 XFreeGC(xDisplay, bufGC);
3728 XFreeGC(xDisplay, maskGC);
3732 InitAnimState (AnimNr anr, XWindowAttributes *info)
3737 /* Each buffer is square size, same depth as window */
3738 animBufs[anr+4] = xBoardWindow;
3739 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
3740 squareSize, squareSize, info->depth);
3741 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
3742 squareSize, squareSize, info->depth);
3744 /* Create a plain GC for blitting */
3745 mask = GCForeground | GCBackground | GCFunction |
3746 GCPlaneMask | GCGraphicsExposures;
3747 values.foreground = XBlackPixel(xDisplay, xScreen);
3748 values.background = XWhitePixel(xDisplay, xScreen);
3749 values.function = GXcopy;
3750 values.plane_mask = AllPlanes;
3751 values.graphics_exposures = False;
3752 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
3754 /* Piece will be copied from an existing context at
3755 the start of each new animation/drag. */
3756 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
3758 /* Outline will be a read-only copy of an existing */
3759 animGCs[anr+4] = None;
3765 XWindowAttributes info;
3767 if (xpmDone && gameInfo.variant == oldVariant) return;
3768 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
3769 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
3771 InitAnimState(Game, &info);
3772 InitAnimState(Player, &info);
3774 /* For XPM pieces, we need bitmaps to use as masks. */
3776 CreateAnimMasks(info.depth), xpmDone = 1;
3781 static Boolean frameWaiting;
3784 FrameAlarm (int sig)
3786 frameWaiting = False;
3787 /* In case System-V style signals. Needed?? */
3788 signal(SIGALRM, FrameAlarm);
3792 FrameDelay (int time)
3794 struct itimerval delay;
3796 XSync(xDisplay, False);
3799 frameWaiting = True;
3800 signal(SIGALRM, FrameAlarm);
3801 delay.it_interval.tv_sec =
3802 delay.it_value.tv_sec = time / 1000;
3803 delay.it_interval.tv_usec =
3804 delay.it_value.tv_usec = (time % 1000) * 1000;
3805 setitimer(ITIMER_REAL, &delay, NULL);
3806 while (frameWaiting) pause();
3807 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
3808 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
3809 setitimer(ITIMER_REAL, &delay, NULL);
3816 FrameDelay (int time)
3818 XSync(xDisplay, False);
3820 usleep(time * 1000);
3826 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
3830 /* Bitmap for piece being moved. */
3831 if (appData.monoMode) {
3832 *mask = *pieceToSolid(piece);
3833 } else if (useImages) {
3835 *mask = xpmMask[piece];
3837 *mask = ximMaskPm[piece];
3840 *mask = *pieceToSolid(piece);
3843 /* GC for piece being moved. Square color doesn't matter, but
3844 since it gets modified we make a copy of the original. */
3846 if (appData.monoMode)
3851 if (appData.monoMode)
3856 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
3858 /* Outline only used in mono mode and is not modified */
3860 *outline = bwPieceGC;
3862 *outline = wbPieceGC;
3866 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
3871 /* Draw solid rectangle which will be clipped to shape of piece */
3872 XFillRectangle(xDisplay, dest, clip,
3873 0, 0, squareSize, squareSize);
3874 if (appData.monoMode)
3875 /* Also draw outline in contrasting color for black
3876 on black / white on white cases */
3877 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
3878 0, 0, squareSize, squareSize, 0, 0, 1);
3880 /* Copy the piece */
3885 if(appData.upsideDown && flipView) kind ^= 2;
3886 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3888 0, 0, squareSize, squareSize,
3894 InsertPiece (AnimNr anr, ChessSquare piece)
3896 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
3900 DrawBlank (AnimNr anr, int x, int y, int startColor)
3902 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
3905 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
3906 int srcX, int srcY, int width, int height, int destX, int destY)
3908 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
3909 srcX, srcY, width, height, destX, destY);
3913 SetDragPiece (AnimNr anr, ChessSquare piece)
3916 /* The piece will be drawn using its own bitmap as a matte */
3917 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
3918 XSetClipMask(xDisplay, animGCs[anr+2], mask);
3921 /* [AS] Arrow highlighting support */
3923 void DrawPolygon(Pnt arrow[], int nr)
3924 { // for now on own surface; eventually this should become a global that is only destroyed on resize
3925 cairo_surface_t *boardSurface;
3928 int w = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3929 int h = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3930 boardSurface = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), w, h);
3931 cr = cairo_create (boardSurface);
3932 cairo_move_to (cr, arrow[nr-1].x, arrow[nr-1].y);
3933 for (i=0;i<nr;i++) {
3934 cairo_line_to(cr, arrow[i].x, arrow[i].y);
3936 if(appData.monoMode) { // should we always outline arrow?
3937 cairo_line_to(cr, arrow[0].x, arrow[0].y);
3938 SetPen(cr, 2, "#000000", 0);
3939 cairo_stroke_preserve(cr);
3941 SetPen(cr, 2, appData.highlightSquareColor, 0);
3946 cairo_surface_destroy (boardSurface);
3950 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
3952 char buf[MSG_SIZ], *logoName = buf;
3953 if(appData.logo[n][0]) {
3954 logoName = appData.logo[n];
3955 } else if(appData.autoLogo) {
3956 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
3957 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
3958 } else if(appData.directory[n] && appData.directory[n][0]) {
3959 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
3963 { ASSIGN(cps->programLogo, logoName); }
3967 UpdateLogos (int displ)
3969 if(optList[W_WHITE-1].handle == NULL) return;
3970 LoadLogo(&first, 0, 0);
3971 LoadLogo(&second, 1, appData.icsActive);
3972 if(displ) DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);