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 FileNamePopUp P((char *label, char *def, char *filter,
260 FileProc proc, char *openMode));
261 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
262 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
263 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
264 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
265 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
266 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
267 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
268 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
269 Boolean TempBackwardActive = False;
270 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
271 void DisplayMove P((int moveNumber));
272 void ICSInitScript P((void));
273 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
274 void update_ics_width P(());
275 int get_term_width P(());
276 int CopyMemoProc P(());
277 void SetupDropMenu P((void));
280 * XBoard depends on Xt R4 or higher
282 int xtVersion = XtSpecificationRelease;
287 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
288 highlightSquareColor, premoveHighlightColor, dialogColor, buttonColor;
289 Pixel lowTimeWarningColor;
290 GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
291 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
293 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
294 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
295 Option *optList; // contains all widgets of main window
296 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
297 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
299 XFontSet fontSet, clockFontSet;
302 XFontStruct *clockFontStruct;
304 Font coordFontID, countFontID;
305 XFontStruct *coordFontStruct, *countFontStruct;
306 XtAppContext appContext;
311 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
313 Position commentX = -1, commentY = -1;
314 Dimension commentW, commentH;
315 typedef unsigned int BoardSize;
317 Boolean chessProgram;
319 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
320 int smallLayout = 0, tinyLayout = 0,
321 marginW, marginH, // [HGM] for run-time resizing
322 fromX = -1, fromY = -1, toX, toY, commentUp = False,
323 errorExitStatus = -1, defaultLineGap;
324 Dimension textHeight;
325 Pixel timerForegroundPixel, timerBackgroundPixel;
326 Pixel buttonForegroundPixel, buttonBackgroundPixel;
327 char *chessDir, *programName, *programVersion;
328 Boolean alwaysOnTop = False;
329 char *icsTextMenuString;
331 char *firstChessProgramNames;
332 char *secondChessProgramNames;
334 WindowPlacement wpMain;
335 WindowPlacement wpConsole;
336 WindowPlacement wpComment;
337 WindowPlacement wpMoveHistory;
338 WindowPlacement wpEvalGraph;
339 WindowPlacement wpEngineOutput;
340 WindowPlacement wpGameList;
341 WindowPlacement wpTags;
346 Pixmap pieceBitmap[2][(int)BlackPawn];
347 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
348 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
349 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
350 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
351 Pixmap xpmBoardBitmap[2];
352 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
353 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
354 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
355 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
356 XImage *ximLightSquare, *ximDarkSquare;
359 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
360 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
362 #define White(piece) ((int)(piece) < (int)BlackPawn)
364 /* Bitmaps for use as masks when drawing XPM pieces.
365 Need one for each black and white piece. */
366 static Pixmap xpmMask[BlackKing + 1];
368 /* This magic number is the number of intermediate frames used
369 in each half of the animation. For short moves it's reduced
370 by 1. The total number of frames will be factor * 2 + 1. */
373 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
380 DropMenuEnables dmEnables[] = {
397 XtResource clientResources[] = {
398 { "flashCount", "flashCount", XtRInt, sizeof(int),
399 XtOffset(AppDataPtr, flashCount), XtRImmediate,
400 (XtPointer) FLASH_COUNT },
403 XrmOptionDescRec shellOptions[] = {
404 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
405 { "-flash", "flashCount", XrmoptionNoArg, "3" },
406 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
409 XtActionsRec boardActions[] = {
410 { "DrawPosition", DrawPositionProc },
411 { "HandlePV", HandlePV },
412 { "SelectPV", SelectPV },
413 { "StopPV", StopPV },
414 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
415 { "QuitProc", QuitWrapper },
416 { "ManProc", ManInner },
417 { "TempBackwardProc", TempBackwardProc },
418 { "TempForwardProc", TempForwardProc },
419 { "CommentClick", (XtActionProc) CommentClick },
420 { "GenericPopDown", (XtActionProc) GenericPopDown },
421 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
422 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
423 { "SelectMove", (XtActionProc) SelectMove },
424 { "LoadSelectedProc", LoadSelectedProc },
425 { "SetFilterProc", SetFilterProc },
426 { "TypeInProc", TypeInProc },
427 { "EnterKeyProc", EnterKeyProc },
428 { "UpKeyProc", UpKeyProc },
429 { "DownKeyProc", DownKeyProc },
430 { "WheelProc", WheelProc },
431 { "TabProc", TabProc },
434 char globalTranslations[] =
435 ":<Key>F9: MenuItem(ResignProc) \n \
436 :Ctrl<Key>n: MenuItem(NewGame) \n \
437 :Meta<Key>V: MenuItem(NewVariant) \n \
438 :Ctrl<Key>o: MenuItem(LoadGame) \n \
439 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
440 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
441 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
442 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
443 :Ctrl<Key>s: MenuItem(SaveGame) \n \
444 :Ctrl<Key>c: MenuItem(CopyGame) \n \
445 :Ctrl<Key>v: MenuItem(PasteGame) \n \
446 :Ctrl<Key>O: MenuItem(LoadPosition) \n \
447 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
448 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
449 :Ctrl<Key>S: MenuItem(SavePosition) \n \
450 :Ctrl<Key>C: MenuItem(CopyPosition) \n \
451 :Ctrl<Key>V: MenuItem(PastePosition) \n \
452 :Ctrl<Key>q: MenuItem(Exit) \n \
453 :Ctrl<Key>w: MenuItem(MachineWhite) \n \
454 :Ctrl<Key>b: MenuItem(MachineBlack) \n \
455 :Ctrl<Key>t: MenuItem(TwoMachines) \n \
456 :Ctrl<Key>a: MenuItem(AnalysisMode) \n \
457 :Ctrl<Key>g: MenuItem(AnalyzeFile) \n \
458 :Ctrl<Key>e: MenuItem(EditGame) \n \
459 :Ctrl<Key>E: MenuItem(EditPosition) \n \
460 :Meta<Key>O: MenuItem(ShowEngineOutput) \n \
461 :Meta<Key>E: MenuItem(ShowEvaluationGraph) \n \
462 :Meta<Key>G: MenuItem(ShowGameList) \n \
463 :Meta<Key>H: MenuItem(ShowMoveHistory) \n \
464 :<Key>Pause: MenuItem(Pause) \n \
465 :<Key>F3: MenuItem(Accept) \n \
466 :<Key>F4: MenuItem(Decline) \n \
467 :<Key>F12: MenuItem(Rematch) \n \
468 :<Key>F5: MenuItem(CallFlag) \n \
469 :<Key>F6: MenuItem(Draw) \n \
470 :<Key>F7: MenuItem(Adjourn) \n \
471 :<Key>F8: MenuItem(Abort) \n \
472 :<Key>F10: MenuItem(StopObserving) \n \
473 :<Key>F11: MenuItem(StopExamining) \n \
474 :Ctrl<Key>d: MenuItem(DebugProc) \n \
475 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
476 :Meta<Key>End: MenuItem(ToEnd) \n \
477 :Meta<Key>Right: MenuItem(Forward) \n \
478 :Meta<Key>Home: MenuItem(ToStart) \n \
479 :Meta<Key>Left: MenuItem(Backward) \n \
480 :<Key>Left: MenuItem(Backward) \n \
481 :<Key>Right: MenuItem(Forward) \n \
482 :<Key>Home: MenuItem(Revert) \n \
483 :<Key>End: MenuItem(TruncateGame) \n \
484 :Ctrl<Key>m: MenuItem(MoveNow) \n \
485 :Ctrl<Key>x: MenuItem(RetractMove) \n \
486 :Meta<Key>J: MenuItem(Adjudications) \n \
487 :Meta<Key>U: MenuItem(CommonEngine) \n \
488 :Meta<Key>T: MenuItem(TimeControl) \n \
489 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
490 #ifndef OPTIONSDIALOG
492 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
493 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
494 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
495 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
496 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
499 :<Key>F1: MenuItem(Manual) \n \
500 :<Key>F2: MenuItem(FlipView) \n \
501 :<KeyDown>Return: TempBackwardProc() \n \
502 :<KeyUp>Return: TempForwardProc() \n";
504 char ICSInputTranslations[] =
505 "<Key>Up: UpKeyProc() \n "
506 "<Key>Down: DownKeyProc() \n "
507 "<Key>Return: EnterKeyProc() \n";
509 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
510 // as the widget is destroyed before the up-click can call extend-end
511 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
513 String xboardResources[] = {
514 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
519 /* Max possible square size */
520 #define MAXSQSIZE 256
522 static int xpm_avail[MAXSQSIZE];
524 #ifdef HAVE_DIR_STRUCT
526 /* Extract piece size from filename */
528 xpm_getsize (char *name, int len, char *ext)
536 if ((p=strchr(name, '.')) == NULL ||
537 StrCaseCmp(p+1, ext) != 0)
543 while (*p && isdigit(*p))
550 /* Setup xpm_avail */
552 xpm_getavail (char *dirname, char *ext)
558 for (i=0; i<MAXSQSIZE; ++i)
561 if (appData.debugMode)
562 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
564 dir = opendir(dirname);
567 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
568 programName, dirname);
572 while ((ent=readdir(dir)) != NULL) {
573 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
574 if (i > 0 && i < MAXSQSIZE)
584 xpm_print_avail (FILE *fp, char *ext)
588 fprintf(fp, _("Available `%s' sizes:\n"), ext);
589 for (i=1; i<MAXSQSIZE; ++i) {
595 /* Return XPM piecesize closest to size */
597 xpm_closest_to (char *dirname, int size, char *ext)
600 int sm_diff = MAXSQSIZE;
604 xpm_getavail(dirname, ext);
606 if (appData.debugMode)
607 xpm_print_avail(stderr, ext);
609 for (i=1; i<MAXSQSIZE; ++i) {
612 diff = (diff<0) ? -diff : diff;
613 if (diff < sm_diff) {
621 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
627 #else /* !HAVE_DIR_STRUCT */
628 /* If we are on a system without a DIR struct, we can't
629 read the directory, so we can't collect a list of
630 filenames, etc., so we can't do any size-fitting. */
632 xpm_closest_to (char *dirname, int size, char *ext)
635 Warning: No DIR structure found on this system --\n\
636 Unable to autosize for XPM/XIM pieces.\n\
637 Please report this error to %s.\n\
638 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
641 #endif /* HAVE_DIR_STRUCT */
644 /* Arrange to catch delete-window events */
645 Atom wm_delete_window;
647 CatchDeleteWindow (Widget w, String procname)
650 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
651 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
652 XtAugmentTranslations(w, XtParseTranslationTable(buf));
659 XtSetArg(args[0], XtNiconic, False);
660 XtSetValues(shellWidget, args, 1);
662 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
665 //---------------------------------------------------------------------------------------------------------
666 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
669 #define CW_USEDEFAULT (1<<31)
670 #define ICS_TEXT_MENU_SIZE 90
671 #define DEBUG_FILE "xboard.debug"
672 #define SetCurrentDirectory chdir
673 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
677 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
680 // front-end part of option handling
682 // [HGM] This platform-dependent table provides the location for storing the color info
683 extern char *crWhite, * crBlack;
687 &appData.whitePieceColor,
688 &appData.blackPieceColor,
689 &appData.lightSquareColor,
690 &appData.darkSquareColor,
691 &appData.highlightSquareColor,
692 &appData.premoveHighlightColor,
693 &appData.lowTimeWarningColor,
704 // [HGM] font: keep a font for each square size, even non-stndard ones
707 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
708 char *fontTable[NUM_FONTS][MAX_SIZE];
711 ParseFont (char *name, int number)
712 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
714 if(sscanf(name, "size%d:", &size)) {
715 // [HGM] font: font is meant for specific boardSize (likely from settings file);
716 // defer processing it until we know if it matches our board size
717 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
718 fontTable[number][size] = strdup(strchr(name, ':')+1);
719 fontValid[number][size] = True;
724 case 0: // CLOCK_FONT
725 appData.clockFont = strdup(name);
727 case 1: // MESSAGE_FONT
728 appData.font = strdup(name);
730 case 2: // COORD_FONT
731 appData.coordFont = strdup(name);
736 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
741 { // only 2 fonts currently
742 appData.clockFont = CLOCK_FONT_NAME;
743 appData.coordFont = COORD_FONT_NAME;
744 appData.font = DEFAULT_FONT_NAME;
749 { // no-op, until we identify the code for this already in XBoard and move it here
753 ParseColor (int n, char *name)
754 { // in XBoard, just copy the color-name string
755 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
759 ParseTextAttribs (ColorClass cc, char *s)
761 (&appData.colorShout)[cc] = strdup(s);
765 ParseBoardSize (void *addr, char *name)
767 appData.boardSize = strdup(name);
772 { // In XBoard the sound-playing program takes care of obtaining the actual sound
776 SetCommPortDefaults ()
777 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
780 // [HGM] args: these three cases taken out to stay in front-end
782 SaveFontArg (FILE *f, ArgDescriptor *ad)
785 int i, n = (int)(intptr_t)ad->argLoc;
787 case 0: // CLOCK_FONT
788 name = appData.clockFont;
790 case 1: // MESSAGE_FONT
793 case 2: // COORD_FONT
794 name = appData.coordFont;
799 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
800 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
801 fontTable[n][squareSize] = strdup(name);
802 fontValid[n][squareSize] = True;
805 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
806 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
811 { // nothing to do, as the sounds are at all times represented by their text-string names already
815 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
816 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
817 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
821 SaveColor (FILE *f, ArgDescriptor *ad)
822 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
823 if(colorVariable[(int)(intptr_t)ad->argLoc])
824 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
828 SaveBoardSize (FILE *f, char *name, void *addr)
829 { // wrapper to shield back-end from BoardSize & sizeInfo
830 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
834 ParseCommPortSettings (char *s)
835 { // no such option in XBoard (yet)
841 GetActualPlacement (Widget wg, WindowPlacement *wp)
846 XWindowAttributes winAt;
853 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
854 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
855 wp->x = rx - winAt.x;
856 wp->y = ry - winAt.y;
857 wp->height = winAt.height;
858 wp->width = winAt.width;
859 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
864 { // wrapper to shield use of window handles from back-end (make addressible by number?)
865 // In XBoard this will have to wait until awareness of window parameters is implemented
866 GetActualPlacement(shellWidget, &wpMain);
867 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
868 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
869 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
870 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
871 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
872 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
876 PrintCommPortSettings (FILE *f, char *name)
877 { // This option does not exist in XBoard
881 EnsureOnScreen (int *x, int *y, int minX, int minY)
888 { // [HGM] args: allows testing if main window is realized from back-end
889 return xBoardWindow != 0;
893 PopUpStartupDialog ()
894 { // start menu not implemented in XBoard
898 ConvertToLine (int argc, char **argv)
900 static char line[128*1024], buf[1024];
904 for(i=1; i<argc; i++)
906 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
907 && argv[i][0] != '{' )
908 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
910 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
911 strncat(line, buf, 128*1024 - strlen(line) - 1 );
914 line[strlen(line)-1] = NULLCHAR;
918 //--------------------------------------------------------------------------------------------
920 #define BoardSize int
922 InitDrawingSizes (BoardSize boardSize, int flags)
923 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
924 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
926 XtGeometryResult gres;
928 static Dimension oldWidth, oldHeight;
929 static VariantClass oldVariant;
930 static int oldDual = -1, oldMono = -1;
932 if(!formWidget) return;
934 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
935 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
936 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
938 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
940 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
942 hOffset = boardWidth + 10;
943 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
944 secondSegments[i] = gridSegments[i];
945 secondSegments[i].x1 += hOffset;
946 secondSegments[i].x2 += hOffset;
950 * Inhibit shell resizing.
952 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
953 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
954 shellArgs[4].value = shellArgs[2].value = w;
955 shellArgs[5].value = shellArgs[3].value = h;
956 XtSetValues(shellWidget, &shellArgs[0], 6);
958 XSync(xDisplay, False);
962 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
965 if(gameInfo.variant != oldVariant) { // and only if variant changed
970 for(p=0; p<=(int)WhiteKing; p++)
971 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
972 if(gameInfo.variant == VariantShogi) {
973 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
974 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
975 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
976 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
977 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
980 if(gameInfo.variant == VariantGothic) {
981 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
984 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
985 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
986 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
989 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
990 for(p=0; p<=(int)WhiteKing; p++)
991 ximMaskPm[p] = ximMaskPm2[p]; // defaults
992 if(gameInfo.variant == VariantShogi) {
993 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
994 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
995 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
996 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
997 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1000 if(gameInfo.variant == VariantGothic) {
1001 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1004 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1005 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1006 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1011 for(i=0; i<2; i++) {
1013 for(p=0; p<=(int)WhiteKing; p++)
1014 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1015 if(gameInfo.variant == VariantShogi) {
1016 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1017 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1018 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1019 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1020 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1023 if(gameInfo.variant == VariantGothic) {
1024 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1027 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1028 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1029 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1033 oldMono = -10; // kludge to force recreation of animation masks
1034 oldVariant = gameInfo.variant;
1037 if(appData.monoMode != oldMono)
1040 oldMono = appData.monoMode;
1044 MakeOneColor (char *name, Pixel *color)
1046 XrmValue vFrom, vTo;
1047 if (!appData.monoMode) {
1048 vFrom.addr = (caddr_t) name;
1049 vFrom.size = strlen(name);
1050 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1051 if (vTo.addr == NULL) {
1052 appData.monoMode = True;
1055 *color = *(Pixel *) vTo.addr;
1063 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1064 int forceMono = False;
1066 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1067 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1068 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1069 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1070 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1071 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1072 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1073 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1080 { // [HGM] taken out of main
1082 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1083 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1084 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1086 if (appData.bitmapDirectory[0] != NULLCHAR) {
1090 CreateXPMBoard(appData.liteBackTextureFile, 1);
1091 CreateXPMBoard(appData.darkBackTextureFile, 0);
1095 /* Create regular pieces */
1096 if (!useImages) CreatePieces();
1101 InitDrawingParams ()
1103 MakeColors(); CreateGCs(True);
1108 main (int argc, char **argv)
1110 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1111 XSetWindowAttributes window_attributes;
1113 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1114 XrmValue vFrom, vTo;
1115 XtGeometryResult gres;
1118 int forceMono = False;
1120 srandom(time(0)); // [HGM] book: make random truly random
1122 setbuf(stdout, NULL);
1123 setbuf(stderr, NULL);
1126 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1127 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1131 programName = strrchr(argv[0], '/');
1132 if (programName == NULL)
1133 programName = argv[0];
1138 XtSetLanguageProc(NULL, NULL, NULL);
1139 bindtextdomain(PACKAGE, LOCALEDIR);
1140 textdomain(PACKAGE);
1144 XtAppInitialize(&appContext, "XBoard", shellOptions,
1145 XtNumber(shellOptions),
1146 &argc, argv, xboardResources, NULL, 0);
1147 appData.boardSize = "";
1148 InitAppData(ConvertToLine(argc, argv));
1150 if (p == NULL) p = "/tmp";
1151 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1152 gameCopyFilename = (char*) malloc(i);
1153 gamePasteFilename = (char*) malloc(i);
1154 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1155 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1157 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1158 clientResources, XtNumber(clientResources),
1161 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1162 static char buf[MSG_SIZ];
1163 EscapeExpand(buf, appData.firstInitString);
1164 appData.firstInitString = strdup(buf);
1165 EscapeExpand(buf, appData.secondInitString);
1166 appData.secondInitString = strdup(buf);
1167 EscapeExpand(buf, appData.firstComputerString);
1168 appData.firstComputerString = strdup(buf);
1169 EscapeExpand(buf, appData.secondComputerString);
1170 appData.secondComputerString = strdup(buf);
1173 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1176 if (chdir(chessDir) != 0) {
1177 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1183 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1184 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1185 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1186 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1189 setbuf(debugFP, NULL);
1193 if (appData.debugMode) {
1194 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1198 /* [HGM,HR] make sure board size is acceptable */
1199 if(appData.NrFiles > BOARD_FILES ||
1200 appData.NrRanks > BOARD_RANKS )
1201 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1204 /* This feature does not work; animation needs a rewrite */
1205 appData.highlightDragging = FALSE;
1209 xDisplay = XtDisplay(shellWidget);
1210 xScreen = DefaultScreen(xDisplay);
1211 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1213 gameInfo.variant = StringToVariant(appData.variant);
1214 InitPosition(FALSE);
1216 if (isdigit(appData.boardSize[0])) {
1217 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1218 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1219 &fontPxlSize, &smallLayout, &tinyLayout);
1221 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1222 programName, appData.boardSize);
1226 /* Find some defaults; use the nearest known size */
1227 SizeDefaults *szd, *nearest;
1228 int distance = 99999;
1229 nearest = szd = sizeDefaults;
1230 while (szd->name != NULL) {
1231 if (abs(szd->squareSize - squareSize) < distance) {
1233 distance = abs(szd->squareSize - squareSize);
1234 if (distance == 0) break;
1238 if (i < 2) lineGap = nearest->lineGap;
1239 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1240 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1241 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1242 if (i < 6) smallLayout = nearest->smallLayout;
1243 if (i < 7) tinyLayout = nearest->tinyLayout;
1246 SizeDefaults *szd = sizeDefaults;
1247 if (*appData.boardSize == NULLCHAR) {
1248 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1249 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1252 if (szd->name == NULL) szd--;
1253 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1255 while (szd->name != NULL &&
1256 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1257 if (szd->name == NULL) {
1258 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1259 programName, appData.boardSize);
1263 squareSize = szd->squareSize;
1264 lineGap = szd->lineGap;
1265 clockFontPxlSize = szd->clockFontPxlSize;
1266 coordFontPxlSize = szd->coordFontPxlSize;
1267 fontPxlSize = szd->fontPxlSize;
1268 smallLayout = szd->smallLayout;
1269 tinyLayout = szd->tinyLayout;
1270 // [HGM] font: use defaults from settings file if available and not overruled
1272 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1273 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1274 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1275 appData.font = fontTable[MESSAGE_FONT][squareSize];
1276 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1277 appData.coordFont = fontTable[COORD_FONT][squareSize];
1279 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1280 if (strlen(appData.pixmapDirectory) > 0) {
1281 p = ExpandPathName(appData.pixmapDirectory);
1283 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1284 appData.pixmapDirectory);
1287 if (appData.debugMode) {
1288 fprintf(stderr, _("\
1289 XBoard square size (hint): %d\n\
1290 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1292 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1293 if (appData.debugMode) {
1294 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1297 defaultLineGap = lineGap;
1298 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1300 /* [HR] height treated separately (hacked) */
1301 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1302 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1305 * Determine what fonts to use.
1308 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1309 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1310 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1311 fontSet = CreateFontSet(appData.font);
1312 clockFontSet = CreateFontSet(appData.clockFont);
1314 /* For the coordFont, use the 0th font of the fontset. */
1315 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1316 XFontStruct **font_struct_list;
1317 XFontSetExtents *fontSize;
1318 char **font_name_list;
1319 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1320 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1321 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1322 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1323 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1326 appData.font = FindFont(appData.font, fontPxlSize);
1327 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1328 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1329 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1330 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1331 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1332 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1333 // textHeight in !NLS mode!
1335 countFontID = coordFontID; // [HGM] holdings
1336 countFontStruct = coordFontStruct;
1338 xdb = XtDatabase(xDisplay);
1340 XrmPutLineResource(&xdb, "*international: True");
1341 vTo.size = sizeof(XFontSet);
1342 vTo.addr = (XtPointer) &fontSet;
1343 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1345 XrmPutStringResource(&xdb, "*font", appData.font);
1349 * Detect if there are not enough colors available and adapt.
1351 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1352 appData.monoMode = True;
1355 forceMono = MakeColors();
1358 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1360 appData.monoMode = True;
1363 if (appData.lowTimeWarning && !appData.monoMode) {
1364 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1365 vFrom.size = strlen(appData.lowTimeWarningColor);
1366 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1367 if (vTo.addr == NULL)
1368 appData.monoMode = True;
1370 lowTimeWarningColor = *(Pixel *) vTo.addr;
1373 if (appData.monoMode && appData.debugMode) {
1374 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1375 (unsigned long) XWhitePixel(xDisplay, xScreen),
1376 (unsigned long) XBlackPixel(xDisplay, xScreen));
1379 ParseIcsTextColors();
1381 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1387 layoutName = "tinyLayout";
1388 } else if (smallLayout) {
1389 layoutName = "smallLayout";
1391 layoutName = "normalLayout";
1394 optList = BoardPopUp(squareSize, lineGap, (void*)
1400 boardWidget = optList[22].handle;
1401 menuBarWidget = optList[ 0].handle;
1402 dropMenu = optList[25].handle;
1403 titleWidget = optList[optList[10].type != -1 ? 10 : 13].handle;
1404 formWidget = XtParent(boardWidget);
1405 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1406 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1407 XtGetValues(optList[11].handle, args, 2);
1408 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1409 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1410 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1411 XtGetValues(optList[18].handle, args, 2);
1414 xBoardWindow = XtWindow(boardWidget);
1416 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1417 // not need to go into InitDrawingSizes().
1420 * Create X checkmark bitmap and initialize option menu checks.
1422 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1423 checkmark_bits, checkmark_width, checkmark_height);
1429 ReadBitmap(&wIconPixmap, "icon_white.bm",
1430 icon_white_bits, icon_white_width, icon_white_height);
1431 ReadBitmap(&bIconPixmap, "icon_black.bm",
1432 icon_black_bits, icon_black_width, icon_black_height);
1433 iconPixmap = wIconPixmap;
1435 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1436 XtSetValues(shellWidget, args, i);
1439 * Create a cursor for the board widget.
1441 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1442 XChangeWindowAttributes(xDisplay, xBoardWindow,
1443 CWCursor, &window_attributes);
1446 * Inhibit shell resizing.
1448 shellArgs[0].value = (XtArgVal) &w;
1449 shellArgs[1].value = (XtArgVal) &h;
1450 XtGetValues(shellWidget, shellArgs, 2);
1451 shellArgs[4].value = shellArgs[2].value = w;
1452 shellArgs[5].value = shellArgs[3].value = h;
1453 XtSetValues(shellWidget, &shellArgs[2], 4);
1454 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1455 marginH = h - boardHeight;
1457 CatchDeleteWindow(shellWidget, "QuitProc");
1463 if (appData.animate || appData.animateDragging)
1466 XtAugmentTranslations(formWidget,
1467 XtParseTranslationTable(globalTranslations));
1469 XtAddEventHandler(formWidget, KeyPressMask, False,
1470 (XtEventHandler) MoveTypeInProc, NULL);
1471 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1472 (XtEventHandler) EventProc, NULL);
1474 /* [AS] Restore layout */
1475 if( wpMoveHistory.visible ) {
1479 if( wpEvalGraph.visible )
1484 if( wpEngineOutput.visible ) {
1485 EngineOutputPopUp();
1490 if (errorExitStatus == -1) {
1491 if (appData.icsActive) {
1492 /* We now wait until we see "login:" from the ICS before
1493 sending the logon script (problems with timestamp otherwise) */
1494 /*ICSInitScript();*/
1495 if (appData.icsInputBox) ICSInputBoxPopUp();
1499 signal(SIGWINCH, TermSizeSigHandler);
1501 signal(SIGINT, IntSigHandler);
1502 signal(SIGTERM, IntSigHandler);
1503 if (*appData.cmailGameName != NULLCHAR) {
1504 signal(SIGUSR1, CmailSigHandler);
1508 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1510 // XtSetKeyboardFocus(shellWidget, formWidget);
1511 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1513 XtAppMainLoop(appContext);
1514 if (appData.debugMode) fclose(debugFP); // [DM] debug
1519 TermSizeSigHandler (int sig)
1525 IntSigHandler (int sig)
1531 CmailSigHandler (int sig)
1536 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1538 /* Activate call-back function CmailSigHandlerCallBack() */
1539 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1541 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1545 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1548 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1550 /**** end signal code ****/
1553 #define Abs(n) ((n)<0 ? -(n) : (n))
1557 InsertPxlSize (char *pattern, int targetPxlSize)
1559 char *base_fnt_lst, strInt[12], *p, *q;
1560 int alternatives, i, len, strIntLen;
1563 * Replace the "*" (if present) in the pixel-size slot of each
1564 * alternative with the targetPxlSize.
1568 while ((p = strchr(p, ',')) != NULL) {
1572 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1573 strIntLen = strlen(strInt);
1574 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1578 while (alternatives--) {
1579 char *comma = strchr(p, ',');
1580 for (i=0; i<14; i++) {
1581 char *hyphen = strchr(p, '-');
1583 if (comma && hyphen > comma) break;
1584 len = hyphen + 1 - p;
1585 if (i == 7 && *p == '*' && len == 2) {
1587 memcpy(q, strInt, strIntLen);
1597 len = comma + 1 - p;
1604 return base_fnt_lst;
1608 CreateFontSet (char *base_fnt_lst)
1611 char **missing_list;
1615 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1616 &missing_list, &missing_count, &def_string);
1617 if (appData.debugMode) {
1619 XFontStruct **font_struct_list;
1620 char **font_name_list;
1621 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1623 fprintf(debugFP, " got list %s, locale %s\n",
1624 XBaseFontNameListOfFontSet(fntSet),
1625 XLocaleOfFontSet(fntSet));
1626 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1627 for (i = 0; i < count; i++) {
1628 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1631 for (i = 0; i < missing_count; i++) {
1632 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1635 if (fntSet == NULL) {
1636 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1641 #else // not ENABLE_NLS
1643 * Find a font that matches "pattern" that is as close as
1644 * possible to the targetPxlSize. Prefer fonts that are k
1645 * pixels smaller to fonts that are k pixels larger. The
1646 * pattern must be in the X Consortium standard format,
1647 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1648 * The return value should be freed with XtFree when no
1652 FindFont (char *pattern, int targetPxlSize)
1654 char **fonts, *p, *best, *scalable, *scalableTail;
1655 int i, j, nfonts, minerr, err, pxlSize;
1657 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1659 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1660 programName, pattern);
1667 for (i=0; i<nfonts; i++) {
1670 if (*p != '-') continue;
1672 if (*p == NULLCHAR) break;
1673 if (*p++ == '-') j++;
1675 if (j < 7) continue;
1678 scalable = fonts[i];
1681 err = pxlSize - targetPxlSize;
1682 if (Abs(err) < Abs(minerr) ||
1683 (minerr > 0 && err < 0 && -err == minerr)) {
1689 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1690 /* If the error is too big and there is a scalable font,
1691 use the scalable font. */
1692 int headlen = scalableTail - scalable;
1693 p = (char *) XtMalloc(strlen(scalable) + 10);
1694 while (isdigit(*scalableTail)) scalableTail++;
1695 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1697 p = (char *) XtMalloc(strlen(best) + 2);
1698 safeStrCpy(p, best, strlen(best)+1 );
1700 if (appData.debugMode) {
1701 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1702 pattern, targetPxlSize, p);
1704 XFreeFontNames(fonts);
1711 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
1712 // must be called before all non-first callse to CreateGCs()
1713 XtReleaseGC(shellWidget, highlineGC);
1714 XtReleaseGC(shellWidget, lightSquareGC);
1715 XtReleaseGC(shellWidget, darkSquareGC);
1716 XtReleaseGC(shellWidget, lineGC);
1717 if (appData.monoMode) {
1718 if (DefaultDepth(xDisplay, xScreen) == 1) {
1719 XtReleaseGC(shellWidget, wbPieceGC);
1721 XtReleaseGC(shellWidget, bwPieceGC);
1724 XtReleaseGC(shellWidget, prelineGC);
1725 XtReleaseGC(shellWidget, wdPieceGC);
1726 XtReleaseGC(shellWidget, wlPieceGC);
1727 XtReleaseGC(shellWidget, bdPieceGC);
1728 XtReleaseGC(shellWidget, blPieceGC);
1733 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
1735 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1736 | GCBackground | GCFunction | GCPlaneMask;
1737 gc_values->foreground = foreground;
1738 gc_values->background = background;
1739 return XtGetGC(shellWidget, value_mask, gc_values);
1743 CreateGCs (int redo)
1745 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1746 | GCBackground | GCFunction | GCPlaneMask;
1747 XGCValues gc_values;
1749 Pixel white = XWhitePixel(xDisplay, xScreen);
1750 Pixel black = XBlackPixel(xDisplay, xScreen);
1752 gc_values.plane_mask = AllPlanes;
1753 gc_values.line_width = lineGap;
1754 gc_values.line_style = LineSolid;
1755 gc_values.function = GXcopy;
1758 DeleteGCs(); // called a second time; clean up old GCs first
1759 } else { // [HGM] grid and font GCs created on first call only
1760 coordGC = CreateOneGC(&gc_values, black, white);
1761 XSetFont(xDisplay, coordGC, coordFontID);
1763 // [HGM] make font for holdings counts (white on black)
1764 countGC = CreateOneGC(&gc_values, white, black);
1765 XSetFont(xDisplay, countGC, countFontID);
1767 lineGC = CreateOneGC(&gc_values, black, black);
1769 if (appData.monoMode) {
1771 highlineGC = CreateOneGC(&gc_values, white, white);
1772 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
1773 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
1775 if (DefaultDepth(xDisplay, xScreen) == 1) {
1776 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
1777 gc_values.function = GXcopyInverted;
1778 copyInvertedGC = CreateOneGC(&gc_values, black, white);
1779 gc_values.function = GXcopy;
1780 if (XBlackPixel(xDisplay, xScreen) == 1) {
1781 bwPieceGC = darkSquareGC;
1782 wbPieceGC = copyInvertedGC;
1784 bwPieceGC = copyInvertedGC;
1785 wbPieceGC = lightSquareGC;
1790 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
1791 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
1792 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
1793 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
1794 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
1795 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
1796 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
1797 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
1802 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
1810 fp = fopen(filename, "rb");
1812 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
1819 for (y=0; y<h; ++y) {
1820 for (x=0; x<h; ++x) {
1825 XPutPixel(xim, x, y, blackPieceColor);
1827 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1830 XPutPixel(xim, x, y, darkSquareColor);
1832 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1835 XPutPixel(xim, x, y, whitePieceColor);
1837 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1840 XPutPixel(xim, x, y, lightSquareColor);
1842 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1850 /* create Pixmap of piece */
1851 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1853 XPutImage(xDisplay, *dest, lightSquareGC, xim,
1856 /* create Pixmap of clipmask
1857 Note: We assume the white/black pieces have the same
1858 outline, so we make only 6 masks. This is okay
1859 since the XPM clipmask routines do the same. */
1861 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1863 XPutImage(xDisplay, temp, lightSquareGC, xmask,
1866 /* now create the 1-bit version */
1867 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1870 values.foreground = 1;
1871 values.background = 0;
1873 /* Don't use XtGetGC, not read only */
1874 maskGC = XCreateGC(xDisplay, *mask,
1875 GCForeground | GCBackground, &values);
1876 XCopyPlane(xDisplay, temp, *mask, maskGC,
1877 0, 0, squareSize, squareSize, 0, 0, 1);
1878 XFreePixmap(xDisplay, temp);
1883 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
1891 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
1896 /* The XSynchronize calls were copied from CreatePieces.
1897 Not sure if needed, but can't hurt */
1898 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
1901 /* temp needed by loadXIM() */
1902 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1903 0, 0, ss, ss, AllPlanes, XYPixmap);
1905 if (strlen(appData.pixmapDirectory) == 0) {
1909 if (appData.monoMode) {
1910 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
1914 fprintf(stderr, _("\nLoading XIMs...\n"));
1916 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
1917 fprintf(stderr, "%d", piece+1);
1918 for (kind=0; kind<4; kind++) {
1919 fprintf(stderr, ".");
1920 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
1921 ExpandPathName(appData.pixmapDirectory),
1922 piece <= (int) WhiteKing ? "" : "w",
1923 pieceBitmapNames[piece],
1925 ximPieceBitmap[kind][piece] =
1926 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1927 0, 0, ss, ss, AllPlanes, XYPixmap);
1928 if (appData.debugMode)
1929 fprintf(stderr, _("(File:%s:) "), buf);
1930 loadXIM(ximPieceBitmap[kind][piece],
1932 &(xpmPieceBitmap2[kind][piece]),
1933 &(ximMaskPm2[piece]));
1934 if(piece <= (int)WhiteKing)
1935 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
1937 fprintf(stderr," ");
1939 /* Load light and dark squares */
1940 /* If the LSQ and DSQ pieces don't exist, we will
1941 draw them with solid squares. */
1942 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
1943 if (access(buf, 0) != 0) {
1947 fprintf(stderr, _("light square "));
1949 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1950 0, 0, ss, ss, AllPlanes, XYPixmap);
1951 if (appData.debugMode)
1952 fprintf(stderr, _("(File:%s:) "), buf);
1954 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
1955 fprintf(stderr, _("dark square "));
1956 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
1957 ExpandPathName(appData.pixmapDirectory), ss);
1958 if (appData.debugMode)
1959 fprintf(stderr, _("(File:%s:) "), buf);
1961 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1962 0, 0, ss, ss, AllPlanes, XYPixmap);
1963 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
1964 xpmJailSquare = xpmLightSquare;
1966 fprintf(stderr, _("Done.\n"));
1968 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
1971 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
1975 CreateXPMBoard (char *s, int kind)
1979 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
1980 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
1981 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
1987 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
1988 // thisroutine has to be called t free the old piece pixmaps
1990 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
1991 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
1993 XFreePixmap(xDisplay, xpmLightSquare);
1994 XFreePixmap(xDisplay, xpmDarkSquare);
2003 u_int ss = squareSize;
2005 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2006 XpmColorSymbol symbols[4];
2007 static int redo = False;
2009 if(redo) FreeXPMPieces(); else redo = 1;
2011 /* The XSynchronize calls were copied from CreatePieces.
2012 Not sure if needed, but can't hurt */
2013 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2015 /* Setup translations so piece colors match square colors */
2016 symbols[0].name = "light_piece";
2017 symbols[0].value = appData.whitePieceColor;
2018 symbols[1].name = "dark_piece";
2019 symbols[1].value = appData.blackPieceColor;
2020 symbols[2].name = "light_square";
2021 symbols[2].value = appData.lightSquareColor;
2022 symbols[3].name = "dark_square";
2023 symbols[3].value = appData.darkSquareColor;
2025 attr.valuemask = XpmColorSymbols;
2026 attr.colorsymbols = symbols;
2027 attr.numsymbols = 4;
2029 if (appData.monoMode) {
2030 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2034 if (strlen(appData.pixmapDirectory) == 0) {
2035 XpmPieces* pieces = builtInXpms;
2038 while (pieces->size != squareSize && pieces->size) pieces++;
2039 if (!pieces->size) {
2040 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2043 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2044 for (kind=0; kind<4; kind++) {
2046 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2047 pieces->xpm[piece][kind],
2048 &(xpmPieceBitmap2[kind][piece]),
2049 NULL, &attr)) != 0) {
2050 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2054 if(piece <= (int) WhiteKing)
2055 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2059 xpmJailSquare = xpmLightSquare;
2063 fprintf(stderr, _("\nLoading XPMs...\n"));
2066 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2067 fprintf(stderr, "%d ", piece+1);
2068 for (kind=0; kind<4; kind++) {
2069 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2070 ExpandPathName(appData.pixmapDirectory),
2071 piece > (int) WhiteKing ? "w" : "",
2072 pieceBitmapNames[piece],
2074 if (appData.debugMode) {
2075 fprintf(stderr, _("(File:%s:) "), buf);
2077 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2078 &(xpmPieceBitmap2[kind][piece]),
2079 NULL, &attr)) != 0) {
2080 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2081 // [HGM] missing: read of unorthodox piece failed; substitute King.
2082 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2083 ExpandPathName(appData.pixmapDirectory),
2085 if (appData.debugMode) {
2086 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2088 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2089 &(xpmPieceBitmap2[kind][piece]),
2093 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2098 if(piece <= (int) WhiteKing)
2099 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2102 /* Load light and dark squares */
2103 /* If the LSQ and DSQ pieces don't exist, we will
2104 draw them with solid squares. */
2105 fprintf(stderr, _("light square "));
2106 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2107 if (access(buf, 0) != 0) {
2111 if (appData.debugMode)
2112 fprintf(stderr, _("(File:%s:) "), buf);
2114 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2115 &xpmLightSquare, NULL, &attr)) != 0) {
2116 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2119 fprintf(stderr, _("dark square "));
2120 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2121 ExpandPathName(appData.pixmapDirectory), ss);
2122 if (appData.debugMode) {
2123 fprintf(stderr, _("(File:%s:) "), buf);
2125 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2126 &xpmDarkSquare, NULL, &attr)) != 0) {
2127 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2131 xpmJailSquare = xpmLightSquare;
2132 fprintf(stderr, _("Done.\n"));
2134 oldVariant = -1; // kludge to force re-makig of animation masks
2135 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2138 #endif /* HAVE_LIBXPM */
2141 /* No built-in bitmaps */
2146 u_int ss = squareSize;
2148 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2151 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2152 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2153 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2154 pieceBitmapNames[piece],
2155 ss, kind == SOLID ? 's' : 'o');
2156 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2157 if(piece <= (int)WhiteKing)
2158 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2162 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2166 /* With built-in bitmaps */
2170 BuiltInBits* bib = builtInBits;
2173 u_int ss = squareSize;
2175 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2178 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2180 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2181 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2182 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2183 pieceBitmapNames[piece],
2184 ss, kind == SOLID ? 's' : 'o');
2185 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2186 bib->bits[kind][piece], ss, ss);
2187 if(piece <= (int)WhiteKing)
2188 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2192 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2198 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2203 char msg[MSG_SIZ], fullname[MSG_SIZ];
2205 if (*appData.bitmapDirectory != NULLCHAR) {
2206 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2207 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2208 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2209 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2210 &w, &h, pm, &x_hot, &y_hot);
2211 fprintf(stderr, "load %s\n", name);
2212 if (errcode != BitmapSuccess) {
2214 case BitmapOpenFailed:
2215 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2217 case BitmapFileInvalid:
2218 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2220 case BitmapNoMemory:
2221 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2225 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2229 fprintf(stderr, _("%s: %s...using built-in\n"),
2231 } else if (w != wreq || h != hreq) {
2233 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2234 programName, fullname, w, h, wreq, hreq);
2240 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2250 if (lineGap == 0) return;
2252 /* [HR] Split this into 2 loops for non-square boards. */
2254 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2255 gridSegments[i].x1 = 0;
2256 gridSegments[i].x2 =
2257 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2258 gridSegments[i].y1 = gridSegments[i].y2
2259 = lineGap / 2 + (i * (squareSize + lineGap));
2262 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2263 gridSegments[j + i].y1 = 0;
2264 gridSegments[j + i].y2 =
2265 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2266 gridSegments[j + i].x1 = gridSegments[j + i].x2
2267 = lineGap / 2 + (j * (squareSize + lineGap));
2271 int nrOfMenuItems = 7;
2272 Widget menuWidget[150];
2273 MenuListItem menuItemList[150] = {
2274 { "LoadNextGameProc", LoadNextGameProc },
2275 { "LoadPrevGameProc", LoadPrevGameProc },
2276 { "ReloadGameProc", ReloadGameProc },
2277 { "ReloadPositionProc", ReloadPositionProc },
2278 #ifndef OPTIONSDIALOG
2279 { "AlwaysQueenProc", AlwaysQueenProc },
2280 { "AnimateDraggingProc", AnimateDraggingProc },
2281 { "AnimateMovingProc", AnimateMovingProc },
2282 { "AutoflagProc", AutoflagProc },
2283 { "AutoflipProc", AutoflipProc },
2284 { "BlindfoldProc", BlindfoldProc },
2285 { "FlashMovesProc", FlashMovesProc },
2287 { "HighlightDraggingProc", HighlightDraggingProc },
2289 { "HighlightLastMoveProc", HighlightLastMoveProc },
2290 // { "IcsAlarmProc", IcsAlarmProc },
2291 { "MoveSoundProc", MoveSoundProc },
2292 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
2293 { "PopupExitMessageProc", PopupExitMessageProc },
2294 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
2295 // { "PremoveProc", PremoveProc },
2296 { "ShowCoordsProc", ShowCoordsProc },
2297 { "ShowThinkingProc", ShowThinkingProc },
2298 { "HideThinkingProc", HideThinkingProc },
2299 { "TestLegalityProc", TestLegalityProc },
2301 { "AboutGameProc", AboutGameEvent },
2302 { "DebugProc", DebugProc },
2303 { "NothingProc", NothingProc },
2308 MarkMenuItem (char *menuRef, int state)
2310 int nr = MenuToNumber(menuRef);
2314 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2315 XtSetValues(menuWidget[nr], args, 1);
2320 EnableMenuItem (char *menuRef, int state)
2322 int nr = MenuToNumber(menuRef);
2324 if(nr >= 0) XtSetSensitive(menuWidget[nr], state);
2328 EnableButtonBar (int state)
2330 XtSetSensitive(optList[15].handle, state);
2335 SetMenuEnables (Enables *enab)
2337 while (enab->name != NULL) {
2338 EnableMenuItem(enab->name, enab->value);
2344 Equal(char *p, char *s)
2345 { // compare strings skipping spaces in second
2347 if(*s == ' ') { s++; continue; }
2348 if(*s++ != *p++) return 0;
2354 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2355 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2358 if(*nprms == 0) return;
2359 for(i=0; menuItemList[i].name; i++) {
2360 if(Equal(prms[0], menuItemList[i].name)) {
2361 (menuItemList[i].proc) ();
2368 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
2370 MenuProc *proc = (MenuProc *) addr;
2376 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2378 RecentEngineEvent((int) (intptr_t) addr);
2381 // some stuff that must remain in front-end
2382 static Widget mainBar, currentMenu;
2383 static int wtot, nr = 0, widths[10];
2386 AppendMenuItem (char *text, char *name, MenuProc *action)
2393 XtSetArg(args[j], XtNleftMargin, 20); j++;
2394 XtSetArg(args[j], XtNrightMargin, 20); j++;
2396 if (strcmp(text, "----") == 0) {
2397 entry = XtCreateManagedWidget(text, smeLineObjectClass,
2398 currentMenu, args, j);
2400 XtSetArg(args[j], XtNlabel, XtNewString(_(text)));
2401 entry = XtCreateManagedWidget(name, smeBSBObjectClass,
2402 currentMenu, args, j+1);
2403 XtAddCallback(entry, XtNcallback,
2404 (XtCallbackProc) (strcmp(name, "recent") ? MenuBarSelect : MenuEngineSelect),
2406 menuWidget[nrOfMenuItems] = entry;
2411 CreateMenuButton (char *name, Menu *mb)
2412 { // create menu button on main bar, and shell for pull-down list
2418 XtSetArg(args[j], XtNmenuName, XtNewString(name)); j++;
2419 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
2420 XtSetArg(args[j], XtNborderWidth, 0); j++;
2421 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
2423 currentMenu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2426 XtSetArg(args[j], XtNwidth, &w); j++;
2427 XtGetValues(mb->subMenu, args, j);
2428 wtot += mb->textWidth = widths[nr++] = w;
2432 CreateMenuBar (Menu *mb, int boardWidth)
2436 char menuName[MSG_SIZ];
2440 // create bar itself
2442 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2443 XtSetArg(args[j], XtNvSpace, 0); j++;
2444 XtSetArg(args[j], XtNborderWidth, 0); j++;
2445 mainBar = XtCreateWidget("menuBar", boxWidgetClass,
2446 formWidget, args, j);
2448 CreateMainMenus(mb); // put menus in bar according to description in back-end
2450 // size buttons to make menu bar fit, clipping menu names where necessary
2451 while(wtot > boardWidth - 40) {
2453 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
2457 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
2459 XtSetArg(args[j], XtNwidth, widths[i]); j++;
2460 XtSetValues(ma[i].subMenu, args, j);
2475 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2476 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2477 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2478 dmEnables[i].piece);
2479 XtSetSensitive(entry, p != NULL || !appData.testLegality
2480 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2481 && !appData.icsActive));
2483 while (p && *p++ == dmEnables[i].piece) count++;
2484 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2486 XtSetArg(args[j], XtNlabel, label); j++;
2487 XtSetValues(entry, args, j);
2493 do_flash_delay (unsigned long msec)
2499 DrawBorder (int x, int y, int type)
2503 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
2505 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
2506 squareSize+lineGap, squareSize+lineGap);
2510 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
2512 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
2513 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
2515 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
2516 if(textureW[kind] < W*squareSize)
2517 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
2519 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
2520 if(textureH[kind] < H*squareSize)
2521 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
2523 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
2528 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
2529 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
2531 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
2532 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
2533 squareSize, squareSize, x*fac, y*fac);
2535 if (useImages && useImageSqs) {
2539 pm = xpmLightSquare;
2544 case 2: /* neutral */
2546 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
2549 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
2550 squareSize, squareSize, x*fac, y*fac);
2560 case 2: /* neutral */
2565 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
2570 I split out the routines to draw a piece so that I could
2571 make a generic flash routine.
2574 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2576 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2577 switch (square_color) {
2579 case 2: /* neutral */
2581 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2582 ? *pieceToOutline(piece)
2583 : *pieceToSolid(piece),
2584 dest, bwPieceGC, 0, 0,
2585 squareSize, squareSize, x, y);
2588 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2589 ? *pieceToSolid(piece)
2590 : *pieceToOutline(piece),
2591 dest, wbPieceGC, 0, 0,
2592 squareSize, squareSize, x, y);
2598 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2600 switch (square_color) {
2602 case 2: /* neutral */
2604 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2605 ? *pieceToOutline(piece)
2606 : *pieceToSolid(piece),
2607 dest, bwPieceGC, 0, 0,
2608 squareSize, squareSize, x, y, 1);
2611 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2612 ? *pieceToSolid(piece)
2613 : *pieceToOutline(piece),
2614 dest, wbPieceGC, 0, 0,
2615 squareSize, squareSize, x, y, 1);
2621 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2623 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
2624 switch (square_color) {
2626 XCopyPlane(xDisplay, *pieceToSolid(piece),
2627 dest, (int) piece < (int) BlackPawn
2628 ? wlPieceGC : blPieceGC, 0, 0,
2629 squareSize, squareSize, x, y, 1);
2632 XCopyPlane(xDisplay, *pieceToSolid(piece),
2633 dest, (int) piece < (int) BlackPawn
2634 ? wdPieceGC : bdPieceGC, 0, 0,
2635 squareSize, squareSize, x, y, 1);
2637 case 2: /* neutral */
2639 break; // should never contain pieces
2644 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2646 int kind, p = piece;
2648 switch (square_color) {
2650 case 2: /* neutral */
2652 if ((int)piece < (int) BlackPawn) {
2660 if ((int)piece < (int) BlackPawn) {
2668 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2669 if(useTexture & square_color+1) {
2670 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2671 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
2672 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
2673 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
2674 XSetClipMask(xDisplay, wlPieceGC, None);
2675 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
2677 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
2678 dest, wlPieceGC, 0, 0,
2679 squareSize, squareSize, x, y);
2682 typedef void (*DrawFunc)();
2687 if (appData.monoMode) {
2688 if (DefaultDepth(xDisplay, xScreen) == 1) {
2689 return monoDrawPiece_1bit;
2691 return monoDrawPiece;
2695 return colorDrawPieceImage;
2697 return colorDrawPiece;
2702 DrawDot (int marker, int x, int y, int r)
2704 if(appData.monoMode) {
2705 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
2706 x, y, r, r, 0, 64*360);
2707 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
2708 x, y, r, r, 0, 64*360);
2710 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
2711 x, y, r, r, 0, 64*360);
2715 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2716 { // basic front-end board-draw function: takes care of everything that can be in square:
2717 // piece, background, coordinate/count, marker dot
2718 int direction, font_ascent, font_descent;
2719 XCharStruct overall;
2722 if (piece == EmptySquare) {
2723 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2725 drawfunc = ChooseDrawFunc();
2726 drawfunc(piece, square_color, x, y, xBoardWindow);
2729 if(align) { // square carries inscription (coord or piece count)
2731 GC hGC = align < 3 ? coordGC : countGC;
2732 // first calculate where it goes
2733 XTextExtents(countFontStruct, string, 1, &direction,
2734 &font_ascent, &font_descent, &overall);
2736 xx += squareSize - overall.width - 2;
2737 yy += squareSize - font_descent - 1;
2738 } else if (align == 2) {
2739 xx += 2, yy += font_ascent + 1;
2740 } else if (align == 3) {
2741 xx += squareSize - overall.width - 2;
2742 yy += font_ascent + 1;
2743 } else if (align == 4) {
2744 xx += 2, yy += font_ascent + 1;
2747 if (appData.monoMode) {
2748 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2750 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2754 if(marker) { // print fat marker dot, if requested
2755 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2760 FlashDelay (int flash_delay)
2762 XSync(xDisplay, False);
2763 if(flash_delay) do_flash_delay(flash_delay);
2767 Fraction (int x, int start, int stop)
2769 double f = ((double) x - start)/(stop - start);
2770 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2774 static WindowPlacement wpNew;
2777 CoDrag (Widget sh, WindowPlacement *wp)
2780 int j=0, touch=0, fudge = 2;
2781 GetActualPlacement(sh, wp);
2782 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
2783 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
2784 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2785 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
2786 if(!touch ) return; // only windows that touch co-move
2787 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2788 int heightInc = wpNew.height - wpMain.height;
2789 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2790 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2791 wp->y += fracTop * heightInc;
2792 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2793 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2794 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2795 int widthInc = wpNew.width - wpMain.width;
2796 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2797 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2798 wp->y += fracLeft * widthInc;
2799 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2800 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2802 wp->x += wpNew.x - wpMain.x;
2803 wp->y += wpNew.y - wpMain.y;
2804 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2805 if(touch == 3) wp->y += wpNew.height - wpMain.height;
2806 XtSetArg(args[j], XtNx, wp->x); j++;
2807 XtSetArg(args[j], XtNy, wp->y); j++;
2808 XtSetValues(sh, args, j);
2811 static XtIntervalId delayedDragID = 0;
2816 GetActualPlacement(shellWidget, &wpNew);
2817 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
2818 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
2819 return; // false alarm
2820 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
2821 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
2822 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
2823 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
2825 DrawPosition(True, NULL);
2826 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
2833 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
2835 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
2839 EventProc (Widget widget, caddr_t unused, XEvent *event)
2841 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
2842 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
2845 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
2847 DrawSeekAxis (int x, int y, int xTo, int yTo)
2849 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
2853 DrawSeekBackground (int left, int top, int right, int bottom)
2855 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
2859 DrawSeekText (char *buf, int x, int y)
2861 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
2865 DrawSeekDot (int x, int y, int colorNr)
2867 int square = colorNr & 0x80;
2870 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
2872 XFillRectangle(xDisplay, xBoardWindow, color,
2873 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
2875 XFillArc(xDisplay, xBoardWindow, color,
2876 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
2880 DrawGrid (int second)
2882 XDrawSegments(xDisplay, xBoardWindow, lineGC,
2883 second ? secondSegments : // [HGM] dual
2884 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
2889 * event handler for redrawing the board
2892 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2894 DrawPosition(True, NULL);
2899 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
2900 { // [HGM] pv: walk PV
2901 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
2904 static int savedIndex; /* gross that this is global */
2907 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
2910 XawTextPosition index, dummy;
2913 XawTextGetSelectionPos(w, &index, &dummy);
2914 XtSetArg(arg, XtNstring, &val);
2915 XtGetValues(w, &arg, 1);
2916 ReplaceComment(savedIndex, val);
2917 if(savedIndex != currentMove) ToNrEvent(savedIndex);
2918 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
2922 EditCommentPopUp (int index, char *title, char *text)
2925 if (text == NULL) text = "";
2926 NewCommentPopup(title, text, index);
2930 CommentPopUp (char *title, char *text)
2932 savedIndex = currentMove; // [HGM] vari
2933 NewCommentPopup(title, text, currentMove);
2939 PopDown(CommentDlg);
2942 static char *openName;
2948 (void) (*fileProc)(openFP, 0, openName);
2952 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
2954 fileProc = proc; /* I can't see a way not */
2955 fileOpenMode = openMode; /* to use globals here */
2956 { // [HGM] use file-selector dialog stolen from Ghostview
2957 int index; // this is not supported yet
2958 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
2959 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
2960 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
2961 ScheduleDelayedEvent(&DelayedLoad, 50);
2966 /* Disable all user input other than deleting the window */
2967 static int frozen = 0;
2973 /* Grab by a widget that doesn't accept input */
2974 XtAddGrab(optList[14].handle, TRUE, FALSE);
2978 /* Undo a FreezeUI */
2982 if (!frozen) return;
2983 XtRemoveGrab(optList[14].handle);
2991 static int oldPausing = FALSE;
2992 static GameMode oldmode = (GameMode) -1;
2995 if (!boardWidget || !XtIsRealized(boardWidget)) return;
2997 if (pausing != oldPausing) {
2998 oldPausing = pausing;
2999 MarkMenuItem("Pause", pausing);
3001 if (appData.showButtonBar) {
3002 /* Always toggle, don't set. Previous code messes up when
3003 invoked while the button is pressed, as releasing it
3004 toggles the state again. */
3007 XtSetArg(args[0], XtNbackground, &oldbg);
3008 XtSetArg(args[1], XtNforeground, &oldfg);
3009 XtGetValues(optList[18].handle,
3011 XtSetArg(args[0], XtNbackground, oldfg);
3012 XtSetArg(args[1], XtNforeground, oldbg);
3014 XtSetValues(optList[18].handle, args, 2);
3018 wname = ModeToWidgetName(oldmode);
3019 if (wname != NULL) {
3020 MarkMenuItem(wname, False);
3022 wname = ModeToWidgetName(gameMode);
3023 if (wname != NULL) {
3024 MarkMenuItem(wname, True);
3027 MarkMenuItem("Machine Match", matchMode && matchGame < appData.matchGames);
3029 /* Maybe all the enables should be handled here, not just this one */
3030 EnableMenuItem("Training", gameMode == Training || gameMode == PlayFromGameFile);
3035 * Button/menu procedures
3038 LoadGamePopUp (FILE *f, int gameNumber, char *title)
3040 cmailMsgLoaded = FALSE;
3041 if (gameNumber == 0) {
3042 int error = GameListBuild(f);
3044 DisplayError(_("Cannot build game list"), error);
3045 } else if (!ListEmpty(&gameList) &&
3046 ((ListGame *) gameList.tailPred)->number > 1) {
3047 GameListPopUp(f, title);
3053 return LoadGame(f, gameNumber, title, FALSE);
3056 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3057 char *selected_fen_position=NULL;
3060 SendPositionSelection (Widget w, Atom *selection, Atom *target,
3061 Atom *type_return, XtPointer *value_return,
3062 unsigned long *length_return, int *format_return)
3064 char *selection_tmp;
3066 // if (!selected_fen_position) return False; /* should never happen */
3067 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3068 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
3069 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
3072 if (f == NULL) return False;
3076 selection_tmp = XtMalloc(len + 1);
3077 count = fread(selection_tmp, 1, len, f);
3080 XtFree(selection_tmp);
3083 selection_tmp[len] = NULLCHAR;
3085 /* note: since no XtSelectionDoneProc was registered, Xt will
3086 * automatically call XtFree on the value returned. So have to
3087 * make a copy of it allocated with XtMalloc */
3088 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3089 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
3092 *value_return=selection_tmp;
3093 *length_return=strlen(selection_tmp);
3094 *type_return=*target;
3095 *format_return = 8; /* bits per byte */
3097 } else if (*target == XA_TARGETS(xDisplay)) {
3098 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3099 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3100 targets_tmp[1] = XA_STRING;
3101 *value_return = targets_tmp;
3102 *type_return = XA_ATOM;
3105 // This code leads to a read of value_return out of bounds on 64-bit systems.
3106 // Other code which I have seen always sets *format_return to 32 independent of
3107 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
3108 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
3109 *format_return = 8 * sizeof(Atom);
3110 if (*format_return > 32) {
3111 *length_return *= *format_return / 32;
3112 *format_return = 32;
3115 *format_return = 32;
3123 /* note: when called from menu all parameters are NULL, so no clue what the
3124 * Widget which was clicked on was, or what the click event was
3127 CopySomething (char *src)
3129 selected_fen_position = src;
3131 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3132 * have a notion of a position that is selected but not copied.
3133 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3135 XtOwnSelection(menuBarWidget, XA_PRIMARY,
3137 SendPositionSelection,
3138 NULL/* lose_ownership_proc */ ,
3139 NULL/* transfer_done_proc */);
3140 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3142 SendPositionSelection,
3143 NULL/* lose_ownership_proc */ ,
3144 NULL/* transfer_done_proc */);
3147 /* function called when the data to Paste is ready */
3149 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3150 Atom *type, XtPointer value, unsigned long *len, int *format)
3153 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3154 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3155 EditPositionPasteFEN(fenstr);
3159 /* called when Paste Position button is pressed,
3160 * all parameters will be NULL */
3162 PastePositionProc ()
3164 XtGetSelectionValue(menuBarWidget,
3165 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3166 /* (XtSelectionCallbackProc) */ PastePositionCB,
3167 NULL, /* client_data passed to PastePositionCB */
3169 /* better to use the time field from the event that triggered the
3170 * call to this function, but that isn't trivial to get
3177 /* note: when called from menu all parameters are NULL, so no clue what the
3178 * Widget which was clicked on was, or what the click event was
3180 /* function called when the data to Paste is ready */
3182 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3183 Atom *type, XtPointer value, unsigned long *len, int *format)
3186 if (value == NULL || *len == 0) {
3187 return; /* nothing had been selected to copy */
3189 f = fopen(gamePasteFilename, "w");
3191 DisplayError(_("Can't open temp file"), errno);
3194 fwrite(value, 1, *len, f);
3197 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3200 /* called when Paste Game button is pressed,
3201 * all parameters will be NULL */
3205 XtGetSelectionValue(menuBarWidget,
3206 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3207 /* (XtSelectionCallbackProc) */ PasteGameCB,
3208 NULL, /* client_data passed to PasteGameCB */
3210 /* better to use the time field from the event that triggered the
3211 * call to this function, but that isn't trivial to get
3220 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3227 { // bassic primitive for determining if modifier keys are pressed
3228 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3231 XQueryKeymap(xDisplay,keys);
3232 for(i=0; i<6; i++) {
3234 j = XKeysymToKeycode(xDisplay, codes[i]);
3235 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3241 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3245 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3246 if ( n == 1 && *buf >= 32 // printable
3247 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3248 ) BoxAutoPopUp (buf);
3252 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3253 { // [HGM] input: let up-arrow recall previous line from history
3258 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3259 { // [HGM] input: let down-arrow recall next line from history
3264 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3270 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3272 if (!TempBackwardActive) {
3273 TempBackwardActive = True;
3279 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3281 /* Check to see if triggered by a key release event for a repeating key.
3282 * If so the next queued event will be a key press of the same key at the same time */
3283 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3285 XPeekEvent(xDisplay, &next);
3286 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3287 next.xkey.keycode == event->xkey.keycode)
3291 TempBackwardActive = False;
3295 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3296 { // called as key binding
3299 if (nprms && *nprms > 0)
3303 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3308 DisplayMessage (char *message, char *extMessage)
3310 /* display a message in the message widget */
3319 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
3324 message = extMessage;
3328 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
3330 /* need to test if messageWidget already exists, since this function
3331 can also be called during the startup, if for example a Xresource
3332 is not set up correctly */
3333 if(optList && optList[14].handle)
3335 XtSetArg(arg, XtNlabel, message);
3336 XtSetValues(optList[14].handle, &arg, 1);
3343 SetWindowTitle (char *text, char *title, char *icon)
3347 if (appData.titleInWindow) {
3349 XtSetArg(args[i], XtNlabel, text); i++;
3350 XtSetValues(titleWidget, args, i);
3353 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3354 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3355 XtSetValues(shellWidget, args, i);
3356 XSync(xDisplay, False);
3361 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3367 DisplayIcsInteractionTitle (String message)
3369 if (oldICSInteractionTitle == NULL) {
3370 /* Magic to find the old window title, adapted from vim */
3371 char *wina = getenv("WINDOWID");
3373 Window win = (Window) atoi(wina);
3374 Window root, parent, *children;
3375 unsigned int nchildren;
3376 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3378 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3379 if (!XQueryTree(xDisplay, win, &root, &parent,
3380 &children, &nchildren)) break;
3381 if (children) XFree((void *)children);
3382 if (parent == root || parent == 0) break;
3385 XSetErrorHandler(oldHandler);
3387 if (oldICSInteractionTitle == NULL) {
3388 oldICSInteractionTitle = "xterm";
3391 printf("\033]0;%s\007", message);
3396 XtIntervalId delayedEventTimerXID = 0;
3397 DelayedEventCallback delayedEventCallback = 0;
3402 delayedEventTimerXID = 0;
3403 delayedEventCallback();
3407 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3409 if(delayedEventTimerXID && delayedEventCallback == cb)
3410 // [HGM] alive: replace, rather than add or flush identical event
3411 XtRemoveTimeOut(delayedEventTimerXID);
3412 delayedEventCallback = cb;
3413 delayedEventTimerXID =
3414 XtAppAddTimeOut(appContext, millisec,
3415 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3418 DelayedEventCallback
3421 if (delayedEventTimerXID) {
3422 return delayedEventCallback;
3429 CancelDelayedEvent ()
3431 if (delayedEventTimerXID) {
3432 XtRemoveTimeOut(delayedEventTimerXID);
3433 delayedEventTimerXID = 0;
3437 XtIntervalId loadGameTimerXID = 0;
3440 LoadGameTimerRunning ()
3442 return loadGameTimerXID != 0;
3446 StopLoadGameTimer ()
3448 if (loadGameTimerXID != 0) {
3449 XtRemoveTimeOut(loadGameTimerXID);
3450 loadGameTimerXID = 0;
3458 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3460 loadGameTimerXID = 0;
3465 StartLoadGameTimer (long millisec)
3468 XtAppAddTimeOut(appContext, millisec,
3469 (XtTimerCallbackProc) LoadGameTimerCallback,
3473 XtIntervalId analysisClockXID = 0;
3476 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3478 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3479 || appData.icsEngineAnalyze) { // [DM]
3480 AnalysisPeriodicEvent(0);
3481 StartAnalysisClock();
3486 StartAnalysisClock ()
3489 XtAppAddTimeOut(appContext, 2000,
3490 (XtTimerCallbackProc) AnalysisClockCallback,
3494 XtIntervalId clockTimerXID = 0;
3497 ClockTimerRunning ()
3499 return clockTimerXID != 0;
3505 if (clockTimerXID != 0) {
3506 XtRemoveTimeOut(clockTimerXID);
3515 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3522 StartClockTimer (long millisec)
3525 XtAppAddTimeOut(appContext, millisec,
3526 (XtTimerCallbackProc) ClockTimerCallback,
3531 DisplayTimerLabel (int optNr, char *color, long timer, int highlight)
3535 Widget w = optList[optNr].handle;
3537 /* check for low time warning */
3538 Pixel foregroundOrWarningColor = timerForegroundPixel;
3541 appData.lowTimeWarning &&
3542 (timer / 1000) < appData.icsAlarmTime)
3543 foregroundOrWarningColor = lowTimeWarningColor;
3545 if (appData.clockMode) {
3546 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
3547 XtSetArg(args[0], XtNlabel, buf);
3549 snprintf(buf, MSG_SIZ, "%s ", color);
3550 XtSetArg(args[0], XtNlabel, buf);
3555 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3556 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3558 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3559 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3562 XtSetValues(w, args, 3);
3565 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3568 SetClockIcon (int color)
3571 Pixmap pm = *clockIcons[color];
3572 if (iconPixmap != pm) {
3574 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3575 XtSetValues(shellWidget, args, 1);
3580 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3582 InputSource *is = (InputSource *) closure;
3587 if (is->lineByLine) {
3588 count = read(is->fd, is->unused,
3589 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3591 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3594 is->unused += count;
3596 while (p < is->unused) {
3597 q = memchr(p, '\n', is->unused - p);
3598 if (q == NULL) break;
3600 (is->func)(is, is->closure, p, q - p, 0);
3604 while (p < is->unused) {
3609 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3614 (is->func)(is, is->closure, is->buf, count, error);
3619 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3622 ChildProc *cp = (ChildProc *) pr;
3624 is = (InputSource *) calloc(1, sizeof(InputSource));
3625 is->lineByLine = lineByLine;
3629 is->fd = fileno(stdin);
3631 is->kind = cp->kind;
3632 is->fd = cp->fdFrom;
3635 is->unused = is->buf;
3638 is->xid = XtAppAddInput(appContext, is->fd,
3639 (XtPointer) (XtInputReadMask),
3640 (XtInputCallbackProc) DoInputCallback,
3642 is->closure = closure;
3643 return (InputSourceRef) is;
3647 RemoveInputSource (InputSourceRef isr)
3649 InputSource *is = (InputSource *) isr;
3651 if (is->xid == 0) return;
3652 XtRemoveInput(is->xid);
3656 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
3658 /* Masks for XPM pieces. Black and white pieces can have
3659 different shapes, but in the interest of retaining my
3660 sanity pieces must have the same outline on both light
3661 and dark squares, and all pieces must use the same
3662 background square colors/images. */
3664 static int xpmDone = 0;
3665 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
3666 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
3669 CreateAnimMasks (int pieceDepth)
3675 unsigned long plane;
3678 /* Need a bitmap just to get a GC with right depth */
3679 buf = XCreatePixmap(xDisplay, xBoardWindow,
3681 values.foreground = 1;
3682 values.background = 0;
3683 /* Don't use XtGetGC, not read only */
3684 maskGC = XCreateGC(xDisplay, buf,
3685 GCForeground | GCBackground, &values);
3686 XFreePixmap(xDisplay, buf);
3688 buf = XCreatePixmap(xDisplay, xBoardWindow,
3689 squareSize, squareSize, pieceDepth);
3690 values.foreground = XBlackPixel(xDisplay, xScreen);
3691 values.background = XWhitePixel(xDisplay, xScreen);
3692 bufGC = XCreateGC(xDisplay, buf,
3693 GCForeground | GCBackground, &values);
3695 for (piece = WhitePawn; piece <= BlackKing; piece++) {
3696 /* Begin with empty mask */
3697 if(!xpmDone) // [HGM] pieces: keep using existing
3698 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
3699 squareSize, squareSize, 1);
3700 XSetFunction(xDisplay, maskGC, GXclear);
3701 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
3702 0, 0, squareSize, squareSize);
3704 /* Take a copy of the piece */
3709 XSetFunction(xDisplay, bufGC, GXcopy);
3710 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
3712 0, 0, squareSize, squareSize, 0, 0);
3714 /* XOR the background (light) over the piece */
3715 XSetFunction(xDisplay, bufGC, GXxor);
3717 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
3718 0, 0, squareSize, squareSize, 0, 0);
3720 XSetForeground(xDisplay, bufGC, lightSquareColor);
3721 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
3724 /* We now have an inverted piece image with the background
3725 erased. Construct mask by just selecting all the non-zero
3726 pixels - no need to reconstruct the original image. */
3727 XSetFunction(xDisplay, maskGC, GXor);
3729 /* Might be quicker to download an XImage and create bitmap
3730 data from it rather than this N copies per piece, but it
3731 only takes a fraction of a second and there is a much
3732 longer delay for loading the pieces. */
3733 for (n = 0; n < pieceDepth; n ++) {
3734 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
3735 0, 0, squareSize, squareSize,
3741 XFreePixmap(xDisplay, buf);
3742 XFreeGC(xDisplay, bufGC);
3743 XFreeGC(xDisplay, maskGC);
3747 InitAnimState (AnimNr anr, XWindowAttributes *info)
3752 /* Each buffer is square size, same depth as window */
3753 animBufs[anr+4] = xBoardWindow;
3754 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
3755 squareSize, squareSize, info->depth);
3756 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
3757 squareSize, squareSize, info->depth);
3759 /* Create a plain GC for blitting */
3760 mask = GCForeground | GCBackground | GCFunction |
3761 GCPlaneMask | GCGraphicsExposures;
3762 values.foreground = XBlackPixel(xDisplay, xScreen);
3763 values.background = XWhitePixel(xDisplay, xScreen);
3764 values.function = GXcopy;
3765 values.plane_mask = AllPlanes;
3766 values.graphics_exposures = False;
3767 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
3769 /* Piece will be copied from an existing context at
3770 the start of each new animation/drag. */
3771 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
3773 /* Outline will be a read-only copy of an existing */
3774 animGCs[anr+4] = None;
3780 XWindowAttributes info;
3782 if (xpmDone && gameInfo.variant == oldVariant) return;
3783 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
3784 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
3786 InitAnimState(Game, &info);
3787 InitAnimState(Player, &info);
3789 /* For XPM pieces, we need bitmaps to use as masks. */
3791 CreateAnimMasks(info.depth), xpmDone = 1;
3796 static Boolean frameWaiting;
3799 FrameAlarm (int sig)
3801 frameWaiting = False;
3802 /* In case System-V style signals. Needed?? */
3803 signal(SIGALRM, FrameAlarm);
3807 FrameDelay (int time)
3809 struct itimerval delay;
3811 XSync(xDisplay, False);
3814 frameWaiting = True;
3815 signal(SIGALRM, FrameAlarm);
3816 delay.it_interval.tv_sec =
3817 delay.it_value.tv_sec = time / 1000;
3818 delay.it_interval.tv_usec =
3819 delay.it_value.tv_usec = (time % 1000) * 1000;
3820 setitimer(ITIMER_REAL, &delay, NULL);
3821 while (frameWaiting) pause();
3822 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
3823 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
3824 setitimer(ITIMER_REAL, &delay, NULL);
3831 FrameDelay (int time)
3833 XSync(xDisplay, False);
3835 usleep(time * 1000);
3841 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
3845 /* Bitmap for piece being moved. */
3846 if (appData.monoMode) {
3847 *mask = *pieceToSolid(piece);
3848 } else if (useImages) {
3850 *mask = xpmMask[piece];
3852 *mask = ximMaskPm[piece];
3855 *mask = *pieceToSolid(piece);
3858 /* GC for piece being moved. Square color doesn't matter, but
3859 since it gets modified we make a copy of the original. */
3861 if (appData.monoMode)
3866 if (appData.monoMode)
3871 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
3873 /* Outline only used in mono mode and is not modified */
3875 *outline = bwPieceGC;
3877 *outline = wbPieceGC;
3881 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
3886 /* Draw solid rectangle which will be clipped to shape of piece */
3887 XFillRectangle(xDisplay, dest, clip,
3888 0, 0, squareSize, squareSize);
3889 if (appData.monoMode)
3890 /* Also draw outline in contrasting color for black
3891 on black / white on white cases */
3892 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
3893 0, 0, squareSize, squareSize, 0, 0, 1);
3895 /* Copy the piece */
3900 if(appData.upsideDown && flipView) kind ^= 2;
3901 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3903 0, 0, squareSize, squareSize,
3909 InsertPiece (AnimNr anr, ChessSquare piece)
3911 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
3915 DrawBlank (AnimNr anr, int x, int y, int startColor)
3917 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
3920 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
3921 int srcX, int srcY, int width, int height, int destX, int destY)
3923 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
3924 srcX, srcY, width, height, destX, destY);
3928 SetDragPiece (AnimNr anr, ChessSquare piece)
3931 /* The piece will be drawn using its own bitmap as a matte */
3932 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
3933 XSetClipMask(xDisplay, animGCs[anr+2], mask);
3936 /* [AS] Arrow highlighting support */
3939 DrawPolygon (Pnt arrow[], int nr)
3943 for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
3944 XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
3945 if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
3949 UpdateLogos (int displ)
3951 return; // no logos in XBoard yet