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>
66 # if HAVE_SYS_SOCKET_H
67 # include <sys/socket.h>
68 # include <netinet/in.h>
70 # else /* not HAVE_SYS_SOCKET_H */
71 # if HAVE_LAN_SOCKET_H
72 # include <lan/socket.h>
74 # include <lan/netdb.h>
75 # else /* not HAVE_LAN_SOCKET_H */
76 # define OMIT_SOCKETS 1
77 # endif /* not HAVE_LAN_SOCKET_H */
78 # endif /* not HAVE_SYS_SOCKET_H */
79 #endif /* !OMIT_SOCKETS */
84 #else /* not STDC_HEADERS */
85 extern char *getenv();
88 # else /* not HAVE_STRING_H */
90 # endif /* not HAVE_STRING_H */
91 #endif /* not STDC_HEADERS */
94 # include <sys/fcntl.h>
95 #else /* not HAVE_SYS_FCNTL_H */
98 # endif /* HAVE_FCNTL_H */
99 #endif /* not HAVE_SYS_FCNTL_H */
101 #if HAVE_SYS_SYSTEMINFO_H
102 # include <sys/systeminfo.h>
103 #endif /* HAVE_SYS_SYSTEMINFO_H */
105 #if TIME_WITH_SYS_TIME
106 # include <sys/time.h>
110 # include <sys/time.h>
121 # include <sys/wait.h>
126 # define NAMLEN(dirent) strlen((dirent)->d_name)
127 # define HAVE_DIR_STRUCT
129 # define dirent direct
130 # define NAMLEN(dirent) (dirent)->d_namlen
132 # include <sys/ndir.h>
133 # define HAVE_DIR_STRUCT
136 # include <sys/dir.h>
137 # define HAVE_DIR_STRUCT
141 # define HAVE_DIR_STRUCT
149 #include <X11/Intrinsic.h>
150 #include <X11/StringDefs.h>
151 #include <X11/Shell.h>
152 #include <X11/cursorfont.h>
153 #include <X11/Xatom.h>
154 #include <X11/Xmu/Atoms.h>
156 #include <X11/Xaw3d/Dialog.h>
157 #include <X11/Xaw3d/Form.h>
158 #include <X11/Xaw3d/List.h>
159 #include <X11/Xaw3d/Label.h>
160 #include <X11/Xaw3d/SimpleMenu.h>
161 #include <X11/Xaw3d/SmeBSB.h>
162 #include <X11/Xaw3d/SmeLine.h>
163 #include <X11/Xaw3d/Box.h>
164 #include <X11/Xaw3d/MenuButton.h>
165 #include <X11/Xaw3d/Text.h>
166 #include <X11/Xaw3d/AsciiText.h>
168 #include <X11/Xaw/Dialog.h>
169 #include <X11/Xaw/Form.h>
170 #include <X11/Xaw/List.h>
171 #include <X11/Xaw/Label.h>
172 #include <X11/Xaw/SimpleMenu.h>
173 #include <X11/Xaw/SmeBSB.h>
174 #include <X11/Xaw/SmeLine.h>
175 #include <X11/Xaw/Box.h>
176 #include <X11/Xaw/MenuButton.h>
177 #include <X11/Xaw/Text.h>
178 #include <X11/Xaw/AsciiText.h>
181 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
186 #include "pixmaps/pixmaps.h"
187 #define IMAGE_EXT "xpm"
189 #define IMAGE_EXT "xim"
190 #include "bitmaps/bitmaps.h"
193 #include "bitmaps/icon_white.bm"
194 #include "bitmaps/icon_black.bm"
195 #include "bitmaps/checkmark.bm"
197 #include "frontend.h"
199 #include "backendz.h"
203 #include "xgamelist.h"
204 #include "xhistory.h"
205 #include "xedittags.h"
209 #include "engineoutput.h"
218 #define usleep(t) _sleep2(((t)+500)/1000)
222 # define _(s) gettext (s)
223 # define N_(s) gettext_noop (s)
229 int main P((int argc, char **argv));
230 RETSIGTYPE CmailSigHandler P((int sig));
231 RETSIGTYPE IntSigHandler P((int sig));
232 RETSIGTYPE TermSizeSigHandler P((int sig));
233 static void CreateGCs P((int redo));
234 static void CreateAnyPieces P((void));
235 void CreateXIMPieces P((void));
236 void CreateXPMPieces P((void));
237 void CreateXPMBoard P((char *s, int n));
238 void CreatePieces P((void));
239 Widget CreateMenuBar P((Menu *mb, int boardWidth));
241 char *InsertPxlSize P((char *pattern, int targetPxlSize));
242 XFontSet CreateFontSet P((char *base_fnt_lst));
244 char *FindFont P((char *pattern, int targetPxlSize));
246 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
247 u_int wreq, u_int hreq));
248 void CreateGrid P((void));
249 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
250 void DelayedDrag P((void));
251 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
252 void HandlePV P((Widget w, XEvent * event,
253 String * params, Cardinal * nParams));
254 void DrawPositionProc P((Widget w, XEvent *event,
255 String *prms, Cardinal *nprms));
256 void CommentClick P((Widget w, XEvent * event,
257 String * params, Cardinal * nParams));
258 void ICSInputBoxPopUp P((void));
259 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
260 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
261 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
262 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
263 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
264 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
265 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
266 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
267 Boolean TempBackwardActive = False;
268 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
269 void DisplayMove P((int moveNumber));
270 void ICSInitScript P((void));
271 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
272 void update_ics_width P(());
273 int CopyMemoProc P(());
276 * XBoard depends on Xt R4 or higher
278 int xtVersion = XtSpecificationRelease;
283 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
284 highlightSquareColor, premoveHighlightColor, dialogColor, buttonColor;
285 Pixel lowTimeWarningColor;
286 GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
287 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
289 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
290 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
291 Option *optList; // contains all widgets of main window
292 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
294 XFontSet fontSet, clockFontSet;
297 XFontStruct *clockFontStruct;
299 Font coordFontID, countFontID;
300 XFontStruct *coordFontStruct, *countFontStruct;
301 XtAppContext appContext;
304 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
306 Position commentX = -1, commentY = -1;
307 Dimension commentW, commentH;
308 typedef unsigned int BoardSize;
310 Boolean chessProgram;
312 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
313 int smallLayout = 0, tinyLayout = 0,
314 marginW, marginH, // [HGM] for run-time resizing
315 fromX = -1, fromY = -1, toX, toY, commentUp = False,
316 errorExitStatus = -1, defaultLineGap;
317 Dimension textHeight;
318 Pixel timerForegroundPixel, timerBackgroundPixel;
319 Pixel buttonForegroundPixel, buttonBackgroundPixel;
320 char *chessDir, *programName, *programVersion;
321 Boolean alwaysOnTop = False;
322 char *icsTextMenuString;
324 char *firstChessProgramNames;
325 char *secondChessProgramNames;
327 WindowPlacement wpMain;
328 WindowPlacement wpConsole;
329 WindowPlacement wpComment;
330 WindowPlacement wpMoveHistory;
331 WindowPlacement wpEvalGraph;
332 WindowPlacement wpEngineOutput;
333 WindowPlacement wpGameList;
334 WindowPlacement wpTags;
339 Pixmap pieceBitmap[2][(int)BlackPawn];
340 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
341 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
342 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
343 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
344 Pixmap xpmBoardBitmap[2];
345 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
346 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
347 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
348 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
349 XImage *ximLightSquare, *ximDarkSquare;
352 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
353 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
355 #define White(piece) ((int)(piece) < (int)BlackPawn)
357 /* Bitmaps for use as masks when drawing XPM pieces.
358 Need one for each black and white piece. */
359 static Pixmap xpmMask[BlackKing + 1];
361 /* This magic number is the number of intermediate frames used
362 in each half of the animation. For short moves it's reduced
363 by 1. The total number of frames will be factor * 2 + 1. */
366 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
373 DropMenuEnables dmEnables[] = {
390 XtResource clientResources[] = {
391 { "flashCount", "flashCount", XtRInt, sizeof(int),
392 XtOffset(AppDataPtr, flashCount), XtRImmediate,
393 (XtPointer) FLASH_COUNT },
396 XrmOptionDescRec shellOptions[] = {
397 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
398 { "-flash", "flashCount", XrmoptionNoArg, "3" },
399 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
402 XtActionsRec boardActions[] = {
403 { "DrawPosition", DrawPositionProc },
404 { "HandlePV", HandlePV },
405 { "SelectPV", SelectPV },
406 { "StopPV", StopPV },
407 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
408 { "QuitProc", QuitWrapper },
409 { "ManProc", ManInner },
410 { "TempBackwardProc", TempBackwardProc },
411 { "TempForwardProc", TempForwardProc },
412 { "CommentClick", (XtActionProc) CommentClick },
413 { "GenericPopDown", (XtActionProc) GenericPopDown },
414 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
415 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
416 { "SelectMove", (XtActionProc) SelectMove },
417 { "LoadSelectedProc", LoadSelectedProc },
418 { "SetFilterProc", SetFilterProc },
419 { "TypeInProc", TypeInProc },
420 { "EnterKeyProc", EnterKeyProc },
421 { "UpKeyProc", UpKeyProc },
422 { "DownKeyProc", DownKeyProc },
423 { "WheelProc", WheelProc },
424 { "TabProc", TabProc },
427 char globalTranslations[] =
428 ":<Key>F9: MenuItem(Actions.Resign) \n \
429 :Ctrl<Key>n: MenuItem(File.NewGame) \n \
430 :Meta<Key>V: MenuItem(File.NewVariant) \n \
431 :Ctrl<Key>o: MenuItem(File.LoadGame) \n \
432 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
433 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
434 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
435 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
436 :Ctrl<Key>s: MenuItem(File.SaveGame) \n \
437 :Ctrl<Key>c: MenuItem(Edit.CopyGame) \n \
438 :Ctrl<Key>v: MenuItem(Edit.PasteGame) \n \
439 :Ctrl<Key>O: MenuItem(File.LoadPosition) \n \
440 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
441 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
442 :Ctrl<Key>S: MenuItem(File.SavePosition) \n \
443 :Ctrl<Key>C: MenuItem(Edit.CopyPosition) \n \
444 :Ctrl<Key>V: MenuItem(Edit.PastePosition) \n \
445 :Ctrl<Key>q: MenuItem(File.Quit) \n \
446 :Ctrl<Key>w: MenuItem(Mode.MachineWhite) \n \
447 :Ctrl<Key>b: MenuItem(Mode.MachineBlack) \n \
448 :Ctrl<Key>t: MenuItem(Mode.TwoMachines) \n \
449 :Ctrl<Key>a: MenuItem(Mode.AnalysisMode) \n \
450 :Ctrl<Key>g: MenuItem(Mode.AnalyzeFile) \n \
451 :Ctrl<Key>e: MenuItem(Mode.EditGame) \n \
452 :Ctrl<Key>E: MenuItem(Mode.EditPosition) \n \
453 :Meta<Key>O: MenuItem(View.EngineOutput) \n \
454 :Meta<Key>E: MenuItem(View.EvaluationGraph) \n \
455 :Meta<Key>G: MenuItem(View.GameList) \n \
456 :Meta<Key>H: MenuItem(View.MoveHistory) \n \
457 :<Key>Pause: MenuItem(Mode.Pause) \n \
458 :<Key>F3: MenuItem(Action.Accept) \n \
459 :<Key>F4: MenuItem(Action.Decline) \n \
460 :<Key>F12: MenuItem(Action.Rematch) \n \
461 :<Key>F5: MenuItem(Action.CallFlag) \n \
462 :<Key>F6: MenuItem(Action.Draw) \n \
463 :<Key>F7: MenuItem(Action.Adjourn) \n \
464 :<Key>F8: MenuItem(Action.Abort) \n \
465 :<Key>F10: MenuItem(Action.StopObserving) \n \
466 :<Key>F11: MenuItem(Action.StopExamining) \n \
467 :Ctrl<Key>d: MenuItem(DebugProc) \n \
468 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
469 :Meta<Key>End: MenuItem(Edit.ForwardtoEnd) \n \
470 :Meta<Key>Right: MenuItem(Edit.Forward) \n \
471 :Meta<Key>Home: MenuItem(Edit.BacktoStart) \n \
472 :Meta<Key>Left: MenuItem(Edit.Backward) \n \
473 :<Key>Left: MenuItem(Edit.Backward) \n \
474 :<Key>Right: MenuItem(Edit.Forward) \n \
475 :<Key>Home: MenuItem(Edit.Revert) \n \
476 :<Key>End: MenuItem(Edit.TruncateGame) \n \
477 :Ctrl<Key>m: MenuItem(Engine.MoveNow) \n \
478 :Ctrl<Key>x: MenuItem(Engine.RetractMove) \n \
479 :Meta<Key>J: MenuItem(Options.Adjudications) \n \
480 :Meta<Key>U: MenuItem(Options.CommonEngine) \n \
481 :Meta<Key>T: MenuItem(Options.TimeControl) \n \
482 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
483 #ifndef OPTIONSDIALOG
485 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
486 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
487 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
488 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
489 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
492 :<Key>F1: MenuItem(Help.ManXBoard) \n \
493 :<Key>F2: MenuItem(View.FlipView) \n \
494 :<KeyDown>Return: TempBackwardProc() \n \
495 :<KeyUp>Return: TempForwardProc() \n";
497 char ICSInputTranslations[] =
498 "<Key>Up: UpKeyProc() \n "
499 "<Key>Down: DownKeyProc() \n "
500 "<Key>Return: EnterKeyProc() \n";
502 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
503 // as the widget is destroyed before the up-click can call extend-end
504 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
506 String xboardResources[] = {
507 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
512 /* Max possible square size */
513 #define MAXSQSIZE 256
515 static int xpm_avail[MAXSQSIZE];
517 #ifdef HAVE_DIR_STRUCT
519 /* Extract piece size from filename */
521 xpm_getsize (char *name, int len, char *ext)
529 if ((p=strchr(name, '.')) == NULL ||
530 StrCaseCmp(p+1, ext) != 0)
536 while (*p && isdigit(*p))
543 /* Setup xpm_avail */
545 xpm_getavail (char *dirname, char *ext)
551 for (i=0; i<MAXSQSIZE; ++i)
554 if (appData.debugMode)
555 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
557 dir = opendir(dirname);
560 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
561 programName, dirname);
565 while ((ent=readdir(dir)) != NULL) {
566 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
567 if (i > 0 && i < MAXSQSIZE)
577 xpm_print_avail (FILE *fp, char *ext)
581 fprintf(fp, _("Available `%s' sizes:\n"), ext);
582 for (i=1; i<MAXSQSIZE; ++i) {
588 /* Return XPM piecesize closest to size */
590 xpm_closest_to (char *dirname, int size, char *ext)
593 int sm_diff = MAXSQSIZE;
597 xpm_getavail(dirname, ext);
599 if (appData.debugMode)
600 xpm_print_avail(stderr, ext);
602 for (i=1; i<MAXSQSIZE; ++i) {
605 diff = (diff<0) ? -diff : diff;
606 if (diff < sm_diff) {
614 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
620 #else /* !HAVE_DIR_STRUCT */
621 /* If we are on a system without a DIR struct, we can't
622 read the directory, so we can't collect a list of
623 filenames, etc., so we can't do any size-fitting. */
625 xpm_closest_to (char *dirname, int size, char *ext)
628 Warning: No DIR structure found on this system --\n\
629 Unable to autosize for XPM/XIM pieces.\n\
630 Please report this error to %s.\n\
631 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
634 #endif /* HAVE_DIR_STRUCT */
637 /* Arrange to catch delete-window events */
638 Atom wm_delete_window;
640 CatchDeleteWindow (Widget w, String procname)
643 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
644 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
645 XtAugmentTranslations(w, XtParseTranslationTable(buf));
652 XtSetArg(args[0], XtNiconic, False);
653 XtSetValues(shellWidget, args, 1);
655 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
658 //---------------------------------------------------------------------------------------------------------
659 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
662 #define CW_USEDEFAULT (1<<31)
663 #define ICS_TEXT_MENU_SIZE 90
664 #define DEBUG_FILE "xboard.debug"
665 #define SetCurrentDirectory chdir
666 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
670 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
673 // front-end part of option handling
675 // [HGM] This platform-dependent table provides the location for storing the color info
676 extern char *crWhite, * crBlack;
680 &appData.whitePieceColor,
681 &appData.blackPieceColor,
682 &appData.lightSquareColor,
683 &appData.darkSquareColor,
684 &appData.highlightSquareColor,
685 &appData.premoveHighlightColor,
686 &appData.lowTimeWarningColor,
697 // [HGM] font: keep a font for each square size, even non-stndard ones
700 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
701 char *fontTable[NUM_FONTS][MAX_SIZE];
704 ParseFont (char *name, int number)
705 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
707 if(sscanf(name, "size%d:", &size)) {
708 // [HGM] font: font is meant for specific boardSize (likely from settings file);
709 // defer processing it until we know if it matches our board size
710 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
711 fontTable[number][size] = strdup(strchr(name, ':')+1);
712 fontValid[number][size] = True;
717 case 0: // CLOCK_FONT
718 appData.clockFont = strdup(name);
720 case 1: // MESSAGE_FONT
721 appData.font = strdup(name);
723 case 2: // COORD_FONT
724 appData.coordFont = strdup(name);
729 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
734 { // only 2 fonts currently
735 appData.clockFont = CLOCK_FONT_NAME;
736 appData.coordFont = COORD_FONT_NAME;
737 appData.font = DEFAULT_FONT_NAME;
742 { // no-op, until we identify the code for this already in XBoard and move it here
746 ParseColor (int n, char *name)
747 { // in XBoard, just copy the color-name string
748 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
752 ParseTextAttribs (ColorClass cc, char *s)
754 (&appData.colorShout)[cc] = strdup(s);
758 ParseBoardSize (void *addr, char *name)
760 appData.boardSize = strdup(name);
765 { // In XBoard the sound-playing program takes care of obtaining the actual sound
769 SetCommPortDefaults ()
770 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
773 // [HGM] args: these three cases taken out to stay in front-end
775 SaveFontArg (FILE *f, ArgDescriptor *ad)
778 int i, n = (int)(intptr_t)ad->argLoc;
780 case 0: // CLOCK_FONT
781 name = appData.clockFont;
783 case 1: // MESSAGE_FONT
786 case 2: // COORD_FONT
787 name = appData.coordFont;
792 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
793 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
794 fontTable[n][squareSize] = strdup(name);
795 fontValid[n][squareSize] = True;
798 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
799 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
804 { // nothing to do, as the sounds are at all times represented by their text-string names already
808 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
809 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
810 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
814 SaveColor (FILE *f, ArgDescriptor *ad)
815 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
816 if(colorVariable[(int)(intptr_t)ad->argLoc])
817 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
821 SaveBoardSize (FILE *f, char *name, void *addr)
822 { // wrapper to shield back-end from BoardSize & sizeInfo
823 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
827 ParseCommPortSettings (char *s)
828 { // no such option in XBoard (yet)
834 GetActualPlacement (Widget wg, WindowPlacement *wp)
836 XWindowAttributes winAt;
843 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
844 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
845 wp->x = rx - winAt.x;
846 wp->y = ry - winAt.y;
847 wp->height = winAt.height;
848 wp->width = winAt.width;
849 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
854 { // wrapper to shield use of window handles from back-end (make addressible by number?)
855 // In XBoard this will have to wait until awareness of window parameters is implemented
856 GetActualPlacement(shellWidget, &wpMain);
857 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
858 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
859 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
860 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
861 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
862 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
866 PrintCommPortSettings (FILE *f, char *name)
867 { // This option does not exist in XBoard
871 EnsureOnScreen (int *x, int *y, int minX, int minY)
878 { // [HGM] args: allows testing if main window is realized from back-end
879 return xBoardWindow != 0;
885 extern Option dualOptions[];
887 Window tmp = xBoardWindow;
888 if(!dual) dual = XtWindow(dualOptions[3].handle); // must be first call
889 xBoardWindow = dual; // swap them
894 PopUpStartupDialog ()
895 { // start menu not implemented in XBoard
899 ConvertToLine (int argc, char **argv)
901 static char line[128*1024], buf[1024];
905 for(i=1; i<argc; i++)
907 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
908 && argv[i][0] != '{' )
909 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
911 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
912 strncat(line, buf, 128*1024 - strlen(line) - 1 );
915 line[strlen(line)-1] = NULLCHAR;
919 //--------------------------------------------------------------------------------------------
921 #define BoardSize int
923 InitDrawingSizes (BoardSize boardSize, int flags)
924 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
925 Dimension boardWidth, boardHeight, w, h;
927 static Dimension oldWidth, oldHeight;
928 static VariantClass oldVariant;
929 static int oldMono = -1, oldTwoBoards = 0;
931 if(!formWidget) return;
933 if(oldTwoBoards && !twoBoards) PopDown(DummyDlg);
934 oldTwoBoards = twoBoards;
936 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
937 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
938 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
940 if(boardWidth != oldWidth || boardHeight != oldHeight) { // do resizing stuff only if size actually changed
942 oldWidth = boardWidth; oldHeight = boardHeight;
946 * Inhibit shell resizing.
948 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW ;
949 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
950 shellArgs[4].value = shellArgs[2].value = w;
951 shellArgs[5].value = shellArgs[3].value = h;
952 XtSetValues(shellWidget, &shellArgs[0], 6);
954 XSync(xDisplay, False);
958 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
961 if(gameInfo.variant != oldVariant) { // and only if variant changed
966 for(p=0; p<=(int)WhiteKing; p++)
967 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
968 if(gameInfo.variant == VariantShogi) {
969 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
970 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
971 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
972 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
973 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
976 if(gameInfo.variant == VariantGothic) {
977 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
980 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
981 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
982 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
985 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
986 for(p=0; p<=(int)WhiteKing; p++)
987 ximMaskPm[p] = ximMaskPm2[p]; // defaults
988 if(gameInfo.variant == VariantShogi) {
989 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
990 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
991 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
992 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
993 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
996 if(gameInfo.variant == VariantGothic) {
997 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1000 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1001 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1002 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1007 for(i=0; i<2; i++) {
1009 for(p=0; p<=(int)WhiteKing; p++)
1010 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1011 if(gameInfo.variant == VariantShogi) {
1012 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1013 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1014 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1015 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1016 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1019 if(gameInfo.variant == VariantGothic) {
1020 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1023 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1024 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1025 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1029 oldMono = -10; // kludge to force recreation of animation masks
1030 oldVariant = gameInfo.variant;
1033 if(appData.monoMode != oldMono)
1036 oldMono = appData.monoMode;
1040 MakeOneColor (char *name, Pixel *color)
1042 XrmValue vFrom, vTo;
1043 if (!appData.monoMode) {
1044 vFrom.addr = (caddr_t) name;
1045 vFrom.size = strlen(name);
1046 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1047 if (vTo.addr == NULL) {
1048 appData.monoMode = True;
1051 *color = *(Pixel *) vTo.addr;
1059 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1060 int forceMono = False;
1062 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1063 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1064 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1065 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1066 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1067 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1068 if (appData.lowTimeWarning)
1069 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
1070 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1071 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1078 { // [HGM] taken out of main
1080 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1081 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1082 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1084 if (appData.bitmapDirectory[0] != NULLCHAR) {
1088 CreateXPMBoard(appData.liteBackTextureFile, 1);
1089 CreateXPMBoard(appData.darkBackTextureFile, 0);
1093 /* Create regular pieces */
1094 if (!useImages) CreatePieces();
1099 InitDrawingParams ()
1101 MakeColors(); CreateGCs(True);
1106 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
1107 { // detervtomine what fonts to use, and create them
1111 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1112 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1113 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1114 appData.font = fontTable[MESSAGE_FONT][squareSize];
1115 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1116 appData.coordFont = fontTable[COORD_FONT][squareSize];
1119 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1120 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1121 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1122 fontSet = CreateFontSet(appData.font);
1123 clockFontSet = CreateFontSet(appData.clockFont);
1125 /* For the coordFont, use the 0th font of the fontset. */
1126 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1127 XFontStruct **font_struct_list;
1128 XFontSetExtents *fontSize;
1129 char **font_name_list;
1130 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1131 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1132 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1133 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1134 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1137 appData.font = FindFont(appData.font, fontPxlSize);
1138 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1139 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1140 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1141 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1142 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1143 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1144 // textHeight in !NLS mode!
1146 countFontID = coordFontID; // [HGM] holdings
1147 countFontStruct = coordFontStruct;
1149 xdb = XtDatabase(xDisplay);
1151 XrmPutLineResource(&xdb, "*international: True");
1152 vTo.size = sizeof(XFontSet);
1153 vTo.addr = (XtPointer) &fontSet;
1154 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1156 XrmPutStringResource(&xdb, "*font", appData.font);
1161 PrintArg (ArgType t)
1166 case ArgInt: p = " N"; break;
1167 case ArgString: p = " STR"; break;
1168 case ArgBoolean: p = " TF"; break;
1169 case ArgSettingsFilename:
1170 case ArgFilename: p = " FILE"; break;
1171 case ArgX: p = " Nx"; break;
1172 case ArgY: p = " Ny"; break;
1173 case ArgAttribs: p = " TEXTCOL"; break;
1174 case ArgColor: p = " COL"; break;
1175 case ArgFont: p = " FONT"; break;
1176 case ArgBoardSize: p = " SIZE"; break;
1177 case ArgFloat: p = " FLOAT"; break;
1182 case ArgCommSettings:
1193 ArgDescriptor *q, *p = argDescriptors+5;
1194 printf("\nXBoard accepts the following options:\n"
1195 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
1196 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
1197 " SIZE = board-size spec(s)\n"
1198 " Within parentheses are short forms, or options to set to true or false.\n"
1199 " Persistent options (saved in the settings file) are marked with *)\n\n");
1201 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
1202 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
1203 if(p->save) strcat(buf+len, "*");
1204 for(q=p+1; q->argLoc == p->argLoc; q++) {
1205 if(q->argName[0] == '-') continue;
1206 strcat(buf+len, q == p+1 ? " (" : " ");
1207 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
1209 if(q != p+1) strcat(buf+len, ")");
1211 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
1214 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
1218 main (int argc, char **argv)
1220 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1221 XSetWindowAttributes window_attributes;
1223 Dimension boardWidth, boardHeight, w, h;
1225 int forceMono = False;
1227 srandom(time(0)); // [HGM] book: make random truly random
1229 setbuf(stdout, NULL);
1230 setbuf(stderr, NULL);
1233 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1234 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1238 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1243 programName = strrchr(argv[0], '/');
1244 if (programName == NULL)
1245 programName = argv[0];
1250 XtSetLanguageProc(NULL, NULL, NULL);
1251 if (appData.debugMode) {
1252 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1255 bindtextdomain(PACKAGE, LOCALEDIR);
1256 textdomain(PACKAGE);
1259 appData.boardSize = "";
1260 InitAppData(ConvertToLine(argc, argv));
1262 if (p == NULL) p = "/tmp";
1263 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1264 gameCopyFilename = (char*) malloc(i);
1265 gamePasteFilename = (char*) malloc(i);
1266 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1267 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1269 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1270 static char buf[MSG_SIZ];
1271 EscapeExpand(buf, appData.firstInitString);
1272 appData.firstInitString = strdup(buf);
1273 EscapeExpand(buf, appData.secondInitString);
1274 appData.secondInitString = strdup(buf);
1275 EscapeExpand(buf, appData.firstComputerString);
1276 appData.firstComputerString = strdup(buf);
1277 EscapeExpand(buf, appData.secondComputerString);
1278 appData.secondComputerString = strdup(buf);
1281 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1284 if (chdir(chessDir) != 0) {
1285 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1291 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1292 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1293 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1294 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1297 setbuf(debugFP, NULL);
1300 /* [HGM,HR] make sure board size is acceptable */
1301 if(appData.NrFiles > BOARD_FILES ||
1302 appData.NrRanks > BOARD_RANKS )
1303 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1306 /* This feature does not work; animation needs a rewrite */
1307 appData.highlightDragging = FALSE;
1311 gameInfo.variant = StringToVariant(appData.variant);
1312 InitPosition(FALSE);
1315 XtAppInitialize(&appContext, "XBoard", shellOptions,
1316 XtNumber(shellOptions),
1317 &argc, argv, xboardResources, NULL, 0);
1319 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1320 clientResources, XtNumber(clientResources),
1323 xDisplay = XtDisplay(shellWidget);
1324 xScreen = DefaultScreen(xDisplay);
1325 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1328 * determine size, based on supplied or remembered -size, or screen size
1330 if (isdigit(appData.boardSize[0])) {
1331 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1332 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1333 &fontPxlSize, &smallLayout, &tinyLayout);
1335 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1336 programName, appData.boardSize);
1340 /* Find some defaults; use the nearest known size */
1341 SizeDefaults *szd, *nearest;
1342 int distance = 99999;
1343 nearest = szd = sizeDefaults;
1344 while (szd->name != NULL) {
1345 if (abs(szd->squareSize - squareSize) < distance) {
1347 distance = abs(szd->squareSize - squareSize);
1348 if (distance == 0) break;
1352 if (i < 2) lineGap = nearest->lineGap;
1353 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1354 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1355 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1356 if (i < 6) smallLayout = nearest->smallLayout;
1357 if (i < 7) tinyLayout = nearest->tinyLayout;
1360 SizeDefaults *szd = sizeDefaults;
1361 if (*appData.boardSize == NULLCHAR) {
1362 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1363 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1366 if (szd->name == NULL) szd--;
1367 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1369 while (szd->name != NULL &&
1370 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1371 if (szd->name == NULL) {
1372 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1373 programName, appData.boardSize);
1377 squareSize = szd->squareSize;
1378 lineGap = szd->lineGap;
1379 clockFontPxlSize = szd->clockFontPxlSize;
1380 coordFontPxlSize = szd->coordFontPxlSize;
1381 fontPxlSize = szd->fontPxlSize;
1382 smallLayout = szd->smallLayout;
1383 tinyLayout = szd->tinyLayout;
1384 // [HGM] font: use defaults from settings file if available and not overruled
1387 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1388 if (strlen(appData.pixmapDirectory) > 0) {
1389 p = ExpandPathName(appData.pixmapDirectory);
1391 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1392 appData.pixmapDirectory);
1395 if (appData.debugMode) {
1396 fprintf(stderr, _("\
1397 XBoard square size (hint): %d\n\
1398 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1400 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1401 if (appData.debugMode) {
1402 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1405 defaultLineGap = lineGap;
1406 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1408 /* [HR] height treated separately (hacked) */
1409 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1410 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1413 * Determine what fonts to use.
1415 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1418 * Detect if there are not enough colors available and adapt.
1420 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1421 appData.monoMode = True;
1424 forceMono = MakeColors();
1427 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1429 appData.monoMode = True;
1432 if (appData.monoMode && appData.debugMode) {
1433 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1434 (unsigned long) XWhitePixel(xDisplay, xScreen),
1435 (unsigned long) XBlackPixel(xDisplay, xScreen));
1438 ParseIcsTextColors();
1440 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1446 layoutName = "tinyLayout";
1447 } else if (smallLayout) {
1448 layoutName = "smallLayout";
1450 layoutName = "normalLayout";
1453 optList = BoardPopUp(squareSize, lineGap, (void*)
1459 boardWidget = optList[W_BOARD].handle;
1460 menuBarWidget = optList[W_MENU].handle;
1461 dropMenu = optList[W_DROP].handle;
1462 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1463 formWidget = XtParent(boardWidget);
1464 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1465 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1466 XtGetValues(optList[W_WHITE].handle, args, 2);
1467 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1468 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1469 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1470 XtGetValues(optList[W_PAUSE].handle, args, 2);
1472 AppendEnginesToMenu(appData.recentEngineList);
1474 xBoardWindow = XtWindow(boardWidget);
1476 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1477 // not need to go into InitDrawingSizes().
1480 * Create X checkmark bitmap and initialize option menu checks.
1482 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1483 checkmark_bits, checkmark_width, checkmark_height);
1489 ReadBitmap(&wIconPixmap, "icon_white.bm",
1490 icon_white_bits, icon_white_width, icon_white_height);
1491 ReadBitmap(&bIconPixmap, "icon_black.bm",
1492 icon_black_bits, icon_black_width, icon_black_height);
1493 iconPixmap = wIconPixmap;
1495 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1496 XtSetValues(shellWidget, args, i);
1499 * Create a cursor for the board widget.
1501 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1502 XChangeWindowAttributes(xDisplay, xBoardWindow,
1503 CWCursor, &window_attributes);
1506 * Inhibit shell resizing.
1508 shellArgs[0].value = (XtArgVal) &w;
1509 shellArgs[1].value = (XtArgVal) &h;
1510 XtGetValues(shellWidget, shellArgs, 2);
1511 shellArgs[4].value = shellArgs[2].value = w;
1512 shellArgs[5].value = shellArgs[3].value = h;
1513 XtSetValues(shellWidget, &shellArgs[2], 4);
1514 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1515 marginH = h - boardHeight;
1517 CatchDeleteWindow(shellWidget, "QuitProc");
1523 if(appData.logoSize)
1524 { // locate and read user logo
1526 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1527 ASSIGN(userLogo, buf);
1530 if (appData.animate || appData.animateDragging)
1533 XtAugmentTranslations(formWidget,
1534 XtParseTranslationTable(globalTranslations));
1536 XtAddEventHandler(formWidget, KeyPressMask, False,
1537 (XtEventHandler) MoveTypeInProc, NULL);
1538 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1539 (XtEventHandler) EventProc, NULL);
1541 /* [AS] Restore layout */
1542 if( wpMoveHistory.visible ) {
1546 if( wpEvalGraph.visible )
1551 if( wpEngineOutput.visible ) {
1552 EngineOutputPopUp();
1557 if (errorExitStatus == -1) {
1558 if (appData.icsActive) {
1559 /* We now wait until we see "login:" from the ICS before
1560 sending the logon script (problems with timestamp otherwise) */
1561 /*ICSInitScript();*/
1562 if (appData.icsInputBox) ICSInputBoxPopUp();
1566 signal(SIGWINCH, TermSizeSigHandler);
1568 signal(SIGINT, IntSigHandler);
1569 signal(SIGTERM, IntSigHandler);
1570 if (*appData.cmailGameName != NULLCHAR) {
1571 signal(SIGUSR1, CmailSigHandler);
1575 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1578 // XtSetKeyboardFocus(shellWidget, formWidget);
1579 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1581 XtAppMainLoop(appContext);
1582 if (appData.debugMode) fclose(debugFP); // [DM] debug
1587 TermSizeSigHandler (int sig)
1593 IntSigHandler (int sig)
1599 CmailSigHandler (int sig)
1604 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1606 /* Activate call-back function CmailSigHandlerCallBack() */
1607 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1609 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1613 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1616 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1618 /**** end signal code ****/
1621 #define Abs(n) ((n)<0 ? -(n) : (n))
1625 InsertPxlSize (char *pattern, int targetPxlSize)
1627 char *base_fnt_lst, strInt[12], *p, *q;
1628 int alternatives, i, len, strIntLen;
1631 * Replace the "*" (if present) in the pixel-size slot of each
1632 * alternative with the targetPxlSize.
1636 while ((p = strchr(p, ',')) != NULL) {
1640 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1641 strIntLen = strlen(strInt);
1642 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1646 while (alternatives--) {
1647 char *comma = strchr(p, ',');
1648 for (i=0; i<14; i++) {
1649 char *hyphen = strchr(p, '-');
1651 if (comma && hyphen > comma) break;
1652 len = hyphen + 1 - p;
1653 if (i == 7 && *p == '*' && len == 2) {
1655 memcpy(q, strInt, strIntLen);
1665 len = comma + 1 - p;
1672 return base_fnt_lst;
1676 CreateFontSet (char *base_fnt_lst)
1679 char **missing_list;
1683 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1684 &missing_list, &missing_count, &def_string);
1685 if (appData.debugMode) {
1687 XFontStruct **font_struct_list;
1688 char **font_name_list;
1689 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1691 fprintf(debugFP, " got list %s, locale %s\n",
1692 XBaseFontNameListOfFontSet(fntSet),
1693 XLocaleOfFontSet(fntSet));
1694 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1695 for (i = 0; i < count; i++) {
1696 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1699 for (i = 0; i < missing_count; i++) {
1700 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1703 if (fntSet == NULL) {
1704 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1709 #else // not ENABLE_NLS
1711 * Find a font that matches "pattern" that is as close as
1712 * possible to the targetPxlSize. Prefer fonts that are k
1713 * pixels smaller to fonts that are k pixels larger. The
1714 * pattern must be in the X Consortium standard format,
1715 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1716 * The return value should be freed with XtFree when no
1720 FindFont (char *pattern, int targetPxlSize)
1722 char **fonts, *p, *best, *scalable, *scalableTail;
1723 int i, j, nfonts, minerr, err, pxlSize;
1725 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1727 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1728 programName, pattern);
1735 for (i=0; i<nfonts; i++) {
1738 if (*p != '-') continue;
1740 if (*p == NULLCHAR) break;
1741 if (*p++ == '-') j++;
1743 if (j < 7) continue;
1746 scalable = fonts[i];
1749 err = pxlSize - targetPxlSize;
1750 if (Abs(err) < Abs(minerr) ||
1751 (minerr > 0 && err < 0 && -err == minerr)) {
1757 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1758 /* If the error is too big and there is a scalable font,
1759 use the scalable font. */
1760 int headlen = scalableTail - scalable;
1761 p = (char *) XtMalloc(strlen(scalable) + 10);
1762 while (isdigit(*scalableTail)) scalableTail++;
1763 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1765 p = (char *) XtMalloc(strlen(best) + 2);
1766 safeStrCpy(p, best, strlen(best)+1 );
1768 if (appData.debugMode) {
1769 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1770 pattern, targetPxlSize, p);
1772 XFreeFontNames(fonts);
1779 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
1780 // must be called before all non-first callse to CreateGCs()
1781 XtReleaseGC(shellWidget, highlineGC);
1782 XtReleaseGC(shellWidget, lightSquareGC);
1783 XtReleaseGC(shellWidget, darkSquareGC);
1784 XtReleaseGC(shellWidget, lineGC);
1785 if (appData.monoMode) {
1786 if (DefaultDepth(xDisplay, xScreen) == 1) {
1787 XtReleaseGC(shellWidget, wbPieceGC);
1789 XtReleaseGC(shellWidget, bwPieceGC);
1792 XtReleaseGC(shellWidget, prelineGC);
1793 XtReleaseGC(shellWidget, wdPieceGC);
1794 XtReleaseGC(shellWidget, wlPieceGC);
1795 XtReleaseGC(shellWidget, bdPieceGC);
1796 XtReleaseGC(shellWidget, blPieceGC);
1801 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
1803 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1804 | GCBackground | GCFunction | GCPlaneMask;
1805 gc_values->foreground = foreground;
1806 gc_values->background = background;
1807 return XtGetGC(shellWidget, value_mask, gc_values);
1811 CreateGCs (int redo)
1813 XGCValues gc_values;
1815 Pixel white = XWhitePixel(xDisplay, xScreen);
1816 Pixel black = XBlackPixel(xDisplay, xScreen);
1818 gc_values.plane_mask = AllPlanes;
1819 gc_values.line_width = lineGap;
1820 gc_values.line_style = LineSolid;
1821 gc_values.function = GXcopy;
1824 DeleteGCs(); // called a second time; clean up old GCs first
1825 } else { // [HGM] grid and font GCs created on first call only
1826 coordGC = CreateOneGC(&gc_values, black, white);
1827 XSetFont(xDisplay, coordGC, coordFontID);
1829 // [HGM] make font for holdings counts (white on black)
1830 countGC = CreateOneGC(&gc_values, white, black);
1831 XSetFont(xDisplay, countGC, countFontID);
1833 lineGC = CreateOneGC(&gc_values, black, black);
1835 if (appData.monoMode) {
1837 highlineGC = CreateOneGC(&gc_values, white, white);
1838 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
1839 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
1841 if (DefaultDepth(xDisplay, xScreen) == 1) {
1842 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
1843 gc_values.function = GXcopyInverted;
1844 copyInvertedGC = CreateOneGC(&gc_values, black, white);
1845 gc_values.function = GXcopy;
1846 if (XBlackPixel(xDisplay, xScreen) == 1) {
1847 bwPieceGC = darkSquareGC;
1848 wbPieceGC = copyInvertedGC;
1850 bwPieceGC = copyInvertedGC;
1851 wbPieceGC = lightSquareGC;
1856 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
1857 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
1858 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
1859 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
1860 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
1861 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
1862 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
1863 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
1868 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
1876 fp = fopen(filename, "rb");
1878 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
1885 for (y=0; y<h; ++y) {
1886 for (x=0; x<h; ++x) {
1891 XPutPixel(xim, x, y, blackPieceColor);
1893 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1896 XPutPixel(xim, x, y, darkSquareColor);
1898 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1901 XPutPixel(xim, x, y, whitePieceColor);
1903 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1906 XPutPixel(xim, x, y, lightSquareColor);
1908 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1916 /* create Pixmap of piece */
1917 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1919 XPutImage(xDisplay, *dest, lightSquareGC, xim,
1922 /* create Pixmap of clipmask
1923 Note: We assume the white/black pieces have the same
1924 outline, so we make only 6 masks. This is okay
1925 since the XPM clipmask routines do the same. */
1927 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1929 XPutImage(xDisplay, temp, lightSquareGC, xmask,
1932 /* now create the 1-bit version */
1933 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1936 values.foreground = 1;
1937 values.background = 0;
1939 /* Don't use XtGetGC, not read only */
1940 maskGC = XCreateGC(xDisplay, *mask,
1941 GCForeground | GCBackground, &values);
1942 XCopyPlane(xDisplay, temp, *mask, maskGC,
1943 0, 0, squareSize, squareSize, 0, 0, 1);
1944 XFreePixmap(xDisplay, temp);
1949 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
1957 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
1962 /* The XSynchronize calls were copied from CreatePieces.
1963 Not sure if needed, but can't hurt */
1964 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
1967 /* temp needed by loadXIM() */
1968 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1969 0, 0, ss, ss, AllPlanes, XYPixmap);
1971 if (strlen(appData.pixmapDirectory) == 0) {
1975 if (appData.monoMode) {
1976 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
1980 fprintf(stderr, _("\nLoading XIMs...\n"));
1982 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
1983 fprintf(stderr, "%d", piece+1);
1984 for (kind=0; kind<4; kind++) {
1985 fprintf(stderr, ".");
1986 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
1987 ExpandPathName(appData.pixmapDirectory),
1988 piece <= (int) WhiteKing ? "" : "w",
1989 pieceBitmapNames[piece],
1991 ximPieceBitmap[kind][piece] =
1992 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1993 0, 0, ss, ss, AllPlanes, XYPixmap);
1994 if (appData.debugMode)
1995 fprintf(stderr, _("(File:%s:) "), buf);
1996 loadXIM(ximPieceBitmap[kind][piece],
1998 &(xpmPieceBitmap2[kind][piece]),
1999 &(ximMaskPm2[piece]));
2000 if(piece <= (int)WhiteKing)
2001 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2003 fprintf(stderr," ");
2005 /* Load light and dark squares */
2006 /* If the LSQ and DSQ pieces don't exist, we will
2007 draw them with solid squares. */
2008 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2009 if (access(buf, 0) != 0) {
2013 fprintf(stderr, _("light square "));
2015 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2016 0, 0, ss, ss, AllPlanes, XYPixmap);
2017 if (appData.debugMode)
2018 fprintf(stderr, _("(File:%s:) "), buf);
2020 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2021 fprintf(stderr, _("dark square "));
2022 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2023 ExpandPathName(appData.pixmapDirectory), ss);
2024 if (appData.debugMode)
2025 fprintf(stderr, _("(File:%s:) "), buf);
2027 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2028 0, 0, ss, ss, AllPlanes, XYPixmap);
2029 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2030 xpmJailSquare = xpmLightSquare;
2032 fprintf(stderr, _("Done.\n"));
2034 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2037 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2041 CreateXPMBoard (char *s, int kind)
2045 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2046 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2047 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2053 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2054 // thisroutine has to be called t free the old piece pixmaps
2056 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2057 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2059 XFreePixmap(xDisplay, xpmLightSquare);
2060 XFreePixmap(xDisplay, xpmDarkSquare);
2069 u_int ss = squareSize;
2071 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2072 XpmColorSymbol symbols[4];
2073 static int redo = False;
2075 if(redo) FreeXPMPieces(); else redo = 1;
2077 /* The XSynchronize calls were copied from CreatePieces.
2078 Not sure if needed, but can't hurt */
2079 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2081 /* Setup translations so piece colors match square colors */
2082 symbols[0].name = "light_piece";
2083 symbols[0].value = appData.whitePieceColor;
2084 symbols[1].name = "dark_piece";
2085 symbols[1].value = appData.blackPieceColor;
2086 symbols[2].name = "light_square";
2087 symbols[2].value = appData.lightSquareColor;
2088 symbols[3].name = "dark_square";
2089 symbols[3].value = appData.darkSquareColor;
2091 attr.valuemask = XpmColorSymbols;
2092 attr.colorsymbols = symbols;
2093 attr.numsymbols = 4;
2095 if (appData.monoMode) {
2096 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2100 if (strlen(appData.pixmapDirectory) == 0) {
2101 XpmPieces* pieces = builtInXpms;
2104 while (pieces->size != squareSize && pieces->size) pieces++;
2105 if (!pieces->size) {
2106 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2109 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2110 for (kind=0; kind<4; kind++) {
2112 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2113 pieces->xpm[piece][kind],
2114 &(xpmPieceBitmap2[kind][piece]),
2115 NULL, &attr)) != 0) {
2116 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2120 if(piece <= (int) WhiteKing)
2121 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2125 xpmJailSquare = xpmLightSquare;
2129 fprintf(stderr, _("\nLoading XPMs...\n"));
2132 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2133 fprintf(stderr, "%d ", piece+1);
2134 for (kind=0; kind<4; kind++) {
2135 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2136 ExpandPathName(appData.pixmapDirectory),
2137 piece > (int) WhiteKing ? "w" : "",
2138 pieceBitmapNames[piece],
2140 if (appData.debugMode) {
2141 fprintf(stderr, _("(File:%s:) "), buf);
2143 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2144 &(xpmPieceBitmap2[kind][piece]),
2145 NULL, &attr)) != 0) {
2146 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2147 // [HGM] missing: read of unorthodox piece failed; substitute King.
2148 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2149 ExpandPathName(appData.pixmapDirectory),
2151 if (appData.debugMode) {
2152 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2154 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2155 &(xpmPieceBitmap2[kind][piece]),
2159 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2164 if(piece <= (int) WhiteKing)
2165 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2168 /* Load light and dark squares */
2169 /* If the LSQ and DSQ pieces don't exist, we will
2170 draw them with solid squares. */
2171 fprintf(stderr, _("light square "));
2172 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2173 if (access(buf, 0) != 0) {
2177 if (appData.debugMode)
2178 fprintf(stderr, _("(File:%s:) "), buf);
2180 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2181 &xpmLightSquare, NULL, &attr)) != 0) {
2182 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2185 fprintf(stderr, _("dark square "));
2186 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2187 ExpandPathName(appData.pixmapDirectory), ss);
2188 if (appData.debugMode) {
2189 fprintf(stderr, _("(File:%s:) "), buf);
2191 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2192 &xpmDarkSquare, NULL, &attr)) != 0) {
2193 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2197 xpmJailSquare = xpmLightSquare;
2198 fprintf(stderr, _("Done.\n"));
2200 oldVariant = -1; // kludge to force re-makig of animation masks
2201 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2204 #endif /* HAVE_LIBXPM */
2207 /* No built-in bitmaps */
2212 u_int ss = squareSize;
2214 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2217 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2218 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2219 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2220 pieceBitmapNames[piece],
2221 ss, kind == SOLID ? 's' : 'o');
2222 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2223 if(piece <= (int)WhiteKing)
2224 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2228 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2232 /* With built-in bitmaps */
2236 BuiltInBits* bib = builtInBits;
2239 u_int ss = squareSize;
2241 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2244 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2246 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2247 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2248 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2249 pieceBitmapNames[piece],
2250 ss, kind == SOLID ? 's' : 'o');
2251 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2252 bib->bits[kind][piece], ss, ss);
2253 if(piece <= (int)WhiteKing)
2254 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2258 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2264 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2269 char msg[MSG_SIZ], fullname[MSG_SIZ];
2271 if (*appData.bitmapDirectory != NULLCHAR) {
2272 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2273 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2274 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2275 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2276 &w, &h, pm, &x_hot, &y_hot);
2277 fprintf(stderr, "load %s\n", name);
2278 if (errcode != BitmapSuccess) {
2280 case BitmapOpenFailed:
2281 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2283 case BitmapFileInvalid:
2284 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2286 case BitmapNoMemory:
2287 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2291 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2295 fprintf(stderr, _("%s: %s...using built-in\n"),
2297 } else if (w != wreq || h != hreq) {
2299 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2300 programName, fullname, w, h, wreq, hreq);
2306 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2316 if (lineGap == 0) return;
2318 /* [HR] Split this into 2 loops for non-square boards. */
2320 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2321 gridSegments[i].x1 = 0;
2322 gridSegments[i].x2 =
2323 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2324 gridSegments[i].y1 = gridSegments[i].y2
2325 = lineGap / 2 + (i * (squareSize + lineGap));
2328 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2329 gridSegments[j + i].y1 = 0;
2330 gridSegments[j + i].y2 =
2331 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2332 gridSegments[j + i].x1 = gridSegments[j + i].x2
2333 = lineGap / 2 + (j * (squareSize + lineGap));
2338 MarkMenuItem (char *menuRef, int state)
2340 MenuItem *item = MenuNameToItem(menuRef);
2344 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2345 XtSetValues(item->handle, args, 1);
2350 EnableNamedMenuItem (char *menuRef, int state)
2352 MenuItem *item = MenuNameToItem(menuRef);
2354 if(item) XtSetSensitive(item->handle, state);
2358 EnableButtonBar (int state)
2360 XtSetSensitive(optList[W_BUTTON].handle, state);
2365 SetMenuEnables (Enables *enab)
2367 while (enab->name != NULL) {
2368 EnableNamedMenuItem(enab->name, enab->value);
2374 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2375 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2377 if(*nprms == 0) return;
2378 item = MenuNameToItem(prms[0]);
2379 if(item) ((MenuProc *) item->proc) ();
2383 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2385 RecentEngineEvent((int) (intptr_t) addr);
2389 AppendMenuItem (char *msg, int n)
2391 CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
2403 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2404 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2405 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2406 dmEnables[i].piece);
2407 XtSetSensitive(entry, p != NULL || !appData.testLegality
2408 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2409 && !appData.icsActive));
2411 while (p && *p++ == dmEnables[i].piece) count++;
2412 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2414 XtSetArg(args[j], XtNlabel, label); j++;
2415 XtSetValues(entry, args, j);
2421 do_flash_delay (unsigned long msec)
2427 DrawBorder (int x, int y, int type)
2431 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
2433 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
2434 squareSize+lineGap, squareSize+lineGap);
2438 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
2440 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
2441 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
2443 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
2444 if(textureW[kind] < W*squareSize)
2445 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
2447 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
2448 if(textureH[kind] < H*squareSize)
2449 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
2451 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
2455 #include <cairo/cairo.h>
2456 #include <cairo/cairo-xlib.h>
2459 DrawLogo (void *handle, void *logo)
2461 cairo_surface_t *img, *cs;
2465 if(!logo || !handle) return;
2466 cs = cairo_xlib_surface_create(xDisplay, XtWindow(handle), DefaultVisual(xDisplay, 0), appData.logoSize, appData.logoSize/2);
2467 img = cairo_image_surface_create_from_png (logo);
2468 w = cairo_image_surface_get_width (img);
2469 h = cairo_image_surface_get_height (img);
2470 cr = cairo_create(cs);
2471 cairo_scale(cr, (float)appData.logoSize/w, appData.logoSize/(2.*h));
2472 cairo_set_source_surface (cr, img, 0, 0);
2475 cairo_surface_destroy (img);
2476 cairo_surface_destroy (cs);
2480 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
2481 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
2483 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
2484 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
2485 squareSize, squareSize, x*fac, y*fac);
2487 if (useImages && useImageSqs) {
2491 pm = xpmLightSquare;
2496 case 2: /* neutral */
2498 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
2501 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
2502 squareSize, squareSize, x*fac, y*fac);
2512 case 2: /* neutral */
2517 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
2522 I split out the routines to draw a piece so that I could
2523 make a generic flash routine.
2526 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2528 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2529 switch (square_color) {
2531 case 2: /* neutral */
2533 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2534 ? *pieceToOutline(piece)
2535 : *pieceToSolid(piece),
2536 dest, bwPieceGC, 0, 0,
2537 squareSize, squareSize, x, y);
2540 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2541 ? *pieceToSolid(piece)
2542 : *pieceToOutline(piece),
2543 dest, wbPieceGC, 0, 0,
2544 squareSize, squareSize, x, y);
2550 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2552 switch (square_color) {
2554 case 2: /* neutral */
2556 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2557 ? *pieceToOutline(piece)
2558 : *pieceToSolid(piece),
2559 dest, bwPieceGC, 0, 0,
2560 squareSize, squareSize, x, y, 1);
2563 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2564 ? *pieceToSolid(piece)
2565 : *pieceToOutline(piece),
2566 dest, wbPieceGC, 0, 0,
2567 squareSize, squareSize, x, y, 1);
2573 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2575 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
2576 switch (square_color) {
2578 XCopyPlane(xDisplay, *pieceToSolid(piece),
2579 dest, (int) piece < (int) BlackPawn
2580 ? wlPieceGC : blPieceGC, 0, 0,
2581 squareSize, squareSize, x, y, 1);
2584 XCopyPlane(xDisplay, *pieceToSolid(piece),
2585 dest, (int) piece < (int) BlackPawn
2586 ? wdPieceGC : bdPieceGC, 0, 0,
2587 squareSize, squareSize, x, y, 1);
2589 case 2: /* neutral */
2591 break; // should never contain pieces
2596 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2598 int kind, p = piece;
2600 switch (square_color) {
2602 case 2: /* neutral */
2604 if ((int)piece < (int) BlackPawn) {
2612 if ((int)piece < (int) BlackPawn) {
2620 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2621 if(useTexture & square_color+1) {
2622 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2623 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
2624 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
2625 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
2626 XSetClipMask(xDisplay, wlPieceGC, None);
2627 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
2629 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
2630 dest, wlPieceGC, 0, 0,
2631 squareSize, squareSize, x, y);
2634 typedef void (*DrawFunc)();
2639 if (appData.monoMode) {
2640 if (DefaultDepth(xDisplay, xScreen) == 1) {
2641 return monoDrawPiece_1bit;
2643 return monoDrawPiece;
2647 return colorDrawPieceImage;
2649 return colorDrawPiece;
2654 DrawDot (int marker, int x, int y, int r)
2656 if(appData.monoMode) {
2657 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
2658 x, y, r, r, 0, 64*360);
2659 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
2660 x, y, r, r, 0, 64*360);
2662 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
2663 x, y, r, r, 0, 64*360);
2667 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2668 { // basic front-end board-draw function: takes care of everything that can be in square:
2669 // piece, background, coordinate/count, marker dot
2670 int direction, font_ascent, font_descent;
2671 XCharStruct overall;
2674 if (piece == EmptySquare) {
2675 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2677 drawfunc = ChooseDrawFunc();
2678 drawfunc(piece, square_color, x, y, xBoardWindow);
2681 if(align) { // square carries inscription (coord or piece count)
2683 GC hGC = align < 3 ? coordGC : countGC;
2684 // first calculate where it goes
2685 XTextExtents(countFontStruct, string, 1, &direction,
2686 &font_ascent, &font_descent, &overall);
2688 xx += squareSize - overall.width - 2;
2689 yy += squareSize - font_descent - 1;
2690 } else if (align == 2) {
2691 xx += 2, yy += font_ascent + 1;
2692 } else if (align == 3) {
2693 xx += squareSize - overall.width - 2;
2694 yy += font_ascent + 1;
2695 } else if (align == 4) {
2696 xx += 2, yy += font_ascent + 1;
2699 if (appData.monoMode) {
2700 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2702 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2706 if(marker) { // print fat marker dot, if requested
2707 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2712 FlashDelay (int flash_delay)
2714 XSync(xDisplay, False);
2715 if(flash_delay) do_flash_delay(flash_delay);
2719 Fraction (int x, int start, int stop)
2721 double f = ((double) x - start)/(stop - start);
2722 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2726 static WindowPlacement wpNew;
2729 CoDrag (Widget sh, WindowPlacement *wp)
2732 int j=0, touch=0, fudge = 2;
2733 GetActualPlacement(sh, wp);
2734 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
2735 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
2736 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2737 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
2738 if(!touch ) return; // only windows that touch co-move
2739 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2740 int heightInc = wpNew.height - wpMain.height;
2741 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2742 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2743 wp->y += fracTop * heightInc;
2744 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2745 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2746 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2747 int widthInc = wpNew.width - wpMain.width;
2748 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2749 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2750 wp->y += fracLeft * widthInc;
2751 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2752 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2754 wp->x += wpNew.x - wpMain.x;
2755 wp->y += wpNew.y - wpMain.y;
2756 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2757 if(touch == 3) wp->y += wpNew.height - wpMain.height;
2758 XtSetArg(args[j], XtNx, wp->x); j++;
2759 XtSetArg(args[j], XtNy, wp->y); j++;
2760 XtSetValues(sh, args, j);
2763 static XtIntervalId delayedDragID = 0;
2768 GetActualPlacement(shellWidget, &wpNew);
2769 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
2770 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
2771 return; // false alarm
2772 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
2773 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
2774 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
2775 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
2777 DrawPosition(True, NULL);
2778 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
2785 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
2787 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
2791 EventProc (Widget widget, caddr_t unused, XEvent *event)
2793 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
2794 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
2797 // [HGM] seekgraph: some low-level drawing routines (by JC, mostly)
2799 static cairo_surface_t *cs; // to keep out of back-end :-(
2801 void DrawSeekAxis( int x, int y, int xTo, int yTo )
2806 cr = cairo_create (cs);
2808 cairo_move_to (cr, x, y);
2809 cairo_line_to(cr, xTo, yTo );
2811 cairo_set_line_width(cr, 2);
2812 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0);
2819 void DrawSeekBackground( int left, int top, int right, int bottom )
2821 cairo_t *cr = cairo_create (cs);
2823 cairo_rectangle (cr, left, top, right-left, bottom-top);
2825 cairo_set_source_rgba(cr, 0.8, 0.8, 0.4,1.0);
2832 void DrawSeekText(char *buf, int x, int y)
2834 cairo_t *cr = cairo_create (cs);
2836 cairo_select_font_face (cr, "Sans",
2837 CAIRO_FONT_SLANT_NORMAL,
2838 CAIRO_FONT_WEIGHT_NORMAL);
2840 cairo_set_font_size (cr, 12.0);
2842 cairo_move_to (cr, x, y+4);
2843 cairo_show_text( cr, buf);
2845 cairo_set_source_rgba(cr, 0, 0, 0,1.0);
2852 void DrawSeekDot(int x, int y, int colorNr)
2854 cairo_t *cr = cairo_create (cs);
2855 int square = colorNr & 0x80;
2859 cairo_rectangle (cr, x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
2861 cairo_arc(cr, x, y, squareSize/8, 0.0, 2*M_PI);
2863 cairo_set_line_width(cr, 2);
2864 cairo_set_source_rgba(cr, 0, 0, 0,1.0);
2865 cairo_stroke_preserve(cr);
2867 case 0: cairo_set_source_rgba(cr, 1.0, 0, 0,1.0); break;
2868 case 1: cairo_set_source_rgba (cr, 0.0, 0.7, 0.2, 1.0); break;
2869 default: cairo_set_source_rgba (cr, 1.0, 1.0, 0.0, 1.0); break;
2880 int boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2881 int boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2882 cs = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), boardWidth, boardHeight);
2888 cairo_surface_destroy(cs);
2894 XDrawSegments(xDisplay, xBoardWindow, lineGC,
2895 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
2900 * event handler for redrawing the board
2903 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2905 DrawPosition(True, NULL);
2910 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
2911 { // [HGM] pv: walk PV
2912 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
2915 static int savedIndex; /* gross that this is global */
2918 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
2921 XawTextPosition index, dummy;
2924 XawTextGetSelectionPos(w, &index, &dummy);
2925 XtSetArg(arg, XtNstring, &val);
2926 XtGetValues(w, &arg, 1);
2927 ReplaceComment(savedIndex, val);
2928 if(savedIndex != currentMove) ToNrEvent(savedIndex);
2929 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
2933 EditCommentPopUp (int index, char *title, char *text)
2936 if (text == NULL) text = "";
2937 NewCommentPopup(title, text, index);
2941 CommentPopUp (char *title, char *text)
2943 savedIndex = currentMove; // [HGM] vari
2944 NewCommentPopup(title, text, currentMove);
2950 PopDown(CommentDlg);
2954 /* Disable all user input other than deleting the window */
2955 static int frozen = 0;
2961 /* Grab by a widget that doesn't accept input */
2962 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
2966 /* Undo a FreezeUI */
2970 if (!frozen) return;
2971 XtRemoveGrab(optList[W_MESSG].handle);
2979 static int oldPausing = FALSE;
2980 static GameMode oldmode = (GameMode) -1;
2983 if (!boardWidget || !XtIsRealized(boardWidget)) return;
2985 if (pausing != oldPausing) {
2986 oldPausing = pausing;
2987 MarkMenuItem("Mode.Pause", pausing);
2989 if (appData.showButtonBar) {
2990 /* Always toggle, don't set. Previous code messes up when
2991 invoked while the button is pressed, as releasing it
2992 toggles the state again. */
2995 XtSetArg(args[0], XtNbackground, &oldbg);
2996 XtSetArg(args[1], XtNforeground, &oldfg);
2997 XtGetValues(optList[W_PAUSE].handle,
2999 XtSetArg(args[0], XtNbackground, oldfg);
3000 XtSetArg(args[1], XtNforeground, oldbg);
3002 XtSetValues(optList[W_PAUSE].handle, args, 2);
3006 wname = ModeToWidgetName(oldmode);
3007 if (wname != NULL) {
3008 MarkMenuItem(wname, False);
3010 wname = ModeToWidgetName(gameMode);
3011 if (wname != NULL) {
3012 MarkMenuItem(wname, True);
3015 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
3017 /* Maybe all the enables should be handled here, not just this one */
3018 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
3020 DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);
3025 * Button/menu procedures
3028 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3029 char *selected_fen_position=NULL;
3032 SendPositionSelection (Widget w, Atom *selection, Atom *target,
3033 Atom *type_return, XtPointer *value_return,
3034 unsigned long *length_return, int *format_return)
3036 char *selection_tmp;
3038 // if (!selected_fen_position) return False; /* should never happen */
3039 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3040 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
3041 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
3044 if (f == NULL) return False;
3048 selection_tmp = XtMalloc(len + 1);
3049 count = fread(selection_tmp, 1, len, f);
3052 XtFree(selection_tmp);
3055 selection_tmp[len] = NULLCHAR;
3057 /* note: since no XtSelectionDoneProc was registered, Xt will
3058 * automatically call XtFree on the value returned. So have to
3059 * make a copy of it allocated with XtMalloc */
3060 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3061 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
3064 *value_return=selection_tmp;
3065 *length_return=strlen(selection_tmp);
3066 *type_return=*target;
3067 *format_return = 8; /* bits per byte */
3069 } else if (*target == XA_TARGETS(xDisplay)) {
3070 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3071 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3072 targets_tmp[1] = XA_STRING;
3073 *value_return = targets_tmp;
3074 *type_return = XA_ATOM;
3077 // This code leads to a read of value_return out of bounds on 64-bit systems.
3078 // Other code which I have seen always sets *format_return to 32 independent of
3079 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
3080 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
3081 *format_return = 8 * sizeof(Atom);
3082 if (*format_return > 32) {
3083 *length_return *= *format_return / 32;
3084 *format_return = 32;
3087 *format_return = 32;
3095 /* note: when called from menu all parameters are NULL, so no clue what the
3096 * Widget which was clicked on was, or what the click event was
3099 CopySomething (char *src)
3101 selected_fen_position = src;
3103 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3104 * have a notion of a position that is selected but not copied.
3105 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3107 XtOwnSelection(menuBarWidget, XA_PRIMARY,
3109 SendPositionSelection,
3110 NULL/* lose_ownership_proc */ ,
3111 NULL/* transfer_done_proc */);
3112 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3114 SendPositionSelection,
3115 NULL/* lose_ownership_proc */ ,
3116 NULL/* transfer_done_proc */);
3119 /* function called when the data to Paste is ready */
3121 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3122 Atom *type, XtPointer value, unsigned long *len, int *format)
3125 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3126 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3127 EditPositionPasteFEN(fenstr);
3131 /* called when Paste Position button is pressed,
3132 * all parameters will be NULL */
3134 PastePositionProc ()
3136 XtGetSelectionValue(menuBarWidget,
3137 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3138 /* (XtSelectionCallbackProc) */ PastePositionCB,
3139 NULL, /* client_data passed to PastePositionCB */
3141 /* better to use the time field from the event that triggered the
3142 * call to this function, but that isn't trivial to get
3149 /* note: when called from menu all parameters are NULL, so no clue what the
3150 * Widget which was clicked on was, or what the click event was
3152 /* function called when the data to Paste is ready */
3154 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3155 Atom *type, XtPointer value, unsigned long *len, int *format)
3158 if (value == NULL || *len == 0) {
3159 return; /* nothing had been selected to copy */
3161 f = fopen(gamePasteFilename, "w");
3163 DisplayError(_("Can't open temp file"), errno);
3166 fwrite(value, 1, *len, f);
3169 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3172 /* called when Paste Game button is pressed,
3173 * all parameters will be NULL */
3177 XtGetSelectionValue(menuBarWidget,
3178 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3179 /* (XtSelectionCallbackProc) */ PasteGameCB,
3180 NULL, /* client_data passed to PasteGameCB */
3182 /* better to use the time field from the event that triggered the
3183 * call to this function, but that isn't trivial to get
3192 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3199 { // bassic primitive for determining if modifier keys are pressed
3200 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3203 XQueryKeymap(xDisplay,keys);
3204 for(i=0; i<6; i++) {
3206 j = XKeysymToKeycode(xDisplay, codes[i]);
3207 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3213 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3217 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3218 if ( n == 1 && *buf >= 32 // printable
3219 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3220 ) BoxAutoPopUp (buf);
3224 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3225 { // [HGM] input: let up-arrow recall previous line from history
3230 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3231 { // [HGM] input: let down-arrow recall next line from history
3236 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3242 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3244 if (!TempBackwardActive) {
3245 TempBackwardActive = True;
3251 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3253 /* Check to see if triggered by a key release event for a repeating key.
3254 * If so the next queued event will be a key press of the same key at the same time */
3255 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3257 XPeekEvent(xDisplay, &next);
3258 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3259 next.xkey.keycode == event->xkey.keycode)
3263 TempBackwardActive = False;
3267 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3268 { // called as key binding
3271 if (nprms && *nprms > 0)
3275 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3281 { // called from menu
3282 ManInner(NULL, NULL, NULL, NULL);
3286 SetWindowTitle (char *text, char *title, char *icon)
3290 if (appData.titleInWindow) {
3292 XtSetArg(args[i], XtNlabel, text); i++;
3293 XtSetValues(titleWidget, args, i);
3296 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3297 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3298 XtSetValues(shellWidget, args, i);
3299 XSync(xDisplay, False);
3304 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3310 DisplayIcsInteractionTitle (String message)
3312 if (oldICSInteractionTitle == NULL) {
3313 /* Magic to find the old window title, adapted from vim */
3314 char *wina = getenv("WINDOWID");
3316 Window win = (Window) atoi(wina);
3317 Window root, parent, *children;
3318 unsigned int nchildren;
3319 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3321 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3322 if (!XQueryTree(xDisplay, win, &root, &parent,
3323 &children, &nchildren)) break;
3324 if (children) XFree((void *)children);
3325 if (parent == root || parent == 0) break;
3328 XSetErrorHandler(oldHandler);
3330 if (oldICSInteractionTitle == NULL) {
3331 oldICSInteractionTitle = "xterm";
3334 printf("\033]0;%s\007", message);
3339 XtIntervalId delayedEventTimerXID = 0;
3340 DelayedEventCallback delayedEventCallback = 0;
3345 delayedEventTimerXID = 0;
3346 delayedEventCallback();
3350 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3352 if(delayedEventTimerXID && delayedEventCallback == cb)
3353 // [HGM] alive: replace, rather than add or flush identical event
3354 XtRemoveTimeOut(delayedEventTimerXID);
3355 delayedEventCallback = cb;
3356 delayedEventTimerXID =
3357 XtAppAddTimeOut(appContext, millisec,
3358 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3361 DelayedEventCallback
3364 if (delayedEventTimerXID) {
3365 return delayedEventCallback;
3372 CancelDelayedEvent ()
3374 if (delayedEventTimerXID) {
3375 XtRemoveTimeOut(delayedEventTimerXID);
3376 delayedEventTimerXID = 0;
3380 XtIntervalId loadGameTimerXID = 0;
3383 LoadGameTimerRunning ()
3385 return loadGameTimerXID != 0;
3389 StopLoadGameTimer ()
3391 if (loadGameTimerXID != 0) {
3392 XtRemoveTimeOut(loadGameTimerXID);
3393 loadGameTimerXID = 0;
3401 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3403 loadGameTimerXID = 0;
3408 StartLoadGameTimer (long millisec)
3411 XtAppAddTimeOut(appContext, millisec,
3412 (XtTimerCallbackProc) LoadGameTimerCallback,
3416 XtIntervalId analysisClockXID = 0;
3419 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3421 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3422 || appData.icsEngineAnalyze) { // [DM]
3423 AnalysisPeriodicEvent(0);
3424 StartAnalysisClock();
3429 StartAnalysisClock ()
3432 XtAppAddTimeOut(appContext, 2000,
3433 (XtTimerCallbackProc) AnalysisClockCallback,
3437 XtIntervalId clockTimerXID = 0;
3440 ClockTimerRunning ()
3442 return clockTimerXID != 0;
3448 if (clockTimerXID != 0) {
3449 XtRemoveTimeOut(clockTimerXID);
3458 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3465 StartClockTimer (long millisec)
3468 XtAppAddTimeOut(appContext, millisec,
3469 (XtTimerCallbackProc) ClockTimerCallback,
3474 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
3478 Widget w = (Widget) opt->handle;
3480 /* check for low time warning */
3481 Pixel foregroundOrWarningColor = timerForegroundPixel;
3484 appData.lowTimeWarning &&
3485 (timer / 1000) < appData.icsAlarmTime)
3486 foregroundOrWarningColor = lowTimeWarningColor;
3488 if (appData.clockMode) {
3489 snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
3490 XtSetArg(args[0], XtNlabel, buf);
3492 snprintf(buf, MSG_SIZ, "%s ", color);
3493 XtSetArg(args[0], XtNlabel, buf);
3498 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3499 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3501 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3502 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3505 XtSetValues(w, args, 3);
3508 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3511 SetClockIcon (int color)
3514 Pixmap pm = *clockIcons[color];
3515 if (iconPixmap != pm) {
3517 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3518 XtSetValues(shellWidget, args, 1);
3523 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3525 InputSource *is = (InputSource *) closure;
3530 if (is->lineByLine) {
3531 count = read(is->fd, is->unused,
3532 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3534 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3537 is->unused += count;
3539 while (p < is->unused) {
3540 q = memchr(p, '\n', is->unused - p);
3541 if (q == NULL) break;
3543 (is->func)(is, is->closure, p, q - p, 0);
3547 while (p < is->unused) {
3552 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3557 (is->func)(is, is->closure, is->buf, count, error);
3562 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3565 ChildProc *cp = (ChildProc *) pr;
3567 is = (InputSource *) calloc(1, sizeof(InputSource));
3568 is->lineByLine = lineByLine;
3572 is->fd = fileno(stdin);
3574 is->kind = cp->kind;
3575 is->fd = cp->fdFrom;
3578 is->unused = is->buf;
3581 is->xid = XtAppAddInput(appContext, is->fd,
3582 (XtPointer) (XtInputReadMask),
3583 (XtInputCallbackProc) DoInputCallback,
3585 is->closure = closure;
3586 return (InputSourceRef) is;
3590 RemoveInputSource (InputSourceRef isr)
3592 InputSource *is = (InputSource *) isr;
3594 if (is->xid == 0) return;
3595 XtRemoveInput(is->xid);
3599 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
3601 /* Masks for XPM pieces. Black and white pieces can have
3602 different shapes, but in the interest of retaining my
3603 sanity pieces must have the same outline on both light
3604 and dark squares, and all pieces must use the same
3605 background square colors/images. */
3607 static int xpmDone = 0;
3608 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
3609 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
3612 CreateAnimMasks (int pieceDepth)
3618 unsigned long plane;
3621 /* Need a bitmap just to get a GC with right depth */
3622 buf = XCreatePixmap(xDisplay, xBoardWindow,
3624 values.foreground = 1;
3625 values.background = 0;
3626 /* Don't use XtGetGC, not read only */
3627 maskGC = XCreateGC(xDisplay, buf,
3628 GCForeground | GCBackground, &values);
3629 XFreePixmap(xDisplay, buf);
3631 buf = XCreatePixmap(xDisplay, xBoardWindow,
3632 squareSize, squareSize, pieceDepth);
3633 values.foreground = XBlackPixel(xDisplay, xScreen);
3634 values.background = XWhitePixel(xDisplay, xScreen);
3635 bufGC = XCreateGC(xDisplay, buf,
3636 GCForeground | GCBackground, &values);
3638 for (piece = WhitePawn; piece <= BlackKing; piece++) {
3639 /* Begin with empty mask */
3640 if(!xpmDone) // [HGM] pieces: keep using existing
3641 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
3642 squareSize, squareSize, 1);
3643 XSetFunction(xDisplay, maskGC, GXclear);
3644 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
3645 0, 0, squareSize, squareSize);
3647 /* Take a copy of the piece */
3652 XSetFunction(xDisplay, bufGC, GXcopy);
3653 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
3655 0, 0, squareSize, squareSize, 0, 0);
3657 /* XOR the background (light) over the piece */
3658 XSetFunction(xDisplay, bufGC, GXxor);
3660 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
3661 0, 0, squareSize, squareSize, 0, 0);
3663 XSetForeground(xDisplay, bufGC, lightSquareColor);
3664 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
3667 /* We now have an inverted piece image with the background
3668 erased. Construct mask by just selecting all the non-zero
3669 pixels - no need to reconstruct the original image. */
3670 XSetFunction(xDisplay, maskGC, GXor);
3672 /* Might be quicker to download an XImage and create bitmap
3673 data from it rather than this N copies per piece, but it
3674 only takes a fraction of a second and there is a much
3675 longer delay for loading the pieces. */
3676 for (n = 0; n < pieceDepth; n ++) {
3677 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
3678 0, 0, squareSize, squareSize,
3684 XFreePixmap(xDisplay, buf);
3685 XFreeGC(xDisplay, bufGC);
3686 XFreeGC(xDisplay, maskGC);
3690 InitAnimState (AnimNr anr, XWindowAttributes *info)
3695 /* Each buffer is square size, same depth as window */
3696 animBufs[anr+4] = xBoardWindow;
3697 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
3698 squareSize, squareSize, info->depth);
3699 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
3700 squareSize, squareSize, info->depth);
3702 /* Create a plain GC for blitting */
3703 mask = GCForeground | GCBackground | GCFunction |
3704 GCPlaneMask | GCGraphicsExposures;
3705 values.foreground = XBlackPixel(xDisplay, xScreen);
3706 values.background = XWhitePixel(xDisplay, xScreen);
3707 values.function = GXcopy;
3708 values.plane_mask = AllPlanes;
3709 values.graphics_exposures = False;
3710 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
3712 /* Piece will be copied from an existing context at
3713 the start of each new animation/drag. */
3714 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
3716 /* Outline will be a read-only copy of an existing */
3717 animGCs[anr+4] = None;
3723 XWindowAttributes info;
3725 if (xpmDone && gameInfo.variant == oldVariant) return;
3726 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
3727 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
3729 InitAnimState(Game, &info);
3730 InitAnimState(Player, &info);
3732 /* For XPM pieces, we need bitmaps to use as masks. */
3734 CreateAnimMasks(info.depth), xpmDone = 1;
3739 static Boolean frameWaiting;
3742 FrameAlarm (int sig)
3744 frameWaiting = False;
3745 /* In case System-V style signals. Needed?? */
3746 signal(SIGALRM, FrameAlarm);
3750 FrameDelay (int time)
3752 struct itimerval delay;
3754 XSync(xDisplay, False);
3757 frameWaiting = True;
3758 signal(SIGALRM, FrameAlarm);
3759 delay.it_interval.tv_sec =
3760 delay.it_value.tv_sec = time / 1000;
3761 delay.it_interval.tv_usec =
3762 delay.it_value.tv_usec = (time % 1000) * 1000;
3763 setitimer(ITIMER_REAL, &delay, NULL);
3764 while (frameWaiting) pause();
3765 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
3766 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
3767 setitimer(ITIMER_REAL, &delay, NULL);
3774 FrameDelay (int time)
3776 XSync(xDisplay, False);
3778 usleep(time * 1000);
3784 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
3788 /* Bitmap for piece being moved. */
3789 if (appData.monoMode) {
3790 *mask = *pieceToSolid(piece);
3791 } else if (useImages) {
3793 *mask = xpmMask[piece];
3795 *mask = ximMaskPm[piece];
3798 *mask = *pieceToSolid(piece);
3801 /* GC for piece being moved. Square color doesn't matter, but
3802 since it gets modified we make a copy of the original. */
3804 if (appData.monoMode)
3809 if (appData.monoMode)
3814 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
3816 /* Outline only used in mono mode and is not modified */
3818 *outline = bwPieceGC;
3820 *outline = wbPieceGC;
3824 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
3829 /* Draw solid rectangle which will be clipped to shape of piece */
3830 XFillRectangle(xDisplay, dest, clip,
3831 0, 0, squareSize, squareSize);
3832 if (appData.monoMode)
3833 /* Also draw outline in contrasting color for black
3834 on black / white on white cases */
3835 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
3836 0, 0, squareSize, squareSize, 0, 0, 1);
3838 /* Copy the piece */
3843 if(appData.upsideDown && flipView) kind ^= 2;
3844 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3846 0, 0, squareSize, squareSize,
3852 InsertPiece (AnimNr anr, ChessSquare piece)
3854 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
3858 DrawBlank (AnimNr anr, int x, int y, int startColor)
3860 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
3863 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
3864 int srcX, int srcY, int width, int height, int destX, int destY)
3866 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
3867 srcX, srcY, width, height, destX, destY);
3871 SetDragPiece (AnimNr anr, ChessSquare piece)
3874 /* The piece will be drawn using its own bitmap as a matte */
3875 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
3876 XSetClipMask(xDisplay, animGCs[anr+2], mask);
3879 /* [AS] Arrow highlighting support */
3881 void DrawPolygon(Pnt arrow[], int nr)
3882 { // for now on own surface; eventually this should become a global that is only destroyed on resize
3883 cairo_surface_t *boardSurface;
3886 int w = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3887 int h = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3888 boardSurface = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), w, h);
3889 cr = cairo_create (boardSurface);
3890 cairo_move_to (cr, arrow[nr-1].x, arrow[nr-1].y);
3891 for (i=0;i<nr;i++) {
3892 cairo_line_to(cr, arrow[i].x, arrow[i].y);
3894 if(appData.monoMode) { // should we always outline arrow?
3895 cairo_line_to(cr, arrow[0].x, arrow[0].y);
3896 cairo_set_line_width(cr, 2);
3897 cairo_set_source_rgba(cr, 0, 0, 0, 1.0);
3898 cairo_stroke_preserve(cr);
3900 cairo_set_line_width(cr, 2);
3901 cairo_set_source_rgba(cr, 1, 1, 0, 1.0);
3906 cairo_surface_destroy (boardSurface);
3910 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
3912 char buf[MSG_SIZ], *logoName = buf;
3913 if(appData.logo[n][0]) {
3914 logoName = appData.logo[n];
3915 } else if(appData.autoLogo) {
3916 if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
3917 sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
3918 } else if(appData.directory[n] && appData.directory[n][0]) {
3919 sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
3923 { ASSIGN(cps->programLogo, logoName); }
3927 UpdateLogos (int displ)
3929 if(optList[W_WHITE-1].handle == NULL) return;
3930 LoadLogo(&first, 0, 0);
3931 LoadLogo(&second, 1, appData.icsActive);
3932 if(displ) DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);