2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
64 #include <cairo/cairo.h>
65 #include <cairo/cairo-xlib.h>
68 # if HAVE_SYS_SOCKET_H
69 # include <sys/socket.h>
70 # include <netinet/in.h>
72 # else /* not HAVE_SYS_SOCKET_H */
73 # if HAVE_LAN_SOCKET_H
74 # include <lan/socket.h>
76 # include <lan/netdb.h>
77 # else /* not HAVE_LAN_SOCKET_H */
78 # define OMIT_SOCKETS 1
79 # endif /* not HAVE_LAN_SOCKET_H */
80 # endif /* not HAVE_SYS_SOCKET_H */
81 #endif /* !OMIT_SOCKETS */
86 #else /* not STDC_HEADERS */
87 extern char *getenv();
90 # else /* not HAVE_STRING_H */
92 # endif /* not HAVE_STRING_H */
93 #endif /* not STDC_HEADERS */
96 # include <sys/fcntl.h>
97 #else /* not HAVE_SYS_FCNTL_H */
100 # endif /* HAVE_FCNTL_H */
101 #endif /* not HAVE_SYS_FCNTL_H */
103 #if HAVE_SYS_SYSTEMINFO_H
104 # include <sys/systeminfo.h>
105 #endif /* HAVE_SYS_SYSTEMINFO_H */
107 #if TIME_WITH_SYS_TIME
108 # include <sys/time.h>
112 # include <sys/time.h>
123 # include <sys/wait.h>
128 # define NAMLEN(dirent) strlen((dirent)->d_name)
129 # define HAVE_DIR_STRUCT
131 # define dirent direct
132 # define NAMLEN(dirent) (dirent)->d_namlen
134 # include <sys/ndir.h>
135 # define HAVE_DIR_STRUCT
138 # include <sys/dir.h>
139 # define HAVE_DIR_STRUCT
143 # define HAVE_DIR_STRUCT
151 #include <X11/Intrinsic.h>
152 #include <X11/StringDefs.h>
153 #include <X11/Shell.h>
154 #include <X11/cursorfont.h>
155 #include <X11/Xatom.h>
156 #include <X11/Xmu/Atoms.h>
158 #include <X11/Xaw3d/Dialog.h>
159 #include <X11/Xaw3d/Form.h>
160 #include <X11/Xaw3d/List.h>
161 #include <X11/Xaw3d/Label.h>
162 #include <X11/Xaw3d/SimpleMenu.h>
163 #include <X11/Xaw3d/SmeBSB.h>
164 #include <X11/Xaw3d/SmeLine.h>
165 #include <X11/Xaw3d/Box.h>
166 #include <X11/Xaw3d/MenuButton.h>
167 #include <X11/Xaw3d/Text.h>
168 #include <X11/Xaw3d/AsciiText.h>
170 #include <X11/Xaw/Dialog.h>
171 #include <X11/Xaw/Form.h>
172 #include <X11/Xaw/List.h>
173 #include <X11/Xaw/Label.h>
174 #include <X11/Xaw/SimpleMenu.h>
175 #include <X11/Xaw/SmeBSB.h>
176 #include <X11/Xaw/SmeLine.h>
177 #include <X11/Xaw/Box.h>
178 #include <X11/Xaw/MenuButton.h>
179 #include <X11/Xaw/Text.h>
180 #include <X11/Xaw/AsciiText.h>
183 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
188 #include "pixmaps/pixmaps.h"
189 #define IMAGE_EXT "xpm"
191 #define IMAGE_EXT "xim"
192 #include "bitmaps/bitmaps.h"
195 #include "bitmaps/icon_white.bm"
196 #include "bitmaps/icon_black.bm"
197 #include "bitmaps/checkmark.bm"
199 #include "frontend.h"
201 #include "backendz.h"
205 #include "xgamelist.h"
206 #include "xhistory.h"
207 #include "xevalgraph.h"
208 #include "xedittags.h"
212 #include "engineoutput.h"
221 #define usleep(t) _sleep2(((t)+500)/1000)
225 # define _(s) gettext (s)
226 # define N_(s) gettext_noop (s)
232 int main P((int argc, char **argv));
233 RETSIGTYPE CmailSigHandler P((int sig));
234 RETSIGTYPE IntSigHandler P((int sig));
235 RETSIGTYPE TermSizeSigHandler P((int sig));
236 static void CreateGCs P((int redo));
237 static void CreateAnyPieces P((void));
238 void CreateXIMPieces P((void));
239 void CreateXPMPieces P((void));
240 void CreatePNGPieces P((void));
241 void CreateXPMBoard P((char *s, int n));
242 void CreatePieces P((void));
243 Widget CreateMenuBar P((Menu *mb, int boardWidth));
245 char *InsertPxlSize P((char *pattern, int targetPxlSize));
246 XFontSet CreateFontSet P((char *base_fnt_lst));
248 char *FindFont P((char *pattern, int targetPxlSize));
250 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
251 u_int wreq, u_int hreq));
252 void CreateGrid P((void));
253 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
254 void DelayedDrag P((void));
255 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
256 void HandlePV P((Widget w, XEvent * event,
257 String * params, Cardinal * nParams));
258 void DrawPositionProc P((Widget w, XEvent *event,
259 String *prms, Cardinal *nprms));
260 void CommentClick P((Widget w, XEvent * event,
261 String * params, Cardinal * nParams));
262 void ICSInputBoxPopUp P((void));
263 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
264 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
265 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
266 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
267 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
268 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
269 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
270 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
271 Boolean TempBackwardActive = False;
272 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
273 void DisplayMove P((int moveNumber));
274 void ICSInitScript P((void));
275 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
276 void update_ics_width P(());
277 int CopyMemoProc P(());
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 gridSegments[BOARD_RANKS + BOARD_FILES + 2];
298 XFontSet fontSet, clockFontSet;
301 XFontStruct *clockFontStruct;
303 Font coordFontID, countFontID;
304 XFontStruct *coordFontStruct, *countFontStruct;
305 XtAppContext appContext;
308 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
310 Position commentX = -1, commentY = -1;
311 Dimension commentW, commentH;
312 typedef unsigned int BoardSize;
314 Boolean chessProgram;
316 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
317 int smallLayout = 0, tinyLayout = 0,
318 marginW, marginH, // [HGM] for run-time resizing
319 fromX = -1, fromY = -1, toX, toY, commentUp = False,
320 errorExitStatus = -1, defaultLineGap;
321 Dimension textHeight;
322 Pixel timerForegroundPixel, timerBackgroundPixel;
323 Pixel buttonForegroundPixel, buttonBackgroundPixel;
324 char *chessDir, *programName, *programVersion;
325 Boolean alwaysOnTop = False;
326 char *icsTextMenuString;
328 char *firstChessProgramNames;
329 char *secondChessProgramNames;
331 WindowPlacement wpMain;
332 WindowPlacement wpConsole;
333 WindowPlacement wpComment;
334 WindowPlacement wpMoveHistory;
335 WindowPlacement wpEvalGraph;
336 WindowPlacement wpEngineOutput;
337 WindowPlacement wpGameList;
338 WindowPlacement wpTags;
343 cairo_surface_t *pngPieceBitmaps[2][(int)BlackPawn]; // scaled pieces as used
344 cairo_surface_t *pngPieceBitmaps2[2][(int)BlackPawn+4]; // scaled pieces in store
345 cairo_surface_t *pngBoardBitmap[2];
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(Actions.Resign) \n \
436 :Ctrl<Key>n: MenuItem(File.NewGame) \n \
437 :Meta<Key>V: MenuItem(File.NewVariant) \n \
438 :Ctrl<Key>o: MenuItem(File.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(File.SaveGame) \n \
444 :Ctrl<Key>c: MenuItem(Edit.CopyGame) \n \
445 :Ctrl<Key>v: MenuItem(Edit.PasteGame) \n \
446 :Ctrl<Key>O: MenuItem(File.LoadPosition) \n \
447 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
448 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
449 :Ctrl<Key>S: MenuItem(File.SavePosition) \n \
450 :Ctrl<Key>C: MenuItem(Edit.CopyPosition) \n \
451 :Ctrl<Key>V: MenuItem(Edit.PastePosition) \n \
452 :Ctrl<Key>q: MenuItem(File.Quit) \n \
453 :Ctrl<Key>w: MenuItem(Mode.MachineWhite) \n \
454 :Ctrl<Key>b: MenuItem(Mode.MachineBlack) \n \
455 :Ctrl<Key>t: MenuItem(Mode.TwoMachines) \n \
456 :Ctrl<Key>a: MenuItem(Mode.AnalysisMode) \n \
457 :Ctrl<Key>g: MenuItem(Mode.AnalyzeFile) \n \
458 :Ctrl<Key>e: MenuItem(Mode.EditGame) \n \
459 :Ctrl<Key>E: MenuItem(Mode.EditPosition) \n \
460 :Meta<Key>O: MenuItem(View.EngineOutput) \n \
461 :Meta<Key>E: MenuItem(View.EvaluationGraph) \n \
462 :Meta<Key>G: MenuItem(View.GameList) \n \
463 :Meta<Key>H: MenuItem(View.MoveHistory) \n \
464 :<Key>Pause: MenuItem(Mode.Pause) \n \
465 :<Key>F3: MenuItem(Action.Accept) \n \
466 :<Key>F4: MenuItem(Action.Decline) \n \
467 :<Key>F12: MenuItem(Action.Rematch) \n \
468 :<Key>F5: MenuItem(Action.CallFlag) \n \
469 :<Key>F6: MenuItem(Action.Draw) \n \
470 :<Key>F7: MenuItem(Action.Adjourn) \n \
471 :<Key>F8: MenuItem(Action.Abort) \n \
472 :<Key>F10: MenuItem(Action.StopObserving) \n \
473 :<Key>F11: MenuItem(Action.StopExamining) \n \
474 :Ctrl<Key>d: MenuItem(DebugProc) \n \
475 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
476 :Meta<Key>End: MenuItem(Edit.ForwardtoEnd) \n \
477 :Meta<Key>Right: MenuItem(Edit.Forward) \n \
478 :Meta<Key>Home: MenuItem(Edit.BacktoStart) \n \
479 :Meta<Key>Left: MenuItem(Edit.Backward) \n \
480 :<Key>Left: MenuItem(Edit.Backward) \n \
481 :<Key>Right: MenuItem(Edit.Forward) \n \
482 :<Key>Home: MenuItem(Edit.Revert) \n \
483 :<Key>End: MenuItem(Edit.TruncateGame) \n \
484 :Ctrl<Key>m: MenuItem(Engine.MoveNow) \n \
485 :Ctrl<Key>x: MenuItem(Engine.RetractMove) \n \
486 :Meta<Key>J: MenuItem(Options.Adjudications) \n \
487 :Meta<Key>U: MenuItem(Options.CommonEngine) \n \
488 :Meta<Key>T: MenuItem(Options.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(Help.ManXBoard) \n \
500 :<Key>F2: MenuItem(View.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)
843 XWindowAttributes winAt;
850 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
851 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
852 wp->x = rx - winAt.x;
853 wp->y = ry - winAt.y;
854 wp->height = winAt.height;
855 wp->width = winAt.width;
856 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
861 { // wrapper to shield use of window handles from back-end (make addressible by number?)
862 // In XBoard this will have to wait until awareness of window parameters is implemented
863 GetActualPlacement(shellWidget, &wpMain);
864 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
865 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
866 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
867 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
868 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
869 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
873 PrintCommPortSettings (FILE *f, char *name)
874 { // This option does not exist in XBoard
878 EnsureOnScreen (int *x, int *y, int minX, int minY)
885 { // [HGM] args: allows testing if main window is realized from back-end
886 return xBoardWindow != 0;
892 extern Option dualOptions[];
894 Window tmp = xBoardWindow;
895 if(!dual) dual = XtWindow(dualOptions[3].handle); // must be first call
896 xBoardWindow = dual; // swap them
901 PopUpStartupDialog ()
902 { // start menu not implemented in XBoard
906 ConvertToLine (int argc, char **argv)
908 static char line[128*1024], buf[1024];
912 for(i=1; i<argc; i++)
914 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
915 && argv[i][0] != '{' )
916 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
918 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
919 strncat(line, buf, 128*1024 - strlen(line) - 1 );
922 line[strlen(line)-1] = NULLCHAR;
926 //--------------------------------------------------------------------------------------------
928 #define BoardSize int
930 InitDrawingSizes (BoardSize boardSize, int flags)
931 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
932 Dimension boardWidth, boardHeight, w, h;
934 static Dimension oldWidth, oldHeight;
935 static VariantClass oldVariant;
936 static int oldMono = -1, oldTwoBoards = 0;
938 if(!formWidget) return;
940 if(oldTwoBoards && !twoBoards) PopDown(DummyDlg);
941 oldTwoBoards = twoBoards;
943 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
944 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
945 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
947 if(boardWidth != oldWidth || boardHeight != oldHeight) { // do resizing stuff only if size actually changed
949 oldWidth = boardWidth; oldHeight = boardHeight;
953 * Inhibit shell resizing.
955 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW ;
956 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
957 shellArgs[4].value = shellArgs[2].value = w;
958 shellArgs[5].value = shellArgs[3].value = h;
959 XtSetValues(shellWidget, &shellArgs[0], 6);
961 XSync(xDisplay, False);
965 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
968 if(gameInfo.variant != oldVariant) { // and only if variant changed
973 for(p=0; p<=(int)WhiteKing; p++)
974 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
975 if(gameInfo.variant == VariantShogi) {
976 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
977 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
978 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
979 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
980 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
983 if(gameInfo.variant == VariantGothic) {
984 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
987 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
988 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
989 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
992 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
993 for(p=0; p<=(int)WhiteKing; p++)
994 ximMaskPm[p] = ximMaskPm2[p]; // defaults
995 if(gameInfo.variant == VariantShogi) {
996 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
997 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
998 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
999 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1000 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1003 if(gameInfo.variant == VariantGothic) {
1004 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1007 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1008 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1009 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1014 for(i=0; i<2; i++) {
1016 for(p=0; p<=(int)WhiteKing; p++)
1017 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1018 if(gameInfo.variant == VariantShogi) {
1019 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1020 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1021 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1022 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1023 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1026 if(gameInfo.variant == VariantGothic) {
1027 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1030 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1031 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1032 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1036 for(i=0; i<2; i++) {
1038 printf("Copy pieces\n");
1039 for(p=0; p<=(int)WhiteKing; p++)
1040 pngPieceBitmaps[i][p] = pngPieceBitmaps2[i][p]; // defaults
1042 oldMono = -10; // kludge to force recreation of animation masks
1043 oldVariant = gameInfo.variant;
1046 if(appData.monoMode != oldMono)
1049 oldMono = appData.monoMode;
1053 MakeOneColor (char *name, Pixel *color)
1055 XrmValue vFrom, vTo;
1056 if (!appData.monoMode) {
1057 vFrom.addr = (caddr_t) name;
1058 vFrom.size = strlen(name);
1059 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1060 if (vTo.addr == NULL) {
1061 appData.monoMode = True;
1064 *color = *(Pixel *) vTo.addr;
1072 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1073 int forceMono = False;
1075 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1076 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1077 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1078 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1079 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1080 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1081 if (appData.lowTimeWarning)
1082 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
1083 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1084 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1091 { // [HGM] taken out of main
1093 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1094 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1095 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1097 if (appData.bitmapDirectory[0] != NULLCHAR) {
1101 CreateXPMBoard(appData.liteBackTextureFile, 1);
1102 CreateXPMBoard(appData.darkBackTextureFile, 0);
1104 if (appData.pngDirectory[0] != NULLCHAR) { // for now do in parallel
1109 /* Create regular pieces */
1110 if (!useImages) CreatePieces();
1115 InitDrawingParams ()
1117 MakeColors(); CreateGCs(True);
1122 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
1123 { // detervtomine what fonts to use, and create them
1127 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1128 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1129 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1130 appData.font = fontTable[MESSAGE_FONT][squareSize];
1131 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1132 appData.coordFont = fontTable[COORD_FONT][squareSize];
1135 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1136 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1137 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1138 fontSet = CreateFontSet(appData.font);
1139 clockFontSet = CreateFontSet(appData.clockFont);
1141 /* For the coordFont, use the 0th font of the fontset. */
1142 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1143 XFontStruct **font_struct_list;
1144 XFontSetExtents *fontSize;
1145 char **font_name_list;
1146 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1147 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1148 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1149 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1150 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1153 appData.font = FindFont(appData.font, fontPxlSize);
1154 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1155 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1156 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1157 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1158 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1159 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1160 // textHeight in !NLS mode!
1162 countFontID = coordFontID; // [HGM] holdings
1163 countFontStruct = coordFontStruct;
1165 xdb = XtDatabase(xDisplay);
1167 XrmPutLineResource(&xdb, "*international: True");
1168 vTo.size = sizeof(XFontSet);
1169 vTo.addr = (XtPointer) &fontSet;
1170 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1172 XrmPutStringResource(&xdb, "*font", appData.font);
1177 PrintArg (ArgType t)
1182 case ArgInt: p = " N"; break;
1183 case ArgString: p = " STR"; break;
1184 case ArgBoolean: p = " TF"; break;
1185 case ArgSettingsFilename:
1186 case ArgFilename: p = " FILE"; break;
1187 case ArgX: p = " Nx"; break;
1188 case ArgY: p = " Ny"; break;
1189 case ArgAttribs: p = " TEXTCOL"; break;
1190 case ArgColor: p = " COL"; break;
1191 case ArgFont: p = " FONT"; break;
1192 case ArgBoardSize: p = " SIZE"; break;
1193 case ArgFloat: p = " FLOAT"; break;
1198 case ArgCommSettings:
1209 ArgDescriptor *q, *p = argDescriptors+5;
1210 printf("\nXBoard accepts the following options:\n"
1211 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
1212 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
1213 " SIZE = board-size spec(s)\n"
1214 " Within parentheses are short forms, or options to set to true or false.\n"
1215 " Persistent options (saved in the settings file) are marked with *)\n\n");
1217 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
1218 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
1219 if(p->save) strcat(buf+len, "*");
1220 for(q=p+1; q->argLoc == p->argLoc; q++) {
1221 if(q->argName[0] == '-') continue;
1222 strcat(buf+len, q == p+1 ? " (" : " ");
1223 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
1225 if(q != p+1) strcat(buf+len, ")");
1227 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
1230 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
1234 main (int argc, char **argv)
1236 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1237 XSetWindowAttributes window_attributes;
1239 Dimension boardWidth, boardHeight, w, h;
1241 int forceMono = False;
1243 srandom(time(0)); // [HGM] book: make random truly random
1245 setbuf(stdout, NULL);
1246 setbuf(stderr, NULL);
1249 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1250 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1254 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1259 programName = strrchr(argv[0], '/');
1260 if (programName == NULL)
1261 programName = argv[0];
1266 XtSetLanguageProc(NULL, NULL, NULL);
1267 if (appData.debugMode) {
1268 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1271 bindtextdomain(PACKAGE, LOCALEDIR);
1272 textdomain(PACKAGE);
1275 appData.boardSize = "";
1276 InitAppData(ConvertToLine(argc, argv));
1278 if (p == NULL) p = "/tmp";
1279 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1280 gameCopyFilename = (char*) malloc(i);
1281 gamePasteFilename = (char*) malloc(i);
1282 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1283 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1285 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1286 static char buf[MSG_SIZ];
1287 EscapeExpand(buf, appData.firstInitString);
1288 appData.firstInitString = strdup(buf);
1289 EscapeExpand(buf, appData.secondInitString);
1290 appData.secondInitString = strdup(buf);
1291 EscapeExpand(buf, appData.firstComputerString);
1292 appData.firstComputerString = strdup(buf);
1293 EscapeExpand(buf, appData.secondComputerString);
1294 appData.secondComputerString = strdup(buf);
1297 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1300 if (chdir(chessDir) != 0) {
1301 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1307 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1308 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1309 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1310 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1313 setbuf(debugFP, NULL);
1316 /* [HGM,HR] make sure board size is acceptable */
1317 if(appData.NrFiles > BOARD_FILES ||
1318 appData.NrRanks > BOARD_RANKS )
1319 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1322 /* This feature does not work; animation needs a rewrite */
1323 appData.highlightDragging = FALSE;
1327 gameInfo.variant = StringToVariant(appData.variant);
1328 InitPosition(FALSE);
1331 XtAppInitialize(&appContext, "XBoard", shellOptions,
1332 XtNumber(shellOptions),
1333 &argc, argv, xboardResources, NULL, 0);
1335 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1336 clientResources, XtNumber(clientResources),
1339 xDisplay = XtDisplay(shellWidget);
1340 xScreen = DefaultScreen(xDisplay);
1341 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1344 * determine size, based on supplied or remembered -size, or screen size
1346 if (isdigit(appData.boardSize[0])) {
1347 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1348 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1349 &fontPxlSize, &smallLayout, &tinyLayout);
1351 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1352 programName, appData.boardSize);
1356 /* Find some defaults; use the nearest known size */
1357 SizeDefaults *szd, *nearest;
1358 int distance = 99999;
1359 nearest = szd = sizeDefaults;
1360 while (szd->name != NULL) {
1361 if (abs(szd->squareSize - squareSize) < distance) {
1363 distance = abs(szd->squareSize - squareSize);
1364 if (distance == 0) break;
1368 if (i < 2) lineGap = nearest->lineGap;
1369 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1370 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1371 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1372 if (i < 6) smallLayout = nearest->smallLayout;
1373 if (i < 7) tinyLayout = nearest->tinyLayout;
1376 SizeDefaults *szd = sizeDefaults;
1377 if (*appData.boardSize == NULLCHAR) {
1378 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1379 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1382 if (szd->name == NULL) szd--;
1383 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1385 while (szd->name != NULL &&
1386 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1387 if (szd->name == NULL) {
1388 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1389 programName, appData.boardSize);
1393 squareSize = szd->squareSize;
1394 lineGap = szd->lineGap;
1395 clockFontPxlSize = szd->clockFontPxlSize;
1396 coordFontPxlSize = szd->coordFontPxlSize;
1397 fontPxlSize = szd->fontPxlSize;
1398 smallLayout = szd->smallLayout;
1399 tinyLayout = szd->tinyLayout;
1400 // [HGM] font: use defaults from settings file if available and not overruled
1403 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1404 if (strlen(appData.pixmapDirectory) > 0) {
1405 p = ExpandPathName(appData.pixmapDirectory);
1407 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1408 appData.pixmapDirectory);
1411 if (appData.debugMode) {
1412 fprintf(stderr, _("\
1413 XBoard square size (hint): %d\n\
1414 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1416 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1417 if (appData.debugMode) {
1418 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1421 defaultLineGap = lineGap;
1422 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1424 /* [HR] height treated separately (hacked) */
1425 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1426 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1429 * Determine what fonts to use.
1431 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1434 * Detect if there are not enough colors available and adapt.
1436 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1437 appData.monoMode = True;
1440 forceMono = MakeColors();
1443 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1445 appData.monoMode = True;
1448 if (appData.monoMode && appData.debugMode) {
1449 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1450 (unsigned long) XWhitePixel(xDisplay, xScreen),
1451 (unsigned long) XBlackPixel(xDisplay, xScreen));
1454 ParseIcsTextColors();
1456 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1462 layoutName = "tinyLayout";
1463 } else if (smallLayout) {
1464 layoutName = "smallLayout";
1466 layoutName = "normalLayout";
1469 optList = BoardPopUp(squareSize, lineGap, (void*)
1475 boardWidget = optList[W_BOARD].handle;
1476 menuBarWidget = optList[W_MENU].handle;
1477 dropMenu = optList[W_DROP].handle;
1478 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1479 formWidget = XtParent(boardWidget);
1480 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1481 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1482 XtGetValues(optList[W_WHITE].handle, args, 2);
1483 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1484 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1485 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1486 XtGetValues(optList[W_PAUSE].handle, args, 2);
1488 AppendEnginesToMenu(appData.recentEngineList);
1490 xBoardWindow = XtWindow(boardWidget);
1492 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1493 // not need to go into InitDrawingSizes().
1496 * Create X checkmark bitmap and initialize option menu checks.
1498 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1499 checkmark_bits, checkmark_width, checkmark_height);
1505 ReadBitmap(&wIconPixmap, "icon_white.bm",
1506 icon_white_bits, icon_white_width, icon_white_height);
1507 ReadBitmap(&bIconPixmap, "icon_black.bm",
1508 icon_black_bits, icon_black_width, icon_black_height);
1509 iconPixmap = wIconPixmap;
1511 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1512 XtSetValues(shellWidget, args, i);
1515 * Create a cursor for the board widget.
1517 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1518 XChangeWindowAttributes(xDisplay, xBoardWindow,
1519 CWCursor, &window_attributes);
1522 * Inhibit shell resizing.
1524 shellArgs[0].value = (XtArgVal) &w;
1525 shellArgs[1].value = (XtArgVal) &h;
1526 XtGetValues(shellWidget, shellArgs, 2);
1527 shellArgs[4].value = shellArgs[2].value = w;
1528 shellArgs[5].value = shellArgs[3].value = h;
1529 XtSetValues(shellWidget, &shellArgs[2], 4);
1530 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1531 marginH = h - boardHeight;
1533 CatchDeleteWindow(shellWidget, "QuitProc");
1539 if(appData.logoSize)
1540 { // locate and read user logo
1542 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1543 ASSIGN(userLogo, buf);
1546 if (appData.animate || appData.animateDragging)
1549 XtAugmentTranslations(formWidget,
1550 XtParseTranslationTable(globalTranslations));
1552 XtAddEventHandler(formWidget, KeyPressMask, False,
1553 (XtEventHandler) MoveTypeInProc, NULL);
1554 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1555 (XtEventHandler) EventProc, NULL);
1557 /* [AS] Restore layout */
1558 if( wpMoveHistory.visible ) {
1562 if( wpEvalGraph.visible )
1567 if( wpEngineOutput.visible ) {
1568 EngineOutputPopUp();
1573 if (errorExitStatus == -1) {
1574 if (appData.icsActive) {
1575 /* We now wait until we see "login:" from the ICS before
1576 sending the logon script (problems with timestamp otherwise) */
1577 /*ICSInitScript();*/
1578 if (appData.icsInputBox) ICSInputBoxPopUp();
1582 signal(SIGWINCH, TermSizeSigHandler);
1584 signal(SIGINT, IntSigHandler);
1585 signal(SIGTERM, IntSigHandler);
1586 if (*appData.cmailGameName != NULLCHAR) {
1587 signal(SIGUSR1, CmailSigHandler);
1591 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1594 // XtSetKeyboardFocus(shellWidget, formWidget);
1595 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1597 XtAppMainLoop(appContext);
1598 if (appData.debugMode) fclose(debugFP); // [DM] debug
1603 TermSizeSigHandler (int sig)
1609 IntSigHandler (int sig)
1615 CmailSigHandler (int sig)
1620 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1622 /* Activate call-back function CmailSigHandlerCallBack() */
1623 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1625 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1629 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1632 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1634 /**** end signal code ****/
1637 #define Abs(n) ((n)<0 ? -(n) : (n))
1641 InsertPxlSize (char *pattern, int targetPxlSize)
1643 char *base_fnt_lst, strInt[12], *p, *q;
1644 int alternatives, i, len, strIntLen;
1647 * Replace the "*" (if present) in the pixel-size slot of each
1648 * alternative with the targetPxlSize.
1652 while ((p = strchr(p, ',')) != NULL) {
1656 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1657 strIntLen = strlen(strInt);
1658 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1662 while (alternatives--) {
1663 char *comma = strchr(p, ',');
1664 for (i=0; i<14; i++) {
1665 char *hyphen = strchr(p, '-');
1667 if (comma && hyphen > comma) break;
1668 len = hyphen + 1 - p;
1669 if (i == 7 && *p == '*' && len == 2) {
1671 memcpy(q, strInt, strIntLen);
1681 len = comma + 1 - p;
1688 return base_fnt_lst;
1692 CreateFontSet (char *base_fnt_lst)
1695 char **missing_list;
1699 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1700 &missing_list, &missing_count, &def_string);
1701 if (appData.debugMode) {
1703 XFontStruct **font_struct_list;
1704 char **font_name_list;
1705 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1707 fprintf(debugFP, " got list %s, locale %s\n",
1708 XBaseFontNameListOfFontSet(fntSet),
1709 XLocaleOfFontSet(fntSet));
1710 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1711 for (i = 0; i < count; i++) {
1712 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1715 for (i = 0; i < missing_count; i++) {
1716 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1719 if (fntSet == NULL) {
1720 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1725 #else // not ENABLE_NLS
1727 * Find a font that matches "pattern" that is as close as
1728 * possible to the targetPxlSize. Prefer fonts that are k
1729 * pixels smaller to fonts that are k pixels larger. The
1730 * pattern must be in the X Consortium standard format,
1731 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1732 * The return value should be freed with XtFree when no
1736 FindFont (char *pattern, int targetPxlSize)
1738 char **fonts, *p, *best, *scalable, *scalableTail;
1739 int i, j, nfonts, minerr, err, pxlSize;
1741 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1743 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1744 programName, pattern);
1751 for (i=0; i<nfonts; i++) {
1754 if (*p != '-') continue;
1756 if (*p == NULLCHAR) break;
1757 if (*p++ == '-') j++;
1759 if (j < 7) continue;
1762 scalable = fonts[i];
1765 err = pxlSize - targetPxlSize;
1766 if (Abs(err) < Abs(minerr) ||
1767 (minerr > 0 && err < 0 && -err == minerr)) {
1773 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1774 /* If the error is too big and there is a scalable font,
1775 use the scalable font. */
1776 int headlen = scalableTail - scalable;
1777 p = (char *) XtMalloc(strlen(scalable) + 10);
1778 while (isdigit(*scalableTail)) scalableTail++;
1779 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1781 p = (char *) XtMalloc(strlen(best) + 2);
1782 safeStrCpy(p, best, strlen(best)+1 );
1784 if (appData.debugMode) {
1785 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1786 pattern, targetPxlSize, p);
1788 XFreeFontNames(fonts);
1795 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
1796 // must be called before all non-first callse to CreateGCs()
1797 XtReleaseGC(shellWidget, highlineGC);
1798 XtReleaseGC(shellWidget, lightSquareGC);
1799 XtReleaseGC(shellWidget, darkSquareGC);
1800 XtReleaseGC(shellWidget, lineGC);
1801 if (appData.monoMode) {
1802 if (DefaultDepth(xDisplay, xScreen) == 1) {
1803 XtReleaseGC(shellWidget, wbPieceGC);
1805 XtReleaseGC(shellWidget, bwPieceGC);
1808 XtReleaseGC(shellWidget, prelineGC);
1809 XtReleaseGC(shellWidget, wdPieceGC);
1810 XtReleaseGC(shellWidget, wlPieceGC);
1811 XtReleaseGC(shellWidget, bdPieceGC);
1812 XtReleaseGC(shellWidget, blPieceGC);
1817 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
1819 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1820 | GCBackground | GCFunction | GCPlaneMask;
1821 gc_values->foreground = foreground;
1822 gc_values->background = background;
1823 return XtGetGC(shellWidget, value_mask, gc_values);
1827 CreateGCs (int redo)
1829 XGCValues gc_values;
1831 Pixel white = XWhitePixel(xDisplay, xScreen);
1832 Pixel black = XBlackPixel(xDisplay, xScreen);
1834 gc_values.plane_mask = AllPlanes;
1835 gc_values.line_width = lineGap;
1836 gc_values.line_style = LineSolid;
1837 gc_values.function = GXcopy;
1840 DeleteGCs(); // called a second time; clean up old GCs first
1841 } else { // [HGM] grid and font GCs created on first call only
1842 coordGC = CreateOneGC(&gc_values, black, white);
1843 XSetFont(xDisplay, coordGC, coordFontID);
1845 // [HGM] make font for holdings counts (white on black)
1846 countGC = CreateOneGC(&gc_values, white, black);
1847 XSetFont(xDisplay, countGC, countFontID);
1849 lineGC = CreateOneGC(&gc_values, black, black);
1851 if (appData.monoMode) {
1853 highlineGC = CreateOneGC(&gc_values, white, white);
1854 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
1855 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
1857 if (DefaultDepth(xDisplay, xScreen) == 1) {
1858 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
1859 gc_values.function = GXcopyInverted;
1860 copyInvertedGC = CreateOneGC(&gc_values, black, white);
1861 gc_values.function = GXcopy;
1862 if (XBlackPixel(xDisplay, xScreen) == 1) {
1863 bwPieceGC = darkSquareGC;
1864 wbPieceGC = copyInvertedGC;
1866 bwPieceGC = copyInvertedGC;
1867 wbPieceGC = lightSquareGC;
1872 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
1873 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
1874 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
1875 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
1876 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
1877 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
1878 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
1879 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
1884 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
1892 fp = fopen(filename, "rb");
1894 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
1901 for (y=0; y<h; ++y) {
1902 for (x=0; x<h; ++x) {
1907 XPutPixel(xim, x, y, blackPieceColor);
1909 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1912 XPutPixel(xim, x, y, darkSquareColor);
1914 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1917 XPutPixel(xim, x, y, whitePieceColor);
1919 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1922 XPutPixel(xim, x, y, lightSquareColor);
1924 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1932 /* create Pixmap of piece */
1933 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1935 XPutImage(xDisplay, *dest, lightSquareGC, xim,
1938 /* create Pixmap of clipmask
1939 Note: We assume the white/black pieces have the same
1940 outline, so we make only 6 masks. This is okay
1941 since the XPM clipmask routines do the same. */
1943 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1945 XPutImage(xDisplay, temp, lightSquareGC, xmask,
1948 /* now create the 1-bit version */
1949 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1952 values.foreground = 1;
1953 values.background = 0;
1955 /* Don't use XtGetGC, not read only */
1956 maskGC = XCreateGC(xDisplay, *mask,
1957 GCForeground | GCBackground, &values);
1958 XCopyPlane(xDisplay, temp, *mask, maskGC,
1959 0, 0, squareSize, squareSize, 0, 0, 1);
1960 XFreePixmap(xDisplay, temp);
1965 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
1973 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
1978 /* The XSynchronize calls were copied from CreatePieces.
1979 Not sure if needed, but can't hurt */
1980 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
1983 /* temp needed by loadXIM() */
1984 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1985 0, 0, ss, ss, AllPlanes, XYPixmap);
1987 if (strlen(appData.pixmapDirectory) == 0) {
1991 if (appData.monoMode) {
1992 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
1996 fprintf(stderr, _("\nLoading XIMs...\n"));
1998 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
1999 fprintf(stderr, "%d", piece+1);
2000 for (kind=0; kind<4; kind++) {
2001 fprintf(stderr, ".");
2002 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2003 ExpandPathName(appData.pixmapDirectory),
2004 piece <= (int) WhiteKing ? "" : "w",
2005 pieceBitmapNames[piece],
2007 ximPieceBitmap[kind][piece] =
2008 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2009 0, 0, ss, ss, AllPlanes, XYPixmap);
2010 if (appData.debugMode)
2011 fprintf(stderr, _("(File:%s:) "), buf);
2012 loadXIM(ximPieceBitmap[kind][piece],
2014 &(xpmPieceBitmap2[kind][piece]),
2015 &(ximMaskPm2[piece]));
2016 if(piece <= (int)WhiteKing)
2017 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2019 fprintf(stderr," ");
2021 /* Load light and dark squares */
2022 /* If the LSQ and DSQ pieces don't exist, we will
2023 draw them with solid squares. */
2024 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2025 if (access(buf, 0) != 0) {
2029 fprintf(stderr, _("light square "));
2031 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2032 0, 0, ss, ss, AllPlanes, XYPixmap);
2033 if (appData.debugMode)
2034 fprintf(stderr, _("(File:%s:) "), buf);
2036 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2037 fprintf(stderr, _("dark square "));
2038 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2039 ExpandPathName(appData.pixmapDirectory), ss);
2040 if (appData.debugMode)
2041 fprintf(stderr, _("(File:%s:) "), buf);
2043 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2044 0, 0, ss, ss, AllPlanes, XYPixmap);
2045 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2046 xpmJailSquare = xpmLightSquare;
2048 fprintf(stderr, _("Done.\n"));
2050 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2053 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2057 CreateXPMBoard (char *s, int kind)
2061 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2062 if(strstr(s, ".png")) {
2063 cairo_surface_t *img = cairo_image_surface_create_from_png (s);
2065 useTexture |= kind + 1; pngBoardBitmap[kind] = img;
2066 textureW[kind] = cairo_image_surface_get_width (img);
2067 textureH[kind] = cairo_image_surface_get_height (img);
2070 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2071 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2077 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2078 // thisroutine has to be called t free the old piece pixmaps
2080 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2081 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2083 XFreePixmap(xDisplay, xpmLightSquare);
2084 XFreePixmap(xDisplay, xpmDarkSquare);
2093 u_int ss = squareSize;
2095 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2096 XpmColorSymbol symbols[4];
2097 static int redo = False;
2099 if(redo) FreeXPMPieces(); else redo = 1;
2101 /* The XSynchronize calls were copied from CreatePieces.
2102 Not sure if needed, but can't hurt */
2103 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2105 /* Setup translations so piece colors match square colors */
2106 symbols[0].name = "light_piece";
2107 symbols[0].value = appData.whitePieceColor;
2108 symbols[1].name = "dark_piece";
2109 symbols[1].value = appData.blackPieceColor;
2110 symbols[2].name = "light_square";
2111 symbols[2].value = appData.lightSquareColor;
2112 symbols[3].name = "dark_square";
2113 symbols[3].value = appData.darkSquareColor;
2115 attr.valuemask = XpmColorSymbols;
2116 attr.colorsymbols = symbols;
2117 attr.numsymbols = 4;
2119 if (appData.monoMode) {
2120 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2124 if (strlen(appData.pixmapDirectory) == 0) {
2125 XpmPieces* pieces = builtInXpms;
2128 while (pieces->size != squareSize && pieces->size) pieces++;
2129 if (!pieces->size) {
2130 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2133 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2134 for (kind=0; kind<4; kind++) {
2136 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2137 pieces->xpm[piece][kind],
2138 &(xpmPieceBitmap2[kind][piece]),
2139 NULL, &attr)) != 0) {
2140 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2144 if(piece <= (int) WhiteKing)
2145 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2149 xpmJailSquare = xpmLightSquare;
2153 fprintf(stderr, _("\nLoading XPMs...\n"));
2156 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2157 fprintf(stderr, "%d ", piece+1);
2158 for (kind=0; kind<4; kind++) {
2159 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2160 ExpandPathName(appData.pixmapDirectory),
2161 piece > (int) WhiteKing ? "w" : "",
2162 pieceBitmapNames[piece],
2164 if (appData.debugMode) {
2165 fprintf(stderr, _("(File:%s:) "), buf);
2167 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2168 &(xpmPieceBitmap2[kind][piece]),
2169 NULL, &attr)) != 0) {
2170 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2171 // [HGM] missing: read of unorthodox piece failed; substitute King.
2172 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2173 ExpandPathName(appData.pixmapDirectory),
2175 if (appData.debugMode) {
2176 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2178 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2179 &(xpmPieceBitmap2[kind][piece]),
2183 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2188 if(piece <= (int) WhiteKing)
2189 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2192 /* Load light and dark squares */
2193 /* If the LSQ and DSQ pieces don't exist, we will
2194 draw them with solid squares. */
2195 fprintf(stderr, _("light square "));
2196 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2197 if (access(buf, 0) != 0) {
2201 if (appData.debugMode)
2202 fprintf(stderr, _("(File:%s:) "), buf);
2204 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2205 &xpmLightSquare, NULL, &attr)) != 0) {
2206 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2209 fprintf(stderr, _("dark square "));
2210 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2211 ExpandPathName(appData.pixmapDirectory), ss);
2212 if (appData.debugMode) {
2213 fprintf(stderr, _("(File:%s:) "), buf);
2215 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2216 &xpmDarkSquare, NULL, &attr)) != 0) {
2217 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2221 xpmJailSquare = xpmLightSquare;
2222 fprintf(stderr, _("Done.\n"));
2224 oldVariant = -1; // kludge to force re-makig of animation masks
2225 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2228 #endif /* HAVE_LIBXPM */
2230 char *pngPieceNames[] = // must be in same order as internal piece encoding
2231 { "Pawn", "Knight", "Bishop", "Rook", "Queen", "Advisor", "Elephant", "Archbishop", "Marshall", "Gold", "Commoner",
2232 "Canon", "Nightrider", "CrownedBishop", "CrownedRook", "Princess", "Chancellor", "Hawk", "Lance", "Cobra", "Unicorn", "King",
2233 "GoldKnight", "GoldLance", "GoldPawn", "GoldSilver", NULL
2237 ScaleOnePiece (char *name, int color, int piece)
2241 cairo_surface_t *img, *cs;
2243 static cairo_surface_t *pngPieceImages[2][(int)BlackPawn+4]; // png 256 x 256 images
2245 if(pngPieceImages[color][piece] == NULL) { // if PNG file for this piece was not yet read, read it now and store it
2246 snprintf(buf, MSG_SIZ, "%s/%s%s.png", appData.pngDirectory, color ? "Black" : "White", pngPieceNames[piece]);
2247 pngPieceImages[color][piece] = img = cairo_image_surface_create_from_png (buf);
2248 w = cairo_image_surface_get_width (img);
2249 h = cairo_image_surface_get_height (img);
2250 if(w != 256 || h != 256) { printf("Bad png size %dx%d in %s\n", w, h, buf); exit(1); }
2253 // create new bitmap to hold scaled piece image (and remove any old)
2254 if(pngPieceBitmaps2[color][piece]) cairo_surface_destroy (pngPieceBitmaps2[color][piece]);
2255 pngPieceBitmaps2[color][piece] = cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
2256 if(piece <= WhiteKing) pngPieceBitmaps[color][piece] = cs;
2257 // scaled copying of the raw png image
2258 cr = cairo_create(cs);
2259 cairo_scale(cr, squareSize/256., squareSize/256.);
2260 cairo_set_source_surface (cr, img, 0, 0);
2263 cairo_surface_destroy (img);
2271 for(p=0; pngPieceNames[p]; p++) {
2272 ScaleOnePiece(pngPieceNames[p], 0, p);
2273 ScaleOnePiece(pngPieceNames[p], 1, p);
2278 /* No built-in bitmaps */
2283 u_int ss = squareSize;
2285 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2288 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2289 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2290 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2291 pieceBitmapNames[piece],
2292 ss, kind == SOLID ? 's' : 'o');
2293 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2294 if(piece <= (int)WhiteKing)
2295 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2299 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2303 /* With built-in bitmaps */
2307 BuiltInBits* bib = builtInBits;
2310 u_int ss = squareSize;
2312 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2315 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2317 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2318 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2319 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2320 pieceBitmapNames[piece],
2321 ss, kind == SOLID ? 's' : 'o');
2322 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2323 bib->bits[kind][piece], ss, ss);
2324 if(piece <= (int)WhiteKing)
2325 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2329 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2335 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2340 char msg[MSG_SIZ], fullname[MSG_SIZ];
2342 if (*appData.bitmapDirectory != NULLCHAR) {
2343 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2344 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2345 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2346 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2347 &w, &h, pm, &x_hot, &y_hot);
2348 fprintf(stderr, "load %s\n", name);
2349 if (errcode != BitmapSuccess) {
2351 case BitmapOpenFailed:
2352 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2354 case BitmapFileInvalid:
2355 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2357 case BitmapNoMemory:
2358 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2362 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2366 fprintf(stderr, _("%s: %s...using built-in\n"),
2368 } else if (w != wreq || h != hreq) {
2370 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2371 programName, fullname, w, h, wreq, hreq);
2377 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2387 if (lineGap == 0) return;
2389 /* [HR] Split this into 2 loops for non-square boards. */
2391 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2392 gridSegments[i].x1 = 0;
2393 gridSegments[i].x2 =
2394 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2395 gridSegments[i].y1 = gridSegments[i].y2
2396 = lineGap / 2 + (i * (squareSize + lineGap));
2399 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2400 gridSegments[j + i].y1 = 0;
2401 gridSegments[j + i].y2 =
2402 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2403 gridSegments[j + i].x1 = gridSegments[j + i].x2
2404 = lineGap / 2 + (j * (squareSize + lineGap));
2409 MarkMenuItem (char *menuRef, int state)
2411 MenuItem *item = MenuNameToItem(menuRef);
2415 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2416 XtSetValues(item->handle, args, 1);
2421 EnableNamedMenuItem (char *menuRef, int state)
2423 MenuItem *item = MenuNameToItem(menuRef);
2425 if(item) XtSetSensitive(item->handle, state);
2429 EnableButtonBar (int state)
2431 XtSetSensitive(optList[W_BUTTON].handle, state);
2436 SetMenuEnables (Enables *enab)
2438 while (enab->name != NULL) {
2439 EnableNamedMenuItem(enab->name, enab->value);
2445 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2446 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2448 if(*nprms == 0) return;
2449 item = MenuNameToItem(prms[0]);
2450 if(item) ((MenuProc *) item->proc) ();
2454 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2456 RecentEngineEvent((int) (intptr_t) addr);
2460 AppendMenuItem (char *msg, int n)
2462 CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
2474 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2475 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2476 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2477 dmEnables[i].piece);
2478 XtSetSensitive(entry, p != NULL || !appData.testLegality
2479 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2480 && !appData.icsActive));
2482 while (p && *p++ == dmEnables[i].piece) count++;
2483 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2485 XtSetArg(args[j], XtNlabel, label); j++;
2486 XtSetValues(entry, args, j);
2492 do_flash_delay (unsigned long msec)
2497 static cairo_surface_t *cs; // to keep out of back-end :-(
2500 DrawBorder (int x, int y, int type)
2505 cr = cairo_create(cs);
2506 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
2507 cairo_rectangle(cr, x, y, squareSize+lineGap, squareSize+lineGap);
2508 SetPen(cr, lineGap, type == 1 ? appData.highlightSquareColor : appData.premoveHighlightColor, 0);
2515 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
2517 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
2518 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
2520 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
2521 if(textureW[kind] < W*squareSize)
2522 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
2524 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
2525 if(textureH[kind] < H*squareSize)
2526 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
2528 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
2533 DrawLogo (void *handle, void *logo)
2535 cairo_surface_t *img, *cs;
2539 if(!logo || !handle) return;
2540 cs = cairo_xlib_surface_create(xDisplay, XtWindow(handle), DefaultVisual(xDisplay, 0), appData.logoSize, appData.logoSize/2);
2541 img = cairo_image_surface_create_from_png (logo);
2542 w = cairo_image_surface_get_width (img);
2543 h = cairo_image_surface_get_height (img);
2544 cr = cairo_create(cs);
2545 cairo_scale(cr, (float)appData.logoSize/w, appData.logoSize/(2.*h));
2546 cairo_set_source_surface (cr, img, 0, 0);
2549 cairo_surface_destroy (img);
2550 cairo_surface_destroy (cs);
2554 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
2555 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
2557 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
2558 if(pngBoardBitmap[color]) {
2560 if(!fac) return; // for now do not use on animate buffer, but ignore dest and draw always to board
2562 cr = cairo_create (cs);
2563 cairo_set_source_surface (cr, pngBoardBitmap[color], x*fac - x0, y*fac - y0);
2564 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
2565 cairo_rectangle (cr, x*fac, y*fac, squareSize, squareSize);
2570 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
2571 squareSize, squareSize, x*fac, y*fac);
2573 if (useImages && useImageSqs) {
2577 pm = xpmLightSquare;
2582 case 2: /* neutral */
2584 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
2587 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
2588 squareSize, squareSize, x*fac, y*fac);
2598 case 2: /* neutral */
2603 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
2608 I split out the routines to draw a piece so that I could
2609 make a generic flash routine.
2612 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2614 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2615 switch (square_color) {
2617 case 2: /* neutral */
2619 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2620 ? *pieceToOutline(piece)
2621 : *pieceToSolid(piece),
2622 dest, bwPieceGC, 0, 0,
2623 squareSize, squareSize, x, y);
2626 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2627 ? *pieceToSolid(piece)
2628 : *pieceToOutline(piece),
2629 dest, wbPieceGC, 0, 0,
2630 squareSize, squareSize, x, y);
2636 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2638 switch (square_color) {
2640 case 2: /* neutral */
2642 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2643 ? *pieceToOutline(piece)
2644 : *pieceToSolid(piece),
2645 dest, bwPieceGC, 0, 0,
2646 squareSize, squareSize, x, y, 1);
2649 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2650 ? *pieceToSolid(piece)
2651 : *pieceToOutline(piece),
2652 dest, wbPieceGC, 0, 0,
2653 squareSize, squareSize, x, y, 1);
2659 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2661 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
2662 switch (square_color) {
2664 XCopyPlane(xDisplay, *pieceToSolid(piece),
2665 dest, (int) piece < (int) BlackPawn
2666 ? wlPieceGC : blPieceGC, 0, 0,
2667 squareSize, squareSize, x, y, 1);
2670 XCopyPlane(xDisplay, *pieceToSolid(piece),
2671 dest, (int) piece < (int) BlackPawn
2672 ? wdPieceGC : bdPieceGC, 0, 0,
2673 squareSize, squareSize, x, y, 1);
2675 case 2: /* neutral */
2677 break; // should never contain pieces
2682 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2684 int kind, p = piece;
2686 switch (square_color) {
2688 case 2: /* neutral */
2690 if ((int)piece < (int) BlackPawn) {
2698 if ((int)piece < (int) BlackPawn) {
2706 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2707 if(useTexture & square_color+1) {
2708 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2709 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
2710 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
2711 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
2712 XSetClipMask(xDisplay, wlPieceGC, None);
2713 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
2715 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
2716 dest, wlPieceGC, 0, 0,
2717 squareSize, squareSize, x, y);
2721 pngDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2723 int kind, p = piece;
2726 if ((int)piece < (int) BlackPawn) {
2732 if(appData.upsideDown && flipView) { p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2733 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2735 cr = cairo_create (cs);
2736 cairo_set_source_surface (cr, pngPieceBitmaps[kind][piece], x, y);
2742 typedef void (*DrawFunc)();
2747 if (appData.monoMode) {
2748 if (DefaultDepth(xDisplay, xScreen) == 1) {
2749 return monoDrawPiece_1bit;
2751 return monoDrawPiece;
2753 } else if(appData.pngDirectory[0]) {
2754 return pngDrawPiece;
2757 return colorDrawPieceImage;
2759 return colorDrawPiece;
2764 DrawDot (int marker, int x, int y, int r)
2768 cr = cairo_create(cs);
2769 cairo_arc(cr, x+r/2, y+r/2, r/2, 0.0, 2*M_PI);
2770 if(appData.monoMode) {
2771 SetPen(cr, 2, marker == 2 ? "#000000" : "#FFFFFF", 0);
2772 cairo_stroke_preserve(cr);
2773 SetPen(cr, 2, marker == 2 ? "#FFFFFF" : "#000000", 0);
2775 SetPen(cr, 2, marker == 2 ? "#FF0000" : "#FFFF00", 0);
2785 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2786 { // basic front-end board-draw function: takes care of everything that can be in square:
2787 // piece, background, coordinate/count, marker dot
2788 int direction, font_ascent, font_descent;
2789 XCharStruct overall;
2792 if (piece == EmptySquare) {
2793 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2795 drawfunc = ChooseDrawFunc();
2796 drawfunc(piece, square_color, x, y, xBoardWindow);
2799 if(align) { // square carries inscription (coord or piece count)
2801 GC hGC = align < 3 ? coordGC : countGC;
2802 // first calculate where it goes
2803 XTextExtents(countFontStruct, string, 1, &direction,
2804 &font_ascent, &font_descent, &overall);
2806 xx += squareSize - overall.width - 2;
2807 yy += squareSize - font_descent - 1;
2808 } else if (align == 2) {
2809 xx += 2, yy += font_ascent + 1;
2810 } else if (align == 3) {
2811 xx += squareSize - overall.width - 2;
2812 yy += font_ascent + 1;
2813 } else if (align == 4) {
2814 xx += 2, yy += font_ascent + 1;
2817 if (appData.monoMode) {
2818 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2820 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2824 if(marker) { // print fat marker dot, if requested
2825 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2830 FlashDelay (int flash_delay)
2832 XSync(xDisplay, False);
2833 if(flash_delay) do_flash_delay(flash_delay);
2837 Fraction (int x, int start, int stop)
2839 double f = ((double) x - start)/(stop - start);
2840 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2844 static WindowPlacement wpNew;
2847 CoDrag (Widget sh, WindowPlacement *wp)
2850 int j=0, touch=0, fudge = 2;
2851 GetActualPlacement(sh, wp);
2852 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
2853 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
2854 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2855 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
2856 if(!touch ) return; // only windows that touch co-move
2857 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2858 int heightInc = wpNew.height - wpMain.height;
2859 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2860 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2861 wp->y += fracTop * heightInc;
2862 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2863 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2864 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2865 int widthInc = wpNew.width - wpMain.width;
2866 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2867 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2868 wp->y += fracLeft * widthInc;
2869 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2870 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2872 wp->x += wpNew.x - wpMain.x;
2873 wp->y += wpNew.y - wpMain.y;
2874 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2875 if(touch == 3) wp->y += wpNew.height - wpMain.height;
2876 XtSetArg(args[j], XtNx, wp->x); j++;
2877 XtSetArg(args[j], XtNy, wp->y); j++;
2878 XtSetValues(sh, args, j);
2881 static XtIntervalId delayedDragID = 0;
2886 GetActualPlacement(shellWidget, &wpNew);
2887 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
2888 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
2889 return; // false alarm
2890 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
2891 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
2892 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
2893 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
2895 DrawPosition(True, NULL);
2896 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
2903 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
2905 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
2909 EventProc (Widget widget, caddr_t unused, XEvent *event)
2911 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
2912 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
2915 // [HGM] seekgraph: some low-level drawing routines (by JC, mostly)
2918 Color (char *col, int n)
2921 sscanf(col, "#%x", &c);
2927 SetPen (cairo_t *cr, float w, char *col, int dash)
2929 static const double dotted[] = {4.0, 4.0};
2930 static int len = sizeof(dotted) / sizeof(dotted[0]);
2931 cairo_set_line_width (cr, w);
2932 cairo_set_source_rgba (cr, Color(col, 4), Color(col, 2), Color(col, 0), 1.0);
2933 if(dash) cairo_set_dash (cr, dotted, len, 0.0);
2936 void DrawSeekAxis( int x, int y, int xTo, int yTo )
2941 cr = cairo_create (cs);
2943 cairo_move_to (cr, x, y);
2944 cairo_line_to(cr, xTo, yTo );
2946 SetPen(cr, 2, "#000000", 0);
2953 void DrawSeekBackground( int left, int top, int right, int bottom )
2955 cairo_t *cr = cairo_create (cs);
2957 cairo_rectangle (cr, left, top, right-left, bottom-top);
2959 cairo_set_source_rgba(cr, 0.8, 0.8, 0.4,1.0);
2966 void DrawSeekText(char *buf, int x, int y)
2968 cairo_t *cr = cairo_create (cs);
2970 cairo_select_font_face (cr, "Sans",
2971 CAIRO_FONT_SLANT_NORMAL,
2972 CAIRO_FONT_WEIGHT_NORMAL);
2974 cairo_set_font_size (cr, 12.0);
2976 cairo_move_to (cr, x, y+4);
2977 cairo_show_text( cr, buf);
2979 cairo_set_source_rgba(cr, 0, 0, 0,1.0);
2986 void DrawSeekDot(int x, int y, int colorNr)
2988 cairo_t *cr = cairo_create (cs);
2989 int square = colorNr & 0x80;
2993 cairo_rectangle (cr, x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
2995 cairo_arc(cr, x, y, squareSize/8, 0.0, 2*M_PI);
2997 SetPen(cr, 2, "#000000", 0);
2998 cairo_stroke_preserve(cr);
3000 case 0: cairo_set_source_rgba(cr, 1.0, 0, 0,1.0); break;
3001 case 1: cairo_set_source_rgba (cr, 0.0, 0.7, 0.2, 1.0); break;
3002 default: cairo_set_source_rgba (cr, 1.0, 1.0, 0.0, 1.0); break;
3013 int boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3014 int boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3015 cs = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), boardWidth, boardHeight);
3021 cairo_surface_destroy(cs);
3027 /* draws a grid starting around Nx, Ny squares starting at x,y */
3033 cr = cairo_create (cs);
3035 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
3036 SetPen(cr, lineGap, "#000000", 0);
3039 for (i = 0; i < BOARD_WIDTH + BOARD_HEIGHT + 2; i++)
3041 cairo_move_to (cr, gridSegments[i].x1, gridSegments[i].y1);
3042 cairo_line_to (cr, gridSegments[i].x2, gridSegments[i].y2);
3054 * event handler for redrawing the board
3057 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3059 DrawPosition(True, NULL);
3064 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3065 { // [HGM] pv: walk PV
3066 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3069 static int savedIndex; /* gross that this is global */
3072 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3075 XawTextPosition index, dummy;
3078 XawTextGetSelectionPos(w, &index, &dummy);
3079 XtSetArg(arg, XtNstring, &val);
3080 XtGetValues(w, &arg, 1);
3081 ReplaceComment(savedIndex, val);
3082 if(savedIndex != currentMove) ToNrEvent(savedIndex);
3083 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3087 EditCommentPopUp (int index, char *title, char *text)
3090 if (text == NULL) text = "";
3091 NewCommentPopup(title, text, index);
3095 CommentPopUp (char *title, char *text)
3097 savedIndex = currentMove; // [HGM] vari
3098 NewCommentPopup(title, text, currentMove);
3104 PopDown(CommentDlg);
3108 /* Disable all user input other than deleting the window */
3109 static int frozen = 0;
3115 /* Grab by a widget that doesn't accept input */
3116 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
3120 /* Undo a FreezeUI */
3124 if (!frozen) return;
3125 XtRemoveGrab(optList[W_MESSG].handle);
3133 static int oldPausing = FALSE;
3134 static GameMode oldmode = (GameMode) -1;
3137 if (!boardWidget || !XtIsRealized(boardWidget)) return;
3139 if (pausing != oldPausing) {
3140 oldPausing = pausing;
3141 MarkMenuItem("Mode.Pause", pausing);
3143 if (appData.showButtonBar) {
3144 /* Always toggle, don't set. Previous code messes up when
3145 invoked while the button is pressed, as releasing it
3146 toggles the state again. */
3149 XtSetArg(args[0], XtNbackground, &oldbg);
3150 XtSetArg(args[1], XtNforeground, &oldfg);
3151 XtGetValues(optList[W_PAUSE].handle,
3153 XtSetArg(args[0], XtNbackground, oldfg);
3154 XtSetArg(args[1], XtNforeground, oldbg);
3156 XtSetValues(optList[W_PAUSE].handle, args, 2);
3160 wname = ModeToWidgetName(oldmode);
3161 if (wname != NULL) {
3162 MarkMenuItem(wname, False);
3164 wname = ModeToWidgetName(gameMode);
3165 if (wname != NULL) {
3166 MarkMenuItem(wname, True);
3169 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
3171 /* Maybe all the enables should be handled here, not just this one */
3172 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
3174 DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);
3179 * Button/menu procedures
3182 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3183 char *selected_fen_position=NULL;
3186 SendPositionSelection (Widget w, Atom *selection, Atom *target,
3187 Atom *type_return, XtPointer *value_return,
3188 unsigned long *length_return, int *format_return)
3190 char *selection_tmp;
3192 // if (!selected_fen_position) return False; /* should never happen */
3193 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3194 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
3195 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
3198 if (f == NULL) return False;
3202 selection_tmp = XtMalloc(len + 1);
3203 count = fread(selection_tmp, 1, len, f);
3206 XtFree(selection_tmp);
3209 selection_tmp[len] = NULLCHAR;
3211 /* note: since no XtSelectionDoneProc was registered, Xt will
3212 * automatically call XtFree on the value returned. So have to
3213 * make a copy of it allocated with XtMalloc */
3214 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3215 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
3218 *value_return=selection_tmp;
3219 *length_return=strlen(selection_tmp);
3220 *type_return=*target;
3221 *format_return = 8; /* bits per byte */
3223 } else if (*target == XA_TARGETS(xDisplay)) {
3224 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3225 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3226 targets_tmp[1] = XA_STRING;
3227 *value_return = targets_tmp;
3228 *type_return = XA_ATOM;
3231 // This code leads to a read of value_return out of bounds on 64-bit systems.
3232 // Other code which I have seen always sets *format_return to 32 independent of
3233 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
3234 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
3235 *format_return = 8 * sizeof(Atom);
3236 if (*format_return > 32) {
3237 *length_return *= *format_return / 32;
3238 *format_return = 32;
3241 *format_return = 32;
3249 /* note: when called from menu all parameters are NULL, so no clue what the
3250 * Widget which was clicked on was, or what the click event was
3253 CopySomething (char *src)
3255 selected_fen_position = src;
3257 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3258 * have a notion of a position that is selected but not copied.
3259 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3261 XtOwnSelection(menuBarWidget, XA_PRIMARY,
3263 SendPositionSelection,
3264 NULL/* lose_ownership_proc */ ,
3265 NULL/* transfer_done_proc */);
3266 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3268 SendPositionSelection,
3269 NULL/* lose_ownership_proc */ ,
3270 NULL/* transfer_done_proc */);
3273 /* function called when the data to Paste is ready */
3275 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3276 Atom *type, XtPointer value, unsigned long *len, int *format)
3279 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3280 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3281 EditPositionPasteFEN(fenstr);
3285 /* called when Paste Position button is pressed,
3286 * all parameters will be NULL */
3288 PastePositionProc ()
3290 XtGetSelectionValue(menuBarWidget,
3291 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3292 /* (XtSelectionCallbackProc) */ PastePositionCB,
3293 NULL, /* client_data passed to PastePositionCB */
3295 /* better to use the time field from the event that triggered the
3296 * call to this function, but that isn't trivial to get
3303 /* note: when called from menu all parameters are NULL, so no clue what the
3304 * Widget which was clicked on was, or what the click event was
3306 /* function called when the data to Paste is ready */
3308 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3309 Atom *type, XtPointer value, unsigned long *len, int *format)
3312 if (value == NULL || *len == 0) {
3313 return; /* nothing had been selected to copy */
3315 f = fopen(gamePasteFilename, "w");
3317 DisplayError(_("Can't open temp file"), errno);
3320 fwrite(value, 1, *len, f);
3323 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3326 /* called when Paste Game button is pressed,
3327 * all parameters will be NULL */
3331 XtGetSelectionValue(menuBarWidget,
3332 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3333 /* (XtSelectionCallbackProc) */ PasteGameCB,
3334 NULL, /* client_data passed to PasteGameCB */
3336 /* better to use the time field from the event that triggered the
3337 * call to this function, but that isn't trivial to get
3346 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3353 { // bassic primitive for determining if modifier keys are pressed
3354 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3357 XQueryKeymap(xDisplay,keys);
3358 for(i=0; i<6; i++) {
3360 j = XKeysymToKeycode(xDisplay, codes[i]);
3361 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3367 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3371 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3372 if ( n == 1 && *buf >= 32 // printable
3373 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3374 ) BoxAutoPopUp (buf);
3378 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3379 { // [HGM] input: let up-arrow recall previous line from history
3384 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3385 { // [HGM] input: let down-arrow recall next line from history
3390 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3396 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3398 if (!TempBackwardActive) {
3399 TempBackwardActive = True;
3405 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3407 /* Check to see if triggered by a key release event for a repeating key.
3408 * If so the next queued event will be a key press of the same key at the same time */
3409 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3411 XPeekEvent(xDisplay, &next);
3412 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3413 next.xkey.keycode == event->xkey.keycode)
3417 TempBackwardActive = False;
3421 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3422 { // called as key binding
3425 if (nprms && *nprms > 0)
3429 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3435 { // called from menu
3436 ManInner(NULL, NULL, NULL, NULL);
3440 SetWindowTitle (char *text, char *title, char *icon)
3444 if (appData.titleInWindow) {
3446 XtSetArg(args[i], XtNlabel, text); i++;
3447 XtSetValues(titleWidget, args, i);
3450 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3451 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3452 XtSetValues(shellWidget, args, i);
3453 XSync(xDisplay, False);
3458 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3464 DisplayIcsInteractionTitle (String message)
3466 if (oldICSInteractionTitle == NULL) {
3467 /* Magic to find the old window title, adapted from vim */
3468 char *wina = getenv("WINDOWID");
3470 Window win = (Window) atoi(wina);
3471 Window root, parent, *children;
3472 unsigned int nchildren;
3473 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3475 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3476 if (!XQueryTree(xDisplay, win, &root, &parent,
3477 &children, &nchildren)) break;
3478 if (children) XFree((void *)children);
3479 if (parent == root || parent == 0) break;
3482 XSetErrorHandler(oldHandler);
3484 if (oldICSInteractionTitle == NULL) {
3485 oldICSInteractionTitle = "xterm";
3488 printf("\033]0;%s\007", message);
3493 XtIntervalId delayedEventTimerXID = 0;
3494 DelayedEventCallback delayedEventCallback = 0;
3499 delayedEventTimerXID = 0;
3500 delayedEventCallback();
3504 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3506 if(delayedEventTimerXID && delayedEventCallback == cb)
3507 // [HGM] alive: replace, rather than add or flush identical event
3508 XtRemoveTimeOut(delayedEventTimerXID);
3509 delayedEventCallback = cb;
3510 delayedEventTimerXID =
3511 XtAppAddTimeOut(appContext, millisec,
3512 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3515 DelayedEventCallback
3518 if (delayedEventTimerXID) {
3519 return delayedEventCallback;
3526 CancelDelayedEvent ()
3528 if (delayedEventTimerXID) {
3529 XtRemoveTimeOut(delayedEventTimerXID);
3530 delayedEventTimerXID = 0;
3534 XtIntervalId loadGameTimerXID = 0;
3537 LoadGameTimerRunning ()
3539 return loadGameTimerXID != 0;
3543 StopLoadGameTimer ()
3545 if (loadGameTimerXID != 0) {
3546 XtRemoveTimeOut(loadGameTimerXID);
3547 loadGameTimerXID = 0;
3555 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3557 loadGameTimerXID = 0;
3562 StartLoadGameTimer (long millisec)
3565 XtAppAddTimeOut(appContext, millisec,
3566 (XtTimerCallbackProc) LoadGameTimerCallback,
3570 XtIntervalId analysisClockXID = 0;
3573 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3575 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3576 || appData.icsEngineAnalyze) { // [DM]
3577 AnalysisPeriodicEvent(0);
3578 StartAnalysisClock();
3583 StartAnalysisClock ()
3586 XtAppAddTimeOut(appContext, 2000,
3587 (XtTimerCallbackProc) AnalysisClockCallback,
3591 XtIntervalId clockTimerXID = 0;
3594 ClockTimerRunning ()
3596 return clockTimerXID != 0;
3602 if (clockTimerXID != 0) {
3603 XtRemoveTimeOut(clockTimerXID);
3612 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3619 StartClockTimer (long millisec)
3622 XtAppAddTimeOut(appContext, millisec,
3623 (XtTimerCallbackProc) ClockTimerCallback,
3628 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
3632 Widget w = (Widget) opt->handle;
3634 /* check for low time warning */
3635 Pixel foregroundOrWarningColor = timerForegroundPixel;
3638 appData.lowTimeWarning &&
3639 (timer / 1000) < appData.icsAlarmTime)
3640 foregroundOrWarningColor = lowTimeWarningColor;
3642 if (appData.clockMode) {
3643 snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
3644 XtSetArg(args[0], XtNlabel, buf);
3646 snprintf(buf, MSG_SIZ, "%s ", color);
3647 XtSetArg(args[0], XtNlabel, buf);
3652 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3653 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3655 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3656 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3659 XtSetValues(w, args, 3);
3662 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3665 SetClockIcon (int color)
3668 Pixmap pm = *clockIcons[color];
3669 if (iconPixmap != pm) {
3671 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3672 XtSetValues(shellWidget, args, 1);
3677 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3679 InputSource *is = (InputSource *) closure;
3684 if (is->lineByLine) {
3685 count = read(is->fd, is->unused,
3686 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3688 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3691 is->unused += count;
3693 while (p < is->unused) {
3694 q = memchr(p, '\n', is->unused - p);
3695 if (q == NULL) break;
3697 (is->func)(is, is->closure, p, q - p, 0);
3701 while (p < is->unused) {
3706 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3711 (is->func)(is, is->closure, is->buf, count, error);
3716 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3719 ChildProc *cp = (ChildProc *) pr;
3721 is = (InputSource *) calloc(1, sizeof(InputSource));
3722 is->lineByLine = lineByLine;
3726 is->fd = fileno(stdin);