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 XtSetLanguageProc(NULL, NULL, NULL);
1197 if (appData.debugMode) {
1198 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1201 bindtextdomain(PACKAGE, LOCALEDIR);
1202 textdomain(PACKAGE);
1205 appData.boardSize = "";
1206 InitAppData(ConvertToLine(argc, argv));
1208 if (p == NULL) p = "/tmp";
1209 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1210 gameCopyFilename = (char*) malloc(i);
1211 gamePasteFilename = (char*) malloc(i);
1212 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1213 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1215 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1216 static char buf[MSG_SIZ];
1217 EscapeExpand(buf, appData.firstInitString);
1218 appData.firstInitString = strdup(buf);
1219 EscapeExpand(buf, appData.secondInitString);
1220 appData.secondInitString = strdup(buf);
1221 EscapeExpand(buf, appData.firstComputerString);
1222 appData.firstComputerString = strdup(buf);
1223 EscapeExpand(buf, appData.secondComputerString);
1224 appData.secondComputerString = strdup(buf);
1227 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1230 if (chdir(chessDir) != 0) {
1231 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1237 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1238 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1239 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1240 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1243 setbuf(debugFP, NULL);
1246 /* [HGM,HR] make sure board size is acceptable */
1247 if(appData.NrFiles > BOARD_FILES ||
1248 appData.NrRanks > BOARD_RANKS )
1249 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1252 /* This feature does not work; animation needs a rewrite */
1253 appData.highlightDragging = FALSE;
1257 gameInfo.variant = StringToVariant(appData.variant);
1258 InitPosition(FALSE);
1261 XtAppInitialize(&appContext, "XBoard", shellOptions,
1262 XtNumber(shellOptions),
1263 &argc, argv, xboardResources, NULL, 0);
1265 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1266 clientResources, XtNumber(clientResources),
1269 xDisplay = XtDisplay(shellWidget);
1270 xScreen = DefaultScreen(xDisplay);
1271 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1274 * determine size, based on supplied or remembered -size, or screen size
1276 if (isdigit(appData.boardSize[0])) {
1277 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1278 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1279 &fontPxlSize, &smallLayout, &tinyLayout);
1281 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1282 programName, appData.boardSize);
1286 /* Find some defaults; use the nearest known size */
1287 SizeDefaults *szd, *nearest;
1288 int distance = 99999;
1289 nearest = szd = sizeDefaults;
1290 while (szd->name != NULL) {
1291 if (abs(szd->squareSize - squareSize) < distance) {
1293 distance = abs(szd->squareSize - squareSize);
1294 if (distance == 0) break;
1298 if (i < 2) lineGap = nearest->lineGap;
1299 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1300 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1301 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1302 if (i < 6) smallLayout = nearest->smallLayout;
1303 if (i < 7) tinyLayout = nearest->tinyLayout;
1306 SizeDefaults *szd = sizeDefaults;
1307 if (*appData.boardSize == NULLCHAR) {
1308 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1309 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1312 if (szd->name == NULL) szd--;
1313 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1315 while (szd->name != NULL &&
1316 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1317 if (szd->name == NULL) {
1318 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1319 programName, appData.boardSize);
1323 squareSize = szd->squareSize;
1324 lineGap = szd->lineGap;
1325 clockFontPxlSize = szd->clockFontPxlSize;
1326 coordFontPxlSize = szd->coordFontPxlSize;
1327 fontPxlSize = szd->fontPxlSize;
1328 smallLayout = szd->smallLayout;
1329 tinyLayout = szd->tinyLayout;
1330 // [HGM] font: use defaults from settings file if available and not overruled
1333 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1334 if (strlen(appData.pixmapDirectory) > 0) {
1335 p = ExpandPathName(appData.pixmapDirectory);
1337 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1338 appData.pixmapDirectory);
1341 if (appData.debugMode) {
1342 fprintf(stderr, _("\
1343 XBoard square size (hint): %d\n\
1344 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1346 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1347 if (appData.debugMode) {
1348 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1351 defaultLineGap = lineGap;
1352 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1354 /* [HR] height treated separately (hacked) */
1355 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1356 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1359 * Determine what fonts to use.
1361 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1364 * Detect if there are not enough colors available and adapt.
1366 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1367 appData.monoMode = True;
1370 forceMono = MakeColors();
1373 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1375 appData.monoMode = True;
1378 if (appData.monoMode && appData.debugMode) {
1379 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1380 (unsigned long) XWhitePixel(xDisplay, xScreen),
1381 (unsigned long) XBlackPixel(xDisplay, xScreen));
1384 ParseIcsTextColors();
1386 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1392 layoutName = "tinyLayout";
1393 } else if (smallLayout) {
1394 layoutName = "smallLayout";
1396 layoutName = "normalLayout";
1399 optList = BoardPopUp(squareSize, lineGap, (void*)
1405 boardWidget = optList[W_BOARD].handle;
1406 menuBarWidget = optList[W_MENU].handle;
1407 dropMenu = optList[W_DROP].handle;
1408 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1409 formWidget = XtParent(boardWidget);
1410 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1411 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1412 XtGetValues(optList[W_WHITE].handle, args, 2);
1413 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1414 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1415 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1416 XtGetValues(optList[W_PAUSE].handle, args, 2);
1418 AppendEnginesToMenu(appData.recentEngineList);
1420 xBoardWindow = XtWindow(boardWidget);
1422 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1423 // not need to go into InitDrawingSizes().
1426 * Create X checkmark bitmap and initialize option menu checks.
1428 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1429 checkmark_bits, checkmark_width, checkmark_height);
1435 ReadBitmap(&wIconPixmap, "icon_white.bm",
1436 icon_white_bits, icon_white_width, icon_white_height);
1437 ReadBitmap(&bIconPixmap, "icon_black.bm",
1438 icon_black_bits, icon_black_width, icon_black_height);
1439 iconPixmap = wIconPixmap;
1441 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1442 XtSetValues(shellWidget, args, i);
1445 * Create a cursor for the board widget.
1447 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1448 XChangeWindowAttributes(xDisplay, xBoardWindow,
1449 CWCursor, &window_attributes);
1452 * Inhibit shell resizing.
1454 shellArgs[0].value = (XtArgVal) &w;
1455 shellArgs[1].value = (XtArgVal) &h;
1456 XtGetValues(shellWidget, shellArgs, 2);
1457 shellArgs[4].value = shellArgs[2].value = w;
1458 shellArgs[5].value = shellArgs[3].value = h;
1459 XtSetValues(shellWidget, &shellArgs[2], 4);
1460 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1461 marginH = h - boardHeight;
1463 CatchDeleteWindow(shellWidget, "QuitProc");
1469 if (appData.animate || appData.animateDragging)
1472 XtAugmentTranslations(formWidget,
1473 XtParseTranslationTable(globalTranslations));
1475 XtAddEventHandler(formWidget, KeyPressMask, False,
1476 (XtEventHandler) MoveTypeInProc, NULL);
1477 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1478 (XtEventHandler) EventProc, NULL);
1480 /* [AS] Restore layout */
1481 if( wpMoveHistory.visible ) {
1485 if( wpEvalGraph.visible )
1490 if( wpEngineOutput.visible ) {
1491 EngineOutputPopUp();
1496 if (errorExitStatus == -1) {
1497 if (appData.icsActive) {
1498 /* We now wait until we see "login:" from the ICS before
1499 sending the logon script (problems with timestamp otherwise) */
1500 /*ICSInitScript();*/
1501 if (appData.icsInputBox) ICSInputBoxPopUp();
1505 signal(SIGWINCH, TermSizeSigHandler);
1507 signal(SIGINT, IntSigHandler);
1508 signal(SIGTERM, IntSigHandler);
1509 if (*appData.cmailGameName != NULLCHAR) {
1510 signal(SIGUSR1, CmailSigHandler);
1514 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1516 // XtSetKeyboardFocus(shellWidget, formWidget);
1517 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1519 XtAppMainLoop(appContext);
1520 if (appData.debugMode) fclose(debugFP); // [DM] debug
1525 TermSizeSigHandler (int sig)
1531 IntSigHandler (int sig)
1537 CmailSigHandler (int sig)
1542 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1544 /* Activate call-back function CmailSigHandlerCallBack() */
1545 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1547 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1551 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1554 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1556 /**** end signal code ****/
1559 #define Abs(n) ((n)<0 ? -(n) : (n))
1563 InsertPxlSize (char *pattern, int targetPxlSize)
1565 char *base_fnt_lst, strInt[12], *p, *q;
1566 int alternatives, i, len, strIntLen;
1569 * Replace the "*" (if present) in the pixel-size slot of each
1570 * alternative with the targetPxlSize.
1574 while ((p = strchr(p, ',')) != NULL) {
1578 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1579 strIntLen = strlen(strInt);
1580 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1584 while (alternatives--) {
1585 char *comma = strchr(p, ',');
1586 for (i=0; i<14; i++) {
1587 char *hyphen = strchr(p, '-');
1589 if (comma && hyphen > comma) break;
1590 len = hyphen + 1 - p;
1591 if (i == 7 && *p == '*' && len == 2) {
1593 memcpy(q, strInt, strIntLen);
1603 len = comma + 1 - p;
1610 return base_fnt_lst;
1614 CreateFontSet (char *base_fnt_lst)
1617 char **missing_list;
1621 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1622 &missing_list, &missing_count, &def_string);
1623 if (appData.debugMode) {
1625 XFontStruct **font_struct_list;
1626 char **font_name_list;
1627 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1629 fprintf(debugFP, " got list %s, locale %s\n",
1630 XBaseFontNameListOfFontSet(fntSet),
1631 XLocaleOfFontSet(fntSet));
1632 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1633 for (i = 0; i < count; i++) {
1634 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1637 for (i = 0; i < missing_count; i++) {
1638 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1641 if (fntSet == NULL) {
1642 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1647 #else // not ENABLE_NLS
1649 * Find a font that matches "pattern" that is as close as
1650 * possible to the targetPxlSize. Prefer fonts that are k
1651 * pixels smaller to fonts that are k pixels larger. The
1652 * pattern must be in the X Consortium standard format,
1653 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1654 * The return value should be freed with XtFree when no
1658 FindFont (char *pattern, int targetPxlSize)
1660 char **fonts, *p, *best, *scalable, *scalableTail;
1661 int i, j, nfonts, minerr, err, pxlSize;
1663 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1665 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1666 programName, pattern);
1673 for (i=0; i<nfonts; i++) {
1676 if (*p != '-') continue;
1678 if (*p == NULLCHAR) break;
1679 if (*p++ == '-') j++;
1681 if (j < 7) continue;
1684 scalable = fonts[i];
1687 err = pxlSize - targetPxlSize;
1688 if (Abs(err) < Abs(minerr) ||
1689 (minerr > 0 && err < 0 && -err == minerr)) {
1695 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1696 /* If the error is too big and there is a scalable font,
1697 use the scalable font. */
1698 int headlen = scalableTail - scalable;
1699 p = (char *) XtMalloc(strlen(scalable) + 10);
1700 while (isdigit(*scalableTail)) scalableTail++;
1701 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1703 p = (char *) XtMalloc(strlen(best) + 2);
1704 safeStrCpy(p, best, strlen(best)+1 );
1706 if (appData.debugMode) {
1707 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1708 pattern, targetPxlSize, p);
1710 XFreeFontNames(fonts);
1717 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
1718 // must be called before all non-first callse to CreateGCs()
1719 XtReleaseGC(shellWidget, highlineGC);
1720 XtReleaseGC(shellWidget, lightSquareGC);
1721 XtReleaseGC(shellWidget, darkSquareGC);
1722 XtReleaseGC(shellWidget, lineGC);
1723 if (appData.monoMode) {
1724 if (DefaultDepth(xDisplay, xScreen) == 1) {
1725 XtReleaseGC(shellWidget, wbPieceGC);
1727 XtReleaseGC(shellWidget, bwPieceGC);
1730 XtReleaseGC(shellWidget, prelineGC);
1731 XtReleaseGC(shellWidget, wdPieceGC);
1732 XtReleaseGC(shellWidget, wlPieceGC);
1733 XtReleaseGC(shellWidget, bdPieceGC);
1734 XtReleaseGC(shellWidget, blPieceGC);
1739 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
1741 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1742 | GCBackground | GCFunction | GCPlaneMask;
1743 gc_values->foreground = foreground;
1744 gc_values->background = background;
1745 return XtGetGC(shellWidget, value_mask, gc_values);
1749 CreateGCs (int redo)
1751 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1752 | GCBackground | GCFunction | GCPlaneMask;
1753 XGCValues gc_values;
1755 Pixel white = XWhitePixel(xDisplay, xScreen);
1756 Pixel black = XBlackPixel(xDisplay, xScreen);
1758 gc_values.plane_mask = AllPlanes;
1759 gc_values.line_width = lineGap;
1760 gc_values.line_style = LineSolid;
1761 gc_values.function = GXcopy;
1764 DeleteGCs(); // called a second time; clean up old GCs first
1765 } else { // [HGM] grid and font GCs created on first call only
1766 coordGC = CreateOneGC(&gc_values, black, white);
1767 XSetFont(xDisplay, coordGC, coordFontID);
1769 // [HGM] make font for holdings counts (white on black)
1770 countGC = CreateOneGC(&gc_values, white, black);
1771 XSetFont(xDisplay, countGC, countFontID);
1773 lineGC = CreateOneGC(&gc_values, black, black);
1775 if (appData.monoMode) {
1777 highlineGC = CreateOneGC(&gc_values, white, white);
1778 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
1779 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
1781 if (DefaultDepth(xDisplay, xScreen) == 1) {
1782 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
1783 gc_values.function = GXcopyInverted;
1784 copyInvertedGC = CreateOneGC(&gc_values, black, white);
1785 gc_values.function = GXcopy;
1786 if (XBlackPixel(xDisplay, xScreen) == 1) {
1787 bwPieceGC = darkSquareGC;
1788 wbPieceGC = copyInvertedGC;
1790 bwPieceGC = copyInvertedGC;
1791 wbPieceGC = lightSquareGC;
1796 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
1797 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
1798 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
1799 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
1800 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
1801 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
1802 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
1803 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
1808 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
1816 fp = fopen(filename, "rb");
1818 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
1825 for (y=0; y<h; ++y) {
1826 for (x=0; x<h; ++x) {
1831 XPutPixel(xim, x, y, blackPieceColor);
1833 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1836 XPutPixel(xim, x, y, darkSquareColor);
1838 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1841 XPutPixel(xim, x, y, whitePieceColor);
1843 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1846 XPutPixel(xim, x, y, lightSquareColor);
1848 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1856 /* create Pixmap of piece */
1857 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1859 XPutImage(xDisplay, *dest, lightSquareGC, xim,
1862 /* create Pixmap of clipmask
1863 Note: We assume the white/black pieces have the same
1864 outline, so we make only 6 masks. This is okay
1865 since the XPM clipmask routines do the same. */
1867 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1869 XPutImage(xDisplay, temp, lightSquareGC, xmask,
1872 /* now create the 1-bit version */
1873 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1876 values.foreground = 1;
1877 values.background = 0;
1879 /* Don't use XtGetGC, not read only */
1880 maskGC = XCreateGC(xDisplay, *mask,
1881 GCForeground | GCBackground, &values);
1882 XCopyPlane(xDisplay, temp, *mask, maskGC,
1883 0, 0, squareSize, squareSize, 0, 0, 1);
1884 XFreePixmap(xDisplay, temp);
1889 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
1897 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
1902 /* The XSynchronize calls were copied from CreatePieces.
1903 Not sure if needed, but can't hurt */
1904 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
1907 /* temp needed by loadXIM() */
1908 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1909 0, 0, ss, ss, AllPlanes, XYPixmap);
1911 if (strlen(appData.pixmapDirectory) == 0) {
1915 if (appData.monoMode) {
1916 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
1920 fprintf(stderr, _("\nLoading XIMs...\n"));
1922 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
1923 fprintf(stderr, "%d", piece+1);
1924 for (kind=0; kind<4; kind++) {
1925 fprintf(stderr, ".");
1926 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
1927 ExpandPathName(appData.pixmapDirectory),
1928 piece <= (int) WhiteKing ? "" : "w",
1929 pieceBitmapNames[piece],
1931 ximPieceBitmap[kind][piece] =
1932 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1933 0, 0, ss, ss, AllPlanes, XYPixmap);
1934 if (appData.debugMode)
1935 fprintf(stderr, _("(File:%s:) "), buf);
1936 loadXIM(ximPieceBitmap[kind][piece],
1938 &(xpmPieceBitmap2[kind][piece]),
1939 &(ximMaskPm2[piece]));
1940 if(piece <= (int)WhiteKing)
1941 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
1943 fprintf(stderr," ");
1945 /* Load light and dark squares */
1946 /* If the LSQ and DSQ pieces don't exist, we will
1947 draw them with solid squares. */
1948 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
1949 if (access(buf, 0) != 0) {
1953 fprintf(stderr, _("light square "));
1955 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1956 0, 0, ss, ss, AllPlanes, XYPixmap);
1957 if (appData.debugMode)
1958 fprintf(stderr, _("(File:%s:) "), buf);
1960 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
1961 fprintf(stderr, _("dark square "));
1962 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
1963 ExpandPathName(appData.pixmapDirectory), ss);
1964 if (appData.debugMode)
1965 fprintf(stderr, _("(File:%s:) "), buf);
1967 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1968 0, 0, ss, ss, AllPlanes, XYPixmap);
1969 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
1970 xpmJailSquare = xpmLightSquare;
1972 fprintf(stderr, _("Done.\n"));
1974 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
1977 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
1981 CreateXPMBoard (char *s, int kind)
1985 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
1986 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
1987 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
1993 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
1994 // thisroutine has to be called t free the old piece pixmaps
1996 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
1997 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
1999 XFreePixmap(xDisplay, xpmLightSquare);
2000 XFreePixmap(xDisplay, xpmDarkSquare);
2009 u_int ss = squareSize;
2011 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2012 XpmColorSymbol symbols[4];
2013 static int redo = False;
2015 if(redo) FreeXPMPieces(); else redo = 1;
2017 /* The XSynchronize calls were copied from CreatePieces.
2018 Not sure if needed, but can't hurt */
2019 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2021 /* Setup translations so piece colors match square colors */
2022 symbols[0].name = "light_piece";
2023 symbols[0].value = appData.whitePieceColor;
2024 symbols[1].name = "dark_piece";
2025 symbols[1].value = appData.blackPieceColor;
2026 symbols[2].name = "light_square";
2027 symbols[2].value = appData.lightSquareColor;
2028 symbols[3].name = "dark_square";
2029 symbols[3].value = appData.darkSquareColor;
2031 attr.valuemask = XpmColorSymbols;
2032 attr.colorsymbols = symbols;
2033 attr.numsymbols = 4;
2035 if (appData.monoMode) {
2036 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2040 if (strlen(appData.pixmapDirectory) == 0) {
2041 XpmPieces* pieces = builtInXpms;
2044 while (pieces->size != squareSize && pieces->size) pieces++;
2045 if (!pieces->size) {
2046 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2049 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2050 for (kind=0; kind<4; kind++) {
2052 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2053 pieces->xpm[piece][kind],
2054 &(xpmPieceBitmap2[kind][piece]),
2055 NULL, &attr)) != 0) {
2056 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2060 if(piece <= (int) WhiteKing)
2061 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2065 xpmJailSquare = xpmLightSquare;
2069 fprintf(stderr, _("\nLoading XPMs...\n"));
2072 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2073 fprintf(stderr, "%d ", piece+1);
2074 for (kind=0; kind<4; kind++) {
2075 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2076 ExpandPathName(appData.pixmapDirectory),
2077 piece > (int) WhiteKing ? "w" : "",
2078 pieceBitmapNames[piece],
2080 if (appData.debugMode) {
2081 fprintf(stderr, _("(File:%s:) "), buf);
2083 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2084 &(xpmPieceBitmap2[kind][piece]),
2085 NULL, &attr)) != 0) {
2086 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2087 // [HGM] missing: read of unorthodox piece failed; substitute King.
2088 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2089 ExpandPathName(appData.pixmapDirectory),
2091 if (appData.debugMode) {
2092 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2094 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2095 &(xpmPieceBitmap2[kind][piece]),
2099 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2104 if(piece <= (int) WhiteKing)
2105 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2108 /* Load light and dark squares */
2109 /* If the LSQ and DSQ pieces don't exist, we will
2110 draw them with solid squares. */
2111 fprintf(stderr, _("light square "));
2112 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2113 if (access(buf, 0) != 0) {
2117 if (appData.debugMode)
2118 fprintf(stderr, _("(File:%s:) "), buf);
2120 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2121 &xpmLightSquare, NULL, &attr)) != 0) {
2122 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2125 fprintf(stderr, _("dark square "));
2126 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2127 ExpandPathName(appData.pixmapDirectory), ss);
2128 if (appData.debugMode) {
2129 fprintf(stderr, _("(File:%s:) "), buf);
2131 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2132 &xpmDarkSquare, NULL, &attr)) != 0) {
2133 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2137 xpmJailSquare = xpmLightSquare;
2138 fprintf(stderr, _("Done.\n"));
2140 oldVariant = -1; // kludge to force re-makig of animation masks
2141 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2144 #endif /* HAVE_LIBXPM */
2147 /* No built-in bitmaps */
2152 u_int ss = squareSize;
2154 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2157 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2158 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2159 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2160 pieceBitmapNames[piece],
2161 ss, kind == SOLID ? 's' : 'o');
2162 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2163 if(piece <= (int)WhiteKing)
2164 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2168 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2172 /* With built-in bitmaps */
2176 BuiltInBits* bib = builtInBits;
2179 u_int ss = squareSize;
2181 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2184 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2186 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2187 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2188 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2189 pieceBitmapNames[piece],
2190 ss, kind == SOLID ? 's' : 'o');
2191 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2192 bib->bits[kind][piece], ss, ss);
2193 if(piece <= (int)WhiteKing)
2194 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2198 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2204 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2209 char msg[MSG_SIZ], fullname[MSG_SIZ];
2211 if (*appData.bitmapDirectory != NULLCHAR) {
2212 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2213 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2214 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2215 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2216 &w, &h, pm, &x_hot, &y_hot);
2217 fprintf(stderr, "load %s\n", name);
2218 if (errcode != BitmapSuccess) {
2220 case BitmapOpenFailed:
2221 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2223 case BitmapFileInvalid:
2224 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2226 case BitmapNoMemory:
2227 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2231 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2235 fprintf(stderr, _("%s: %s...using built-in\n"),
2237 } else if (w != wreq || h != hreq) {
2239 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2240 programName, fullname, w, h, wreq, hreq);
2246 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2256 if (lineGap == 0) return;
2258 /* [HR] Split this into 2 loops for non-square boards. */
2260 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2261 gridSegments[i].x1 = 0;
2262 gridSegments[i].x2 =
2263 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2264 gridSegments[i].y1 = gridSegments[i].y2
2265 = lineGap / 2 + (i * (squareSize + lineGap));
2268 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2269 gridSegments[j + i].y1 = 0;
2270 gridSegments[j + i].y2 =
2271 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2272 gridSegments[j + i].x1 = gridSegments[j + i].x2
2273 = lineGap / 2 + (j * (squareSize + lineGap));
2278 MarkMenuItem (char *menuRef, int state)
2280 MenuItem *item = MenuNameToItem(menuRef);
2284 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2285 XtSetValues(item->handle, args, 1);
2290 EnableMenuItem (char *menuRef, int state)
2292 MenuItem *item = MenuNameToItem(menuRef);
2294 if(item) XtSetSensitive(item->handle, state);
2298 EnableButtonBar (int state)
2300 XtSetSensitive(optList[W_BUTTON].handle, state);
2305 SetMenuEnables (Enables *enab)
2307 while (enab->name != NULL) {
2308 EnableMenuItem(enab->name, enab->value);
2314 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2315 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2319 if(*nprms == 0) return;
2320 item = MenuNameToItem(prms[0]);
2321 if(item) ((MenuProc *) item->proc) ();
2325 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
2327 MenuProc *proc = (MenuProc *) addr;
2333 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2335 RecentEngineEvent((int) (intptr_t) addr);
2339 AppendMenuItem (char *msg, int n)
2341 CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
2353 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2354 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2355 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2356 dmEnables[i].piece);
2357 XtSetSensitive(entry, p != NULL || !appData.testLegality
2358 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2359 && !appData.icsActive));
2361 while (p && *p++ == dmEnables[i].piece) count++;
2362 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2364 XtSetArg(args[j], XtNlabel, label); j++;
2365 XtSetValues(entry, args, j);
2371 do_flash_delay (unsigned long msec)
2377 DrawBorder (int x, int y, int type)
2381 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
2383 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
2384 squareSize+lineGap, squareSize+lineGap);
2388 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
2390 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
2391 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
2393 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
2394 if(textureW[kind] < W*squareSize)
2395 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
2397 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
2398 if(textureH[kind] < H*squareSize)
2399 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
2401 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
2406 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
2407 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
2409 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
2410 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
2411 squareSize, squareSize, x*fac, y*fac);
2413 if (useImages && useImageSqs) {
2417 pm = xpmLightSquare;
2422 case 2: /* neutral */
2424 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
2427 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
2428 squareSize, squareSize, x*fac, y*fac);
2438 case 2: /* neutral */
2443 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
2448 I split out the routines to draw a piece so that I could
2449 make a generic flash routine.
2452 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2454 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2455 switch (square_color) {
2457 case 2: /* neutral */
2459 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2460 ? *pieceToOutline(piece)
2461 : *pieceToSolid(piece),
2462 dest, bwPieceGC, 0, 0,
2463 squareSize, squareSize, x, y);
2466 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2467 ? *pieceToSolid(piece)
2468 : *pieceToOutline(piece),
2469 dest, wbPieceGC, 0, 0,
2470 squareSize, squareSize, x, y);
2476 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2478 switch (square_color) {
2480 case 2: /* neutral */
2482 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2483 ? *pieceToOutline(piece)
2484 : *pieceToSolid(piece),
2485 dest, bwPieceGC, 0, 0,
2486 squareSize, squareSize, x, y, 1);
2489 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2490 ? *pieceToSolid(piece)
2491 : *pieceToOutline(piece),
2492 dest, wbPieceGC, 0, 0,
2493 squareSize, squareSize, x, y, 1);
2499 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2501 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
2502 switch (square_color) {
2504 XCopyPlane(xDisplay, *pieceToSolid(piece),
2505 dest, (int) piece < (int) BlackPawn
2506 ? wlPieceGC : blPieceGC, 0, 0,
2507 squareSize, squareSize, x, y, 1);
2510 XCopyPlane(xDisplay, *pieceToSolid(piece),
2511 dest, (int) piece < (int) BlackPawn
2512 ? wdPieceGC : bdPieceGC, 0, 0,
2513 squareSize, squareSize, x, y, 1);
2515 case 2: /* neutral */
2517 break; // should never contain pieces
2522 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2524 int kind, p = piece;
2526 switch (square_color) {
2528 case 2: /* neutral */
2530 if ((int)piece < (int) BlackPawn) {
2538 if ((int)piece < (int) BlackPawn) {
2546 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2547 if(useTexture & square_color+1) {
2548 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2549 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
2550 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
2551 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
2552 XSetClipMask(xDisplay, wlPieceGC, None);
2553 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
2555 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
2556 dest, wlPieceGC, 0, 0,
2557 squareSize, squareSize, x, y);
2560 typedef void (*DrawFunc)();
2565 if (appData.monoMode) {
2566 if (DefaultDepth(xDisplay, xScreen) == 1) {
2567 return monoDrawPiece_1bit;
2569 return monoDrawPiece;
2573 return colorDrawPieceImage;
2575 return colorDrawPiece;
2580 DrawDot (int marker, int x, int y, int r)
2582 if(appData.monoMode) {
2583 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
2584 x, y, r, r, 0, 64*360);
2585 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
2586 x, y, r, r, 0, 64*360);
2588 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
2589 x, y, r, r, 0, 64*360);
2593 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2594 { // basic front-end board-draw function: takes care of everything that can be in square:
2595 // piece, background, coordinate/count, marker dot
2596 int direction, font_ascent, font_descent;
2597 XCharStruct overall;
2600 if (piece == EmptySquare) {
2601 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2603 drawfunc = ChooseDrawFunc();
2604 drawfunc(piece, square_color, x, y, xBoardWindow);
2607 if(align) { // square carries inscription (coord or piece count)
2609 GC hGC = align < 3 ? coordGC : countGC;
2610 // first calculate where it goes
2611 XTextExtents(countFontStruct, string, 1, &direction,
2612 &font_ascent, &font_descent, &overall);
2614 xx += squareSize - overall.width - 2;
2615 yy += squareSize - font_descent - 1;
2616 } else if (align == 2) {
2617 xx += 2, yy += font_ascent + 1;
2618 } else if (align == 3) {
2619 xx += squareSize - overall.width - 2;
2620 yy += font_ascent + 1;
2621 } else if (align == 4) {
2622 xx += 2, yy += font_ascent + 1;
2625 if (appData.monoMode) {
2626 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2628 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2632 if(marker) { // print fat marker dot, if requested
2633 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2638 FlashDelay (int flash_delay)
2640 XSync(xDisplay, False);
2641 if(flash_delay) do_flash_delay(flash_delay);
2645 Fraction (int x, int start, int stop)
2647 double f = ((double) x - start)/(stop - start);
2648 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2652 static WindowPlacement wpNew;
2655 CoDrag (Widget sh, WindowPlacement *wp)
2658 int j=0, touch=0, fudge = 2;
2659 GetActualPlacement(sh, wp);
2660 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
2661 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
2662 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2663 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
2664 if(!touch ) return; // only windows that touch co-move
2665 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2666 int heightInc = wpNew.height - wpMain.height;
2667 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2668 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2669 wp->y += fracTop * heightInc;
2670 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2671 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2672 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2673 int widthInc = wpNew.width - wpMain.width;
2674 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2675 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2676 wp->y += fracLeft * widthInc;
2677 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2678 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2680 wp->x += wpNew.x - wpMain.x;
2681 wp->y += wpNew.y - wpMain.y;
2682 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2683 if(touch == 3) wp->y += wpNew.height - wpMain.height;
2684 XtSetArg(args[j], XtNx, wp->x); j++;
2685 XtSetArg(args[j], XtNy, wp->y); j++;
2686 XtSetValues(sh, args, j);
2689 static XtIntervalId delayedDragID = 0;
2694 GetActualPlacement(shellWidget, &wpNew);
2695 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
2696 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
2697 return; // false alarm
2698 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
2699 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
2700 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
2701 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
2703 DrawPosition(True, NULL);
2704 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
2711 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
2713 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
2717 EventProc (Widget widget, caddr_t unused, XEvent *event)
2719 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
2720 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
2723 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
2725 DrawSeekAxis (int x, int y, int xTo, int yTo)
2727 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
2731 DrawSeekBackground (int left, int top, int right, int bottom)
2733 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
2737 DrawSeekText (char *buf, int x, int y)
2739 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
2743 DrawSeekDot (int x, int y, int colorNr)
2745 int square = colorNr & 0x80;
2748 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
2750 XFillRectangle(xDisplay, xBoardWindow, color,
2751 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
2753 XFillArc(xDisplay, xBoardWindow, color,
2754 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
2760 XDrawSegments(xDisplay, xBoardWindow, lineGC,
2761 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
2766 * event handler for redrawing the board
2769 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2771 DrawPosition(True, NULL);
2776 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
2777 { // [HGM] pv: walk PV
2778 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
2781 static int savedIndex; /* gross that this is global */
2784 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
2787 XawTextPosition index, dummy;
2790 XawTextGetSelectionPos(w, &index, &dummy);
2791 XtSetArg(arg, XtNstring, &val);
2792 XtGetValues(w, &arg, 1);
2793 ReplaceComment(savedIndex, val);
2794 if(savedIndex != currentMove) ToNrEvent(savedIndex);
2795 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
2799 EditCommentPopUp (int index, char *title, char *text)
2802 if (text == NULL) text = "";
2803 NewCommentPopup(title, text, index);
2807 CommentPopUp (char *title, char *text)
2809 savedIndex = currentMove; // [HGM] vari
2810 NewCommentPopup(title, text, currentMove);
2816 PopDown(CommentDlg);
2819 static char *openName;
2825 (void) (*fileProc)(openFP, 0, openName);
2829 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
2831 fileProc = proc; /* I can't see a way not */
2832 fileOpenMode = openMode; /* to use globals here */
2833 { // [HGM] use file-selector dialog stolen from Ghostview
2834 int index; // this is not supported yet
2835 Browse(BoardWindow, label, (def[0] ? def : NULL), filter, False, openMode, &openName, &openFP);
2840 /* Disable all user input other than deleting the window */
2841 static int frozen = 0;
2847 /* Grab by a widget that doesn't accept input */
2848 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
2852 /* Undo a FreezeUI */
2856 if (!frozen) return;
2857 XtRemoveGrab(optList[W_MESSG].handle);
2865 static int oldPausing = FALSE;
2866 static GameMode oldmode = (GameMode) -1;
2869 if (!boardWidget || !XtIsRealized(boardWidget)) return;
2871 if (pausing != oldPausing) {
2872 oldPausing = pausing;
2873 MarkMenuItem("Mode.Pause", pausing);
2875 if (appData.showButtonBar) {
2876 /* Always toggle, don't set. Previous code messes up when
2877 invoked while the button is pressed, as releasing it
2878 toggles the state again. */
2881 XtSetArg(args[0], XtNbackground, &oldbg);
2882 XtSetArg(args[1], XtNforeground, &oldfg);
2883 XtGetValues(optList[W_PAUSE].handle,
2885 XtSetArg(args[0], XtNbackground, oldfg);
2886 XtSetArg(args[1], XtNforeground, oldbg);
2888 XtSetValues(optList[W_PAUSE].handle, args, 2);
2892 wname = ModeToWidgetName(oldmode);
2893 if (wname != NULL) {
2894 MarkMenuItem(wname, False);
2896 wname = ModeToWidgetName(gameMode);
2897 if (wname != NULL) {
2898 MarkMenuItem(wname, True);
2901 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
2903 /* Maybe all the enables should be handled here, not just this one */
2904 EnableMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
2909 * Button/menu procedures
2912 /* this variable is shared between CopyPositionProc and SendPositionSelection */
2913 char *selected_fen_position=NULL;
2916 SendPositionSelection (Widget w, Atom *selection, Atom *target,
2917 Atom *type_return, XtPointer *value_return,
2918 unsigned long *length_return, int *format_return)
2920 char *selection_tmp;
2922 // if (!selected_fen_position) return False; /* should never happen */
2923 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
2924 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
2925 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
2928 if (f == NULL) return False;
2932 selection_tmp = XtMalloc(len + 1);
2933 count = fread(selection_tmp, 1, len, f);
2936 XtFree(selection_tmp);
2939 selection_tmp[len] = NULLCHAR;
2941 /* note: since no XtSelectionDoneProc was registered, Xt will
2942 * automatically call XtFree on the value returned. So have to
2943 * make a copy of it allocated with XtMalloc */
2944 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
2945 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
2948 *value_return=selection_tmp;
2949 *length_return=strlen(selection_tmp);
2950 *type_return=*target;
2951 *format_return = 8; /* bits per byte */
2953 } else if (*target == XA_TARGETS(xDisplay)) {
2954 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
2955 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
2956 targets_tmp[1] = XA_STRING;
2957 *value_return = targets_tmp;
2958 *type_return = XA_ATOM;
2961 // This code leads to a read of value_return out of bounds on 64-bit systems.
2962 // Other code which I have seen always sets *format_return to 32 independent of
2963 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
2964 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
2965 *format_return = 8 * sizeof(Atom);
2966 if (*format_return > 32) {
2967 *length_return *= *format_return / 32;
2968 *format_return = 32;
2971 *format_return = 32;
2979 /* note: when called from menu all parameters are NULL, so no clue what the
2980 * Widget which was clicked on was, or what the click event was
2983 CopySomething (char *src)
2985 selected_fen_position = src;
2987 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
2988 * have a notion of a position that is selected but not copied.
2989 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
2991 XtOwnSelection(menuBarWidget, XA_PRIMARY,
2993 SendPositionSelection,
2994 NULL/* lose_ownership_proc */ ,
2995 NULL/* transfer_done_proc */);
2996 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
2998 SendPositionSelection,
2999 NULL/* lose_ownership_proc */ ,
3000 NULL/* transfer_done_proc */);
3003 /* function called when the data to Paste is ready */
3005 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3006 Atom *type, XtPointer value, unsigned long *len, int *format)
3009 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3010 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3011 EditPositionPasteFEN(fenstr);
3015 /* called when Paste Position button is pressed,
3016 * all parameters will be NULL */
3018 PastePositionProc ()
3020 XtGetSelectionValue(menuBarWidget,
3021 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3022 /* (XtSelectionCallbackProc) */ PastePositionCB,
3023 NULL, /* client_data passed to PastePositionCB */
3025 /* better to use the time field from the event that triggered the
3026 * call to this function, but that isn't trivial to get
3033 /* note: when called from menu all parameters are NULL, so no clue what the
3034 * Widget which was clicked on was, or what the click event was
3036 /* function called when the data to Paste is ready */
3038 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3039 Atom *type, XtPointer value, unsigned long *len, int *format)
3042 if (value == NULL || *len == 0) {
3043 return; /* nothing had been selected to copy */
3045 f = fopen(gamePasteFilename, "w");
3047 DisplayError(_("Can't open temp file"), errno);
3050 fwrite(value, 1, *len, f);
3053 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3056 /* called when Paste Game button is pressed,
3057 * all parameters will be NULL */
3061 XtGetSelectionValue(menuBarWidget,
3062 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3063 /* (XtSelectionCallbackProc) */ PasteGameCB,
3064 NULL, /* client_data passed to PasteGameCB */
3066 /* better to use the time field from the event that triggered the
3067 * call to this function, but that isn't trivial to get
3076 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3083 { // bassic primitive for determining if modifier keys are pressed
3084 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3087 XQueryKeymap(xDisplay,keys);
3088 for(i=0; i<6; i++) {
3090 j = XKeysymToKeycode(xDisplay, codes[i]);
3091 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3097 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3101 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3102 if ( n == 1 && *buf >= 32 // printable
3103 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3104 ) BoxAutoPopUp (buf);
3108 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3109 { // [HGM] input: let up-arrow recall previous line from history
3114 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3115 { // [HGM] input: let down-arrow recall next line from history
3120 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3126 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3128 if (!TempBackwardActive) {
3129 TempBackwardActive = True;
3135 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3137 /* Check to see if triggered by a key release event for a repeating key.
3138 * If so the next queued event will be a key press of the same key at the same time */
3139 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3141 XPeekEvent(xDisplay, &next);
3142 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3143 next.xkey.keycode == event->xkey.keycode)
3147 TempBackwardActive = False;
3151 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3152 { // called as key binding
3155 if (nprms && *nprms > 0)
3159 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3164 SetWindowTitle (char *text, char *title, char *icon)
3168 if (appData.titleInWindow) {
3170 XtSetArg(args[i], XtNlabel, text); i++;
3171 XtSetValues(titleWidget, args, i);
3174 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3175 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3176 XtSetValues(shellWidget, args, i);
3177 XSync(xDisplay, False);
3182 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3188 DisplayIcsInteractionTitle (String message)
3190 if (oldICSInteractionTitle == NULL) {
3191 /* Magic to find the old window title, adapted from vim */
3192 char *wina = getenv("WINDOWID");
3194 Window win = (Window) atoi(wina);
3195 Window root, parent, *children;
3196 unsigned int nchildren;
3197 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3199 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3200 if (!XQueryTree(xDisplay, win, &root, &parent,
3201 &children, &nchildren)) break;
3202 if (children) XFree((void *)children);
3203 if (parent == root || parent == 0) break;
3206 XSetErrorHandler(oldHandler);
3208 if (oldICSInteractionTitle == NULL) {
3209 oldICSInteractionTitle = "xterm";
3212 printf("\033]0;%s\007", message);
3217 XtIntervalId delayedEventTimerXID = 0;
3218 DelayedEventCallback delayedEventCallback = 0;
3223 delayedEventTimerXID = 0;
3224 delayedEventCallback();
3228 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3230 if(delayedEventTimerXID && delayedEventCallback == cb)
3231 // [HGM] alive: replace, rather than add or flush identical event
3232 XtRemoveTimeOut(delayedEventTimerXID);
3233 delayedEventCallback = cb;
3234 delayedEventTimerXID =
3235 XtAppAddTimeOut(appContext, millisec,
3236 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3239 DelayedEventCallback
3242 if (delayedEventTimerXID) {
3243 return delayedEventCallback;
3250 CancelDelayedEvent ()
3252 if (delayedEventTimerXID) {
3253 XtRemoveTimeOut(delayedEventTimerXID);
3254 delayedEventTimerXID = 0;
3258 XtIntervalId loadGameTimerXID = 0;
3261 LoadGameTimerRunning ()
3263 return loadGameTimerXID != 0;
3267 StopLoadGameTimer ()
3269 if (loadGameTimerXID != 0) {
3270 XtRemoveTimeOut(loadGameTimerXID);
3271 loadGameTimerXID = 0;
3279 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3281 loadGameTimerXID = 0;
3286 StartLoadGameTimer (long millisec)
3289 XtAppAddTimeOut(appContext, millisec,
3290 (XtTimerCallbackProc) LoadGameTimerCallback,
3294 XtIntervalId analysisClockXID = 0;
3297 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3299 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3300 || appData.icsEngineAnalyze) { // [DM]
3301 AnalysisPeriodicEvent(0);
3302 StartAnalysisClock();
3307 StartAnalysisClock ()
3310 XtAppAddTimeOut(appContext, 2000,
3311 (XtTimerCallbackProc) AnalysisClockCallback,
3315 XtIntervalId clockTimerXID = 0;
3318 ClockTimerRunning ()
3320 return clockTimerXID != 0;
3326 if (clockTimerXID != 0) {
3327 XtRemoveTimeOut(clockTimerXID);
3336 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3343 StartClockTimer (long millisec)
3346 XtAppAddTimeOut(appContext, millisec,
3347 (XtTimerCallbackProc) ClockTimerCallback,
3352 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
3356 Widget w = (Widget) opt->handle;
3358 /* check for low time warning */
3359 Pixel foregroundOrWarningColor = timerForegroundPixel;
3362 appData.lowTimeWarning &&
3363 (timer / 1000) < appData.icsAlarmTime)
3364 foregroundOrWarningColor = lowTimeWarningColor;
3366 if (appData.clockMode) {
3367 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
3368 XtSetArg(args[0], XtNlabel, buf);
3370 snprintf(buf, MSG_SIZ, "%s ", color);
3371 XtSetArg(args[0], XtNlabel, buf);
3376 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3377 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3379 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3380 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3383 XtSetValues(w, args, 3);
3386 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3389 SetClockIcon (int color)
3392 Pixmap pm = *clockIcons[color];
3393 if (iconPixmap != pm) {
3395 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3396 XtSetValues(shellWidget, args, 1);
3401 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3403 InputSource *is = (InputSource *) closure;
3408 if (is->lineByLine) {
3409 count = read(is->fd, is->unused,
3410 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3412 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3415 is->unused += count;
3417 while (p < is->unused) {
3418 q = memchr(p, '\n', is->unused - p);
3419 if (q == NULL) break;
3421 (is->func)(is, is->closure, p, q - p, 0);
3425 while (p < is->unused) {
3430 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3435 (is->func)(is, is->closure, is->buf, count, error);
3440 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3443 ChildProc *cp = (ChildProc *) pr;
3445 is = (InputSource *) calloc(1, sizeof(InputSource));
3446 is->lineByLine = lineByLine;
3450 is->fd = fileno(stdin);
3452 is->kind = cp->kind;
3453 is->fd = cp->fdFrom;
3456 is->unused = is->buf;
3459 is->xid = XtAppAddInput(appContext, is->fd,
3460 (XtPointer) (XtInputReadMask),
3461 (XtInputCallbackProc) DoInputCallback,
3463 is->closure = closure;
3464 return (InputSourceRef) is;
3468 RemoveInputSource (InputSourceRef isr)
3470 InputSource *is = (InputSource *) isr;
3472 if (is->xid == 0) return;
3473 XtRemoveInput(is->xid);
3477 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
3479 /* Masks for XPM pieces. Black and white pieces can have
3480 different shapes, but in the interest of retaining my
3481 sanity pieces must have the same outline on both light
3482 and dark squares, and all pieces must use the same
3483 background square colors/images. */
3485 static int xpmDone = 0;
3486 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
3487 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
3490 CreateAnimMasks (int pieceDepth)
3496 unsigned long plane;
3499 /* Need a bitmap just to get a GC with right depth */
3500 buf = XCreatePixmap(xDisplay, xBoardWindow,
3502 values.foreground = 1;
3503 values.background = 0;
3504 /* Don't use XtGetGC, not read only */
3505 maskGC = XCreateGC(xDisplay, buf,
3506 GCForeground | GCBackground, &values);
3507 XFreePixmap(xDisplay, buf);
3509 buf = XCreatePixmap(xDisplay, xBoardWindow,
3510 squareSize, squareSize, pieceDepth);
3511 values.foreground = XBlackPixel(xDisplay, xScreen);
3512 values.background = XWhitePixel(xDisplay, xScreen);
3513 bufGC = XCreateGC(xDisplay, buf,
3514 GCForeground | GCBackground, &values);
3516 for (piece = WhitePawn; piece <= BlackKing; piece++) {
3517 /* Begin with empty mask */
3518 if(!xpmDone) // [HGM] pieces: keep using existing
3519 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
3520 squareSize, squareSize, 1);
3521 XSetFunction(xDisplay, maskGC, GXclear);
3522 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
3523 0, 0, squareSize, squareSize);
3525 /* Take a copy of the piece */
3530 XSetFunction(xDisplay, bufGC, GXcopy);
3531 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
3533 0, 0, squareSize, squareSize, 0, 0);
3535 /* XOR the background (light) over the piece */
3536 XSetFunction(xDisplay, bufGC, GXxor);
3538 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
3539 0, 0, squareSize, squareSize, 0, 0);
3541 XSetForeground(xDisplay, bufGC, lightSquareColor);
3542 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
3545 /* We now have an inverted piece image with the background
3546 erased. Construct mask by just selecting all the non-zero
3547 pixels - no need to reconstruct the original image. */
3548 XSetFunction(xDisplay, maskGC, GXor);
3550 /* Might be quicker to download an XImage and create bitmap
3551 data from it rather than this N copies per piece, but it
3552 only takes a fraction of a second and there is a much
3553 longer delay for loading the pieces. */
3554 for (n = 0; n < pieceDepth; n ++) {
3555 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
3556 0, 0, squareSize, squareSize,
3562 XFreePixmap(xDisplay, buf);
3563 XFreeGC(xDisplay, bufGC);
3564 XFreeGC(xDisplay, maskGC);
3568 InitAnimState (AnimNr anr, XWindowAttributes *info)
3573 /* Each buffer is square size, same depth as window */
3574 animBufs[anr+4] = xBoardWindow;
3575 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
3576 squareSize, squareSize, info->depth);
3577 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
3578 squareSize, squareSize, info->depth);
3580 /* Create a plain GC for blitting */
3581 mask = GCForeground | GCBackground | GCFunction |
3582 GCPlaneMask | GCGraphicsExposures;
3583 values.foreground = XBlackPixel(xDisplay, xScreen);
3584 values.background = XWhitePixel(xDisplay, xScreen);
3585 values.function = GXcopy;
3586 values.plane_mask = AllPlanes;
3587 values.graphics_exposures = False;
3588 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
3590 /* Piece will be copied from an existing context at
3591 the start of each new animation/drag. */
3592 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
3594 /* Outline will be a read-only copy of an existing */
3595 animGCs[anr+4] = None;
3601 XWindowAttributes info;
3603 if (xpmDone && gameInfo.variant == oldVariant) return;
3604 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
3605 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
3607 InitAnimState(Game, &info);
3608 InitAnimState(Player, &info);
3610 /* For XPM pieces, we need bitmaps to use as masks. */
3612 CreateAnimMasks(info.depth), xpmDone = 1;
3617 static Boolean frameWaiting;
3620 FrameAlarm (int sig)
3622 frameWaiting = False;
3623 /* In case System-V style signals. Needed?? */
3624 signal(SIGALRM, FrameAlarm);
3628 FrameDelay (int time)
3630 struct itimerval delay;
3632 XSync(xDisplay, False);
3635 frameWaiting = True;
3636 signal(SIGALRM, FrameAlarm);
3637 delay.it_interval.tv_sec =
3638 delay.it_value.tv_sec = time / 1000;
3639 delay.it_interval.tv_usec =
3640 delay.it_value.tv_usec = (time % 1000) * 1000;
3641 setitimer(ITIMER_REAL, &delay, NULL);
3642 while (frameWaiting) pause();
3643 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
3644 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
3645 setitimer(ITIMER_REAL, &delay, NULL);
3652 FrameDelay (int time)
3654 XSync(xDisplay, False);
3656 usleep(time * 1000);
3662 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
3666 /* Bitmap for piece being moved. */
3667 if (appData.monoMode) {
3668 *mask = *pieceToSolid(piece);
3669 } else if (useImages) {
3671 *mask = xpmMask[piece];
3673 *mask = ximMaskPm[piece];
3676 *mask = *pieceToSolid(piece);
3679 /* GC for piece being moved. Square color doesn't matter, but
3680 since it gets modified we make a copy of the original. */
3682 if (appData.monoMode)
3687 if (appData.monoMode)
3692 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
3694 /* Outline only used in mono mode and is not modified */
3696 *outline = bwPieceGC;
3698 *outline = wbPieceGC;
3702 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
3707 /* Draw solid rectangle which will be clipped to shape of piece */
3708 XFillRectangle(xDisplay, dest, clip,
3709 0, 0, squareSize, squareSize);
3710 if (appData.monoMode)
3711 /* Also draw outline in contrasting color for black
3712 on black / white on white cases */
3713 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
3714 0, 0, squareSize, squareSize, 0, 0, 1);
3716 /* Copy the piece */
3721 if(appData.upsideDown && flipView) kind ^= 2;
3722 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3724 0, 0, squareSize, squareSize,
3730 InsertPiece (AnimNr anr, ChessSquare piece)
3732 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
3736 DrawBlank (AnimNr anr, int x, int y, int startColor)
3738 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
3741 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
3742 int srcX, int srcY, int width, int height, int destX, int destY)
3744 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
3745 srcX, srcY, width, height, destX, destY);
3749 SetDragPiece (AnimNr anr, ChessSquare piece)
3752 /* The piece will be drawn using its own bitmap as a matte */
3753 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
3754 XSetClipMask(xDisplay, animGCs[anr+2], mask);
3757 /* [AS] Arrow highlighting support */
3760 DrawPolygon (Pnt arrow[], int nr)
3764 for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
3765 XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
3766 if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
3770 UpdateLogos (int displ)
3772 return; // no logos in XBoard yet