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)
842 XWindowAttributes winAt;
849 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
850 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
851 wp->x = rx - winAt.x;
852 wp->y = ry - winAt.y;
853 wp->height = winAt.height;
854 wp->width = winAt.width;
855 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
860 { // wrapper to shield use of window handles from back-end (make addressible by number?)
861 // In XBoard this will have to wait until awareness of window parameters is implemented
862 GetActualPlacement(shellWidget, &wpMain);
863 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
864 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
865 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
866 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
867 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
868 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
872 PrintCommPortSettings (FILE *f, char *name)
873 { // This option does not exist in XBoard
877 EnsureOnScreen (int *x, int *y, int minX, int minY)
884 { // [HGM] args: allows testing if main window is realized from back-end
885 return xBoardWindow != 0;
890 extern Option dualOptions[];
892 Window tmp = xBoardWindow;
893 if(!dual) dual = XtWindow(dualOptions[3].handle); // must be first call
894 xBoardWindow = dual; // swap them
899 PopUpStartupDialog ()
900 { // start menu not implemented in XBoard
904 ConvertToLine (int argc, char **argv)
906 static char line[128*1024], buf[1024];
910 for(i=1; i<argc; i++)
912 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
913 && argv[i][0] != '{' )
914 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
916 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
917 strncat(line, buf, 128*1024 - strlen(line) - 1 );
920 line[strlen(line)-1] = NULLCHAR;
924 //--------------------------------------------------------------------------------------------
926 #define BoardSize int
928 InitDrawingSizes (BoardSize boardSize, int flags)
929 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
930 Dimension boardWidth, boardHeight, w, h;
932 static Dimension oldWidth, oldHeight;
933 static VariantClass oldVariant;
934 static int oldMono = -1, oldTwoBoards = 0;
936 if(!formWidget) return;
938 if(oldTwoBoards && !twoBoards) PopDown(DummyDlg);
939 oldTwoBoards = twoBoards;
941 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
942 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
943 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
945 if(boardWidth != oldWidth || boardHeight != oldHeight) { // do resizing stuff only if size actually changed
947 oldWidth = boardWidth; oldHeight = boardHeight;
951 * Inhibit shell resizing.
953 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW ;
954 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
955 shellArgs[4].value = shellArgs[2].value = w;
956 shellArgs[5].value = shellArgs[3].value = h;
957 XtSetValues(shellWidget, &shellArgs[0], 6);
959 XSync(xDisplay, False);
963 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
966 if(gameInfo.variant != oldVariant) { // and only if variant changed
971 for(p=0; p<=(int)WhiteKing; p++)
972 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
973 if(gameInfo.variant == VariantShogi) {
974 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
975 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
976 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
977 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
978 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
981 if(gameInfo.variant == VariantGothic) {
982 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
985 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
986 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
987 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
990 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
991 for(p=0; p<=(int)WhiteKing; p++)
992 ximMaskPm[p] = ximMaskPm2[p]; // defaults
993 if(gameInfo.variant == VariantShogi) {
994 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
995 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
996 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
997 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
998 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1001 if(gameInfo.variant == VariantGothic) {
1002 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1005 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1006 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1007 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1012 for(i=0; i<2; i++) {
1014 for(p=0; p<=(int)WhiteKing; p++)
1015 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1016 if(gameInfo.variant == VariantShogi) {
1017 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1018 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1019 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1020 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1021 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1024 if(gameInfo.variant == VariantGothic) {
1025 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1028 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1029 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1030 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1034 oldMono = -10; // kludge to force recreation of animation masks
1035 oldVariant = gameInfo.variant;
1038 if(appData.monoMode != oldMono)
1041 oldMono = appData.monoMode;
1045 MakeOneColor (char *name, Pixel *color)
1047 XrmValue vFrom, vTo;
1048 if (!appData.monoMode) {
1049 vFrom.addr = (caddr_t) name;
1050 vFrom.size = strlen(name);
1051 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1052 if (vTo.addr == NULL) {
1053 appData.monoMode = True;
1056 *color = *(Pixel *) vTo.addr;
1064 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1065 int forceMono = False;
1067 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1068 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1069 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1070 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1071 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1072 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1073 if (appData.lowTimeWarning)
1074 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
1075 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1076 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1083 { // [HGM] taken out of main
1085 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1086 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1087 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1089 if (appData.bitmapDirectory[0] != NULLCHAR) {
1093 CreateXPMBoard(appData.liteBackTextureFile, 1);
1094 CreateXPMBoard(appData.darkBackTextureFile, 0);
1098 /* Create regular pieces */
1099 if (!useImages) CreatePieces();
1104 InitDrawingParams ()
1106 MakeColors(); CreateGCs(True);
1111 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
1112 { // detervtomine what fonts to use, and create them
1116 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1117 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1118 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1119 appData.font = fontTable[MESSAGE_FONT][squareSize];
1120 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1121 appData.coordFont = fontTable[COORD_FONT][squareSize];
1124 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1125 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1126 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1127 fontSet = CreateFontSet(appData.font);
1128 clockFontSet = CreateFontSet(appData.clockFont);
1130 /* For the coordFont, use the 0th font of the fontset. */
1131 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1132 XFontStruct **font_struct_list;
1133 XFontSetExtents *fontSize;
1134 char **font_name_list;
1135 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1136 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1137 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1138 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1139 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1142 appData.font = FindFont(appData.font, fontPxlSize);
1143 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1144 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1145 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1146 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1147 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1148 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1149 // textHeight in !NLS mode!
1151 countFontID = coordFontID; // [HGM] holdings
1152 countFontStruct = coordFontStruct;
1154 xdb = XtDatabase(xDisplay);
1156 XrmPutLineResource(&xdb, "*international: True");
1157 vTo.size = sizeof(XFontSet);
1158 vTo.addr = (XtPointer) &fontSet;
1159 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1161 XrmPutStringResource(&xdb, "*font", appData.font);
1166 main (int argc, char **argv)
1168 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1169 XSetWindowAttributes window_attributes;
1171 Dimension boardWidth, boardHeight, w, h;
1173 int forceMono = False;
1175 srandom(time(0)); // [HGM] book: make random truly random
1177 setbuf(stdout, NULL);
1178 setbuf(stderr, NULL);
1181 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1182 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1186 programName = strrchr(argv[0], '/');
1187 if (programName == NULL)
1188 programName = argv[0];
1193 XtSetLanguageProc(NULL, NULL, NULL);
1194 if (appData.debugMode) {
1195 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1198 bindtextdomain(PACKAGE, LOCALEDIR);
1199 textdomain(PACKAGE);
1202 appData.boardSize = "";
1203 InitAppData(ConvertToLine(argc, argv));
1205 if (p == NULL) p = "/tmp";
1206 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1207 gameCopyFilename = (char*) malloc(i);
1208 gamePasteFilename = (char*) malloc(i);
1209 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1210 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1212 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1213 static char buf[MSG_SIZ];
1214 EscapeExpand(buf, appData.firstInitString);
1215 appData.firstInitString = strdup(buf);
1216 EscapeExpand(buf, appData.secondInitString);
1217 appData.secondInitString = strdup(buf);
1218 EscapeExpand(buf, appData.firstComputerString);
1219 appData.firstComputerString = strdup(buf);
1220 EscapeExpand(buf, appData.secondComputerString);
1221 appData.secondComputerString = strdup(buf);
1224 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1227 if (chdir(chessDir) != 0) {
1228 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1234 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1235 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1236 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1237 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1240 setbuf(debugFP, NULL);
1243 /* [HGM,HR] make sure board size is acceptable */
1244 if(appData.NrFiles > BOARD_FILES ||
1245 appData.NrRanks > BOARD_RANKS )
1246 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1249 /* This feature does not work; animation needs a rewrite */
1250 appData.highlightDragging = FALSE;
1254 gameInfo.variant = StringToVariant(appData.variant);
1255 InitPosition(FALSE);
1258 XtAppInitialize(&appContext, "XBoard", shellOptions,
1259 XtNumber(shellOptions),
1260 &argc, argv, xboardResources, NULL, 0);
1262 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1263 clientResources, XtNumber(clientResources),
1266 xDisplay = XtDisplay(shellWidget);
1267 xScreen = DefaultScreen(xDisplay);
1268 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1271 * determine size, based on supplied or remembered -size, or screen size
1273 if (isdigit(appData.boardSize[0])) {
1274 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1275 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1276 &fontPxlSize, &smallLayout, &tinyLayout);
1278 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1279 programName, appData.boardSize);
1283 /* Find some defaults; use the nearest known size */
1284 SizeDefaults *szd, *nearest;
1285 int distance = 99999;
1286 nearest = szd = sizeDefaults;
1287 while (szd->name != NULL) {
1288 if (abs(szd->squareSize - squareSize) < distance) {
1290 distance = abs(szd->squareSize - squareSize);
1291 if (distance == 0) break;
1295 if (i < 2) lineGap = nearest->lineGap;
1296 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1297 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1298 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1299 if (i < 6) smallLayout = nearest->smallLayout;
1300 if (i < 7) tinyLayout = nearest->tinyLayout;
1303 SizeDefaults *szd = sizeDefaults;
1304 if (*appData.boardSize == NULLCHAR) {
1305 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1306 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1309 if (szd->name == NULL) szd--;
1310 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1312 while (szd->name != NULL &&
1313 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1314 if (szd->name == NULL) {
1315 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1316 programName, appData.boardSize);
1320 squareSize = szd->squareSize;
1321 lineGap = szd->lineGap;
1322 clockFontPxlSize = szd->clockFontPxlSize;
1323 coordFontPxlSize = szd->coordFontPxlSize;
1324 fontPxlSize = szd->fontPxlSize;
1325 smallLayout = szd->smallLayout;
1326 tinyLayout = szd->tinyLayout;
1327 // [HGM] font: use defaults from settings file if available and not overruled
1330 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1331 if (strlen(appData.pixmapDirectory) > 0) {
1332 p = ExpandPathName(appData.pixmapDirectory);
1334 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1335 appData.pixmapDirectory);
1338 if (appData.debugMode) {
1339 fprintf(stderr, _("\
1340 XBoard square size (hint): %d\n\
1341 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1343 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1344 if (appData.debugMode) {
1345 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1348 defaultLineGap = lineGap;
1349 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1351 /* [HR] height treated separately (hacked) */
1352 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1353 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1356 * Determine what fonts to use.
1358 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1361 * Detect if there are not enough colors available and adapt.
1363 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1364 appData.monoMode = True;
1367 forceMono = MakeColors();
1370 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1372 appData.monoMode = True;
1375 if (appData.monoMode && appData.debugMode) {
1376 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1377 (unsigned long) XWhitePixel(xDisplay, xScreen),
1378 (unsigned long) XBlackPixel(xDisplay, xScreen));
1381 ParseIcsTextColors();
1383 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1389 layoutName = "tinyLayout";
1390 } else if (smallLayout) {
1391 layoutName = "smallLayout";
1393 layoutName = "normalLayout";
1396 optList = BoardPopUp(squareSize, lineGap, (void*)
1402 boardWidget = optList[W_BOARD].handle;
1403 menuBarWidget = optList[W_MENU].handle;
1404 dropMenu = optList[W_DROP].handle;
1405 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1406 formWidget = XtParent(boardWidget);
1407 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1408 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1409 XtGetValues(optList[W_WHITE].handle, args, 2);
1410 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1411 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1412 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1413 XtGetValues(optList[W_PAUSE].handle, args, 2);
1415 AppendEnginesToMenu(appData.recentEngineList);
1417 xBoardWindow = XtWindow(boardWidget);
1419 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1420 // not need to go into InitDrawingSizes().
1423 * Create X checkmark bitmap and initialize option menu checks.
1425 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1426 checkmark_bits, checkmark_width, checkmark_height);
1432 ReadBitmap(&wIconPixmap, "icon_white.bm",
1433 icon_white_bits, icon_white_width, icon_white_height);
1434 ReadBitmap(&bIconPixmap, "icon_black.bm",
1435 icon_black_bits, icon_black_width, icon_black_height);
1436 iconPixmap = wIconPixmap;
1438 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1439 XtSetValues(shellWidget, args, i);
1442 * Create a cursor for the board widget.
1444 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1445 XChangeWindowAttributes(xDisplay, xBoardWindow,
1446 CWCursor, &window_attributes);
1449 * Inhibit shell resizing.
1451 shellArgs[0].value = (XtArgVal) &w;
1452 shellArgs[1].value = (XtArgVal) &h;
1453 XtGetValues(shellWidget, shellArgs, 2);
1454 shellArgs[4].value = shellArgs[2].value = w;
1455 shellArgs[5].value = shellArgs[3].value = h;
1456 XtSetValues(shellWidget, &shellArgs[2], 4);
1457 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1458 marginH = h - boardHeight;
1460 CatchDeleteWindow(shellWidget, "QuitProc");
1466 if (appData.animate || appData.animateDragging)
1469 XtAugmentTranslations(formWidget,
1470 XtParseTranslationTable(globalTranslations));
1472 XtAddEventHandler(formWidget, KeyPressMask, False,
1473 (XtEventHandler) MoveTypeInProc, NULL);
1474 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1475 (XtEventHandler) EventProc, NULL);
1477 /* [AS] Restore layout */
1478 if( wpMoveHistory.visible ) {
1482 if( wpEvalGraph.visible )
1487 if( wpEngineOutput.visible ) {
1488 EngineOutputPopUp();
1493 if (errorExitStatus == -1) {
1494 if (appData.icsActive) {
1495 /* We now wait until we see "login:" from the ICS before
1496 sending the logon script (problems with timestamp otherwise) */
1497 /*ICSInitScript();*/
1498 if (appData.icsInputBox) ICSInputBoxPopUp();
1502 signal(SIGWINCH, TermSizeSigHandler);
1504 signal(SIGINT, IntSigHandler);
1505 signal(SIGTERM, IntSigHandler);
1506 if (*appData.cmailGameName != NULLCHAR) {
1507 signal(SIGUSR1, CmailSigHandler);
1511 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1513 // XtSetKeyboardFocus(shellWidget, formWidget);
1514 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1516 XtAppMainLoop(appContext);
1517 if (appData.debugMode) fclose(debugFP); // [DM] debug
1522 TermSizeSigHandler (int sig)
1528 IntSigHandler (int sig)
1534 CmailSigHandler (int sig)
1539 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1541 /* Activate call-back function CmailSigHandlerCallBack() */
1542 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1544 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1548 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1551 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1553 /**** end signal code ****/
1556 #define Abs(n) ((n)<0 ? -(n) : (n))
1560 InsertPxlSize (char *pattern, int targetPxlSize)
1562 char *base_fnt_lst, strInt[12], *p, *q;
1563 int alternatives, i, len, strIntLen;
1566 * Replace the "*" (if present) in the pixel-size slot of each
1567 * alternative with the targetPxlSize.
1571 while ((p = strchr(p, ',')) != NULL) {
1575 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1576 strIntLen = strlen(strInt);
1577 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1581 while (alternatives--) {
1582 char *comma = strchr(p, ',');
1583 for (i=0; i<14; i++) {
1584 char *hyphen = strchr(p, '-');
1586 if (comma && hyphen > comma) break;
1587 len = hyphen + 1 - p;
1588 if (i == 7 && *p == '*' && len == 2) {
1590 memcpy(q, strInt, strIntLen);
1600 len = comma + 1 - p;
1607 return base_fnt_lst;
1611 CreateFontSet (char *base_fnt_lst)
1614 char **missing_list;
1618 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1619 &missing_list, &missing_count, &def_string);
1620 if (appData.debugMode) {
1622 XFontStruct **font_struct_list;
1623 char **font_name_list;
1624 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1626 fprintf(debugFP, " got list %s, locale %s\n",
1627 XBaseFontNameListOfFontSet(fntSet),
1628 XLocaleOfFontSet(fntSet));
1629 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1630 for (i = 0; i < count; i++) {
1631 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1634 for (i = 0; i < missing_count; i++) {
1635 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1638 if (fntSet == NULL) {
1639 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1644 #else // not ENABLE_NLS
1646 * Find a font that matches "pattern" that is as close as
1647 * possible to the targetPxlSize. Prefer fonts that are k
1648 * pixels smaller to fonts that are k pixels larger. The
1649 * pattern must be in the X Consortium standard format,
1650 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1651 * The return value should be freed with XtFree when no
1655 FindFont (char *pattern, int targetPxlSize)
1657 char **fonts, *p, *best, *scalable, *scalableTail;
1658 int i, j, nfonts, minerr, err, pxlSize;
1660 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1662 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1663 programName, pattern);
1670 for (i=0; i<nfonts; i++) {
1673 if (*p != '-') continue;
1675 if (*p == NULLCHAR) break;
1676 if (*p++ == '-') j++;
1678 if (j < 7) continue;
1681 scalable = fonts[i];
1684 err = pxlSize - targetPxlSize;
1685 if (Abs(err) < Abs(minerr) ||
1686 (minerr > 0 && err < 0 && -err == minerr)) {
1692 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1693 /* If the error is too big and there is a scalable font,
1694 use the scalable font. */
1695 int headlen = scalableTail - scalable;
1696 p = (char *) XtMalloc(strlen(scalable) + 10);
1697 while (isdigit(*scalableTail)) scalableTail++;
1698 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1700 p = (char *) XtMalloc(strlen(best) + 2);
1701 safeStrCpy(p, best, strlen(best)+1 );
1703 if (appData.debugMode) {
1704 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1705 pattern, targetPxlSize, p);
1707 XFreeFontNames(fonts);
1714 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
1715 // must be called before all non-first callse to CreateGCs()
1716 XtReleaseGC(shellWidget, highlineGC);
1717 XtReleaseGC(shellWidget, lightSquareGC);
1718 XtReleaseGC(shellWidget, darkSquareGC);
1719 XtReleaseGC(shellWidget, lineGC);
1720 if (appData.monoMode) {
1721 if (DefaultDepth(xDisplay, xScreen) == 1) {
1722 XtReleaseGC(shellWidget, wbPieceGC);
1724 XtReleaseGC(shellWidget, bwPieceGC);
1727 XtReleaseGC(shellWidget, prelineGC);
1728 XtReleaseGC(shellWidget, wdPieceGC);
1729 XtReleaseGC(shellWidget, wlPieceGC);
1730 XtReleaseGC(shellWidget, bdPieceGC);
1731 XtReleaseGC(shellWidget, blPieceGC);
1736 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
1738 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1739 | GCBackground | GCFunction | GCPlaneMask;
1740 gc_values->foreground = foreground;
1741 gc_values->background = background;
1742 return XtGetGC(shellWidget, value_mask, gc_values);
1746 CreateGCs (int redo)
1748 XGCValues gc_values;
1750 Pixel white = XWhitePixel(xDisplay, xScreen);
1751 Pixel black = XBlackPixel(xDisplay, xScreen);
1753 gc_values.plane_mask = AllPlanes;
1754 gc_values.line_width = lineGap;
1755 gc_values.line_style = LineSolid;
1756 gc_values.function = GXcopy;
1759 DeleteGCs(); // called a second time; clean up old GCs first
1760 } else { // [HGM] grid and font GCs created on first call only
1761 coordGC = CreateOneGC(&gc_values, black, white);
1762 XSetFont(xDisplay, coordGC, coordFontID);
1764 // [HGM] make font for holdings counts (white on black)
1765 countGC = CreateOneGC(&gc_values, white, black);
1766 XSetFont(xDisplay, countGC, countFontID);
1768 lineGC = CreateOneGC(&gc_values, black, black);
1770 if (appData.monoMode) {
1772 highlineGC = CreateOneGC(&gc_values, white, white);
1773 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
1774 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
1776 if (DefaultDepth(xDisplay, xScreen) == 1) {
1777 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
1778 gc_values.function = GXcopyInverted;
1779 copyInvertedGC = CreateOneGC(&gc_values, black, white);
1780 gc_values.function = GXcopy;
1781 if (XBlackPixel(xDisplay, xScreen) == 1) {
1782 bwPieceGC = darkSquareGC;
1783 wbPieceGC = copyInvertedGC;
1785 bwPieceGC = copyInvertedGC;
1786 wbPieceGC = lightSquareGC;
1791 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
1792 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
1793 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
1794 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
1795 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
1796 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
1797 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
1798 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
1803 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
1811 fp = fopen(filename, "rb");
1813 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
1820 for (y=0; y<h; ++y) {
1821 for (x=0; x<h; ++x) {
1826 XPutPixel(xim, x, y, blackPieceColor);
1828 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1831 XPutPixel(xim, x, y, darkSquareColor);
1833 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1836 XPutPixel(xim, x, y, whitePieceColor);
1838 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1841 XPutPixel(xim, x, y, lightSquareColor);
1843 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1851 /* create Pixmap of piece */
1852 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1854 XPutImage(xDisplay, *dest, lightSquareGC, xim,
1857 /* create Pixmap of clipmask
1858 Note: We assume the white/black pieces have the same
1859 outline, so we make only 6 masks. This is okay
1860 since the XPM clipmask routines do the same. */
1862 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1864 XPutImage(xDisplay, temp, lightSquareGC, xmask,
1867 /* now create the 1-bit version */
1868 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1871 values.foreground = 1;
1872 values.background = 0;
1874 /* Don't use XtGetGC, not read only */
1875 maskGC = XCreateGC(xDisplay, *mask,
1876 GCForeground | GCBackground, &values);
1877 XCopyPlane(xDisplay, temp, *mask, maskGC,
1878 0, 0, squareSize, squareSize, 0, 0, 1);
1879 XFreePixmap(xDisplay, temp);
1884 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
1892 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
1897 /* The XSynchronize calls were copied from CreatePieces.
1898 Not sure if needed, but can't hurt */
1899 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
1902 /* temp needed by loadXIM() */
1903 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1904 0, 0, ss, ss, AllPlanes, XYPixmap);
1906 if (strlen(appData.pixmapDirectory) == 0) {
1910 if (appData.monoMode) {
1911 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
1915 fprintf(stderr, _("\nLoading XIMs...\n"));
1917 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
1918 fprintf(stderr, "%d", piece+1);
1919 for (kind=0; kind<4; kind++) {
1920 fprintf(stderr, ".");
1921 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
1922 ExpandPathName(appData.pixmapDirectory),
1923 piece <= (int) WhiteKing ? "" : "w",
1924 pieceBitmapNames[piece],
1926 ximPieceBitmap[kind][piece] =
1927 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1928 0, 0, ss, ss, AllPlanes, XYPixmap);
1929 if (appData.debugMode)
1930 fprintf(stderr, _("(File:%s:) "), buf);
1931 loadXIM(ximPieceBitmap[kind][piece],
1933 &(xpmPieceBitmap2[kind][piece]),
1934 &(ximMaskPm2[piece]));
1935 if(piece <= (int)WhiteKing)
1936 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
1938 fprintf(stderr," ");
1940 /* Load light and dark squares */
1941 /* If the LSQ and DSQ pieces don't exist, we will
1942 draw them with solid squares. */
1943 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
1944 if (access(buf, 0) != 0) {
1948 fprintf(stderr, _("light square "));
1950 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1951 0, 0, ss, ss, AllPlanes, XYPixmap);
1952 if (appData.debugMode)
1953 fprintf(stderr, _("(File:%s:) "), buf);
1955 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
1956 fprintf(stderr, _("dark square "));
1957 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
1958 ExpandPathName(appData.pixmapDirectory), ss);
1959 if (appData.debugMode)
1960 fprintf(stderr, _("(File:%s:) "), buf);
1962 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1963 0, 0, ss, ss, AllPlanes, XYPixmap);
1964 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
1965 xpmJailSquare = xpmLightSquare;
1967 fprintf(stderr, _("Done.\n"));
1969 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
1972 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
1976 CreateXPMBoard (char *s, int kind)
1980 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
1981 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
1982 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
1988 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
1989 // thisroutine has to be called t free the old piece pixmaps
1991 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
1992 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
1994 XFreePixmap(xDisplay, xpmLightSquare);
1995 XFreePixmap(xDisplay, xpmDarkSquare);
2004 u_int ss = squareSize;
2006 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2007 XpmColorSymbol symbols[4];
2008 static int redo = False;
2010 if(redo) FreeXPMPieces(); else redo = 1;
2012 /* The XSynchronize calls were copied from CreatePieces.
2013 Not sure if needed, but can't hurt */
2014 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2016 /* Setup translations so piece colors match square colors */
2017 symbols[0].name = "light_piece";
2018 symbols[0].value = appData.whitePieceColor;
2019 symbols[1].name = "dark_piece";
2020 symbols[1].value = appData.blackPieceColor;
2021 symbols[2].name = "light_square";
2022 symbols[2].value = appData.lightSquareColor;
2023 symbols[3].name = "dark_square";
2024 symbols[3].value = appData.darkSquareColor;
2026 attr.valuemask = XpmColorSymbols;
2027 attr.colorsymbols = symbols;
2028 attr.numsymbols = 4;
2030 if (appData.monoMode) {
2031 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2035 if (strlen(appData.pixmapDirectory) == 0) {
2036 XpmPieces* pieces = builtInXpms;
2039 while (pieces->size != squareSize && pieces->size) pieces++;
2040 if (!pieces->size) {
2041 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2044 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2045 for (kind=0; kind<4; kind++) {
2047 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2048 pieces->xpm[piece][kind],
2049 &(xpmPieceBitmap2[kind][piece]),
2050 NULL, &attr)) != 0) {
2051 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2055 if(piece <= (int) WhiteKing)
2056 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2060 xpmJailSquare = xpmLightSquare;
2064 fprintf(stderr, _("\nLoading XPMs...\n"));
2067 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2068 fprintf(stderr, "%d ", piece+1);
2069 for (kind=0; kind<4; kind++) {
2070 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2071 ExpandPathName(appData.pixmapDirectory),
2072 piece > (int) WhiteKing ? "w" : "",
2073 pieceBitmapNames[piece],
2075 if (appData.debugMode) {
2076 fprintf(stderr, _("(File:%s:) "), buf);
2078 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2079 &(xpmPieceBitmap2[kind][piece]),
2080 NULL, &attr)) != 0) {
2081 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2082 // [HGM] missing: read of unorthodox piece failed; substitute King.
2083 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2084 ExpandPathName(appData.pixmapDirectory),
2086 if (appData.debugMode) {
2087 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2089 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2090 &(xpmPieceBitmap2[kind][piece]),
2094 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2099 if(piece <= (int) WhiteKing)
2100 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2103 /* Load light and dark squares */
2104 /* If the LSQ and DSQ pieces don't exist, we will
2105 draw them with solid squares. */
2106 fprintf(stderr, _("light square "));
2107 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2108 if (access(buf, 0) != 0) {
2112 if (appData.debugMode)
2113 fprintf(stderr, _("(File:%s:) "), buf);
2115 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2116 &xpmLightSquare, NULL, &attr)) != 0) {
2117 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2120 fprintf(stderr, _("dark square "));
2121 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2122 ExpandPathName(appData.pixmapDirectory), ss);
2123 if (appData.debugMode) {
2124 fprintf(stderr, _("(File:%s:) "), buf);
2126 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2127 &xpmDarkSquare, NULL, &attr)) != 0) {
2128 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2132 xpmJailSquare = xpmLightSquare;
2133 fprintf(stderr, _("Done.\n"));
2135 oldVariant = -1; // kludge to force re-makig of animation masks
2136 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2139 #endif /* HAVE_LIBXPM */
2142 /* No built-in bitmaps */
2147 u_int ss = squareSize;
2149 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2152 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2153 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2154 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2155 pieceBitmapNames[piece],
2156 ss, kind == SOLID ? 's' : 'o');
2157 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2158 if(piece <= (int)WhiteKing)
2159 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2163 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2167 /* With built-in bitmaps */
2171 BuiltInBits* bib = builtInBits;
2174 u_int ss = squareSize;
2176 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2179 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2181 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2182 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2183 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2184 pieceBitmapNames[piece],
2185 ss, kind == SOLID ? 's' : 'o');
2186 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2187 bib->bits[kind][piece], ss, ss);
2188 if(piece <= (int)WhiteKing)
2189 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2193 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2199 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2204 char msg[MSG_SIZ], fullname[MSG_SIZ];
2206 if (*appData.bitmapDirectory != NULLCHAR) {
2207 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2208 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2209 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2210 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2211 &w, &h, pm, &x_hot, &y_hot);
2212 fprintf(stderr, "load %s\n", name);
2213 if (errcode != BitmapSuccess) {
2215 case BitmapOpenFailed:
2216 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2218 case BitmapFileInvalid:
2219 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2221 case BitmapNoMemory:
2222 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2226 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2230 fprintf(stderr, _("%s: %s...using built-in\n"),
2232 } else if (w != wreq || h != hreq) {
2234 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2235 programName, fullname, w, h, wreq, hreq);
2241 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2251 if (lineGap == 0) return;
2253 /* [HR] Split this into 2 loops for non-square boards. */
2255 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2256 gridSegments[i].x1 = 0;
2257 gridSegments[i].x2 =
2258 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2259 gridSegments[i].y1 = gridSegments[i].y2
2260 = lineGap / 2 + (i * (squareSize + lineGap));
2263 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2264 gridSegments[j + i].y1 = 0;
2265 gridSegments[j + i].y2 =
2266 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2267 gridSegments[j + i].x1 = gridSegments[j + i].x2
2268 = lineGap / 2 + (j * (squareSize + lineGap));
2273 MarkMenuItem (char *menuRef, int state)
2275 MenuItem *item = MenuNameToItem(menuRef);
2279 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2280 XtSetValues(item->handle, args, 1);
2285 EnableMenuItem (char *menuRef, int state)
2287 MenuItem *item = MenuNameToItem(menuRef);
2289 if(item) XtSetSensitive(item->handle, state);
2293 EnableButtonBar (int state)
2295 XtSetSensitive(optList[W_BUTTON].handle, state);
2300 SetMenuEnables (Enables *enab)
2302 while (enab->name != NULL) {
2303 EnableMenuItem(enab->name, enab->value);
2309 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2310 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2312 if(*nprms == 0) return;
2313 item = MenuNameToItem(prms[0]);
2314 if(item) ((MenuProc *) item->proc) ();
2318 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
2320 MenuProc *proc = (MenuProc *) addr;
2326 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2328 RecentEngineEvent((int) (intptr_t) addr);
2332 AppendMenuItem (char *msg, int n)
2334 CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
2346 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2347 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2348 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2349 dmEnables[i].piece);
2350 XtSetSensitive(entry, p != NULL || !appData.testLegality
2351 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2352 && !appData.icsActive));
2354 while (p && *p++ == dmEnables[i].piece) count++;
2355 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2357 XtSetArg(args[j], XtNlabel, label); j++;
2358 XtSetValues(entry, args, j);
2364 do_flash_delay (unsigned long msec)
2370 DrawBorder (int x, int y, int type)
2374 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
2376 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
2377 squareSize+lineGap, squareSize+lineGap);
2381 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
2383 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
2384 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
2386 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
2387 if(textureW[kind] < W*squareSize)
2388 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
2390 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
2391 if(textureH[kind] < H*squareSize)
2392 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
2394 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
2399 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
2400 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
2402 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
2403 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
2404 squareSize, squareSize, x*fac, y*fac);
2406 if (useImages && useImageSqs) {
2410 pm = xpmLightSquare;
2415 case 2: /* neutral */
2417 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
2420 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
2421 squareSize, squareSize, x*fac, y*fac);
2431 case 2: /* neutral */
2436 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
2441 I split out the routines to draw a piece so that I could
2442 make a generic flash routine.
2445 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2447 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2448 switch (square_color) {
2450 case 2: /* neutral */
2452 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2453 ? *pieceToOutline(piece)
2454 : *pieceToSolid(piece),
2455 dest, bwPieceGC, 0, 0,
2456 squareSize, squareSize, x, y);
2459 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2460 ? *pieceToSolid(piece)
2461 : *pieceToOutline(piece),
2462 dest, wbPieceGC, 0, 0,
2463 squareSize, squareSize, x, y);
2469 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2471 switch (square_color) {
2473 case 2: /* neutral */
2475 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2476 ? *pieceToOutline(piece)
2477 : *pieceToSolid(piece),
2478 dest, bwPieceGC, 0, 0,
2479 squareSize, squareSize, x, y, 1);
2482 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2483 ? *pieceToSolid(piece)
2484 : *pieceToOutline(piece),
2485 dest, wbPieceGC, 0, 0,
2486 squareSize, squareSize, x, y, 1);
2492 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2494 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
2495 switch (square_color) {
2497 XCopyPlane(xDisplay, *pieceToSolid(piece),
2498 dest, (int) piece < (int) BlackPawn
2499 ? wlPieceGC : blPieceGC, 0, 0,
2500 squareSize, squareSize, x, y, 1);
2503 XCopyPlane(xDisplay, *pieceToSolid(piece),
2504 dest, (int) piece < (int) BlackPawn
2505 ? wdPieceGC : bdPieceGC, 0, 0,
2506 squareSize, squareSize, x, y, 1);
2508 case 2: /* neutral */
2510 break; // should never contain pieces
2515 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2517 int kind, p = piece;
2519 switch (square_color) {
2521 case 2: /* neutral */
2523 if ((int)piece < (int) BlackPawn) {
2531 if ((int)piece < (int) BlackPawn) {
2539 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2540 if(useTexture & square_color+1) {
2541 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2542 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
2543 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
2544 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
2545 XSetClipMask(xDisplay, wlPieceGC, None);
2546 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
2548 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
2549 dest, wlPieceGC, 0, 0,
2550 squareSize, squareSize, x, y);
2553 typedef void (*DrawFunc)();
2558 if (appData.monoMode) {
2559 if (DefaultDepth(xDisplay, xScreen) == 1) {
2560 return monoDrawPiece_1bit;
2562 return monoDrawPiece;
2566 return colorDrawPieceImage;
2568 return colorDrawPiece;
2573 DrawDot (int marker, int x, int y, int r)
2575 if(appData.monoMode) {
2576 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
2577 x, y, r, r, 0, 64*360);
2578 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
2579 x, y, r, r, 0, 64*360);
2581 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
2582 x, y, r, r, 0, 64*360);
2586 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2587 { // basic front-end board-draw function: takes care of everything that can be in square:
2588 // piece, background, coordinate/count, marker dot
2589 int direction, font_ascent, font_descent;
2590 XCharStruct overall;
2593 if (piece == EmptySquare) {
2594 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2596 drawfunc = ChooseDrawFunc();
2597 drawfunc(piece, square_color, x, y, xBoardWindow);
2600 if(align) { // square carries inscription (coord or piece count)
2602 GC hGC = align < 3 ? coordGC : countGC;
2603 // first calculate where it goes
2604 XTextExtents(countFontStruct, string, 1, &direction,
2605 &font_ascent, &font_descent, &overall);
2607 xx += squareSize - overall.width - 2;
2608 yy += squareSize - font_descent - 1;
2609 } else if (align == 2) {
2610 xx += 2, yy += font_ascent + 1;
2611 } else if (align == 3) {
2612 xx += squareSize - overall.width - 2;
2613 yy += font_ascent + 1;
2614 } else if (align == 4) {
2615 xx += 2, yy += font_ascent + 1;
2618 if (appData.monoMode) {
2619 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2621 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2625 if(marker) { // print fat marker dot, if requested
2626 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2631 FlashDelay (int flash_delay)
2633 XSync(xDisplay, False);
2634 if(flash_delay) do_flash_delay(flash_delay);
2638 Fraction (int x, int start, int stop)
2640 double f = ((double) x - start)/(stop - start);
2641 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2645 static WindowPlacement wpNew;
2648 CoDrag (Widget sh, WindowPlacement *wp)
2651 int j=0, touch=0, fudge = 2;
2652 GetActualPlacement(sh, wp);
2653 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
2654 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
2655 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2656 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
2657 if(!touch ) return; // only windows that touch co-move
2658 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2659 int heightInc = wpNew.height - wpMain.height;
2660 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2661 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2662 wp->y += fracTop * heightInc;
2663 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2664 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2665 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2666 int widthInc = wpNew.width - wpMain.width;
2667 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2668 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2669 wp->y += fracLeft * widthInc;
2670 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2671 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2673 wp->x += wpNew.x - wpMain.x;
2674 wp->y += wpNew.y - wpMain.y;
2675 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2676 if(touch == 3) wp->y += wpNew.height - wpMain.height;
2677 XtSetArg(args[j], XtNx, wp->x); j++;
2678 XtSetArg(args[j], XtNy, wp->y); j++;
2679 XtSetValues(sh, args, j);
2682 static XtIntervalId delayedDragID = 0;
2687 GetActualPlacement(shellWidget, &wpNew);
2688 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
2689 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
2690 return; // false alarm
2691 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
2692 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
2693 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
2694 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
2696 DrawPosition(True, NULL);
2697 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
2704 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
2706 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
2710 EventProc (Widget widget, caddr_t unused, XEvent *event)
2712 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
2713 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
2716 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
2718 DrawSeekAxis (int x, int y, int xTo, int yTo)
2720 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
2724 DrawSeekBackground (int left, int top, int right, int bottom)
2726 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
2730 DrawSeekText (char *buf, int x, int y)
2732 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
2736 DrawSeekDot (int x, int y, int colorNr)
2738 int square = colorNr & 0x80;
2741 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
2743 XFillRectangle(xDisplay, xBoardWindow, color,
2744 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
2746 XFillArc(xDisplay, xBoardWindow, color,
2747 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
2753 XDrawSegments(xDisplay, xBoardWindow, lineGC,
2754 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
2759 * event handler for redrawing the board
2762 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2764 DrawPosition(True, NULL);
2769 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
2770 { // [HGM] pv: walk PV
2771 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
2774 static int savedIndex; /* gross that this is global */
2777 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
2780 XawTextPosition index, dummy;
2783 XawTextGetSelectionPos(w, &index, &dummy);
2784 XtSetArg(arg, XtNstring, &val);
2785 XtGetValues(w, &arg, 1);
2786 ReplaceComment(savedIndex, val);
2787 if(savedIndex != currentMove) ToNrEvent(savedIndex);
2788 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
2792 EditCommentPopUp (int index, char *title, char *text)
2795 if (text == NULL) text = "";
2796 NewCommentPopup(title, text, index);
2800 CommentPopUp (char *title, char *text)
2802 savedIndex = currentMove; // [HGM] vari
2803 NewCommentPopup(title, text, currentMove);
2809 PopDown(CommentDlg);
2812 static char *openName;
2818 (void) (*fileProc)(openFP, 0, openName);
2822 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
2824 fileProc = proc; /* I can't see a way not */
2825 fileOpenMode = openMode; /* to use globals here */
2826 { // [HGM] use file-selector dialog stolen from Ghostview
2827 // int index; // this is not supported yet
2828 Browse(BoardWindow, label, (def[0] ? def : NULL), filter, False, openMode, &openName, &openFP);
2833 /* Disable all user input other than deleting the window */
2834 static int frozen = 0;
2840 /* Grab by a widget that doesn't accept input */
2841 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
2845 /* Undo a FreezeUI */
2849 if (!frozen) return;
2850 XtRemoveGrab(optList[W_MESSG].handle);
2858 static int oldPausing = FALSE;
2859 static GameMode oldmode = (GameMode) -1;
2862 if (!boardWidget || !XtIsRealized(boardWidget)) return;
2864 if (pausing != oldPausing) {
2865 oldPausing = pausing;
2866 MarkMenuItem("Mode.Pause", pausing);
2868 if (appData.showButtonBar) {
2869 /* Always toggle, don't set. Previous code messes up when
2870 invoked while the button is pressed, as releasing it
2871 toggles the state again. */
2874 XtSetArg(args[0], XtNbackground, &oldbg);
2875 XtSetArg(args[1], XtNforeground, &oldfg);
2876 XtGetValues(optList[W_PAUSE].handle,
2878 XtSetArg(args[0], XtNbackground, oldfg);
2879 XtSetArg(args[1], XtNforeground, oldbg);
2881 XtSetValues(optList[W_PAUSE].handle, args, 2);
2885 wname = ModeToWidgetName(oldmode);
2886 if (wname != NULL) {
2887 MarkMenuItem(wname, False);
2889 wname = ModeToWidgetName(gameMode);
2890 if (wname != NULL) {
2891 MarkMenuItem(wname, True);
2894 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
2896 /* Maybe all the enables should be handled here, not just this one */
2897 EnableMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
2902 * Button/menu procedures
2905 /* this variable is shared between CopyPositionProc and SendPositionSelection */
2906 char *selected_fen_position=NULL;
2909 SendPositionSelection (Widget w, Atom *selection, Atom *target,
2910 Atom *type_return, XtPointer *value_return,
2911 unsigned long *length_return, int *format_return)
2913 char *selection_tmp;
2915 // if (!selected_fen_position) return False; /* should never happen */
2916 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
2917 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
2918 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
2921 if (f == NULL) return False;
2925 selection_tmp = XtMalloc(len + 1);
2926 count = fread(selection_tmp, 1, len, f);
2929 XtFree(selection_tmp);
2932 selection_tmp[len] = NULLCHAR;
2934 /* note: since no XtSelectionDoneProc was registered, Xt will
2935 * automatically call XtFree on the value returned. So have to
2936 * make a copy of it allocated with XtMalloc */
2937 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
2938 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
2941 *value_return=selection_tmp;
2942 *length_return=strlen(selection_tmp);
2943 *type_return=*target;
2944 *format_return = 8; /* bits per byte */
2946 } else if (*target == XA_TARGETS(xDisplay)) {
2947 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
2948 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
2949 targets_tmp[1] = XA_STRING;
2950 *value_return = targets_tmp;
2951 *type_return = XA_ATOM;
2954 // This code leads to a read of value_return out of bounds on 64-bit systems.
2955 // Other code which I have seen always sets *format_return to 32 independent of
2956 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
2957 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
2958 *format_return = 8 * sizeof(Atom);
2959 if (*format_return > 32) {
2960 *length_return *= *format_return / 32;
2961 *format_return = 32;
2964 *format_return = 32;
2972 /* note: when called from menu all parameters are NULL, so no clue what the
2973 * Widget which was clicked on was, or what the click event was
2976 CopySomething (char *src)
2978 selected_fen_position = src;
2980 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
2981 * have a notion of a position that is selected but not copied.
2982 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
2984 XtOwnSelection(menuBarWidget, XA_PRIMARY,
2986 SendPositionSelection,
2987 NULL/* lose_ownership_proc */ ,
2988 NULL/* transfer_done_proc */);
2989 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
2991 SendPositionSelection,
2992 NULL/* lose_ownership_proc */ ,
2993 NULL/* transfer_done_proc */);
2996 /* function called when the data to Paste is ready */
2998 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
2999 Atom *type, XtPointer value, unsigned long *len, int *format)
3002 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3003 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3004 EditPositionPasteFEN(fenstr);
3008 /* called when Paste Position button is pressed,
3009 * all parameters will be NULL */
3011 PastePositionProc ()
3013 XtGetSelectionValue(menuBarWidget,
3014 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3015 /* (XtSelectionCallbackProc) */ PastePositionCB,
3016 NULL, /* client_data passed to PastePositionCB */
3018 /* better to use the time field from the event that triggered the
3019 * call to this function, but that isn't trivial to get
3026 /* note: when called from menu all parameters are NULL, so no clue what the
3027 * Widget which was clicked on was, or what the click event was
3029 /* function called when the data to Paste is ready */
3031 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3032 Atom *type, XtPointer value, unsigned long *len, int *format)
3035 if (value == NULL || *len == 0) {
3036 return; /* nothing had been selected to copy */
3038 f = fopen(gamePasteFilename, "w");
3040 DisplayError(_("Can't open temp file"), errno);
3043 fwrite(value, 1, *len, f);
3046 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3049 /* called when Paste Game button is pressed,
3050 * all parameters will be NULL */
3054 XtGetSelectionValue(menuBarWidget,
3055 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3056 /* (XtSelectionCallbackProc) */ PasteGameCB,
3057 NULL, /* client_data passed to PasteGameCB */
3059 /* better to use the time field from the event that triggered the
3060 * call to this function, but that isn't trivial to get
3069 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3076 { // bassic primitive for determining if modifier keys are pressed
3077 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3080 XQueryKeymap(xDisplay,keys);
3081 for(i=0; i<6; i++) {
3083 j = XKeysymToKeycode(xDisplay, codes[i]);
3084 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3090 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3094 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3095 if ( n == 1 && *buf >= 32 // printable
3096 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3097 ) BoxAutoPopUp (buf);
3101 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3102 { // [HGM] input: let up-arrow recall previous line from history
3107 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3108 { // [HGM] input: let down-arrow recall next line from history
3113 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3119 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3121 if (!TempBackwardActive) {
3122 TempBackwardActive = True;
3128 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3130 /* Check to see if triggered by a key release event for a repeating key.
3131 * If so the next queued event will be a key press of the same key at the same time */
3132 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3134 XPeekEvent(xDisplay, &next);
3135 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3136 next.xkey.keycode == event->xkey.keycode)
3140 TempBackwardActive = False;
3144 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3145 { // called as key binding
3148 if (nprms && *nprms > 0)
3152 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3157 SetWindowTitle (char *text, char *title, char *icon)
3161 if (appData.titleInWindow) {
3163 XtSetArg(args[i], XtNlabel, text); i++;
3164 XtSetValues(titleWidget, args, i);
3167 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3168 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3169 XtSetValues(shellWidget, args, i);
3170 XSync(xDisplay, False);
3175 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3181 DisplayIcsInteractionTitle (String message)
3183 if (oldICSInteractionTitle == NULL) {
3184 /* Magic to find the old window title, adapted from vim */
3185 char *wina = getenv("WINDOWID");
3187 Window win = (Window) atoi(wina);
3188 Window root, parent, *children;
3189 unsigned int nchildren;
3190 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3192 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3193 if (!XQueryTree(xDisplay, win, &root, &parent,
3194 &children, &nchildren)) break;
3195 if (children) XFree((void *)children);
3196 if (parent == root || parent == 0) break;
3199 XSetErrorHandler(oldHandler);
3201 if (oldICSInteractionTitle == NULL) {
3202 oldICSInteractionTitle = "xterm";
3205 printf("\033]0;%s\007", message);
3210 XtIntervalId delayedEventTimerXID = 0;
3211 DelayedEventCallback delayedEventCallback = 0;
3216 delayedEventTimerXID = 0;
3217 delayedEventCallback();
3221 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3223 if(delayedEventTimerXID && delayedEventCallback == cb)
3224 // [HGM] alive: replace, rather than add or flush identical event
3225 XtRemoveTimeOut(delayedEventTimerXID);
3226 delayedEventCallback = cb;
3227 delayedEventTimerXID =
3228 XtAppAddTimeOut(appContext, millisec,
3229 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3232 DelayedEventCallback
3235 if (delayedEventTimerXID) {
3236 return delayedEventCallback;
3243 CancelDelayedEvent ()
3245 if (delayedEventTimerXID) {
3246 XtRemoveTimeOut(delayedEventTimerXID);
3247 delayedEventTimerXID = 0;
3251 XtIntervalId loadGameTimerXID = 0;
3254 LoadGameTimerRunning ()
3256 return loadGameTimerXID != 0;
3260 StopLoadGameTimer ()
3262 if (loadGameTimerXID != 0) {
3263 XtRemoveTimeOut(loadGameTimerXID);
3264 loadGameTimerXID = 0;
3272 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3274 loadGameTimerXID = 0;
3279 StartLoadGameTimer (long millisec)
3282 XtAppAddTimeOut(appContext, millisec,
3283 (XtTimerCallbackProc) LoadGameTimerCallback,
3287 XtIntervalId analysisClockXID = 0;
3290 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3292 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3293 || appData.icsEngineAnalyze) { // [DM]
3294 AnalysisPeriodicEvent(0);
3295 StartAnalysisClock();
3300 StartAnalysisClock ()
3303 XtAppAddTimeOut(appContext, 2000,
3304 (XtTimerCallbackProc) AnalysisClockCallback,
3308 XtIntervalId clockTimerXID = 0;
3311 ClockTimerRunning ()
3313 return clockTimerXID != 0;
3319 if (clockTimerXID != 0) {
3320 XtRemoveTimeOut(clockTimerXID);
3329 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3336 StartClockTimer (long millisec)
3339 XtAppAddTimeOut(appContext, millisec,
3340 (XtTimerCallbackProc) ClockTimerCallback,
3345 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
3349 Widget w = (Widget) opt->handle;
3351 /* check for low time warning */
3352 Pixel foregroundOrWarningColor = timerForegroundPixel;
3355 appData.lowTimeWarning &&
3356 (timer / 1000) < appData.icsAlarmTime)
3357 foregroundOrWarningColor = lowTimeWarningColor;
3359 if (appData.clockMode) {
3360 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
3361 XtSetArg(args[0], XtNlabel, buf);
3363 snprintf(buf, MSG_SIZ, "%s ", color);
3364 XtSetArg(args[0], XtNlabel, buf);
3369 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3370 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3372 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3373 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3376 XtSetValues(w, args, 3);
3379 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3382 SetClockIcon (int color)
3385 Pixmap pm = *clockIcons[color];
3386 if (iconPixmap != pm) {
3388 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3389 XtSetValues(shellWidget, args, 1);
3394 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3396 InputSource *is = (InputSource *) closure;
3401 if (is->lineByLine) {
3402 count = read(is->fd, is->unused,
3403 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3405 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3408 is->unused += count;
3410 while (p < is->unused) {
3411 q = memchr(p, '\n', is->unused - p);
3412 if (q == NULL) break;
3414 (is->func)(is, is->closure, p, q - p, 0);
3418 while (p < is->unused) {
3423 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3428 (is->func)(is, is->closure, is->buf, count, error);
3433 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3436 ChildProc *cp = (ChildProc *) pr;
3438 is = (InputSource *) calloc(1, sizeof(InputSource));
3439 is->lineByLine = lineByLine;
3443 is->fd = fileno(stdin);
3445 is->kind = cp->kind;
3446 is->fd = cp->fdFrom;
3449 is->unused = is->buf;
3452 is->xid = XtAppAddInput(appContext, is->fd,
3453 (XtPointer) (XtInputReadMask),
3454 (XtInputCallbackProc) DoInputCallback,
3456 is->closure = closure;
3457 return (InputSourceRef) is;
3461 RemoveInputSource (InputSourceRef isr)
3463 InputSource *is = (InputSource *) isr;
3465 if (is->xid == 0) return;
3466 XtRemoveInput(is->xid);
3470 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
3472 /* Masks for XPM pieces. Black and white pieces can have
3473 different shapes, but in the interest of retaining my
3474 sanity pieces must have the same outline on both light
3475 and dark squares, and all pieces must use the same
3476 background square colors/images. */
3478 static int xpmDone = 0;
3479 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
3480 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
3483 CreateAnimMasks (int pieceDepth)
3489 unsigned long plane;
3492 /* Need a bitmap just to get a GC with right depth */
3493 buf = XCreatePixmap(xDisplay, xBoardWindow,
3495 values.foreground = 1;
3496 values.background = 0;
3497 /* Don't use XtGetGC, not read only */
3498 maskGC = XCreateGC(xDisplay, buf,
3499 GCForeground | GCBackground, &values);
3500 XFreePixmap(xDisplay, buf);
3502 buf = XCreatePixmap(xDisplay, xBoardWindow,
3503 squareSize, squareSize, pieceDepth);
3504 values.foreground = XBlackPixel(xDisplay, xScreen);
3505 values.background = XWhitePixel(xDisplay, xScreen);
3506 bufGC = XCreateGC(xDisplay, buf,
3507 GCForeground | GCBackground, &values);
3509 for (piece = WhitePawn; piece <= BlackKing; piece++) {
3510 /* Begin with empty mask */
3511 if(!xpmDone) // [HGM] pieces: keep using existing
3512 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
3513 squareSize, squareSize, 1);
3514 XSetFunction(xDisplay, maskGC, GXclear);
3515 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
3516 0, 0, squareSize, squareSize);
3518 /* Take a copy of the piece */
3523 XSetFunction(xDisplay, bufGC, GXcopy);
3524 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
3526 0, 0, squareSize, squareSize, 0, 0);
3528 /* XOR the background (light) over the piece */
3529 XSetFunction(xDisplay, bufGC, GXxor);
3531 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
3532 0, 0, squareSize, squareSize, 0, 0);
3534 XSetForeground(xDisplay, bufGC, lightSquareColor);
3535 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
3538 /* We now have an inverted piece image with the background
3539 erased. Construct mask by just selecting all the non-zero
3540 pixels - no need to reconstruct the original image. */
3541 XSetFunction(xDisplay, maskGC, GXor);
3543 /* Might be quicker to download an XImage and create bitmap
3544 data from it rather than this N copies per piece, but it
3545 only takes a fraction of a second and there is a much
3546 longer delay for loading the pieces. */
3547 for (n = 0; n < pieceDepth; n ++) {
3548 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
3549 0, 0, squareSize, squareSize,
3555 XFreePixmap(xDisplay, buf);
3556 XFreeGC(xDisplay, bufGC);
3557 XFreeGC(xDisplay, maskGC);
3561 InitAnimState (AnimNr anr, XWindowAttributes *info)
3566 /* Each buffer is square size, same depth as window */
3567 animBufs[anr+4] = xBoardWindow;
3568 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
3569 squareSize, squareSize, info->depth);
3570 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
3571 squareSize, squareSize, info->depth);
3573 /* Create a plain GC for blitting */
3574 mask = GCForeground | GCBackground | GCFunction |
3575 GCPlaneMask | GCGraphicsExposures;
3576 values.foreground = XBlackPixel(xDisplay, xScreen);
3577 values.background = XWhitePixel(xDisplay, xScreen);
3578 values.function = GXcopy;
3579 values.plane_mask = AllPlanes;
3580 values.graphics_exposures = False;
3581 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
3583 /* Piece will be copied from an existing context at
3584 the start of each new animation/drag. */
3585 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
3587 /* Outline will be a read-only copy of an existing */
3588 animGCs[anr+4] = None;
3594 XWindowAttributes info;
3596 if (xpmDone && gameInfo.variant == oldVariant) return;
3597 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
3598 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
3600 InitAnimState(Game, &info);
3601 InitAnimState(Player, &info);
3603 /* For XPM pieces, we need bitmaps to use as masks. */
3605 CreateAnimMasks(info.depth), xpmDone = 1;
3610 static Boolean frameWaiting;
3613 FrameAlarm (int sig)
3615 frameWaiting = False;
3616 /* In case System-V style signals. Needed?? */
3617 signal(SIGALRM, FrameAlarm);
3621 FrameDelay (int time)
3623 struct itimerval delay;
3625 XSync(xDisplay, False);
3628 frameWaiting = True;
3629 signal(SIGALRM, FrameAlarm);
3630 delay.it_interval.tv_sec =
3631 delay.it_value.tv_sec = time / 1000;
3632 delay.it_interval.tv_usec =
3633 delay.it_value.tv_usec = (time % 1000) * 1000;
3634 setitimer(ITIMER_REAL, &delay, NULL);
3635 while (frameWaiting) pause();
3636 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
3637 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
3638 setitimer(ITIMER_REAL, &delay, NULL);
3645 FrameDelay (int time)
3647 XSync(xDisplay, False);
3649 usleep(time * 1000);
3655 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
3659 /* Bitmap for piece being moved. */
3660 if (appData.monoMode) {
3661 *mask = *pieceToSolid(piece);
3662 } else if (useImages) {
3664 *mask = xpmMask[piece];
3666 *mask = ximMaskPm[piece];
3669 *mask = *pieceToSolid(piece);
3672 /* GC for piece being moved. Square color doesn't matter, but
3673 since it gets modified we make a copy of the original. */
3675 if (appData.monoMode)
3680 if (appData.monoMode)
3685 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
3687 /* Outline only used in mono mode and is not modified */
3689 *outline = bwPieceGC;
3691 *outline = wbPieceGC;
3695 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
3700 /* Draw solid rectangle which will be clipped to shape of piece */
3701 XFillRectangle(xDisplay, dest, clip,
3702 0, 0, squareSize, squareSize);
3703 if (appData.monoMode)
3704 /* Also draw outline in contrasting color for black
3705 on black / white on white cases */
3706 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
3707 0, 0, squareSize, squareSize, 0, 0, 1);
3709 /* Copy the piece */
3714 if(appData.upsideDown && flipView) kind ^= 2;
3715 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3717 0, 0, squareSize, squareSize,
3723 InsertPiece (AnimNr anr, ChessSquare piece)
3725 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
3729 DrawBlank (AnimNr anr, int x, int y, int startColor)
3731 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
3734 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
3735 int srcX, int srcY, int width, int height, int destX, int destY)
3737 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
3738 srcX, srcY, width, height, destX, destY);
3742 SetDragPiece (AnimNr anr, ChessSquare piece)
3745 /* The piece will be drawn using its own bitmap as a matte */
3746 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
3747 XSetClipMask(xDisplay, animGCs[anr+2], mask);
3750 /* [AS] Arrow highlighting support */
3753 DrawPolygon (Pnt arrow[], int nr)
3757 for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
3758 XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
3759 if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
3763 UpdateLogos (int displ)
3765 return; // no logos in XBoard yet