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 Pixmap pieceBitmap[2][(int)BlackPawn];
346 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
347 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
348 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
349 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
350 Pixmap xpmBoardBitmap[2];
351 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
352 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
353 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
354 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
355 XImage *ximLightSquare, *ximDarkSquare;
358 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
359 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
361 #define White(piece) ((int)(piece) < (int)BlackPawn)
363 /* Bitmaps for use as masks when drawing XPM pieces.
364 Need one for each black and white piece. */
365 static Pixmap xpmMask[BlackKing + 1];
367 /* This magic number is the number of intermediate frames used
368 in each half of the animation. For short moves it's reduced
369 by 1. The total number of frames will be factor * 2 + 1. */
372 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
379 DropMenuEnables dmEnables[] = {
396 XtResource clientResources[] = {
397 { "flashCount", "flashCount", XtRInt, sizeof(int),
398 XtOffset(AppDataPtr, flashCount), XtRImmediate,
399 (XtPointer) FLASH_COUNT },
402 XrmOptionDescRec shellOptions[] = {
403 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
404 { "-flash", "flashCount", XrmoptionNoArg, "3" },
405 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
408 XtActionsRec boardActions[] = {
409 { "DrawPosition", DrawPositionProc },
410 { "HandlePV", HandlePV },
411 { "SelectPV", SelectPV },
412 { "StopPV", StopPV },
413 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
414 { "QuitProc", QuitWrapper },
415 { "ManProc", ManInner },
416 { "TempBackwardProc", TempBackwardProc },
417 { "TempForwardProc", TempForwardProc },
418 { "CommentClick", (XtActionProc) CommentClick },
419 { "GenericPopDown", (XtActionProc) GenericPopDown },
420 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
421 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
422 { "SelectMove", (XtActionProc) SelectMove },
423 { "LoadSelectedProc", LoadSelectedProc },
424 { "SetFilterProc", SetFilterProc },
425 { "TypeInProc", TypeInProc },
426 { "EnterKeyProc", EnterKeyProc },
427 { "UpKeyProc", UpKeyProc },
428 { "DownKeyProc", DownKeyProc },
429 { "WheelProc", WheelProc },
430 { "TabProc", TabProc },
433 char globalTranslations[] =
434 ":<Key>F9: MenuItem(Actions.Resign) \n \
435 :Ctrl<Key>n: MenuItem(File.NewGame) \n \
436 :Meta<Key>V: MenuItem(File.NewVariant) \n \
437 :Ctrl<Key>o: MenuItem(File.LoadGame) \n \
438 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
439 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
440 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
441 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
442 :Ctrl<Key>s: MenuItem(File.SaveGame) \n \
443 :Ctrl<Key>c: MenuItem(Edit.CopyGame) \n \
444 :Ctrl<Key>v: MenuItem(Edit.PasteGame) \n \
445 :Ctrl<Key>O: MenuItem(File.LoadPosition) \n \
446 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
447 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
448 :Ctrl<Key>S: MenuItem(File.SavePosition) \n \
449 :Ctrl<Key>C: MenuItem(Edit.CopyPosition) \n \
450 :Ctrl<Key>V: MenuItem(Edit.PastePosition) \n \
451 :Ctrl<Key>q: MenuItem(File.Quit) \n \
452 :Ctrl<Key>w: MenuItem(Mode.MachineWhite) \n \
453 :Ctrl<Key>b: MenuItem(Mode.MachineBlack) \n \
454 :Ctrl<Key>t: MenuItem(Mode.TwoMachines) \n \
455 :Ctrl<Key>a: MenuItem(Mode.AnalysisMode) \n \
456 :Ctrl<Key>g: MenuItem(Mode.AnalyzeFile) \n \
457 :Ctrl<Key>e: MenuItem(Mode.EditGame) \n \
458 :Ctrl<Key>E: MenuItem(Mode.EditPosition) \n \
459 :Meta<Key>O: MenuItem(View.EngineOutput) \n \
460 :Meta<Key>E: MenuItem(View.EvaluationGraph) \n \
461 :Meta<Key>G: MenuItem(View.GameList) \n \
462 :Meta<Key>H: MenuItem(View.MoveHistory) \n \
463 :<Key>Pause: MenuItem(Mode.Pause) \n \
464 :<Key>F3: MenuItem(Action.Accept) \n \
465 :<Key>F4: MenuItem(Action.Decline) \n \
466 :<Key>F12: MenuItem(Action.Rematch) \n \
467 :<Key>F5: MenuItem(Action.CallFlag) \n \
468 :<Key>F6: MenuItem(Action.Draw) \n \
469 :<Key>F7: MenuItem(Action.Adjourn) \n \
470 :<Key>F8: MenuItem(Action.Abort) \n \
471 :<Key>F10: MenuItem(Action.StopObserving) \n \
472 :<Key>F11: MenuItem(Action.StopExamining) \n \
473 :Ctrl<Key>d: MenuItem(DebugProc) \n \
474 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
475 :Meta<Key>End: MenuItem(Edit.ForwardtoEnd) \n \
476 :Meta<Key>Right: MenuItem(Edit.Forward) \n \
477 :Meta<Key>Home: MenuItem(Edit.BacktoStart) \n \
478 :Meta<Key>Left: MenuItem(Edit.Backward) \n \
479 :<Key>Left: MenuItem(Edit.Backward) \n \
480 :<Key>Right: MenuItem(Edit.Forward) \n \
481 :<Key>Home: MenuItem(Edit.Revert) \n \
482 :<Key>End: MenuItem(Edit.TruncateGame) \n \
483 :Ctrl<Key>m: MenuItem(Engine.MoveNow) \n \
484 :Ctrl<Key>x: MenuItem(Engine.RetractMove) \n \
485 :Meta<Key>J: MenuItem(Options.Adjudications) \n \
486 :Meta<Key>U: MenuItem(Options.CommonEngine) \n \
487 :Meta<Key>T: MenuItem(Options.TimeControl) \n \
488 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
489 #ifndef OPTIONSDIALOG
491 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
492 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
493 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
494 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
495 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
498 :<Key>F1: MenuItem(Help.ManXBoard) \n \
499 :<Key>F2: MenuItem(View.FlipView) \n \
500 :<KeyDown>Return: TempBackwardProc() \n \
501 :<KeyUp>Return: TempForwardProc() \n";
503 char ICSInputTranslations[] =
504 "<Key>Up: UpKeyProc() \n "
505 "<Key>Down: DownKeyProc() \n "
506 "<Key>Return: EnterKeyProc() \n";
508 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
509 // as the widget is destroyed before the up-click can call extend-end
510 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
512 String xboardResources[] = {
513 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
518 /* Max possible square size */
519 #define MAXSQSIZE 256
521 static int xpm_avail[MAXSQSIZE];
523 #ifdef HAVE_DIR_STRUCT
525 /* Extract piece size from filename */
527 xpm_getsize (char *name, int len, char *ext)
535 if ((p=strchr(name, '.')) == NULL ||
536 StrCaseCmp(p+1, ext) != 0)
542 while (*p && isdigit(*p))
549 /* Setup xpm_avail */
551 xpm_getavail (char *dirname, char *ext)
557 for (i=0; i<MAXSQSIZE; ++i)
560 if (appData.debugMode)
561 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
563 dir = opendir(dirname);
566 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
567 programName, dirname);
571 while ((ent=readdir(dir)) != NULL) {
572 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
573 if (i > 0 && i < MAXSQSIZE)
583 xpm_print_avail (FILE *fp, char *ext)
587 fprintf(fp, _("Available `%s' sizes:\n"), ext);
588 for (i=1; i<MAXSQSIZE; ++i) {
594 /* Return XPM piecesize closest to size */
596 xpm_closest_to (char *dirname, int size, char *ext)
599 int sm_diff = MAXSQSIZE;
603 xpm_getavail(dirname, ext);
605 if (appData.debugMode)
606 xpm_print_avail(stderr, ext);
608 for (i=1; i<MAXSQSIZE; ++i) {
611 diff = (diff<0) ? -diff : diff;
612 if (diff < sm_diff) {
620 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
626 #else /* !HAVE_DIR_STRUCT */
627 /* If we are on a system without a DIR struct, we can't
628 read the directory, so we can't collect a list of
629 filenames, etc., so we can't do any size-fitting. */
631 xpm_closest_to (char *dirname, int size, char *ext)
634 Warning: No DIR structure found on this system --\n\
635 Unable to autosize for XPM/XIM pieces.\n\
636 Please report this error to %s.\n\
637 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
640 #endif /* HAVE_DIR_STRUCT */
643 /* Arrange to catch delete-window events */
644 Atom wm_delete_window;
646 CatchDeleteWindow (Widget w, String procname)
649 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
650 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
651 XtAugmentTranslations(w, XtParseTranslationTable(buf));
658 XtSetArg(args[0], XtNiconic, False);
659 XtSetValues(shellWidget, args, 1);
661 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
664 //---------------------------------------------------------------------------------------------------------
665 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
668 #define CW_USEDEFAULT (1<<31)
669 #define ICS_TEXT_MENU_SIZE 90
670 #define DEBUG_FILE "xboard.debug"
671 #define SetCurrentDirectory chdir
672 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
676 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
679 // front-end part of option handling
681 // [HGM] This platform-dependent table provides the location for storing the color info
682 extern char *crWhite, * crBlack;
686 &appData.whitePieceColor,
687 &appData.blackPieceColor,
688 &appData.lightSquareColor,
689 &appData.darkSquareColor,
690 &appData.highlightSquareColor,
691 &appData.premoveHighlightColor,
692 &appData.lowTimeWarningColor,
703 // [HGM] font: keep a font for each square size, even non-stndard ones
706 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
707 char *fontTable[NUM_FONTS][MAX_SIZE];
710 ParseFont (char *name, int number)
711 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
713 if(sscanf(name, "size%d:", &size)) {
714 // [HGM] font: font is meant for specific boardSize (likely from settings file);
715 // defer processing it until we know if it matches our board size
716 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
717 fontTable[number][size] = strdup(strchr(name, ':')+1);
718 fontValid[number][size] = True;
723 case 0: // CLOCK_FONT
724 appData.clockFont = strdup(name);
726 case 1: // MESSAGE_FONT
727 appData.font = strdup(name);
729 case 2: // COORD_FONT
730 appData.coordFont = strdup(name);
735 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
740 { // only 2 fonts currently
741 appData.clockFont = CLOCK_FONT_NAME;
742 appData.coordFont = COORD_FONT_NAME;
743 appData.font = DEFAULT_FONT_NAME;
748 { // no-op, until we identify the code for this already in XBoard and move it here
752 ParseColor (int n, char *name)
753 { // in XBoard, just copy the color-name string
754 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
758 ParseTextAttribs (ColorClass cc, char *s)
760 (&appData.colorShout)[cc] = strdup(s);
764 ParseBoardSize (void *addr, char *name)
766 appData.boardSize = strdup(name);
771 { // In XBoard the sound-playing program takes care of obtaining the actual sound
775 SetCommPortDefaults ()
776 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
779 // [HGM] args: these three cases taken out to stay in front-end
781 SaveFontArg (FILE *f, ArgDescriptor *ad)
784 int i, n = (int)(intptr_t)ad->argLoc;
786 case 0: // CLOCK_FONT
787 name = appData.clockFont;
789 case 1: // MESSAGE_FONT
792 case 2: // COORD_FONT
793 name = appData.coordFont;
798 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
799 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
800 fontTable[n][squareSize] = strdup(name);
801 fontValid[n][squareSize] = True;
804 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
805 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
810 { // nothing to do, as the sounds are at all times represented by their text-string names already
814 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
815 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
816 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
820 SaveColor (FILE *f, ArgDescriptor *ad)
821 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
822 if(colorVariable[(int)(intptr_t)ad->argLoc])
823 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
827 SaveBoardSize (FILE *f, char *name, void *addr)
828 { // wrapper to shield back-end from BoardSize & sizeInfo
829 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
833 ParseCommPortSettings (char *s)
834 { // no such option in XBoard (yet)
840 GetActualPlacement (Widget wg, WindowPlacement *wp)
842 XWindowAttributes winAt;
849 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
850 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
851 wp->x = rx - winAt.x;
852 wp->y = ry - winAt.y;
853 wp->height = winAt.height;
854 wp->width = winAt.width;
855 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
860 { // wrapper to shield use of window handles from back-end (make addressible by number?)
861 // In XBoard this will have to wait until awareness of window parameters is implemented
862 GetActualPlacement(shellWidget, &wpMain);
863 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
864 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
865 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
866 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
867 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
868 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
872 PrintCommPortSettings (FILE *f, char *name)
873 { // This option does not exist in XBoard
877 EnsureOnScreen (int *x, int *y, int minX, int minY)
884 { // [HGM] args: allows testing if main window is realized from back-end
885 return xBoardWindow != 0;
891 extern Option dualOptions[];
893 Window tmp = xBoardWindow;
894 if(!dual) dual = XtWindow(dualOptions[3].handle); // must be first call
895 xBoardWindow = dual; // swap them
900 PopUpStartupDialog ()
901 { // start menu not implemented in XBoard
905 ConvertToLine (int argc, char **argv)
907 static char line[128*1024], buf[1024];
911 for(i=1; i<argc; i++)
913 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
914 && argv[i][0] != '{' )
915 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
917 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
918 strncat(line, buf, 128*1024 - strlen(line) - 1 );
921 line[strlen(line)-1] = NULLCHAR;
925 //--------------------------------------------------------------------------------------------
927 #define BoardSize int
929 InitDrawingSizes (BoardSize boardSize, int flags)
930 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
931 Dimension boardWidth, boardHeight, w, h;
933 static Dimension oldWidth, oldHeight;
934 static VariantClass oldVariant;
935 static int oldMono = -1, oldTwoBoards = 0;
937 if(!formWidget) return;
939 if(oldTwoBoards && !twoBoards) PopDown(DummyDlg);
940 oldTwoBoards = twoBoards;
942 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
943 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
944 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
946 if(boardWidth != oldWidth || boardHeight != oldHeight) { // do resizing stuff only if size actually changed
948 oldWidth = boardWidth; oldHeight = boardHeight;
952 * Inhibit shell resizing.
954 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW ;
955 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
956 shellArgs[4].value = shellArgs[2].value = w;
957 shellArgs[5].value = shellArgs[3].value = h;
958 XtSetValues(shellWidget, &shellArgs[0], 6);
960 XSync(xDisplay, False);
964 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
967 if(gameInfo.variant != oldVariant) { // and only if variant changed
972 for(p=0; p<=(int)WhiteKing; p++)
973 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
974 if(gameInfo.variant == VariantShogi) {
975 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
976 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
977 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
978 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
979 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
982 if(gameInfo.variant == VariantGothic) {
983 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
986 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
987 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
988 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
991 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
992 for(p=0; p<=(int)WhiteKing; p++)
993 ximMaskPm[p] = ximMaskPm2[p]; // defaults
994 if(gameInfo.variant == VariantShogi) {
995 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
996 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
997 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
998 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
999 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1002 if(gameInfo.variant == VariantGothic) {
1003 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1006 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1007 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1008 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1013 for(i=0; i<2; i++) {
1015 for(p=0; p<=(int)WhiteKing; p++)
1016 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1017 if(gameInfo.variant == VariantShogi) {
1018 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1019 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1020 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1021 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1022 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1025 if(gameInfo.variant == VariantGothic) {
1026 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1029 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1030 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1031 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1035 for(i=0; i<2; i++) {
1037 printf("Copy pieces\n");
1038 for(p=0; p<=(int)WhiteKing; p++)
1039 pngPieceBitmaps[i][p] = pngPieceBitmaps2[i][p]; // defaults
1041 oldMono = -10; // kludge to force recreation of animation masks
1042 oldVariant = gameInfo.variant;
1045 if(appData.monoMode != oldMono)
1048 oldMono = appData.monoMode;
1052 MakeOneColor (char *name, Pixel *color)
1054 XrmValue vFrom, vTo;
1055 if (!appData.monoMode) {
1056 vFrom.addr = (caddr_t) name;
1057 vFrom.size = strlen(name);
1058 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1059 if (vTo.addr == NULL) {
1060 appData.monoMode = True;
1063 *color = *(Pixel *) vTo.addr;
1071 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1072 int forceMono = False;
1074 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1075 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1076 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1077 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1078 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1079 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1080 if (appData.lowTimeWarning)
1081 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
1082 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1083 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1090 { // [HGM] taken out of main
1092 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1093 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1094 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1096 if (appData.bitmapDirectory[0] != NULLCHAR) {
1100 CreateXPMBoard(appData.liteBackTextureFile, 1);
1101 CreateXPMBoard(appData.darkBackTextureFile, 0);
1103 if (appData.pngDirectory[0] != NULLCHAR) { // for now do in parallel
1108 /* Create regular pieces */
1109 if (!useImages) CreatePieces();
1114 InitDrawingParams ()
1116 MakeColors(); CreateGCs(True);
1121 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
1122 { // detervtomine what fonts to use, and create them
1126 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1127 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1128 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1129 appData.font = fontTable[MESSAGE_FONT][squareSize];
1130 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1131 appData.coordFont = fontTable[COORD_FONT][squareSize];
1134 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1135 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1136 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1137 fontSet = CreateFontSet(appData.font);
1138 clockFontSet = CreateFontSet(appData.clockFont);
1140 /* For the coordFont, use the 0th font of the fontset. */
1141 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1142 XFontStruct **font_struct_list;
1143 XFontSetExtents *fontSize;
1144 char **font_name_list;
1145 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1146 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1147 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1148 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1149 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1152 appData.font = FindFont(appData.font, fontPxlSize);
1153 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1154 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1155 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1156 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1157 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1158 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1159 // textHeight in !NLS mode!
1161 countFontID = coordFontID; // [HGM] holdings
1162 countFontStruct = coordFontStruct;
1164 xdb = XtDatabase(xDisplay);
1166 XrmPutLineResource(&xdb, "*international: True");
1167 vTo.size = sizeof(XFontSet);
1168 vTo.addr = (XtPointer) &fontSet;
1169 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1171 XrmPutStringResource(&xdb, "*font", appData.font);
1176 PrintArg (ArgType t)
1181 case ArgInt: p = " N"; break;
1182 case ArgString: p = " STR"; break;
1183 case ArgBoolean: p = " TF"; break;
1184 case ArgSettingsFilename:
1185 case ArgFilename: p = " FILE"; break;
1186 case ArgX: p = " Nx"; break;
1187 case ArgY: p = " Ny"; break;
1188 case ArgAttribs: p = " TEXTCOL"; break;
1189 case ArgColor: p = " COL"; break;
1190 case ArgFont: p = " FONT"; break;
1191 case ArgBoardSize: p = " SIZE"; break;
1192 case ArgFloat: p = " FLOAT"; break;
1197 case ArgCommSettings:
1208 ArgDescriptor *q, *p = argDescriptors+5;
1209 printf("\nXBoard accepts the following options:\n"
1210 "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
1211 " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
1212 " SIZE = board-size spec(s)\n"
1213 " Within parentheses are short forms, or options to set to true or false.\n"
1214 " Persistent options (saved in the settings file) are marked with *)\n\n");
1216 if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
1217 snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
1218 if(p->save) strcat(buf+len, "*");
1219 for(q=p+1; q->argLoc == p->argLoc; q++) {
1220 if(q->argName[0] == '-') continue;
1221 strcat(buf+len, q == p+1 ? " (" : " ");
1222 sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
1224 if(q != p+1) strcat(buf+len, ")");
1226 if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
1229 if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
1233 main (int argc, char **argv)
1235 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1236 XSetWindowAttributes window_attributes;
1238 Dimension boardWidth, boardHeight, w, h;
1240 int forceMono = False;
1242 srandom(time(0)); // [HGM] book: make random truly random
1244 setbuf(stdout, NULL);
1245 setbuf(stderr, NULL);
1248 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1249 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1253 if(argc > 1 && !strcmp(argv[1], "--help" )) {
1258 programName = strrchr(argv[0], '/');
1259 if (programName == NULL)
1260 programName = argv[0];
1265 XtSetLanguageProc(NULL, NULL, NULL);
1266 if (appData.debugMode) {
1267 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1270 bindtextdomain(PACKAGE, LOCALEDIR);
1271 textdomain(PACKAGE);
1274 appData.boardSize = "";
1275 InitAppData(ConvertToLine(argc, argv));
1277 if (p == NULL) p = "/tmp";
1278 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1279 gameCopyFilename = (char*) malloc(i);
1280 gamePasteFilename = (char*) malloc(i);
1281 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1282 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1284 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1285 static char buf[MSG_SIZ];
1286 EscapeExpand(buf, appData.firstInitString);
1287 appData.firstInitString = strdup(buf);
1288 EscapeExpand(buf, appData.secondInitString);
1289 appData.secondInitString = strdup(buf);
1290 EscapeExpand(buf, appData.firstComputerString);
1291 appData.firstComputerString = strdup(buf);
1292 EscapeExpand(buf, appData.secondComputerString);
1293 appData.secondComputerString = strdup(buf);
1296 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1299 if (chdir(chessDir) != 0) {
1300 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1306 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1307 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1308 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1309 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1312 setbuf(debugFP, NULL);
1315 /* [HGM,HR] make sure board size is acceptable */
1316 if(appData.NrFiles > BOARD_FILES ||
1317 appData.NrRanks > BOARD_RANKS )
1318 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1321 /* This feature does not work; animation needs a rewrite */
1322 appData.highlightDragging = FALSE;
1326 gameInfo.variant = StringToVariant(appData.variant);
1327 InitPosition(FALSE);
1330 XtAppInitialize(&appContext, "XBoard", shellOptions,
1331 XtNumber(shellOptions),
1332 &argc, argv, xboardResources, NULL, 0);
1334 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1335 clientResources, XtNumber(clientResources),
1338 xDisplay = XtDisplay(shellWidget);
1339 xScreen = DefaultScreen(xDisplay);
1340 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1343 * determine size, based on supplied or remembered -size, or screen size
1345 if (isdigit(appData.boardSize[0])) {
1346 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1347 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1348 &fontPxlSize, &smallLayout, &tinyLayout);
1350 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1351 programName, appData.boardSize);
1355 /* Find some defaults; use the nearest known size */
1356 SizeDefaults *szd, *nearest;
1357 int distance = 99999;
1358 nearest = szd = sizeDefaults;
1359 while (szd->name != NULL) {
1360 if (abs(szd->squareSize - squareSize) < distance) {
1362 distance = abs(szd->squareSize - squareSize);
1363 if (distance == 0) break;
1367 if (i < 2) lineGap = nearest->lineGap;
1368 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1369 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1370 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1371 if (i < 6) smallLayout = nearest->smallLayout;
1372 if (i < 7) tinyLayout = nearest->tinyLayout;
1375 SizeDefaults *szd = sizeDefaults;
1376 if (*appData.boardSize == NULLCHAR) {
1377 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1378 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1381 if (szd->name == NULL) szd--;
1382 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1384 while (szd->name != NULL &&
1385 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1386 if (szd->name == NULL) {
1387 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1388 programName, appData.boardSize);
1392 squareSize = szd->squareSize;
1393 lineGap = szd->lineGap;
1394 clockFontPxlSize = szd->clockFontPxlSize;
1395 coordFontPxlSize = szd->coordFontPxlSize;
1396 fontPxlSize = szd->fontPxlSize;
1397 smallLayout = szd->smallLayout;
1398 tinyLayout = szd->tinyLayout;
1399 // [HGM] font: use defaults from settings file if available and not overruled
1402 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1403 if (strlen(appData.pixmapDirectory) > 0) {
1404 p = ExpandPathName(appData.pixmapDirectory);
1406 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1407 appData.pixmapDirectory);
1410 if (appData.debugMode) {
1411 fprintf(stderr, _("\
1412 XBoard square size (hint): %d\n\
1413 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1415 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1416 if (appData.debugMode) {
1417 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1420 defaultLineGap = lineGap;
1421 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1423 /* [HR] height treated separately (hacked) */
1424 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1425 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1428 * Determine what fonts to use.
1430 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1433 * Detect if there are not enough colors available and adapt.
1435 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1436 appData.monoMode = True;
1439 forceMono = MakeColors();
1442 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1444 appData.monoMode = True;
1447 if (appData.monoMode && appData.debugMode) {
1448 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1449 (unsigned long) XWhitePixel(xDisplay, xScreen),
1450 (unsigned long) XBlackPixel(xDisplay, xScreen));
1453 ParseIcsTextColors();
1455 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1461 layoutName = "tinyLayout";
1462 } else if (smallLayout) {
1463 layoutName = "smallLayout";
1465 layoutName = "normalLayout";
1468 optList = BoardPopUp(squareSize, lineGap, (void*)
1474 boardWidget = optList[W_BOARD].handle;
1475 menuBarWidget = optList[W_MENU].handle;
1476 dropMenu = optList[W_DROP].handle;
1477 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1478 formWidget = XtParent(boardWidget);
1479 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1480 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1481 XtGetValues(optList[W_WHITE].handle, args, 2);
1482 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1483 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1484 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1485 XtGetValues(optList[W_PAUSE].handle, args, 2);
1487 AppendEnginesToMenu(appData.recentEngineList);
1489 xBoardWindow = XtWindow(boardWidget);
1491 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1492 // not need to go into InitDrawingSizes().
1495 * Create X checkmark bitmap and initialize option menu checks.
1497 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1498 checkmark_bits, checkmark_width, checkmark_height);
1504 ReadBitmap(&wIconPixmap, "icon_white.bm",
1505 icon_white_bits, icon_white_width, icon_white_height);
1506 ReadBitmap(&bIconPixmap, "icon_black.bm",
1507 icon_black_bits, icon_black_width, icon_black_height);
1508 iconPixmap = wIconPixmap;
1510 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1511 XtSetValues(shellWidget, args, i);
1514 * Create a cursor for the board widget.
1516 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1517 XChangeWindowAttributes(xDisplay, xBoardWindow,
1518 CWCursor, &window_attributes);
1521 * Inhibit shell resizing.
1523 shellArgs[0].value = (XtArgVal) &w;
1524 shellArgs[1].value = (XtArgVal) &h;
1525 XtGetValues(shellWidget, shellArgs, 2);
1526 shellArgs[4].value = shellArgs[2].value = w;
1527 shellArgs[5].value = shellArgs[3].value = h;
1528 XtSetValues(shellWidget, &shellArgs[2], 4);
1529 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1530 marginH = h - boardHeight;
1532 CatchDeleteWindow(shellWidget, "QuitProc");
1538 if(appData.logoSize)
1539 { // locate and read user logo
1541 snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1542 ASSIGN(userLogo, buf);
1545 if (appData.animate || appData.animateDragging)
1548 XtAugmentTranslations(formWidget,
1549 XtParseTranslationTable(globalTranslations));
1551 XtAddEventHandler(formWidget, KeyPressMask, False,
1552 (XtEventHandler) MoveTypeInProc, NULL);
1553 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1554 (XtEventHandler) EventProc, NULL);
1556 /* [AS] Restore layout */
1557 if( wpMoveHistory.visible ) {
1561 if( wpEvalGraph.visible )
1566 if( wpEngineOutput.visible ) {
1567 EngineOutputPopUp();
1572 if (errorExitStatus == -1) {
1573 if (appData.icsActive) {
1574 /* We now wait until we see "login:" from the ICS before
1575 sending the logon script (problems with timestamp otherwise) */
1576 /*ICSInitScript();*/
1577 if (appData.icsInputBox) ICSInputBoxPopUp();
1581 signal(SIGWINCH, TermSizeSigHandler);
1583 signal(SIGINT, IntSigHandler);
1584 signal(SIGTERM, IntSigHandler);
1585 if (*appData.cmailGameName != NULLCHAR) {
1586 signal(SIGUSR1, CmailSigHandler);
1590 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1593 // XtSetKeyboardFocus(shellWidget, formWidget);
1594 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1596 XtAppMainLoop(appContext);
1597 if (appData.debugMode) fclose(debugFP); // [DM] debug
1602 TermSizeSigHandler (int sig)
1608 IntSigHandler (int sig)
1614 CmailSigHandler (int sig)
1619 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1621 /* Activate call-back function CmailSigHandlerCallBack() */
1622 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1624 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1628 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1631 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1633 /**** end signal code ****/
1636 #define Abs(n) ((n)<0 ? -(n) : (n))
1640 InsertPxlSize (char *pattern, int targetPxlSize)
1642 char *base_fnt_lst, strInt[12], *p, *q;
1643 int alternatives, i, len, strIntLen;
1646 * Replace the "*" (if present) in the pixel-size slot of each
1647 * alternative with the targetPxlSize.
1651 while ((p = strchr(p, ',')) != NULL) {
1655 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1656 strIntLen = strlen(strInt);
1657 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1661 while (alternatives--) {
1662 char *comma = strchr(p, ',');
1663 for (i=0; i<14; i++) {
1664 char *hyphen = strchr(p, '-');
1666 if (comma && hyphen > comma) break;
1667 len = hyphen + 1 - p;
1668 if (i == 7 && *p == '*' && len == 2) {
1670 memcpy(q, strInt, strIntLen);
1680 len = comma + 1 - p;
1687 return base_fnt_lst;
1691 CreateFontSet (char *base_fnt_lst)
1694 char **missing_list;
1698 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1699 &missing_list, &missing_count, &def_string);
1700 if (appData.debugMode) {
1702 XFontStruct **font_struct_list;
1703 char **font_name_list;
1704 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1706 fprintf(debugFP, " got list %s, locale %s\n",
1707 XBaseFontNameListOfFontSet(fntSet),
1708 XLocaleOfFontSet(fntSet));
1709 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1710 for (i = 0; i < count; i++) {
1711 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1714 for (i = 0; i < missing_count; i++) {
1715 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1718 if (fntSet == NULL) {
1719 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1724 #else // not ENABLE_NLS
1726 * Find a font that matches "pattern" that is as close as
1727 * possible to the targetPxlSize. Prefer fonts that are k
1728 * pixels smaller to fonts that are k pixels larger. The
1729 * pattern must be in the X Consortium standard format,
1730 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1731 * The return value should be freed with XtFree when no
1735 FindFont (char *pattern, int targetPxlSize)
1737 char **fonts, *p, *best, *scalable, *scalableTail;
1738 int i, j, nfonts, minerr, err, pxlSize;
1740 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1742 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1743 programName, pattern);
1750 for (i=0; i<nfonts; i++) {
1753 if (*p != '-') continue;
1755 if (*p == NULLCHAR) break;
1756 if (*p++ == '-') j++;
1758 if (j < 7) continue;
1761 scalable = fonts[i];
1764 err = pxlSize - targetPxlSize;
1765 if (Abs(err) < Abs(minerr) ||
1766 (minerr > 0 && err < 0 && -err == minerr)) {
1772 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1773 /* If the error is too big and there is a scalable font,
1774 use the scalable font. */
1775 int headlen = scalableTail - scalable;
1776 p = (char *) XtMalloc(strlen(scalable) + 10);
1777 while (isdigit(*scalableTail)) scalableTail++;
1778 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1780 p = (char *) XtMalloc(strlen(best) + 2);
1781 safeStrCpy(p, best, strlen(best)+1 );
1783 if (appData.debugMode) {
1784 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1785 pattern, targetPxlSize, p);
1787 XFreeFontNames(fonts);
1794 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
1795 // must be called before all non-first callse to CreateGCs()
1796 XtReleaseGC(shellWidget, highlineGC);
1797 XtReleaseGC(shellWidget, lightSquareGC);
1798 XtReleaseGC(shellWidget, darkSquareGC);
1799 XtReleaseGC(shellWidget, lineGC);
1800 if (appData.monoMode) {
1801 if (DefaultDepth(xDisplay, xScreen) == 1) {
1802 XtReleaseGC(shellWidget, wbPieceGC);
1804 XtReleaseGC(shellWidget, bwPieceGC);
1807 XtReleaseGC(shellWidget, prelineGC);
1808 XtReleaseGC(shellWidget, wdPieceGC);
1809 XtReleaseGC(shellWidget, wlPieceGC);
1810 XtReleaseGC(shellWidget, bdPieceGC);
1811 XtReleaseGC(shellWidget, blPieceGC);
1816 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
1818 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1819 | GCBackground | GCFunction | GCPlaneMask;
1820 gc_values->foreground = foreground;
1821 gc_values->background = background;
1822 return XtGetGC(shellWidget, value_mask, gc_values);
1826 CreateGCs (int redo)
1828 XGCValues gc_values;
1830 Pixel white = XWhitePixel(xDisplay, xScreen);
1831 Pixel black = XBlackPixel(xDisplay, xScreen);
1833 gc_values.plane_mask = AllPlanes;
1834 gc_values.line_width = lineGap;
1835 gc_values.line_style = LineSolid;
1836 gc_values.function = GXcopy;
1839 DeleteGCs(); // called a second time; clean up old GCs first
1840 } else { // [HGM] grid and font GCs created on first call only
1841 coordGC = CreateOneGC(&gc_values, black, white);
1842 XSetFont(xDisplay, coordGC, coordFontID);
1844 // [HGM] make font for holdings counts (white on black)
1845 countGC = CreateOneGC(&gc_values, white, black);
1846 XSetFont(xDisplay, countGC, countFontID);
1848 lineGC = CreateOneGC(&gc_values, black, black);
1850 if (appData.monoMode) {
1852 highlineGC = CreateOneGC(&gc_values, white, white);
1853 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
1854 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
1856 if (DefaultDepth(xDisplay, xScreen) == 1) {
1857 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
1858 gc_values.function = GXcopyInverted;
1859 copyInvertedGC = CreateOneGC(&gc_values, black, white);
1860 gc_values.function = GXcopy;
1861 if (XBlackPixel(xDisplay, xScreen) == 1) {
1862 bwPieceGC = darkSquareGC;
1863 wbPieceGC = copyInvertedGC;
1865 bwPieceGC = copyInvertedGC;
1866 wbPieceGC = lightSquareGC;
1871 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
1872 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
1873 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
1874 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
1875 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
1876 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
1877 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
1878 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
1883 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
1891 fp = fopen(filename, "rb");
1893 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
1900 for (y=0; y<h; ++y) {
1901 for (x=0; x<h; ++x) {
1906 XPutPixel(xim, x, y, blackPieceColor);
1908 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1911 XPutPixel(xim, x, y, darkSquareColor);
1913 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1916 XPutPixel(xim, x, y, whitePieceColor);
1918 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1921 XPutPixel(xim, x, y, lightSquareColor);
1923 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1931 /* create Pixmap of piece */
1932 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1934 XPutImage(xDisplay, *dest, lightSquareGC, xim,
1937 /* create Pixmap of clipmask
1938 Note: We assume the white/black pieces have the same
1939 outline, so we make only 6 masks. This is okay
1940 since the XPM clipmask routines do the same. */
1942 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1944 XPutImage(xDisplay, temp, lightSquareGC, xmask,
1947 /* now create the 1-bit version */
1948 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1951 values.foreground = 1;
1952 values.background = 0;
1954 /* Don't use XtGetGC, not read only */
1955 maskGC = XCreateGC(xDisplay, *mask,
1956 GCForeground | GCBackground, &values);
1957 XCopyPlane(xDisplay, temp, *mask, maskGC,
1958 0, 0, squareSize, squareSize, 0, 0, 1);
1959 XFreePixmap(xDisplay, temp);
1964 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
1972 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
1977 /* The XSynchronize calls were copied from CreatePieces.
1978 Not sure if needed, but can't hurt */
1979 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
1982 /* temp needed by loadXIM() */
1983 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1984 0, 0, ss, ss, AllPlanes, XYPixmap);
1986 if (strlen(appData.pixmapDirectory) == 0) {
1990 if (appData.monoMode) {
1991 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
1995 fprintf(stderr, _("\nLoading XIMs...\n"));
1997 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
1998 fprintf(stderr, "%d", piece+1);
1999 for (kind=0; kind<4; kind++) {
2000 fprintf(stderr, ".");
2001 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2002 ExpandPathName(appData.pixmapDirectory),
2003 piece <= (int) WhiteKing ? "" : "w",
2004 pieceBitmapNames[piece],
2006 ximPieceBitmap[kind][piece] =
2007 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2008 0, 0, ss, ss, AllPlanes, XYPixmap);
2009 if (appData.debugMode)
2010 fprintf(stderr, _("(File:%s:) "), buf);
2011 loadXIM(ximPieceBitmap[kind][piece],
2013 &(xpmPieceBitmap2[kind][piece]),
2014 &(ximMaskPm2[piece]));
2015 if(piece <= (int)WhiteKing)
2016 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2018 fprintf(stderr," ");
2020 /* Load light and dark squares */
2021 /* If the LSQ and DSQ pieces don't exist, we will
2022 draw them with solid squares. */
2023 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2024 if (access(buf, 0) != 0) {
2028 fprintf(stderr, _("light square "));
2030 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2031 0, 0, ss, ss, AllPlanes, XYPixmap);
2032 if (appData.debugMode)
2033 fprintf(stderr, _("(File:%s:) "), buf);
2035 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2036 fprintf(stderr, _("dark square "));
2037 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2038 ExpandPathName(appData.pixmapDirectory), ss);
2039 if (appData.debugMode)
2040 fprintf(stderr, _("(File:%s:) "), buf);
2042 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2043 0, 0, ss, ss, AllPlanes, XYPixmap);
2044 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2045 xpmJailSquare = xpmLightSquare;
2047 fprintf(stderr, _("Done.\n"));
2049 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2052 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2056 CreateXPMBoard (char *s, int kind)
2060 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2061 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2062 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2068 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2069 // thisroutine has to be called t free the old piece pixmaps
2071 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2072 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2074 XFreePixmap(xDisplay, xpmLightSquare);
2075 XFreePixmap(xDisplay, xpmDarkSquare);
2084 u_int ss = squareSize;
2086 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2087 XpmColorSymbol symbols[4];
2088 static int redo = False;
2090 if(redo) FreeXPMPieces(); else redo = 1;
2092 /* The XSynchronize calls were copied from CreatePieces.
2093 Not sure if needed, but can't hurt */
2094 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2096 /* Setup translations so piece colors match square colors */
2097 symbols[0].name = "light_piece";
2098 symbols[0].value = appData.whitePieceColor;
2099 symbols[1].name = "dark_piece";
2100 symbols[1].value = appData.blackPieceColor;
2101 symbols[2].name = "light_square";
2102 symbols[2].value = appData.lightSquareColor;
2103 symbols[3].name = "dark_square";
2104 symbols[3].value = appData.darkSquareColor;
2106 attr.valuemask = XpmColorSymbols;
2107 attr.colorsymbols = symbols;
2108 attr.numsymbols = 4;
2110 if (appData.monoMode) {
2111 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2115 if (strlen(appData.pixmapDirectory) == 0) {
2116 XpmPieces* pieces = builtInXpms;
2119 while (pieces->size != squareSize && pieces->size) pieces++;
2120 if (!pieces->size) {
2121 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2124 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2125 for (kind=0; kind<4; kind++) {
2127 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2128 pieces->xpm[piece][kind],
2129 &(xpmPieceBitmap2[kind][piece]),
2130 NULL, &attr)) != 0) {
2131 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2135 if(piece <= (int) WhiteKing)
2136 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2140 xpmJailSquare = xpmLightSquare;
2144 fprintf(stderr, _("\nLoading XPMs...\n"));
2147 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2148 fprintf(stderr, "%d ", piece+1);
2149 for (kind=0; kind<4; kind++) {
2150 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2151 ExpandPathName(appData.pixmapDirectory),
2152 piece > (int) WhiteKing ? "w" : "",
2153 pieceBitmapNames[piece],
2155 if (appData.debugMode) {
2156 fprintf(stderr, _("(File:%s:) "), buf);
2158 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2159 &(xpmPieceBitmap2[kind][piece]),
2160 NULL, &attr)) != 0) {
2161 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2162 // [HGM] missing: read of unorthodox piece failed; substitute King.
2163 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2164 ExpandPathName(appData.pixmapDirectory),
2166 if (appData.debugMode) {
2167 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2169 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2170 &(xpmPieceBitmap2[kind][piece]),
2174 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2179 if(piece <= (int) WhiteKing)
2180 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2183 /* Load light and dark squares */
2184 /* If the LSQ and DSQ pieces don't exist, we will
2185 draw them with solid squares. */
2186 fprintf(stderr, _("light square "));
2187 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2188 if (access(buf, 0) != 0) {
2192 if (appData.debugMode)
2193 fprintf(stderr, _("(File:%s:) "), buf);
2195 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2196 &xpmLightSquare, NULL, &attr)) != 0) {
2197 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2200 fprintf(stderr, _("dark square "));
2201 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2202 ExpandPathName(appData.pixmapDirectory), ss);
2203 if (appData.debugMode) {
2204 fprintf(stderr, _("(File:%s:) "), buf);
2206 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2207 &xpmDarkSquare, NULL, &attr)) != 0) {
2208 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2212 xpmJailSquare = xpmLightSquare;
2213 fprintf(stderr, _("Done.\n"));
2215 oldVariant = -1; // kludge to force re-makig of animation masks
2216 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2219 #endif /* HAVE_LIBXPM */
2221 char *pngPieceNames[] = // must be in same order as internal piece encoding
2222 { "Pawn", "Knight", "Bishop", "Rook", "Queen", "Advisor", "Elephant", "Archbishop", "Marshall", "Gold", "Commoner",
2223 "Canon", "Nightrider", "CrownedBishop", "CrownedRook", "Princess", "Chancellor", "Hawk", "Lance", "Cobra", "Unicorn", "King",
2224 "GoldKnight", "GoldLance", "GoldPawn", "GoldSilver", NULL
2228 ScaleOnePiece (char *name, int color, int piece)
2232 cairo_surface_t *img, *cs;
2234 static cairo_surface_t *pngPieceImages[2][(int)BlackPawn+4]; // png 256 x 256 images
2236 if(pngPieceImages[color][piece] == NULL) { // if PNG file for this piece was not yet read, read it now and store it
2237 snprintf(buf, MSG_SIZ, "%s/%s%s.png", appData.pngDirectory, color ? "Black" : "White", pngPieceNames[piece]);
2238 pngPieceImages[color][piece] = img = cairo_image_surface_create_from_png (buf);
2239 w = cairo_image_surface_get_width (img);
2240 h = cairo_image_surface_get_height (img);
2241 if(w != 256 || h != 256) { printf("Bad png size %dx%d in %s\n", w, h, buf); exit(1); }
2244 // create new bitmap to hold scaled piece image (and remove any old)
2245 if(pngPieceBitmaps2[color][piece]) cairo_surface_destroy (pngPieceBitmaps2[color][piece]);
2246 pngPieceBitmaps2[color][piece] = cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, squareSize, squareSize);
2247 if(piece <= WhiteKing) pngPieceBitmaps[color][piece] = cs;
2248 // scaled copying of the raw png image
2249 cr = cairo_create(cs);
2250 cairo_scale(cr, squareSize/256., squareSize/256.);
2251 cairo_set_source_surface (cr, img, 0, 0);
2254 cairo_surface_destroy (img);
2262 for(p=0; pngPieceNames[p]; p++) {
2263 ScaleOnePiece(pngPieceNames[p], 0, p);
2264 ScaleOnePiece(pngPieceNames[p], 1, p);
2269 /* No built-in bitmaps */
2274 u_int ss = squareSize;
2276 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2279 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2280 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2281 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2282 pieceBitmapNames[piece],
2283 ss, kind == SOLID ? 's' : 'o');
2284 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2285 if(piece <= (int)WhiteKing)
2286 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2290 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2294 /* With built-in bitmaps */
2298 BuiltInBits* bib = builtInBits;
2301 u_int ss = squareSize;
2303 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2306 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2308 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2309 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2310 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2311 pieceBitmapNames[piece],
2312 ss, kind == SOLID ? 's' : 'o');
2313 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2314 bib->bits[kind][piece], ss, ss);
2315 if(piece <= (int)WhiteKing)
2316 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2320 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2326 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2331 char msg[MSG_SIZ], fullname[MSG_SIZ];
2333 if (*appData.bitmapDirectory != NULLCHAR) {
2334 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2335 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2336 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2337 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2338 &w, &h, pm, &x_hot, &y_hot);
2339 fprintf(stderr, "load %s\n", name);
2340 if (errcode != BitmapSuccess) {
2342 case BitmapOpenFailed:
2343 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2345 case BitmapFileInvalid:
2346 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2348 case BitmapNoMemory:
2349 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2353 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2357 fprintf(stderr, _("%s: %s...using built-in\n"),
2359 } else if (w != wreq || h != hreq) {
2361 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2362 programName, fullname, w, h, wreq, hreq);
2368 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2378 if (lineGap == 0) return;
2380 /* [HR] Split this into 2 loops for non-square boards. */
2382 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2383 gridSegments[i].x1 = 0;
2384 gridSegments[i].x2 =
2385 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2386 gridSegments[i].y1 = gridSegments[i].y2
2387 = lineGap / 2 + (i * (squareSize + lineGap));
2390 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2391 gridSegments[j + i].y1 = 0;
2392 gridSegments[j + i].y2 =
2393 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2394 gridSegments[j + i].x1 = gridSegments[j + i].x2
2395 = lineGap / 2 + (j * (squareSize + lineGap));
2400 MarkMenuItem (char *menuRef, int state)
2402 MenuItem *item = MenuNameToItem(menuRef);
2406 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2407 XtSetValues(item->handle, args, 1);
2412 EnableNamedMenuItem (char *menuRef, int state)
2414 MenuItem *item = MenuNameToItem(menuRef);
2416 if(item) XtSetSensitive(item->handle, state);
2420 EnableButtonBar (int state)
2422 XtSetSensitive(optList[W_BUTTON].handle, state);
2427 SetMenuEnables (Enables *enab)
2429 while (enab->name != NULL) {
2430 EnableNamedMenuItem(enab->name, enab->value);
2436 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2437 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2439 if(*nprms == 0) return;
2440 item = MenuNameToItem(prms[0]);
2441 if(item) ((MenuProc *) item->proc) ();
2445 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2447 RecentEngineEvent((int) (intptr_t) addr);
2451 AppendMenuItem (char *msg, int n)
2453 CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
2465 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2466 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2467 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2468 dmEnables[i].piece);
2469 XtSetSensitive(entry, p != NULL || !appData.testLegality
2470 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2471 && !appData.icsActive));
2473 while (p && *p++ == dmEnables[i].piece) count++;
2474 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2476 XtSetArg(args[j], XtNlabel, label); j++;
2477 XtSetValues(entry, args, j);
2483 do_flash_delay (unsigned long msec)
2488 static cairo_surface_t *cs; // to keep out of back-end :-(
2491 DrawBorder (int x, int y, int type)
2496 cr = cairo_create(cs);
2497 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
2498 cairo_rectangle(cr, x, y, squareSize+lineGap, squareSize+lineGap);
2499 SetPen(cr, lineGap, type == 1 ? appData.highlightSquareColor : appData.premoveHighlightColor, 0);
2506 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
2508 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
2509 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
2511 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
2512 if(textureW[kind] < W*squareSize)
2513 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
2515 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
2516 if(textureH[kind] < H*squareSize)
2517 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
2519 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
2524 DrawLogo (void *handle, void *logo)
2526 cairo_surface_t *img, *cs;
2530 if(!logo || !handle) return;
2531 cs = cairo_xlib_surface_create(xDisplay, XtWindow(handle), DefaultVisual(xDisplay, 0), appData.logoSize, appData.logoSize/2);
2532 img = cairo_image_surface_create_from_png (logo);
2533 w = cairo_image_surface_get_width (img);
2534 h = cairo_image_surface_get_height (img);
2535 cr = cairo_create(cs);
2536 cairo_scale(cr, (float)appData.logoSize/w, appData.logoSize/(2.*h));
2537 cairo_set_source_surface (cr, img, 0, 0);
2540 cairo_surface_destroy (img);
2541 cairo_surface_destroy (cs);
2545 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
2546 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
2548 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
2549 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
2550 squareSize, squareSize, x*fac, y*fac);
2552 if (useImages && useImageSqs) {
2556 pm = xpmLightSquare;
2561 case 2: /* neutral */
2563 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
2566 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
2567 squareSize, squareSize, x*fac, y*fac);
2577 case 2: /* neutral */
2582 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
2587 I split out the routines to draw a piece so that I could
2588 make a generic flash routine.
2591 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2593 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2594 switch (square_color) {
2596 case 2: /* neutral */
2598 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2599 ? *pieceToOutline(piece)
2600 : *pieceToSolid(piece),
2601 dest, bwPieceGC, 0, 0,
2602 squareSize, squareSize, x, y);
2605 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2606 ? *pieceToSolid(piece)
2607 : *pieceToOutline(piece),
2608 dest, wbPieceGC, 0, 0,
2609 squareSize, squareSize, x, y);
2615 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2617 switch (square_color) {
2619 case 2: /* neutral */
2621 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2622 ? *pieceToOutline(piece)
2623 : *pieceToSolid(piece),
2624 dest, bwPieceGC, 0, 0,
2625 squareSize, squareSize, x, y, 1);
2628 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2629 ? *pieceToSolid(piece)
2630 : *pieceToOutline(piece),
2631 dest, wbPieceGC, 0, 0,
2632 squareSize, squareSize, x, y, 1);
2638 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2640 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
2641 switch (square_color) {
2643 XCopyPlane(xDisplay, *pieceToSolid(piece),
2644 dest, (int) piece < (int) BlackPawn
2645 ? wlPieceGC : blPieceGC, 0, 0,
2646 squareSize, squareSize, x, y, 1);
2649 XCopyPlane(xDisplay, *pieceToSolid(piece),
2650 dest, (int) piece < (int) BlackPawn
2651 ? wdPieceGC : bdPieceGC, 0, 0,
2652 squareSize, squareSize, x, y, 1);
2654 case 2: /* neutral */
2656 break; // should never contain pieces
2661 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2663 int kind, p = piece;
2665 switch (square_color) {
2667 case 2: /* neutral */
2669 if ((int)piece < (int) BlackPawn) {
2677 if ((int)piece < (int) BlackPawn) {
2685 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2686 if(useTexture & square_color+1) {
2687 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2688 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
2689 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
2690 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
2691 XSetClipMask(xDisplay, wlPieceGC, None);
2692 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
2694 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
2695 dest, wlPieceGC, 0, 0,
2696 squareSize, squareSize, x, y);
2700 pngDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2702 int kind, p = piece;
2705 if ((int)piece < (int) BlackPawn) {
2711 if(appData.upsideDown && flipView) { p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2712 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2714 cr = cairo_create (cs);
2715 cairo_set_source_surface (cr, pngPieceBitmaps[kind][piece], x, y);
2721 typedef void (*DrawFunc)();
2726 if (appData.monoMode) {
2727 if (DefaultDepth(xDisplay, xScreen) == 1) {
2728 return monoDrawPiece_1bit;
2730 return monoDrawPiece;
2732 } else if(appData.pngDirectory[0]) {
2733 return pngDrawPiece;
2736 return colorDrawPieceImage;
2738 return colorDrawPiece;
2743 DrawDot (int marker, int x, int y, int r)
2747 cr = cairo_create(cs);
2748 cairo_arc(cr, x+r/2, y+r/2, r/2, 0.0, 2*M_PI);
2749 if(appData.monoMode) {
2750 SetPen(cr, 2, marker == 2 ? "#000000" : "#FFFFFF", 0);
2751 cairo_stroke_preserve(cr);
2752 SetPen(cr, 2, marker == 2 ? "#FFFFFF" : "#000000", 0);
2754 SetPen(cr, 2, marker == 2 ? "#FF0000" : "#FFFF00", 0);
2764 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2765 { // basic front-end board-draw function: takes care of everything that can be in square:
2766 // piece, background, coordinate/count, marker dot
2767 int direction, font_ascent, font_descent;
2768 XCharStruct overall;
2771 if (piece == EmptySquare) {
2772 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2774 drawfunc = ChooseDrawFunc();
2775 drawfunc(piece, square_color, x, y, xBoardWindow);
2778 if(align) { // square carries inscription (coord or piece count)
2780 GC hGC = align < 3 ? coordGC : countGC;
2781 // first calculate where it goes
2782 XTextExtents(countFontStruct, string, 1, &direction,
2783 &font_ascent, &font_descent, &overall);
2785 xx += squareSize - overall.width - 2;
2786 yy += squareSize - font_descent - 1;
2787 } else if (align == 2) {
2788 xx += 2, yy += font_ascent + 1;
2789 } else if (align == 3) {
2790 xx += squareSize - overall.width - 2;
2791 yy += font_ascent + 1;
2792 } else if (align == 4) {
2793 xx += 2, yy += font_ascent + 1;
2796 if (appData.monoMode) {
2797 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2799 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2803 if(marker) { // print fat marker dot, if requested
2804 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2809 FlashDelay (int flash_delay)
2811 XSync(xDisplay, False);
2812 if(flash_delay) do_flash_delay(flash_delay);
2816 Fraction (int x, int start, int stop)
2818 double f = ((double) x - start)/(stop - start);
2819 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2823 static WindowPlacement wpNew;
2826 CoDrag (Widget sh, WindowPlacement *wp)
2829 int j=0, touch=0, fudge = 2;
2830 GetActualPlacement(sh, wp);
2831 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
2832 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
2833 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2834 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
2835 if(!touch ) return; // only windows that touch co-move
2836 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2837 int heightInc = wpNew.height - wpMain.height;
2838 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2839 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2840 wp->y += fracTop * heightInc;
2841 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2842 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2843 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2844 int widthInc = wpNew.width - wpMain.width;
2845 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2846 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2847 wp->y += fracLeft * widthInc;
2848 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2849 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2851 wp->x += wpNew.x - wpMain.x;
2852 wp->y += wpNew.y - wpMain.y;
2853 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2854 if(touch == 3) wp->y += wpNew.height - wpMain.height;
2855 XtSetArg(args[j], XtNx, wp->x); j++;
2856 XtSetArg(args[j], XtNy, wp->y); j++;
2857 XtSetValues(sh, args, j);
2860 static XtIntervalId delayedDragID = 0;
2865 GetActualPlacement(shellWidget, &wpNew);
2866 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
2867 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
2868 return; // false alarm
2869 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
2870 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
2871 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
2872 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
2874 DrawPosition(True, NULL);
2875 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
2882 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
2884 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
2888 EventProc (Widget widget, caddr_t unused, XEvent *event)
2890 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
2891 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
2894 // [HGM] seekgraph: some low-level drawing routines (by JC, mostly)
2897 Color (char *col, int n)
2900 sscanf(col, "#%x", &c);
2906 SetPen (cairo_t *cr, float w, char *col, int dash)
2908 static const double dotted[] = {4.0, 4.0};
2909 static int len = sizeof(dotted) / sizeof(dotted[0]);
2910 cairo_set_line_width (cr, w);
2911 cairo_set_source_rgba (cr, Color(col, 4), Color(col, 2), Color(col, 0), 1.0);
2912 if(dash) cairo_set_dash (cr, dotted, len, 0.0);
2915 void DrawSeekAxis( int x, int y, int xTo, int yTo )
2920 cr = cairo_create (cs);
2922 cairo_move_to (cr, x, y);
2923 cairo_line_to(cr, xTo, yTo );
2925 SetPen(cr, 2, "#000000", 0);
2932 void DrawSeekBackground( int left, int top, int right, int bottom )
2934 cairo_t *cr = cairo_create (cs);
2936 cairo_rectangle (cr, left, top, right-left, bottom-top);
2938 cairo_set_source_rgba(cr, 0.8, 0.8, 0.4,1.0);
2945 void DrawSeekText(char *buf, int x, int y)
2947 cairo_t *cr = cairo_create (cs);
2949 cairo_select_font_face (cr, "Sans",
2950 CAIRO_FONT_SLANT_NORMAL,
2951 CAIRO_FONT_WEIGHT_NORMAL);
2953 cairo_set_font_size (cr, 12.0);
2955 cairo_move_to (cr, x, y+4);
2956 cairo_show_text( cr, buf);
2958 cairo_set_source_rgba(cr, 0, 0, 0,1.0);
2965 void DrawSeekDot(int x, int y, int colorNr)
2967 cairo_t *cr = cairo_create (cs);
2968 int square = colorNr & 0x80;
2972 cairo_rectangle (cr, x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
2974 cairo_arc(cr, x, y, squareSize/8, 0.0, 2*M_PI);
2976 SetPen(cr, 2, "#000000", 0);
2977 cairo_stroke_preserve(cr);
2979 case 0: cairo_set_source_rgba(cr, 1.0, 0, 0,1.0); break;
2980 case 1: cairo_set_source_rgba (cr, 0.0, 0.7, 0.2, 1.0); break;
2981 default: cairo_set_source_rgba (cr, 1.0, 1.0, 0.0, 1.0); break;
2992 int boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2993 int boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2994 cs = cairo_xlib_surface_create(xDisplay, xBoardWindow, DefaultVisual(xDisplay, 0), boardWidth, boardHeight);
3000 cairo_surface_destroy(cs);
3006 /* draws a grid starting around Nx, Ny squares starting at x,y */
3012 cr = cairo_create (cs);
3014 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
3015 SetPen(cr, lineGap, "#000000", 0);
3018 for (i = 0; i < BOARD_WIDTH + BOARD_HEIGHT + 2; i++)
3020 cairo_move_to (cr, gridSegments[i].x1, gridSegments[i].y1);
3021 cairo_line_to (cr, gridSegments[i].x2, gridSegments[i].y2);
3033 * event handler for redrawing the board
3036 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3038 DrawPosition(True, NULL);
3043 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3044 { // [HGM] pv: walk PV
3045 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3048 static int savedIndex; /* gross that this is global */
3051 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3054 XawTextPosition index, dummy;
3057 XawTextGetSelectionPos(w, &index, &dummy);
3058 XtSetArg(arg, XtNstring, &val);
3059 XtGetValues(w, &arg, 1);
3060 ReplaceComment(savedIndex, val);
3061 if(savedIndex != currentMove) ToNrEvent(savedIndex);
3062 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3066 EditCommentPopUp (int index, char *title, char *text)
3069 if (text == NULL) text = "";
3070 NewCommentPopup(title, text, index);
3074 CommentPopUp (char *title, char *text)
3076 savedIndex = currentMove; // [HGM] vari
3077 NewCommentPopup(title, text, currentMove);
3083 PopDown(CommentDlg);
3087 /* Disable all user input other than deleting the window */
3088 static int frozen = 0;
3094 /* Grab by a widget that doesn't accept input */
3095 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
3099 /* Undo a FreezeUI */
3103 if (!frozen) return;
3104 XtRemoveGrab(optList[W_MESSG].handle);
3112 static int oldPausing = FALSE;
3113 static GameMode oldmode = (GameMode) -1;
3116 if (!boardWidget || !XtIsRealized(boardWidget)) return;
3118 if (pausing != oldPausing) {
3119 oldPausing = pausing;
3120 MarkMenuItem("Mode.Pause", pausing);
3122 if (appData.showButtonBar) {
3123 /* Always toggle, don't set. Previous code messes up when
3124 invoked while the button is pressed, as releasing it
3125 toggles the state again. */
3128 XtSetArg(args[0], XtNbackground, &oldbg);
3129 XtSetArg(args[1], XtNforeground, &oldfg);
3130 XtGetValues(optList[W_PAUSE].handle,
3132 XtSetArg(args[0], XtNbackground, oldfg);
3133 XtSetArg(args[1], XtNforeground, oldbg);
3135 XtSetValues(optList[W_PAUSE].handle, args, 2);
3139 wname = ModeToWidgetName(oldmode);
3140 if (wname != NULL) {
3141 MarkMenuItem(wname, False);
3143 wname = ModeToWidgetName(gameMode);
3144 if (wname != NULL) {
3145 MarkMenuItem(wname, True);
3148 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
3150 /* Maybe all the enables should be handled here, not just this one */
3151 EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
3153 DisplayLogos(optList[W_WHITE-1].handle, optList[W_BLACK+1].handle);
3158 * Button/menu procedures
3161 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3162 char *selected_fen_position=NULL;
3165 SendPositionSelection (Widget w, Atom *selection, Atom *target,
3166 Atom *type_return, XtPointer *value_return,
3167 unsigned long *length_return, int *format_return)
3169 char *selection_tmp;
3171 // if (!selected_fen_position) return False; /* should never happen */
3172 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3173 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
3174 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
3177 if (f == NULL) return False;
3181 selection_tmp = XtMalloc(len + 1);
3182 count = fread(selection_tmp, 1, len, f);
3185 XtFree(selection_tmp);
3188 selection_tmp[len] = NULLCHAR;
3190 /* note: since no XtSelectionDoneProc was registered, Xt will
3191 * automatically call XtFree on the value returned. So have to
3192 * make a copy of it allocated with XtMalloc */
3193 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3194 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
3197 *value_return=selection_tmp;
3198 *length_return=strlen(selection_tmp);
3199 *type_return=*target;
3200 *format_return = 8; /* bits per byte */
3202 } else if (*target == XA_TARGETS(xDisplay)) {
3203 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3204 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3205 targets_tmp[1] = XA_STRING;
3206 *value_return = targets_tmp;
3207 *type_return = XA_ATOM;
3210 // This code leads to a read of value_return out of bounds on 64-bit systems.
3211 // Other code which I have seen always sets *format_return to 32 independent of
3212 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
3213 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
3214 *format_return = 8 * sizeof(Atom);
3215 if (*format_return > 32) {
3216 *length_return *= *format_return / 32;
3217 *format_return = 32;
3220 *format_return = 32;
3228 /* note: when called from menu all parameters are NULL, so no clue what the
3229 * Widget which was clicked on was, or what the click event was
3232 CopySomething (char *src)
3234 selected_fen_position = src;
3236 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3237 * have a notion of a position that is selected but not copied.
3238 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3240 XtOwnSelection(menuBarWidget, XA_PRIMARY,
3242 SendPositionSelection,
3243 NULL/* lose_ownership_proc */ ,
3244 NULL/* transfer_done_proc */);
3245 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3247 SendPositionSelection,
3248 NULL/* lose_ownership_proc */ ,
3249 NULL/* transfer_done_proc */);
3252 /* function called when the data to Paste is ready */
3254 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3255 Atom *type, XtPointer value, unsigned long *len, int *format)
3258 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3259 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3260 EditPositionPasteFEN(fenstr);
3264 /* called when Paste Position button is pressed,
3265 * all parameters will be NULL */
3267 PastePositionProc ()
3269 XtGetSelectionValue(menuBarWidget,
3270 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3271 /* (XtSelectionCallbackProc) */ PastePositionCB,
3272 NULL, /* client_data passed to PastePositionCB */
3274 /* better to use the time field from the event that triggered the
3275 * call to this function, but that isn't trivial to get
3282 /* note: when called from menu all parameters are NULL, so no clue what the
3283 * Widget which was clicked on was, or what the click event was
3285 /* function called when the data to Paste is ready */
3287 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3288 Atom *type, XtPointer value, unsigned long *len, int *format)
3291 if (value == NULL || *len == 0) {
3292 return; /* nothing had been selected to copy */
3294 f = fopen(gamePasteFilename, "w");
3296 DisplayError(_("Can't open temp file"), errno);
3299 fwrite(value, 1, *len, f);
3302 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3305 /* called when Paste Game button is pressed,
3306 * all parameters will be NULL */
3310 XtGetSelectionValue(menuBarWidget,
3311 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3312 /* (XtSelectionCallbackProc) */ PasteGameCB,
3313 NULL, /* client_data passed to PasteGameCB */
3315 /* better to use the time field from the event that triggered the
3316 * call to this function, but that isn't trivial to get
3325 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3332 { // bassic primitive for determining if modifier keys are pressed
3333 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3336 XQueryKeymap(xDisplay,keys);
3337 for(i=0; i<6; i++) {
3339 j = XKeysymToKeycode(xDisplay, codes[i]);
3340 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3346 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3350 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3351 if ( n == 1 && *buf >= 32 // printable
3352 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3353 ) BoxAutoPopUp (buf);
3357 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3358 { // [HGM] input: let up-arrow recall previous line from history
3363 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3364 { // [HGM] input: let down-arrow recall next line from history
3369 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3375 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3377 if (!TempBackwardActive) {
3378 TempBackwardActive = True;
3384 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3386 /* Check to see if triggered by a key release event for a repeating key.
3387 * If so the next queued event will be a key press of the same key at the same time */
3388 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3390 XPeekEvent(xDisplay, &next);
3391 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3392 next.xkey.keycode == event->xkey.keycode)
3396 TempBackwardActive = False;
3400 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3401 { // called as key binding
3404 if (nprms && *nprms > 0)
3408 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3414 { // called from menu
3415 ManInner(NULL, NULL, NULL, NULL);
3419 SetWindowTitle (char *text, char *title, char *icon)
3423 if (appData.titleInWindow) {
3425 XtSetArg(args[i], XtNlabel, text); i++;
3426 XtSetValues(titleWidget, args, i);
3429 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3430 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3431 XtSetValues(shellWidget, args, i);
3432 XSync(xDisplay, False);
3437 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3443 DisplayIcsInteractionTitle (String message)
3445 if (oldICSInteractionTitle == NULL) {
3446 /* Magic to find the old window title, adapted from vim */
3447 char *wina = getenv("WINDOWID");
3449 Window win = (Window) atoi(wina);
3450 Window root, parent, *children;
3451 unsigned int nchildren;
3452 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3454 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3455 if (!XQueryTree(xDisplay, win, &root, &parent,
3456 &children, &nchildren)) break;
3457 if (children) XFree((void *)children);
3458 if (parent == root || parent == 0) break;
3461 XSetErrorHandler(oldHandler);
3463 if (oldICSInteractionTitle == NULL) {
3464 oldICSInteractionTitle = "xterm";
3467 printf("\033]0;%s\007", message);
3472 XtIntervalId delayedEventTimerXID = 0;
3473 DelayedEventCallback delayedEventCallback = 0;
3478 delayedEventTimerXID = 0;
3479 delayedEventCallback();
3483 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3485 if(delayedEventTimerXID && delayedEventCallback == cb)
3486 // [HGM] alive: replace, rather than add or flush identical event
3487 XtRemoveTimeOut(delayedEventTimerXID);
3488 delayedEventCallback = cb;
3489 delayedEventTimerXID =
3490 XtAppAddTimeOut(appContext, millisec,
3491 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3494 DelayedEventCallback
3497 if (delayedEventTimerXID) {
3498 return delayedEventCallback;
3505 CancelDelayedEvent ()
3507 if (delayedEventTimerXID) {
3508 XtRemoveTimeOut(delayedEventTimerXID);
3509 delayedEventTimerXID = 0;
3513 XtIntervalId loadGameTimerXID = 0;
3516 LoadGameTimerRunning ()
3518 return loadGameTimerXID != 0;
3522 StopLoadGameTimer ()
3524 if (loadGameTimerXID != 0) {
3525 XtRemoveTimeOut(loadGameTimerXID);
3526 loadGameTimerXID = 0;
3534 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3536 loadGameTimerXID = 0;
3541 StartLoadGameTimer (long millisec)
3544 XtAppAddTimeOut(appContext, millisec,
3545 (XtTimerCallbackProc) LoadGameTimerCallback,
3549 XtIntervalId analysisClockXID = 0;
3552 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3554 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3555 || appData.icsEngineAnalyze) { // [DM]
3556 AnalysisPeriodicEvent(0);
3557 StartAnalysisClock();
3562 StartAnalysisClock ()
3565 XtAppAddTimeOut(appContext, 2000,
3566 (XtTimerCallbackProc) AnalysisClockCallback,
3570 XtIntervalId clockTimerXID = 0;
3573 ClockTimerRunning ()
3575 return clockTimerXID != 0;
3581 if (clockTimerXID != 0) {
3582 XtRemoveTimeOut(clockTimerXID);
3591 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3598 StartClockTimer (long millisec)
3601 XtAppAddTimeOut(appContext, millisec,
3602 (XtTimerCallbackProc) ClockTimerCallback,
3607 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
3611 Widget w = (Widget) opt->handle;
3613 /* check for low time warning */
3614 Pixel foregroundOrWarningColor = timerForegroundPixel;
3617 appData.lowTimeWarning &&
3618 (timer / 1000) < appData.icsAlarmTime)
3619 foregroundOrWarningColor = lowTimeWarningColor;
3621 if (appData.clockMode) {
3622 snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
3623 XtSetArg(args[0], XtNlabel, buf);
3625 snprintf(buf, MSG_SIZ, "%s ", color);
3626 XtSetArg(args[0], XtNlabel, buf);
3631 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3632 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3634 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3635 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3638 XtSetValues(w, args, 3);
3641 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3644 SetClockIcon (int color)
3647 Pixmap pm = *clockIcons[color];
3648 if (iconPixmap != pm) {
3650 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3651 XtSetValues(shellWidget, args, 1);
3656 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3658 InputSource *is = (InputSource *) closure;
3663 if (is->lineByLine) {
3664 count = read(is->fd, is->unused,
3665 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3667 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3670 is->unused += count;
3672 while (p < is->unused) {
3673 q = memchr(p, '\n', is->unused - p);
3674 if (q == NULL) break;
3676 (is->func)(is, is->closure, p, q - p, 0);
3680 while (p < is->unused) {
3685 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3690 (is->func)(is, is->closure, is->buf, count, error);
3695 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3698 ChildProc *cp = (ChildProc *) pr;
3700 is = (InputSource *) calloc(1, sizeof(InputSource));
3701 is->lineByLine = lineByLine;
3705 is->fd = fileno(stdin);
3707 is->kind = cp->kind;
3708 is->fd = cp->fdFrom;
3711 is->unused = is->buf;
3714 is->xid = XtAppAddInput(appContext, is->fd,
3715 (XtPointer) (XtInputReadMask),
3716 (XtInputCallbackProc) DoInputCallback,
3718 is->closure = closure;
3719 return (InputSourceRef) is;
3723 RemoveInputSource (InputSourceRef isr)
3725 InputSource *is = (InputSource *) isr;
3727 if (is->xid == 0) return;
3728 XtRemoveInput(is->xid);