2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
66 # if HAVE_SYS_SOCKET_H
67 # include <sys/socket.h>
68 # include <netinet/in.h>
70 # else /* not HAVE_SYS_SOCKET_H */
71 # if HAVE_LAN_SOCKET_H
72 # include <lan/socket.h>
74 # include <lan/netdb.h>
75 # else /* not HAVE_LAN_SOCKET_H */
76 # define OMIT_SOCKETS 1
77 # endif /* not HAVE_LAN_SOCKET_H */
78 # endif /* not HAVE_SYS_SOCKET_H */
79 #endif /* !OMIT_SOCKETS */
84 #else /* not STDC_HEADERS */
85 extern char *getenv();
88 # else /* not HAVE_STRING_H */
90 # endif /* not HAVE_STRING_H */
91 #endif /* not STDC_HEADERS */
94 # include <sys/fcntl.h>
95 #else /* not HAVE_SYS_FCNTL_H */
98 # endif /* HAVE_FCNTL_H */
99 #endif /* not HAVE_SYS_FCNTL_H */
101 #if HAVE_SYS_SYSTEMINFO_H
102 # include <sys/systeminfo.h>
103 #endif /* HAVE_SYS_SYSTEMINFO_H */
105 #if TIME_WITH_SYS_TIME
106 # include <sys/time.h>
110 # include <sys/time.h>
121 # include <sys/wait.h>
126 # define NAMLEN(dirent) strlen((dirent)->d_name)
127 # define HAVE_DIR_STRUCT
129 # define dirent direct
130 # define NAMLEN(dirent) (dirent)->d_namlen
132 # include <sys/ndir.h>
133 # define HAVE_DIR_STRUCT
136 # include <sys/dir.h>
137 # define HAVE_DIR_STRUCT
141 # define HAVE_DIR_STRUCT
149 #include <X11/Intrinsic.h>
150 #include <X11/StringDefs.h>
151 #include <X11/Shell.h>
152 #include <X11/cursorfont.h>
153 #include <X11/Xatom.h>
154 #include <X11/Xmu/Atoms.h>
156 #include <X11/Xaw3d/Dialog.h>
157 #include <X11/Xaw3d/Form.h>
158 #include <X11/Xaw3d/List.h>
159 #include <X11/Xaw3d/Label.h>
160 #include <X11/Xaw3d/SimpleMenu.h>
161 #include <X11/Xaw3d/SmeBSB.h>
162 #include <X11/Xaw3d/SmeLine.h>
163 #include <X11/Xaw3d/Box.h>
164 #include <X11/Xaw3d/MenuButton.h>
165 #include <X11/Xaw3d/Text.h>
166 #include <X11/Xaw3d/AsciiText.h>
168 #include <X11/Xaw/Dialog.h>
169 #include <X11/Xaw/Form.h>
170 #include <X11/Xaw/List.h>
171 #include <X11/Xaw/Label.h>
172 #include <X11/Xaw/SimpleMenu.h>
173 #include <X11/Xaw/SmeBSB.h>
174 #include <X11/Xaw/SmeLine.h>
175 #include <X11/Xaw/Box.h>
176 #include <X11/Xaw/MenuButton.h>
177 #include <X11/Xaw/Text.h>
178 #include <X11/Xaw/AsciiText.h>
181 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
186 #include "pixmaps/pixmaps.h"
187 #define IMAGE_EXT "xpm"
189 #define IMAGE_EXT "xim"
190 #include "bitmaps/bitmaps.h"
193 #include "bitmaps/icon_white.bm"
194 #include "bitmaps/icon_black.bm"
195 #include "bitmaps/checkmark.bm"
197 #include "frontend.h"
199 #include "backendz.h"
203 #include "xgamelist.h"
204 #include "xhistory.h"
205 #include "xedittags.h"
209 #include "engineoutput.h"
218 #define usleep(t) _sleep2(((t)+500)/1000)
222 # define _(s) gettext (s)
223 # define N_(s) gettext_noop (s)
229 int main P((int argc, char **argv));
230 RETSIGTYPE CmailSigHandler P((int sig));
231 RETSIGTYPE IntSigHandler P((int sig));
232 RETSIGTYPE TermSizeSigHandler P((int sig));
233 static void CreateGCs P((int redo));
234 static void CreateAnyPieces P((void));
235 void CreateXIMPieces P((void));
236 void CreateXPMPieces P((void));
237 void CreateXPMBoard P((char *s, int n));
238 void CreatePieces P((void));
239 Widget CreateMenuBar P((Menu *mb, int boardWidth));
241 char *InsertPxlSize P((char *pattern, int targetPxlSize));
242 XFontSet CreateFontSet P((char *base_fnt_lst));
244 char *FindFont P((char *pattern, int targetPxlSize));
246 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
247 u_int wreq, u_int hreq));
248 void CreateGrid P((void));
249 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
250 void DelayedDrag P((void));
251 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
252 void HandlePV P((Widget w, XEvent * event,
253 String * params, Cardinal * nParams));
254 void DrawPositionProc P((Widget w, XEvent *event,
255 String *prms, Cardinal *nprms));
256 void CommentClick P((Widget w, XEvent * event,
257 String * params, Cardinal * nParams));
258 void ICSInputBoxPopUp P((void));
259 void FileNamePopUp P((char *label, char *def, char *filter,
260 FileProc proc, char *openMode));
261 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
262 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
263 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
264 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
265 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
266 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
267 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
268 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
269 Boolean TempBackwardActive = False;
270 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
271 void DisplayMove P((int moveNumber));
272 void ICSInitScript P((void));
273 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
274 void update_ics_width P(());
275 int get_term_width P(());
276 int CopyMemoProc P(());
277 void SetupDropMenu P((void));
280 * XBoard depends on Xt R4 or higher
282 int xtVersion = XtSpecificationRelease;
287 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
288 highlightSquareColor, premoveHighlightColor, dialogColor, buttonColor;
289 Pixel lowTimeWarningColor;
290 GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
291 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
293 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
294 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
295 Option *optList; // contains all widgets of main window
296 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
298 XFontSet fontSet, clockFontSet;
301 XFontStruct *clockFontStruct;
303 Font coordFontID, countFontID;
304 XFontStruct *coordFontStruct, *countFontStruct;
305 XtAppContext appContext;
310 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
312 Position commentX = -1, commentY = -1;
313 Dimension commentW, commentH;
314 typedef unsigned int BoardSize;
316 Boolean chessProgram;
318 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
319 int smallLayout = 0, tinyLayout = 0,
320 marginW, marginH, // [HGM] for run-time resizing
321 fromX = -1, fromY = -1, toX, toY, commentUp = False,
322 errorExitStatus = -1, defaultLineGap;
323 Dimension textHeight;
324 Pixel timerForegroundPixel, timerBackgroundPixel;
325 Pixel buttonForegroundPixel, buttonBackgroundPixel;
326 char *chessDir, *programName, *programVersion;
327 Boolean alwaysOnTop = False;
328 char *icsTextMenuString;
330 char *firstChessProgramNames;
331 char *secondChessProgramNames;
333 WindowPlacement wpMain;
334 WindowPlacement wpConsole;
335 WindowPlacement wpComment;
336 WindowPlacement wpMoveHistory;
337 WindowPlacement wpEvalGraph;
338 WindowPlacement wpEngineOutput;
339 WindowPlacement wpGameList;
340 WindowPlacement wpTags;
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)
845 XWindowAttributes winAt;
852 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
853 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
854 wp->x = rx - winAt.x;
855 wp->y = ry - winAt.y;
856 wp->height = winAt.height;
857 wp->width = winAt.width;
858 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
863 { // wrapper to shield use of window handles from back-end (make addressible by number?)
864 // In XBoard this will have to wait until awareness of window parameters is implemented
865 GetActualPlacement(shellWidget, &wpMain);
866 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
867 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
868 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
869 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
870 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
871 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
875 PrintCommPortSettings (FILE *f, char *name)
876 { // This option does not exist in XBoard
880 EnsureOnScreen (int *x, int *y, int minX, int minY)
887 { // [HGM] args: allows testing if main window is realized from back-end
888 return xBoardWindow != 0;
893 extern Option dualOptions[];
895 Window tmp = xBoardWindow;
896 if(!dual) dual = XtWindow(dualOptions[3].handle); // must be first call
897 xBoardWindow = dual; // swap them
902 PopUpStartupDialog ()
903 { // start menu not implemented in XBoard
907 ConvertToLine (int argc, char **argv)
909 static char line[128*1024], buf[1024];
913 for(i=1; i<argc; i++)
915 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
916 && argv[i][0] != '{' )
917 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
919 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
920 strncat(line, buf, 128*1024 - strlen(line) - 1 );
923 line[strlen(line)-1] = NULLCHAR;
927 //--------------------------------------------------------------------------------------------
929 #define BoardSize int
931 InitDrawingSizes (BoardSize boardSize, int flags)
932 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
933 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
935 XtGeometryResult gres;
937 static Dimension oldWidth, oldHeight;
938 static VariantClass oldVariant;
939 static int oldMono = -1;
941 if(!formWidget) return;
943 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
944 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
945 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
947 if(boardWidth != oldWidth || boardHeight != oldHeight) { // do resizing stuff only if size actually changed
949 oldWidth = boardWidth; oldHeight = boardHeight;
953 * Inhibit shell resizing.
955 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW ;
956 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
957 shellArgs[4].value = shellArgs[2].value = w;
958 shellArgs[5].value = shellArgs[3].value = h;
959 XtSetValues(shellWidget, &shellArgs[0], 6);
961 XSync(xDisplay, False);
965 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
968 if(gameInfo.variant != oldVariant) { // and only if variant changed
973 for(p=0; p<=(int)WhiteKing; p++)
974 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
975 if(gameInfo.variant == VariantShogi) {
976 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
977 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
978 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
979 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
980 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
983 if(gameInfo.variant == VariantGothic) {
984 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
987 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
988 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
989 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
992 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
993 for(p=0; p<=(int)WhiteKing; p++)
994 ximMaskPm[p] = ximMaskPm2[p]; // defaults
995 if(gameInfo.variant == VariantShogi) {
996 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
997 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
998 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
999 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1000 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1003 if(gameInfo.variant == VariantGothic) {
1004 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1007 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1008 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1009 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1014 for(i=0; i<2; i++) {
1016 for(p=0; p<=(int)WhiteKing; p++)
1017 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1018 if(gameInfo.variant == VariantShogi) {
1019 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1020 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1021 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1022 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1023 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1026 if(gameInfo.variant == VariantGothic) {
1027 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1030 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1031 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1032 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1036 oldMono = -10; // kludge to force recreation of animation masks
1037 oldVariant = gameInfo.variant;
1040 if(appData.monoMode != oldMono)
1043 oldMono = appData.monoMode;
1047 MakeOneColor (char *name, Pixel *color)
1049 XrmValue vFrom, vTo;
1050 if (!appData.monoMode) {
1051 vFrom.addr = (caddr_t) name;
1052 vFrom.size = strlen(name);
1053 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1054 if (vTo.addr == NULL) {
1055 appData.monoMode = True;
1058 *color = *(Pixel *) vTo.addr;
1066 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1067 int forceMono = False;
1069 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1070 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1071 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1072 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1073 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1074 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1075 if (appData.lowTimeWarning)
1076 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
1077 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1078 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1085 { // [HGM] taken out of main
1087 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1088 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1089 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1091 if (appData.bitmapDirectory[0] != NULLCHAR) {
1095 CreateXPMBoard(appData.liteBackTextureFile, 1);
1096 CreateXPMBoard(appData.darkBackTextureFile, 0);
1100 /* Create regular pieces */
1101 if (!useImages) CreatePieces();
1106 InitDrawingParams ()
1108 MakeColors(); CreateGCs(True);
1113 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
1114 { // determine what fonts to use, and create them
1115 XrmValue vFrom, vTo;
1118 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1119 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1120 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1121 appData.font = fontTable[MESSAGE_FONT][squareSize];
1122 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1123 appData.coordFont = fontTable[COORD_FONT][squareSize];
1126 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1127 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1128 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1129 fontSet = CreateFontSet(appData.font);
1130 clockFontSet = CreateFontSet(appData.clockFont);
1132 /* For the coordFont, use the 0th font of the fontset. */
1133 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1134 XFontStruct **font_struct_list;
1135 XFontSetExtents *fontSize;
1136 char **font_name_list;
1137 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1138 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1139 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1140 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1141 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1144 appData.font = FindFont(appData.font, fontPxlSize);
1145 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1146 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1147 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1148 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1149 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1150 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1151 // textHeight in !NLS mode!
1153 countFontID = coordFontID; // [HGM] holdings
1154 countFontStruct = coordFontStruct;
1156 xdb = XtDatabase(xDisplay);
1158 XrmPutLineResource(&xdb, "*international: True");
1159 vTo.size = sizeof(XFontSet);
1160 vTo.addr = (XtPointer) &fontSet;
1161 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1163 XrmPutStringResource(&xdb, "*font", appData.font);
1168 main (int argc, char **argv)
1170 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1171 XSetWindowAttributes window_attributes;
1173 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1174 XtGeometryResult gres;
1176 int forceMono = False;
1178 srandom(time(0)); // [HGM] book: make random truly random
1180 setbuf(stdout, NULL);
1181 setbuf(stderr, NULL);
1184 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1185 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1189 programName = strrchr(argv[0], '/');
1190 if (programName == NULL)
1191 programName = argv[0];
1196 bindtextdomain(PACKAGE, LOCALEDIR);
1197 textdomain(PACKAGE);
1200 appData.boardSize = "";
1201 InitAppData(ConvertToLine(argc, argv));
1203 if (p == NULL) p = "/tmp";
1204 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1205 gameCopyFilename = (char*) malloc(i);
1206 gamePasteFilename = (char*) malloc(i);
1207 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1208 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1210 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1211 static char buf[MSG_SIZ];
1212 EscapeExpand(buf, appData.firstInitString);
1213 appData.firstInitString = strdup(buf);
1214 EscapeExpand(buf, appData.secondInitString);
1215 appData.secondInitString = strdup(buf);
1216 EscapeExpand(buf, appData.firstComputerString);
1217 appData.firstComputerString = strdup(buf);
1218 EscapeExpand(buf, appData.secondComputerString);
1219 appData.secondComputerString = strdup(buf);
1222 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1225 if (chdir(chessDir) != 0) {
1226 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1232 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1233 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1234 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1235 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1238 setbuf(debugFP, NULL);
1241 /* [HGM,HR] make sure board size is acceptable */
1242 if(appData.NrFiles > BOARD_FILES ||
1243 appData.NrRanks > BOARD_RANKS )
1244 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1247 /* This feature does not work; animation needs a rewrite */
1248 appData.highlightDragging = FALSE;
1252 gameInfo.variant = StringToVariant(appData.variant);
1253 InitPosition(FALSE);
1256 XtAppInitialize(&appContext, "XBoard", shellOptions,
1257 XtNumber(shellOptions),
1258 &argc, argv, xboardResources, NULL, 0);
1260 XtSetLanguageProc(NULL, NULL, NULL);
1261 if (appData.debugMode) {
1262 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1266 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1267 clientResources, XtNumber(clientResources),
1270 xDisplay = XtDisplay(shellWidget);
1271 xScreen = DefaultScreen(xDisplay);
1272 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1275 * determine size, based on supplied or remembered -size, or screen size
1277 if (isdigit(appData.boardSize[0])) {
1278 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1279 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1280 &fontPxlSize, &smallLayout, &tinyLayout);
1282 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1283 programName, appData.boardSize);
1287 /* Find some defaults; use the nearest known size */
1288 SizeDefaults *szd, *nearest;
1289 int distance = 99999;
1290 nearest = szd = sizeDefaults;
1291 while (szd->name != NULL) {
1292 if (abs(szd->squareSize - squareSize) < distance) {
1294 distance = abs(szd->squareSize - squareSize);
1295 if (distance == 0) break;
1299 if (i < 2) lineGap = nearest->lineGap;
1300 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1301 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1302 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1303 if (i < 6) smallLayout = nearest->smallLayout;
1304 if (i < 7) tinyLayout = nearest->tinyLayout;
1307 SizeDefaults *szd = sizeDefaults;
1308 if (*appData.boardSize == NULLCHAR) {
1309 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1310 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1313 if (szd->name == NULL) szd--;
1314 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1316 while (szd->name != NULL &&
1317 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1318 if (szd->name == NULL) {
1319 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1320 programName, appData.boardSize);
1324 squareSize = szd->squareSize;
1325 lineGap = szd->lineGap;
1326 clockFontPxlSize = szd->clockFontPxlSize;
1327 coordFontPxlSize = szd->coordFontPxlSize;
1328 fontPxlSize = szd->fontPxlSize;
1329 smallLayout = szd->smallLayout;
1330 tinyLayout = szd->tinyLayout;
1331 // [HGM] font: use defaults from settings file if available and not overruled
1334 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1335 if (strlen(appData.pixmapDirectory) > 0) {
1336 p = ExpandPathName(appData.pixmapDirectory);
1338 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1339 appData.pixmapDirectory);
1342 if (appData.debugMode) {
1343 fprintf(stderr, _("\
1344 XBoard square size (hint): %d\n\
1345 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1347 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1348 if (appData.debugMode) {
1349 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1352 defaultLineGap = lineGap;
1353 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1355 /* [HR] height treated separately (hacked) */
1356 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1357 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1360 * Determine what fonts to use.
1362 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1365 * Detect if there are not enough colors available and adapt.
1367 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1368 appData.monoMode = True;
1371 forceMono = MakeColors();
1374 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1376 appData.monoMode = True;
1379 if (appData.monoMode && appData.debugMode) {
1380 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1381 (unsigned long) XWhitePixel(xDisplay, xScreen),
1382 (unsigned long) XBlackPixel(xDisplay, xScreen));
1385 ParseIcsTextColors();
1387 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1393 layoutName = "tinyLayout";
1394 } else if (smallLayout) {
1395 layoutName = "smallLayout";
1397 layoutName = "normalLayout";
1400 optList = BoardPopUp(squareSize, lineGap, (void*)
1406 boardWidget = optList[W_BOARD].handle;
1407 menuBarWidget = optList[W_MENU].handle;
1408 dropMenu = optList[W_DROP].handle;
1409 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1410 formWidget = XtParent(boardWidget);
1411 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1412 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1413 XtGetValues(optList[W_WHITE].handle, args, 2);
1414 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1415 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1416 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1417 XtGetValues(optList[W_PAUSE].handle, args, 2);
1419 AppendEnginesToMenu(appData.recentEngineList);
1421 xBoardWindow = XtWindow(boardWidget);
1423 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1424 // not need to go into InitDrawingSizes().
1427 * Create X checkmark bitmap and initialize option menu checks.
1429 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1430 checkmark_bits, checkmark_width, checkmark_height);
1436 ReadBitmap(&wIconPixmap, "icon_white.bm",
1437 icon_white_bits, icon_white_width, icon_white_height);
1438 ReadBitmap(&bIconPixmap, "icon_black.bm",
1439 icon_black_bits, icon_black_width, icon_black_height);
1440 iconPixmap = wIconPixmap;
1442 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1443 XtSetValues(shellWidget, args, i);
1446 * Create a cursor for the board widget.
1448 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1449 XChangeWindowAttributes(xDisplay, xBoardWindow,
1450 CWCursor, &window_attributes);
1453 * Inhibit shell resizing.
1455 shellArgs[0].value = (XtArgVal) &w;
1456 shellArgs[1].value = (XtArgVal) &h;
1457 XtGetValues(shellWidget, shellArgs, 2);
1458 shellArgs[4].value = shellArgs[2].value = w;
1459 shellArgs[5].value = shellArgs[3].value = h;
1460 XtSetValues(shellWidget, &shellArgs[2], 4);
1461 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1462 marginH = h - boardHeight;
1464 CatchDeleteWindow(shellWidget, "QuitProc");
1470 if (appData.animate || appData.animateDragging)
1473 XtAugmentTranslations(formWidget,
1474 XtParseTranslationTable(globalTranslations));
1476 XtAddEventHandler(formWidget, KeyPressMask, False,
1477 (XtEventHandler) MoveTypeInProc, NULL);
1478 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1479 (XtEventHandler) EventProc, NULL);
1481 /* [AS] Restore layout */
1482 if( wpMoveHistory.visible ) {
1486 if( wpEvalGraph.visible )
1491 if( wpEngineOutput.visible ) {
1492 EngineOutputPopUp();
1497 if (errorExitStatus == -1) {
1498 if (appData.icsActive) {
1499 /* We now wait until we see "login:" from the ICS before
1500 sending the logon script (problems with timestamp otherwise) */
1501 /*ICSInitScript();*/
1502 if (appData.icsInputBox) ICSInputBoxPopUp();
1506 signal(SIGWINCH, TermSizeSigHandler);
1508 signal(SIGINT, IntSigHandler);
1509 signal(SIGTERM, IntSigHandler);
1510 if (*appData.cmailGameName != NULLCHAR) {
1511 signal(SIGUSR1, CmailSigHandler);
1515 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1517 // XtSetKeyboardFocus(shellWidget, formWidget);
1518 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1520 XtAppMainLoop(appContext);
1521 if (appData.debugMode) fclose(debugFP); // [DM] debug
1526 TermSizeSigHandler (int sig)
1532 IntSigHandler (int sig)
1538 CmailSigHandler (int sig)
1543 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1545 /* Activate call-back function CmailSigHandlerCallBack() */
1546 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1548 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1552 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1555 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1557 /**** end signal code ****/
1560 #define Abs(n) ((n)<0 ? -(n) : (n))
1564 InsertPxlSize (char *pattern, int targetPxlSize)
1566 char *base_fnt_lst, strInt[12], *p, *q;
1567 int alternatives, i, len, strIntLen;
1570 * Replace the "*" (if present) in the pixel-size slot of each
1571 * alternative with the targetPxlSize.
1575 while ((p = strchr(p, ',')) != NULL) {
1579 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1580 strIntLen = strlen(strInt);
1581 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1585 while (alternatives--) {
1586 char *comma = strchr(p, ',');
1587 for (i=0; i<14; i++) {
1588 char *hyphen = strchr(p, '-');
1590 if (comma && hyphen > comma) break;
1591 len = hyphen + 1 - p;
1592 if (i == 7 && *p == '*' && len == 2) {
1594 memcpy(q, strInt, strIntLen);
1604 len = comma + 1 - p;
1611 return base_fnt_lst;
1615 CreateFontSet (char *base_fnt_lst)
1618 char **missing_list;
1622 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1623 &missing_list, &missing_count, &def_string);
1624 if (appData.debugMode) {
1626 XFontStruct **font_struct_list;
1627 char **font_name_list;
1628 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1630 fprintf(debugFP, " got list %s, locale %s\n",
1631 XBaseFontNameListOfFontSet(fntSet),
1632 XLocaleOfFontSet(fntSet));
1633 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1634 for (i = 0; i < count; i++) {
1635 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1638 for (i = 0; i < missing_count; i++) {
1639 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1642 if (fntSet == NULL) {
1643 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1648 #else // not ENABLE_NLS
1650 * Find a font that matches "pattern" that is as close as
1651 * possible to the targetPxlSize. Prefer fonts that are k
1652 * pixels smaller to fonts that are k pixels larger. The
1653 * pattern must be in the X Consortium standard format,
1654 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1655 * The return value should be freed with XtFree when no
1659 FindFont (char *pattern, int targetPxlSize)
1661 char **fonts, *p, *best, *scalable, *scalableTail;
1662 int i, j, nfonts, minerr, err, pxlSize;
1664 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1666 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1667 programName, pattern);
1674 for (i=0; i<nfonts; i++) {
1677 if (*p != '-') continue;
1679 if (*p == NULLCHAR) break;
1680 if (*p++ == '-') j++;
1682 if (j < 7) continue;
1685 scalable = fonts[i];
1688 err = pxlSize - targetPxlSize;
1689 if (Abs(err) < Abs(minerr) ||
1690 (minerr > 0 && err < 0 && -err == minerr)) {
1696 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1697 /* If the error is too big and there is a scalable font,
1698 use the scalable font. */
1699 int headlen = scalableTail - scalable;
1700 p = (char *) XtMalloc(strlen(scalable) + 10);
1701 while (isdigit(*scalableTail)) scalableTail++;
1702 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1704 p = (char *) XtMalloc(strlen(best) + 2);
1705 safeStrCpy(p, best, strlen(best)+1 );
1707 if (appData.debugMode) {
1708 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1709 pattern, targetPxlSize, p);
1711 XFreeFontNames(fonts);
1718 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
1719 // must be called before all non-first callse to CreateGCs()
1720 XtReleaseGC(shellWidget, highlineGC);
1721 XtReleaseGC(shellWidget, lightSquareGC);
1722 XtReleaseGC(shellWidget, darkSquareGC);
1723 XtReleaseGC(shellWidget, lineGC);
1724 if (appData.monoMode) {
1725 if (DefaultDepth(xDisplay, xScreen) == 1) {
1726 XtReleaseGC(shellWidget, wbPieceGC);
1728 XtReleaseGC(shellWidget, bwPieceGC);
1731 XtReleaseGC(shellWidget, prelineGC);
1732 XtReleaseGC(shellWidget, wdPieceGC);
1733 XtReleaseGC(shellWidget, wlPieceGC);
1734 XtReleaseGC(shellWidget, bdPieceGC);
1735 XtReleaseGC(shellWidget, blPieceGC);
1740 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
1742 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1743 | GCBackground | GCFunction | GCPlaneMask;
1744 gc_values->foreground = foreground;
1745 gc_values->background = background;
1746 return XtGetGC(shellWidget, value_mask, gc_values);
1750 CreateGCs (int redo)
1752 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1753 | GCBackground | GCFunction | GCPlaneMask;
1754 XGCValues gc_values;
1756 Pixel white = XWhitePixel(xDisplay, xScreen);
1757 Pixel black = XBlackPixel(xDisplay, xScreen);
1759 gc_values.plane_mask = AllPlanes;
1760 gc_values.line_width = lineGap;
1761 gc_values.line_style = LineSolid;
1762 gc_values.function = GXcopy;
1765 DeleteGCs(); // called a second time; clean up old GCs first
1766 } else { // [HGM] grid and font GCs created on first call only
1767 coordGC = CreateOneGC(&gc_values, black, white);
1768 XSetFont(xDisplay, coordGC, coordFontID);
1770 // [HGM] make font for holdings counts (white on black)
1771 countGC = CreateOneGC(&gc_values, white, black);
1772 XSetFont(xDisplay, countGC, countFontID);
1774 lineGC = CreateOneGC(&gc_values, black, black);
1776 if (appData.monoMode) {
1778 highlineGC = CreateOneGC(&gc_values, white, white);
1779 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
1780 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
1782 if (DefaultDepth(xDisplay, xScreen) == 1) {
1783 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
1784 gc_values.function = GXcopyInverted;
1785 copyInvertedGC = CreateOneGC(&gc_values, black, white);
1786 gc_values.function = GXcopy;
1787 if (XBlackPixel(xDisplay, xScreen) == 1) {
1788 bwPieceGC = darkSquareGC;
1789 wbPieceGC = copyInvertedGC;
1791 bwPieceGC = copyInvertedGC;
1792 wbPieceGC = lightSquareGC;
1797 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
1798 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
1799 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
1800 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
1801 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
1802 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
1803 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
1804 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
1809 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
1817 fp = fopen(filename, "rb");
1819 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
1826 for (y=0; y<h; ++y) {
1827 for (x=0; x<h; ++x) {
1832 XPutPixel(xim, x, y, blackPieceColor);
1834 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1837 XPutPixel(xim, x, y, darkSquareColor);
1839 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1842 XPutPixel(xim, x, y, whitePieceColor);
1844 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1847 XPutPixel(xim, x, y, lightSquareColor);
1849 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1857 /* create Pixmap of piece */
1858 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1860 XPutImage(xDisplay, *dest, lightSquareGC, xim,
1863 /* create Pixmap of clipmask
1864 Note: We assume the white/black pieces have the same
1865 outline, so we make only 6 masks. This is okay
1866 since the XPM clipmask routines do the same. */
1868 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1870 XPutImage(xDisplay, temp, lightSquareGC, xmask,
1873 /* now create the 1-bit version */
1874 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1877 values.foreground = 1;
1878 values.background = 0;
1880 /* Don't use XtGetGC, not read only */
1881 maskGC = XCreateGC(xDisplay, *mask,
1882 GCForeground | GCBackground, &values);
1883 XCopyPlane(xDisplay, temp, *mask, maskGC,
1884 0, 0, squareSize, squareSize, 0, 0, 1);
1885 XFreePixmap(xDisplay, temp);
1890 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
1898 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
1903 /* The XSynchronize calls were copied from CreatePieces.
1904 Not sure if needed, but can't hurt */
1905 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
1908 /* temp needed by loadXIM() */
1909 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1910 0, 0, ss, ss, AllPlanes, XYPixmap);
1912 if (strlen(appData.pixmapDirectory) == 0) {
1916 if (appData.monoMode) {
1917 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
1921 fprintf(stderr, _("\nLoading XIMs...\n"));
1923 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
1924 fprintf(stderr, "%d", piece+1);
1925 for (kind=0; kind<4; kind++) {
1926 fprintf(stderr, ".");
1927 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
1928 ExpandPathName(appData.pixmapDirectory),
1929 piece <= (int) WhiteKing ? "" : "w",
1930 pieceBitmapNames[piece],
1932 ximPieceBitmap[kind][piece] =
1933 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1934 0, 0, ss, ss, AllPlanes, XYPixmap);
1935 if (appData.debugMode)
1936 fprintf(stderr, _("(File:%s:) "), buf);
1937 loadXIM(ximPieceBitmap[kind][piece],
1939 &(xpmPieceBitmap2[kind][piece]),
1940 &(ximMaskPm2[piece]));
1941 if(piece <= (int)WhiteKing)
1942 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
1944 fprintf(stderr," ");
1946 /* Load light and dark squares */
1947 /* If the LSQ and DSQ pieces don't exist, we will
1948 draw them with solid squares. */
1949 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
1950 if (access(buf, 0) != 0) {
1954 fprintf(stderr, _("light square "));
1956 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1957 0, 0, ss, ss, AllPlanes, XYPixmap);
1958 if (appData.debugMode)
1959 fprintf(stderr, _("(File:%s:) "), buf);
1961 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
1962 fprintf(stderr, _("dark square "));
1963 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
1964 ExpandPathName(appData.pixmapDirectory), ss);
1965 if (appData.debugMode)
1966 fprintf(stderr, _("(File:%s:) "), buf);
1968 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1969 0, 0, ss, ss, AllPlanes, XYPixmap);
1970 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
1971 xpmJailSquare = xpmLightSquare;
1973 fprintf(stderr, _("Done.\n"));
1975 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
1978 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
1982 CreateXPMBoard (char *s, int kind)
1986 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
1987 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
1988 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
1994 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
1995 // thisroutine has to be called t free the old piece pixmaps
1997 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
1998 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2000 XFreePixmap(xDisplay, xpmLightSquare);
2001 XFreePixmap(xDisplay, xpmDarkSquare);
2010 u_int ss = squareSize;
2012 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2013 XpmColorSymbol symbols[4];
2014 static int redo = False;
2016 if(redo) FreeXPMPieces(); else redo = 1;
2018 /* The XSynchronize calls were copied from CreatePieces.
2019 Not sure if needed, but can't hurt */
2020 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2022 /* Setup translations so piece colors match square colors */
2023 symbols[0].name = "light_piece";
2024 symbols[0].value = appData.whitePieceColor;
2025 symbols[1].name = "dark_piece";
2026 symbols[1].value = appData.blackPieceColor;
2027 symbols[2].name = "light_square";
2028 symbols[2].value = appData.lightSquareColor;
2029 symbols[3].name = "dark_square";
2030 symbols[3].value = appData.darkSquareColor;
2032 attr.valuemask = XpmColorSymbols;
2033 attr.colorsymbols = symbols;
2034 attr.numsymbols = 4;
2036 if (appData.monoMode) {
2037 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2041 if (strlen(appData.pixmapDirectory) == 0) {
2042 XpmPieces* pieces = builtInXpms;
2045 while (pieces->size != squareSize && pieces->size) pieces++;
2046 if (!pieces->size) {
2047 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2050 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2051 for (kind=0; kind<4; kind++) {
2053 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2054 pieces->xpm[piece][kind],
2055 &(xpmPieceBitmap2[kind][piece]),
2056 NULL, &attr)) != 0) {
2057 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2061 if(piece <= (int) WhiteKing)
2062 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2066 xpmJailSquare = xpmLightSquare;
2070 fprintf(stderr, _("\nLoading XPMs...\n"));
2073 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2074 fprintf(stderr, "%d ", piece+1);
2075 for (kind=0; kind<4; kind++) {
2076 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2077 ExpandPathName(appData.pixmapDirectory),
2078 piece > (int) WhiteKing ? "w" : "",
2079 pieceBitmapNames[piece],
2081 if (appData.debugMode) {
2082 fprintf(stderr, _("(File:%s:) "), buf);
2084 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2085 &(xpmPieceBitmap2[kind][piece]),
2086 NULL, &attr)) != 0) {
2087 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2088 // [HGM] missing: read of unorthodox piece failed; substitute King.
2089 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2090 ExpandPathName(appData.pixmapDirectory),
2092 if (appData.debugMode) {
2093 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2095 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2096 &(xpmPieceBitmap2[kind][piece]),
2100 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2105 if(piece <= (int) WhiteKing)
2106 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2109 /* Load light and dark squares */
2110 /* If the LSQ and DSQ pieces don't exist, we will
2111 draw them with solid squares. */
2112 fprintf(stderr, _("light square "));
2113 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2114 if (access(buf, 0) != 0) {
2118 if (appData.debugMode)
2119 fprintf(stderr, _("(File:%s:) "), buf);
2121 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2122 &xpmLightSquare, NULL, &attr)) != 0) {
2123 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2126 fprintf(stderr, _("dark square "));
2127 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2128 ExpandPathName(appData.pixmapDirectory), ss);
2129 if (appData.debugMode) {
2130 fprintf(stderr, _("(File:%s:) "), buf);
2132 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2133 &xpmDarkSquare, NULL, &attr)) != 0) {
2134 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2138 xpmJailSquare = xpmLightSquare;
2139 fprintf(stderr, _("Done.\n"));
2141 oldVariant = -1; // kludge to force re-makig of animation masks
2142 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2145 #endif /* HAVE_LIBXPM */
2148 /* No built-in bitmaps */
2153 u_int ss = squareSize;
2155 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2158 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2159 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2160 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2161 pieceBitmapNames[piece],
2162 ss, kind == SOLID ? 's' : 'o');
2163 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2164 if(piece <= (int)WhiteKing)
2165 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2169 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2173 /* With built-in bitmaps */
2177 BuiltInBits* bib = builtInBits;
2180 u_int ss = squareSize;
2182 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2185 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2187 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2188 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2189 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2190 pieceBitmapNames[piece],
2191 ss, kind == SOLID ? 's' : 'o');
2192 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2193 bib->bits[kind][piece], ss, ss);
2194 if(piece <= (int)WhiteKing)
2195 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2199 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2205 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2210 char msg[MSG_SIZ], fullname[MSG_SIZ];
2212 if (*appData.bitmapDirectory != NULLCHAR) {
2213 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2214 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2215 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2216 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2217 &w, &h, pm, &x_hot, &y_hot);
2218 fprintf(stderr, "load %s\n", name);
2219 if (errcode != BitmapSuccess) {
2221 case BitmapOpenFailed:
2222 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2224 case BitmapFileInvalid:
2225 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2227 case BitmapNoMemory:
2228 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2232 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2236 fprintf(stderr, _("%s: %s...using built-in\n"),
2238 } else if (w != wreq || h != hreq) {
2240 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2241 programName, fullname, w, h, wreq, hreq);
2247 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2257 if (lineGap == 0) return;
2259 /* [HR] Split this into 2 loops for non-square boards. */
2261 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2262 gridSegments[i].x1 = 0;
2263 gridSegments[i].x2 =
2264 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2265 gridSegments[i].y1 = gridSegments[i].y2
2266 = lineGap / 2 + (i * (squareSize + lineGap));
2269 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2270 gridSegments[j + i].y1 = 0;
2271 gridSegments[j + i].y2 =
2272 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2273 gridSegments[j + i].x1 = gridSegments[j + i].x2
2274 = lineGap / 2 + (j * (squareSize + lineGap));
2279 MarkMenuItem (char *menuRef, int state)
2281 MenuItem *item = MenuNameToItem(menuRef);
2285 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2286 XtSetValues(item->handle, args, 1);
2291 EnableMenuItem (char *menuRef, int state)
2293 MenuItem *item = MenuNameToItem(menuRef);
2295 if(item) XtSetSensitive(item->handle, state);
2299 EnableButtonBar (int state)
2301 XtSetSensitive(optList[W_BUTTON].handle, state);
2306 SetMenuEnables (Enables *enab)
2308 while (enab->name != NULL) {
2309 EnableMenuItem(enab->name, enab->value);
2315 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2316 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2320 if(*nprms == 0) return;
2321 item = MenuNameToItem(prms[0]);
2322 if(item) ((MenuProc *) item->proc) ();
2326 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
2328 MenuProc *proc = (MenuProc *) addr;
2334 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2336 RecentEngineEvent((int) (intptr_t) addr);
2340 AppendMenuItem (char *msg, int n)
2342 CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
2354 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2355 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2356 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2357 dmEnables[i].piece);
2358 XtSetSensitive(entry, p != NULL || !appData.testLegality
2359 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2360 && !appData.icsActive));
2362 while (p && *p++ == dmEnables[i].piece) count++;
2363 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2365 XtSetArg(args[j], XtNlabel, label); j++;
2366 XtSetValues(entry, args, j);
2372 do_flash_delay (unsigned long msec)
2378 DrawBorder (int x, int y, int type)
2382 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
2384 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
2385 squareSize+lineGap, squareSize+lineGap);
2389 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
2391 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
2392 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
2394 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
2395 if(textureW[kind] < W*squareSize)
2396 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
2398 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
2399 if(textureH[kind] < H*squareSize)
2400 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
2402 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
2407 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
2408 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
2410 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
2411 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
2412 squareSize, squareSize, x*fac, y*fac);
2414 if (useImages && useImageSqs) {
2418 pm = xpmLightSquare;
2423 case 2: /* neutral */
2425 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
2428 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
2429 squareSize, squareSize, x*fac, y*fac);
2439 case 2: /* neutral */
2444 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
2449 I split out the routines to draw a piece so that I could
2450 make a generic flash routine.
2453 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2455 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2456 switch (square_color) {
2458 case 2: /* neutral */
2460 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2461 ? *pieceToOutline(piece)
2462 : *pieceToSolid(piece),
2463 dest, bwPieceGC, 0, 0,
2464 squareSize, squareSize, x, y);
2467 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2468 ? *pieceToSolid(piece)
2469 : *pieceToOutline(piece),
2470 dest, wbPieceGC, 0, 0,
2471 squareSize, squareSize, x, y);
2477 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2479 switch (square_color) {
2481 case 2: /* neutral */
2483 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2484 ? *pieceToOutline(piece)
2485 : *pieceToSolid(piece),
2486 dest, bwPieceGC, 0, 0,
2487 squareSize, squareSize, x, y, 1);
2490 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2491 ? *pieceToSolid(piece)
2492 : *pieceToOutline(piece),
2493 dest, wbPieceGC, 0, 0,
2494 squareSize, squareSize, x, y, 1);
2500 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2502 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
2503 switch (square_color) {
2505 XCopyPlane(xDisplay, *pieceToSolid(piece),
2506 dest, (int) piece < (int) BlackPawn
2507 ? wlPieceGC : blPieceGC, 0, 0,
2508 squareSize, squareSize, x, y, 1);
2511 XCopyPlane(xDisplay, *pieceToSolid(piece),
2512 dest, (int) piece < (int) BlackPawn
2513 ? wdPieceGC : bdPieceGC, 0, 0,
2514 squareSize, squareSize, x, y, 1);
2516 case 2: /* neutral */
2518 break; // should never contain pieces
2523 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2525 int kind, p = piece;
2527 switch (square_color) {
2529 case 2: /* neutral */
2531 if ((int)piece < (int) BlackPawn) {
2539 if ((int)piece < (int) BlackPawn) {
2547 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2548 if(useTexture & square_color+1) {
2549 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2550 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
2551 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
2552 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
2553 XSetClipMask(xDisplay, wlPieceGC, None);
2554 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
2556 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
2557 dest, wlPieceGC, 0, 0,
2558 squareSize, squareSize, x, y);
2561 typedef void (*DrawFunc)();
2566 if (appData.monoMode) {
2567 if (DefaultDepth(xDisplay, xScreen) == 1) {
2568 return monoDrawPiece_1bit;
2570 return monoDrawPiece;
2574 return colorDrawPieceImage;
2576 return colorDrawPiece;
2581 DrawDot (int marker, int x, int y, int r)
2583 if(appData.monoMode) {
2584 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
2585 x, y, r, r, 0, 64*360);
2586 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
2587 x, y, r, r, 0, 64*360);
2589 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
2590 x, y, r, r, 0, 64*360);
2594 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2595 { // basic front-end board-draw function: takes care of everything that can be in square:
2596 // piece, background, coordinate/count, marker dot
2597 int direction, font_ascent, font_descent;
2598 XCharStruct overall;
2601 if (piece == EmptySquare) {
2602 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2604 drawfunc = ChooseDrawFunc();
2605 drawfunc(piece, square_color, x, y, xBoardWindow);
2608 if(align) { // square carries inscription (coord or piece count)
2610 GC hGC = align < 3 ? coordGC : countGC;
2611 // first calculate where it goes
2612 XTextExtents(countFontStruct, string, 1, &direction,
2613 &font_ascent, &font_descent, &overall);
2615 xx += squareSize - overall.width - 2;
2616 yy += squareSize - font_descent - 1;
2617 } else if (align == 2) {
2618 xx += 2, yy += font_ascent + 1;
2619 } else if (align == 3) {
2620 xx += squareSize - overall.width - 2;
2621 yy += font_ascent + 1;
2622 } else if (align == 4) {
2623 xx += 2, yy += font_ascent + 1;
2626 if (appData.monoMode) {
2627 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2629 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2633 if(marker) { // print fat marker dot, if requested
2634 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2639 FlashDelay (int flash_delay)
2641 XSync(xDisplay, False);
2642 if(flash_delay) do_flash_delay(flash_delay);
2646 Fraction (int x, int start, int stop)
2648 double f = ((double) x - start)/(stop - start);
2649 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2653 static WindowPlacement wpNew;
2656 CoDrag (Widget sh, WindowPlacement *wp)
2659 int j=0, touch=0, fudge = 2;
2660 GetActualPlacement(sh, wp);
2661 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
2662 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
2663 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2664 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
2665 if(!touch ) return; // only windows that touch co-move
2666 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2667 int heightInc = wpNew.height - wpMain.height;
2668 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2669 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2670 wp->y += fracTop * heightInc;
2671 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2672 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2673 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2674 int widthInc = wpNew.width - wpMain.width;
2675 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2676 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2677 wp->y += fracLeft * widthInc;
2678 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2679 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2681 wp->x += wpNew.x - wpMain.x;
2682 wp->y += wpNew.y - wpMain.y;
2683 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2684 if(touch == 3) wp->y += wpNew.height - wpMain.height;
2685 XtSetArg(args[j], XtNx, wp->x); j++;
2686 XtSetArg(args[j], XtNy, wp->y); j++;
2687 XtSetValues(sh, args, j);
2690 static XtIntervalId delayedDragID = 0;
2695 GetActualPlacement(shellWidget, &wpNew);
2696 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
2697 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
2698 return; // false alarm
2699 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
2700 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
2701 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
2702 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
2704 DrawPosition(True, NULL);
2705 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
2712 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
2714 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
2718 EventProc (Widget widget, caddr_t unused, XEvent *event)
2720 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
2721 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
2724 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
2726 DrawSeekAxis (int x, int y, int xTo, int yTo)
2728 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
2732 DrawSeekBackground (int left, int top, int right, int bottom)
2734 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
2738 DrawSeekText (char *buf, int x, int y)
2740 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
2744 DrawSeekDot (int x, int y, int colorNr)
2746 int square = colorNr & 0x80;
2749 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
2751 XFillRectangle(xDisplay, xBoardWindow, color,
2752 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
2754 XFillArc(xDisplay, xBoardWindow, color,
2755 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
2761 XDrawSegments(xDisplay, xBoardWindow, lineGC,
2762 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
2767 * event handler for redrawing the board
2770 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2772 DrawPosition(True, NULL);
2777 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
2778 { // [HGM] pv: walk PV
2779 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
2782 static int savedIndex; /* gross that this is global */
2785 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
2788 XawTextPosition index, dummy;
2791 XawTextGetSelectionPos(w, &index, &dummy);
2792 XtSetArg(arg, XtNstring, &val);
2793 XtGetValues(w, &arg, 1);
2794 ReplaceComment(savedIndex, val);
2795 if(savedIndex != currentMove) ToNrEvent(savedIndex);
2796 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
2800 EditCommentPopUp (int index, char *title, char *text)
2803 if (text == NULL) text = "";
2804 NewCommentPopup(title, text, index);
2808 CommentPopUp (char *title, char *text)
2810 savedIndex = currentMove; // [HGM] vari
2811 NewCommentPopup(title, text, currentMove);
2817 PopDown(CommentDlg);
2820 static char *openName;
2826 (void) (*fileProc)(openFP, 0, openName);
2830 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
2832 fileProc = proc; /* I can't see a way not */
2833 fileOpenMode = openMode; /* to use globals here */
2834 { // [HGM] use file-selector dialog stolen from Ghostview
2835 int index; // this is not supported yet
2836 Browse(BoardWindow, label, (def[0] ? def : NULL), filter, False, openMode, &openName, &openFP);
2841 /* Disable all user input other than deleting the window */
2842 static int frozen = 0;
2848 /* Grab by a widget that doesn't accept input */
2849 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
2853 /* Undo a FreezeUI */
2857 if (!frozen) return;
2858 XtRemoveGrab(optList[W_MESSG].handle);
2866 static int oldPausing = FALSE;
2867 static GameMode oldmode = (GameMode) -1;
2870 if (!boardWidget || !XtIsRealized(boardWidget)) return;
2872 if (pausing != oldPausing) {
2873 oldPausing = pausing;
2874 MarkMenuItem("Mode.Pause", pausing);
2876 if (appData.showButtonBar) {
2877 /* Always toggle, don't set. Previous code messes up when
2878 invoked while the button is pressed, as releasing it
2879 toggles the state again. */
2882 XtSetArg(args[0], XtNbackground, &oldbg);
2883 XtSetArg(args[1], XtNforeground, &oldfg);
2884 XtGetValues(optList[W_PAUSE].handle,
2886 XtSetArg(args[0], XtNbackground, oldfg);
2887 XtSetArg(args[1], XtNforeground, oldbg);
2889 XtSetValues(optList[W_PAUSE].handle, args, 2);
2893 wname = ModeToWidgetName(oldmode);
2894 if (wname != NULL) {
2895 MarkMenuItem(wname, False);
2897 wname = ModeToWidgetName(gameMode);
2898 if (wname != NULL) {
2899 MarkMenuItem(wname, True);
2902 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
2904 /* Maybe all the enables should be handled here, not just this one */
2905 EnableMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
2910 * Button/menu procedures
2913 /* this variable is shared between CopyPositionProc and SendPositionSelection */
2914 char *selected_fen_position=NULL;
2917 SendPositionSelection (Widget w, Atom *selection, Atom *target,
2918 Atom *type_return, XtPointer *value_return,
2919 unsigned long *length_return, int *format_return)
2921 char *selection_tmp;
2923 // if (!selected_fen_position) return False; /* should never happen */
2924 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
2925 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
2926 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
2929 if (f == NULL) return False;
2933 selection_tmp = XtMalloc(len + 1);
2934 count = fread(selection_tmp, 1, len, f);
2937 XtFree(selection_tmp);
2940 selection_tmp[len] = NULLCHAR;
2942 /* note: since no XtSelectionDoneProc was registered, Xt will
2943 * automatically call XtFree on the value returned. So have to
2944 * make a copy of it allocated with XtMalloc */
2945 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
2946 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
2949 *value_return=selection_tmp;
2950 *length_return=strlen(selection_tmp);
2951 *type_return=*target;
2952 *format_return = 8; /* bits per byte */
2954 } else if (*target == XA_TARGETS(xDisplay)) {
2955 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
2956 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
2957 targets_tmp[1] = XA_STRING;
2958 *value_return = targets_tmp;
2959 *type_return = XA_ATOM;
2962 // This code leads to a read of value_return out of bounds on 64-bit systems.
2963 // Other code which I have seen always sets *format_return to 32 independent of
2964 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
2965 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
2966 *format_return = 8 * sizeof(Atom);
2967 if (*format_return > 32) {
2968 *length_return *= *format_return / 32;
2969 *format_return = 32;
2972 *format_return = 32;
2980 /* note: when called from menu all parameters are NULL, so no clue what the
2981 * Widget which was clicked on was, or what the click event was
2984 CopySomething (char *src)
2986 selected_fen_position = src;
2988 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
2989 * have a notion of a position that is selected but not copied.
2990 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
2992 XtOwnSelection(menuBarWidget, XA_PRIMARY,
2994 SendPositionSelection,
2995 NULL/* lose_ownership_proc */ ,
2996 NULL/* transfer_done_proc */);
2997 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
2999 SendPositionSelection,
3000 NULL/* lose_ownership_proc */ ,
3001 NULL/* transfer_done_proc */);
3004 /* function called when the data to Paste is ready */
3006 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3007 Atom *type, XtPointer value, unsigned long *len, int *format)
3010 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3011 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3012 EditPositionPasteFEN(fenstr);
3016 /* called when Paste Position button is pressed,
3017 * all parameters will be NULL */
3019 PastePositionProc ()
3021 XtGetSelectionValue(menuBarWidget,
3022 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3023 /* (XtSelectionCallbackProc) */ PastePositionCB,
3024 NULL, /* client_data passed to PastePositionCB */
3026 /* better to use the time field from the event that triggered the
3027 * call to this function, but that isn't trivial to get
3034 /* note: when called from menu all parameters are NULL, so no clue what the
3035 * Widget which was clicked on was, or what the click event was
3037 /* function called when the data to Paste is ready */
3039 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3040 Atom *type, XtPointer value, unsigned long *len, int *format)
3043 if (value == NULL || *len == 0) {
3044 return; /* nothing had been selected to copy */
3046 f = fopen(gamePasteFilename, "w");
3048 DisplayError(_("Can't open temp file"), errno);
3051 fwrite(value, 1, *len, f);
3054 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3057 /* called when Paste Game button is pressed,
3058 * all parameters will be NULL */
3062 XtGetSelectionValue(menuBarWidget,
3063 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3064 /* (XtSelectionCallbackProc) */ PasteGameCB,
3065 NULL, /* client_data passed to PasteGameCB */
3067 /* better to use the time field from the event that triggered the
3068 * call to this function, but that isn't trivial to get
3077 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3084 { // bassic primitive for determining if modifier keys are pressed
3085 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3088 XQueryKeymap(xDisplay,keys);
3089 for(i=0; i<6; i++) {
3091 j = XKeysymToKeycode(xDisplay, codes[i]);
3092 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3098 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3102 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3103 if ( n == 1 && *buf >= 32 // printable
3104 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3105 ) BoxAutoPopUp (buf);
3109 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3110 { // [HGM] input: let up-arrow recall previous line from history
3115 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3116 { // [HGM] input: let down-arrow recall next line from history
3121 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3127 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3129 if (!TempBackwardActive) {
3130 TempBackwardActive = True;
3136 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3138 /* Check to see if triggered by a key release event for a repeating key.
3139 * If so the next queued event will be a key press of the same key at the same time */
3140 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3142 XPeekEvent(xDisplay, &next);
3143 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3144 next.xkey.keycode == event->xkey.keycode)
3148 TempBackwardActive = False;
3152 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3153 { // called as key binding
3156 if (nprms && *nprms > 0)
3160 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3165 SetWindowTitle (char *text, char *title, char *icon)
3169 if (appData.titleInWindow) {
3171 XtSetArg(args[i], XtNlabel, text); i++;
3172 XtSetValues(titleWidget, args, i);
3175 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3176 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3177 XtSetValues(shellWidget, args, i);
3178 XSync(xDisplay, False);
3183 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3189 DisplayIcsInteractionTitle (String message)
3191 if (oldICSInteractionTitle == NULL) {
3192 /* Magic to find the old window title, adapted from vim */
3193 char *wina = getenv("WINDOWID");
3195 Window win = (Window) atoi(wina);
3196 Window root, parent, *children;
3197 unsigned int nchildren;
3198 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3200 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3201 if (!XQueryTree(xDisplay, win, &root, &parent,
3202 &children, &nchildren)) break;
3203 if (children) XFree((void *)children);
3204 if (parent == root || parent == 0) break;
3207 XSetErrorHandler(oldHandler);
3209 if (oldICSInteractionTitle == NULL) {
3210 oldICSInteractionTitle = "xterm";
3213 printf("\033]0;%s\007", message);
3218 XtIntervalId delayedEventTimerXID = 0;
3219 DelayedEventCallback delayedEventCallback = 0;
3224 delayedEventTimerXID = 0;
3225 delayedEventCallback();
3229 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3231 if(delayedEventTimerXID && delayedEventCallback == cb)
3232 // [HGM] alive: replace, rather than add or flush identical event
3233 XtRemoveTimeOut(delayedEventTimerXID);
3234 delayedEventCallback = cb;
3235 delayedEventTimerXID =
3236 XtAppAddTimeOut(appContext, millisec,
3237 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3240 DelayedEventCallback
3243 if (delayedEventTimerXID) {
3244 return delayedEventCallback;
3251 CancelDelayedEvent ()
3253 if (delayedEventTimerXID) {
3254 XtRemoveTimeOut(delayedEventTimerXID);
3255 delayedEventTimerXID = 0;
3259 XtIntervalId loadGameTimerXID = 0;
3262 LoadGameTimerRunning ()
3264 return loadGameTimerXID != 0;
3268 StopLoadGameTimer ()
3270 if (loadGameTimerXID != 0) {
3271 XtRemoveTimeOut(loadGameTimerXID);
3272 loadGameTimerXID = 0;
3280 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3282 loadGameTimerXID = 0;
3287 StartLoadGameTimer (long millisec)
3290 XtAppAddTimeOut(appContext, millisec,
3291 (XtTimerCallbackProc) LoadGameTimerCallback,
3295 XtIntervalId analysisClockXID = 0;
3298 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3300 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3301 || appData.icsEngineAnalyze) { // [DM]
3302 AnalysisPeriodicEvent(0);
3303 StartAnalysisClock();
3308 StartAnalysisClock ()
3311 XtAppAddTimeOut(appContext, 2000,
3312 (XtTimerCallbackProc) AnalysisClockCallback,
3316 XtIntervalId clockTimerXID = 0;
3319 ClockTimerRunning ()
3321 return clockTimerXID != 0;
3327 if (clockTimerXID != 0) {
3328 XtRemoveTimeOut(clockTimerXID);
3337 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3344 StartClockTimer (long millisec)
3347 XtAppAddTimeOut(appContext, millisec,
3348 (XtTimerCallbackProc) ClockTimerCallback,
3353 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
3357 Widget w = (Widget) opt->handle;
3359 /* check for low time warning */
3360 Pixel foregroundOrWarningColor = timerForegroundPixel;
3363 appData.lowTimeWarning &&
3364 (timer / 1000) < appData.icsAlarmTime)
3365 foregroundOrWarningColor = lowTimeWarningColor;
3367 if (appData.clockMode) {
3368 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
3369 XtSetArg(args[0], XtNlabel, buf);
3371 snprintf(buf, MSG_SIZ, "%s ", color);
3372 XtSetArg(args[0], XtNlabel, buf);
3377 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3378 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3380 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3381 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3384 XtSetValues(w, args, 3);
3387 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3390 SetClockIcon (int color)
3393 Pixmap pm = *clockIcons[color];
3394 if (iconPixmap != pm) {
3396 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3397 XtSetValues(shellWidget, args, 1);
3402 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3404 InputSource *is = (InputSource *) closure;
3409 if (is->lineByLine) {
3410 count = read(is->fd, is->unused,
3411 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3413 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3416 is->unused += count;
3418 while (p < is->unused) {
3419 q = memchr(p, '\n', is->unused - p);
3420 if (q == NULL) break;
3422 (is->func)(is, is->closure, p, q - p, 0);
3426 while (p < is->unused) {
3431 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3436 (is->func)(is, is->closure, is->buf, count, error);
3441 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3444 ChildProc *cp = (ChildProc *) pr;
3446 is = (InputSource *) calloc(1, sizeof(InputSource));
3447 is->lineByLine = lineByLine;
3451 is->fd = fileno(stdin);
3453 is->kind = cp->kind;
3454 is->fd = cp->fdFrom;
3457 is->unused = is->buf;
3460 is->xid = XtAppAddInput(appContext, is->fd,
3461 (XtPointer) (XtInputReadMask),
3462 (XtInputCallbackProc) DoInputCallback,
3464 is->closure = closure;
3465 return (InputSourceRef) is;
3469 RemoveInputSource (InputSourceRef isr)
3471 InputSource *is = (InputSource *) isr;
3473 if (is->xid == 0) return;
3474 XtRemoveInput(is->xid);
3478 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
3480 /* Masks for XPM pieces. Black and white pieces can have
3481 different shapes, but in the interest of retaining my
3482 sanity pieces must have the same outline on both light
3483 and dark squares, and all pieces must use the same
3484 background square colors/images. */
3486 static int xpmDone = 0;
3487 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
3488 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
3491 CreateAnimMasks (int pieceDepth)
3497 unsigned long plane;
3500 /* Need a bitmap just to get a GC with right depth */
3501 buf = XCreatePixmap(xDisplay, xBoardWindow,
3503 values.foreground = 1;
3504 values.background = 0;
3505 /* Don't use XtGetGC, not read only */
3506 maskGC = XCreateGC(xDisplay, buf,
3507 GCForeground | GCBackground, &values);
3508 XFreePixmap(xDisplay, buf);
3510 buf = XCreatePixmap(xDisplay, xBoardWindow,
3511 squareSize, squareSize, pieceDepth);
3512 values.foreground = XBlackPixel(xDisplay, xScreen);
3513 values.background = XWhitePixel(xDisplay, xScreen);
3514 bufGC = XCreateGC(xDisplay, buf,
3515 GCForeground | GCBackground, &values);
3517 for (piece = WhitePawn; piece <= BlackKing; piece++) {
3518 /* Begin with empty mask */
3519 if(!xpmDone) // [HGM] pieces: keep using existing
3520 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
3521 squareSize, squareSize, 1);
3522 XSetFunction(xDisplay, maskGC, GXclear);
3523 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
3524 0, 0, squareSize, squareSize);
3526 /* Take a copy of the piece */
3531 XSetFunction(xDisplay, bufGC, GXcopy);
3532 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
3534 0, 0, squareSize, squareSize, 0, 0);
3536 /* XOR the background (light) over the piece */
3537 XSetFunction(xDisplay, bufGC, GXxor);
3539 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
3540 0, 0, squareSize, squareSize, 0, 0);
3542 XSetForeground(xDisplay, bufGC, lightSquareColor);
3543 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
3546 /* We now have an inverted piece image with the background
3547 erased. Construct mask by just selecting all the non-zero
3548 pixels - no need to reconstruct the original image. */
3549 XSetFunction(xDisplay, maskGC, GXor);
3551 /* Might be quicker to download an XImage and create bitmap
3552 data from it rather than this N copies per piece, but it
3553 only takes a fraction of a second and there is a much
3554 longer delay for loading the pieces. */
3555 for (n = 0; n < pieceDepth; n ++) {
3556 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
3557 0, 0, squareSize, squareSize,
3563 XFreePixmap(xDisplay, buf);
3564 XFreeGC(xDisplay, bufGC);
3565 XFreeGC(xDisplay, maskGC);
3569 InitAnimState (AnimNr anr, XWindowAttributes *info)
3574 /* Each buffer is square size, same depth as window */
3575 animBufs[anr+4] = xBoardWindow;
3576 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
3577 squareSize, squareSize, info->depth);
3578 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
3579 squareSize, squareSize, info->depth);
3581 /* Create a plain GC for blitting */
3582 mask = GCForeground | GCBackground | GCFunction |
3583 GCPlaneMask | GCGraphicsExposures;
3584 values.foreground = XBlackPixel(xDisplay, xScreen);
3585 values.background = XWhitePixel(xDisplay, xScreen);
3586 values.function = GXcopy;
3587 values.plane_mask = AllPlanes;
3588 values.graphics_exposures = False;
3589 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
3591 /* Piece will be copied from an existing context at
3592 the start of each new animation/drag. */
3593 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
3595 /* Outline will be a read-only copy of an existing */
3596 animGCs[anr+4] = None;
3602 XWindowAttributes info;
3604 if (xpmDone && gameInfo.variant == oldVariant) return;
3605 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
3606 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
3608 InitAnimState(Game, &info);
3609 InitAnimState(Player, &info);
3611 /* For XPM pieces, we need bitmaps to use as masks. */
3613 CreateAnimMasks(info.depth), xpmDone = 1;
3618 static Boolean frameWaiting;
3621 FrameAlarm (int sig)
3623 frameWaiting = False;
3624 /* In case System-V style signals. Needed?? */
3625 signal(SIGALRM, FrameAlarm);
3629 FrameDelay (int time)
3631 struct itimerval delay;
3633 XSync(xDisplay, False);
3636 frameWaiting = True;
3637 signal(SIGALRM, FrameAlarm);
3638 delay.it_interval.tv_sec =
3639 delay.it_value.tv_sec = time / 1000;
3640 delay.it_interval.tv_usec =
3641 delay.it_value.tv_usec = (time % 1000) * 1000;
3642 setitimer(ITIMER_REAL, &delay, NULL);
3643 while (frameWaiting) pause();
3644 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
3645 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
3646 setitimer(ITIMER_REAL, &delay, NULL);
3653 FrameDelay (int time)
3655 XSync(xDisplay, False);
3657 usleep(time * 1000);
3663 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
3667 /* Bitmap for piece being moved. */
3668 if (appData.monoMode) {
3669 *mask = *pieceToSolid(piece);
3670 } else if (useImages) {
3672 *mask = xpmMask[piece];
3674 *mask = ximMaskPm[piece];
3677 *mask = *pieceToSolid(piece);
3680 /* GC for piece being moved. Square color doesn't matter, but
3681 since it gets modified we make a copy of the original. */
3683 if (appData.monoMode)
3688 if (appData.monoMode)
3693 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
3695 /* Outline only used in mono mode and is not modified */
3697 *outline = bwPieceGC;
3699 *outline = wbPieceGC;
3703 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
3708 /* Draw solid rectangle which will be clipped to shape of piece */
3709 XFillRectangle(xDisplay, dest, clip,
3710 0, 0, squareSize, squareSize);
3711 if (appData.monoMode)
3712 /* Also draw outline in contrasting color for black
3713 on black / white on white cases */
3714 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
3715 0, 0, squareSize, squareSize, 0, 0, 1);
3717 /* Copy the piece */
3722 if(appData.upsideDown && flipView) kind ^= 2;
3723 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3725 0, 0, squareSize, squareSize,
3731 InsertPiece (AnimNr anr, ChessSquare piece)
3733 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
3737 DrawBlank (AnimNr anr, int x, int y, int startColor)
3739 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
3742 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
3743 int srcX, int srcY, int width, int height, int destX, int destY)
3745 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
3746 srcX, srcY, width, height, destX, destY);
3750 SetDragPiece (AnimNr anr, ChessSquare piece)
3753 /* The piece will be drawn using its own bitmap as a matte */
3754 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
3755 XSetClipMask(xDisplay, animGCs[anr+2], mask);
3758 /* [AS] Arrow highlighting support */
3761 DrawPolygon (Pnt arrow[], int nr)
3765 for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
3766 XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
3767 if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
3771 UpdateLogos (int displ)
3773 return; // no logos in XBoard yet