2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
66 # if HAVE_SYS_SOCKET_H
67 # include <sys/socket.h>
68 # include <netinet/in.h>
70 # else /* not HAVE_SYS_SOCKET_H */
71 # if HAVE_LAN_SOCKET_H
72 # include <lan/socket.h>
74 # include <lan/netdb.h>
75 # else /* not HAVE_LAN_SOCKET_H */
76 # define OMIT_SOCKETS 1
77 # endif /* not HAVE_LAN_SOCKET_H */
78 # endif /* not HAVE_SYS_SOCKET_H */
79 #endif /* !OMIT_SOCKETS */
84 #else /* not STDC_HEADERS */
85 extern char *getenv();
88 # else /* not HAVE_STRING_H */
90 # endif /* not HAVE_STRING_H */
91 #endif /* not STDC_HEADERS */
94 # include <sys/fcntl.h>
95 #else /* not HAVE_SYS_FCNTL_H */
98 # endif /* HAVE_FCNTL_H */
99 #endif /* not HAVE_SYS_FCNTL_H */
101 #if HAVE_SYS_SYSTEMINFO_H
102 # include <sys/systeminfo.h>
103 #endif /* HAVE_SYS_SYSTEMINFO_H */
105 #if TIME_WITH_SYS_TIME
106 # include <sys/time.h>
110 # include <sys/time.h>
121 # include <sys/wait.h>
126 # define NAMLEN(dirent) strlen((dirent)->d_name)
127 # define HAVE_DIR_STRUCT
129 # define dirent direct
130 # define NAMLEN(dirent) (dirent)->d_namlen
132 # include <sys/ndir.h>
133 # define HAVE_DIR_STRUCT
136 # include <sys/dir.h>
137 # define HAVE_DIR_STRUCT
141 # define HAVE_DIR_STRUCT
149 #include <X11/Intrinsic.h>
150 #include <X11/StringDefs.h>
151 #include <X11/Shell.h>
152 #include <X11/cursorfont.h>
153 #include <X11/Xatom.h>
154 #include <X11/Xmu/Atoms.h>
156 #include <X11/Xaw3d/Dialog.h>
157 #include <X11/Xaw3d/Form.h>
158 #include <X11/Xaw3d/List.h>
159 #include <X11/Xaw3d/Label.h>
160 #include <X11/Xaw3d/SimpleMenu.h>
161 #include <X11/Xaw3d/SmeBSB.h>
162 #include <X11/Xaw3d/SmeLine.h>
163 #include <X11/Xaw3d/Box.h>
164 #include <X11/Xaw3d/MenuButton.h>
165 #include <X11/Xaw3d/Text.h>
166 #include <X11/Xaw3d/AsciiText.h>
168 #include <X11/Xaw/Dialog.h>
169 #include <X11/Xaw/Form.h>
170 #include <X11/Xaw/List.h>
171 #include <X11/Xaw/Label.h>
172 #include <X11/Xaw/SimpleMenu.h>
173 #include <X11/Xaw/SmeBSB.h>
174 #include <X11/Xaw/SmeLine.h>
175 #include <X11/Xaw/Box.h>
176 #include <X11/Xaw/MenuButton.h>
177 #include <X11/Xaw/Text.h>
178 #include <X11/Xaw/AsciiText.h>
181 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
186 #include "pixmaps/pixmaps.h"
187 #define IMAGE_EXT "xpm"
189 #define IMAGE_EXT "xim"
190 #include "bitmaps/bitmaps.h"
193 #include "bitmaps/icon_white.bm"
194 #include "bitmaps/icon_black.bm"
195 #include "bitmaps/checkmark.bm"
197 #include "frontend.h"
199 #include "backendz.h"
203 #include "xgamelist.h"
204 #include "xhistory.h"
205 #include "xedittags.h"
209 #include "engineoutput.h"
218 #define usleep(t) _sleep2(((t)+500)/1000)
222 # define _(s) gettext (s)
223 # define N_(s) gettext_noop (s)
229 int main P((int argc, char **argv));
230 RETSIGTYPE CmailSigHandler P((int sig));
231 RETSIGTYPE IntSigHandler P((int sig));
232 RETSIGTYPE TermSizeSigHandler P((int sig));
233 static void CreateGCs P((int redo));
234 static void CreateAnyPieces P((void));
235 void CreateXIMPieces P((void));
236 void CreateXPMPieces P((void));
237 void CreateXPMBoard P((char *s, int n));
238 void CreatePieces P((void));
239 Widget CreateMenuBar P((Menu *mb, int boardWidth));
241 char *InsertPxlSize P((char *pattern, int targetPxlSize));
242 XFontSet CreateFontSet P((char *base_fnt_lst));
244 char *FindFont P((char *pattern, int targetPxlSize));
246 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
247 u_int wreq, u_int hreq));
248 void CreateGrid P((void));
249 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
250 void DelayedDrag P((void));
251 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
252 void HandlePV P((Widget w, XEvent * event,
253 String * params, Cardinal * nParams));
254 void DrawPositionProc P((Widget w, XEvent *event,
255 String *prms, Cardinal *nprms));
256 void CommentClick P((Widget w, XEvent * event,
257 String * params, Cardinal * nParams));
258 void ICSInputBoxPopUp P((void));
259 void FileNamePopUp P((char *label, char *def, char *filter,
260 FileProc proc, char *openMode));
261 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
262 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
263 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
264 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
265 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
266 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
267 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
268 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
269 Boolean TempBackwardActive = False;
270 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
271 void DisplayMove P((int moveNumber));
272 void ICSInitScript P((void));
273 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
274 void update_ics_width P(());
275 int get_term_width P(());
276 int CopyMemoProc P(());
277 void SetupDropMenu P((void));
280 * XBoard depends on Xt R4 or higher
282 int xtVersion = XtSpecificationRelease;
287 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
288 highlightSquareColor, premoveHighlightColor, dialogColor, buttonColor;
289 Pixel lowTimeWarningColor;
290 GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
291 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
293 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
294 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
295 Option *optList; // contains all widgets of main window
296 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
298 XFontSet fontSet, clockFontSet;
301 XFontStruct *clockFontStruct;
303 Font coordFontID, countFontID;
304 XFontStruct *coordFontStruct, *countFontStruct;
305 XtAppContext appContext;
310 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
312 Position commentX = -1, commentY = -1;
313 Dimension commentW, commentH;
314 typedef unsigned int BoardSize;
316 Boolean chessProgram;
318 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
319 int smallLayout = 0, tinyLayout = 0,
320 marginW, marginH, // [HGM] for run-time resizing
321 fromX = -1, fromY = -1, toX, toY, commentUp = False,
322 errorExitStatus = -1, defaultLineGap;
323 Dimension textHeight;
324 Pixel timerForegroundPixel, timerBackgroundPixel;
325 Pixel buttonForegroundPixel, buttonBackgroundPixel;
326 char *chessDir, *programName, *programVersion;
327 Boolean alwaysOnTop = False;
328 char *icsTextMenuString;
330 char *firstChessProgramNames;
331 char *secondChessProgramNames;
333 WindowPlacement wpMain;
334 WindowPlacement wpConsole;
335 WindowPlacement wpComment;
336 WindowPlacement wpMoveHistory;
337 WindowPlacement wpEvalGraph;
338 WindowPlacement wpEngineOutput;
339 WindowPlacement wpGameList;
340 WindowPlacement wpTags;
345 Pixmap pieceBitmap[2][(int)BlackPawn];
346 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
347 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
348 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
349 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
350 Pixmap xpmBoardBitmap[2];
351 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
352 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
353 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
354 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
355 XImage *ximLightSquare, *ximDarkSquare;
358 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
359 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
361 #define White(piece) ((int)(piece) < (int)BlackPawn)
363 /* Bitmaps for use as masks when drawing XPM pieces.
364 Need one for each black and white piece. */
365 static Pixmap xpmMask[BlackKing + 1];
367 /* This magic number is the number of intermediate frames used
368 in each half of the animation. For short moves it's reduced
369 by 1. The total number of frames will be factor * 2 + 1. */
372 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
379 DropMenuEnables dmEnables[] = {
396 XtResource clientResources[] = {
397 { "flashCount", "flashCount", XtRInt, sizeof(int),
398 XtOffset(AppDataPtr, flashCount), XtRImmediate,
399 (XtPointer) FLASH_COUNT },
402 XrmOptionDescRec shellOptions[] = {
403 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
404 { "-flash", "flashCount", XrmoptionNoArg, "3" },
405 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
408 XtActionsRec boardActions[] = {
409 { "DrawPosition", DrawPositionProc },
410 { "HandlePV", HandlePV },
411 { "SelectPV", SelectPV },
412 { "StopPV", StopPV },
413 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
414 { "QuitProc", QuitWrapper },
415 { "ManProc", ManInner },
416 { "TempBackwardProc", TempBackwardProc },
417 { "TempForwardProc", TempForwardProc },
418 { "CommentClick", (XtActionProc) CommentClick },
419 { "GenericPopDown", (XtActionProc) GenericPopDown },
420 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
421 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
422 { "SelectMove", (XtActionProc) SelectMove },
423 { "LoadSelectedProc", LoadSelectedProc },
424 { "SetFilterProc", SetFilterProc },
425 { "TypeInProc", TypeInProc },
426 { "EnterKeyProc", EnterKeyProc },
427 { "UpKeyProc", UpKeyProc },
428 { "DownKeyProc", DownKeyProc },
429 { "WheelProc", WheelProc },
430 { "TabProc", TabProc },
433 char globalTranslations[] =
434 ":<Key>F9: MenuItem(Actions.Resign) \n \
435 :Ctrl<Key>n: MenuItem(File.NewGame) \n \
436 :Meta<Key>V: MenuItem(File.NewVariant) \n \
437 :Ctrl<Key>o: MenuItem(File.LoadGame) \n \
438 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
439 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
440 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
441 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
442 :Ctrl<Key>s: MenuItem(File.SaveGame) \n \
443 :Ctrl<Key>c: MenuItem(Edit.CopyGame) \n \
444 :Ctrl<Key>v: MenuItem(Edit.PasteGame) \n \
445 :Ctrl<Key>O: MenuItem(File.LoadPosition) \n \
446 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
447 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
448 :Ctrl<Key>S: MenuItem(File.SavePosition) \n \
449 :Ctrl<Key>C: MenuItem(Edit.CopyPosition) \n \
450 :Ctrl<Key>V: MenuItem(Edit.PastePosition) \n \
451 :Ctrl<Key>q: MenuItem(File.Quit) \n \
452 :Ctrl<Key>w: MenuItem(Mode.MachineWhite) \n \
453 :Ctrl<Key>b: MenuItem(Mode.MachineBlack) \n \
454 :Ctrl<Key>t: MenuItem(Mode.TwoMachines) \n \
455 :Ctrl<Key>a: MenuItem(Mode.AnalysisMode) \n \
456 :Ctrl<Key>g: MenuItem(Mode.AnalyzeFile) \n \
457 :Ctrl<Key>e: MenuItem(Mode.EditGame) \n \
458 :Ctrl<Key>E: MenuItem(Mode.EditPosition) \n \
459 :Meta<Key>O: MenuItem(View.EngineOutput) \n \
460 :Meta<Key>E: MenuItem(View.EvaluationGraph) \n \
461 :Meta<Key>G: MenuItem(View.GameList) \n \
462 :Meta<Key>H: MenuItem(View.MoveHistory) \n \
463 :<Key>Pause: MenuItem(Mode.Pause) \n \
464 :<Key>F3: MenuItem(Action.Accept) \n \
465 :<Key>F4: MenuItem(Action.Decline) \n \
466 :<Key>F12: MenuItem(Action.Rematch) \n \
467 :<Key>F5: MenuItem(Action.CallFlag) \n \
468 :<Key>F6: MenuItem(Action.Draw) \n \
469 :<Key>F7: MenuItem(Action.Adjourn) \n \
470 :<Key>F8: MenuItem(Action.Abort) \n \
471 :<Key>F10: MenuItem(Action.StopObserving) \n \
472 :<Key>F11: MenuItem(Action.StopExamining) \n \
473 :Ctrl<Key>d: MenuItem(DebugProc) \n \
474 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
475 :Meta<Key>End: MenuItem(Edit.ForwardtoEnd) \n \
476 :Meta<Key>Right: MenuItem(Edit.Forward) \n \
477 :Meta<Key>Home: MenuItem(Edit.BacktoStart) \n \
478 :Meta<Key>Left: MenuItem(Edit.Backward) \n \
479 :<Key>Left: MenuItem(Edit.Backward) \n \
480 :<Key>Right: MenuItem(Edit.Forward) \n \
481 :<Key>Home: MenuItem(Edit.Revert) \n \
482 :<Key>End: MenuItem(Edit.TruncateGame) \n \
483 :Ctrl<Key>m: MenuItem(Engine.MoveNow) \n \
484 :Ctrl<Key>x: MenuItem(Engine.RetractMove) \n \
485 :Meta<Key>J: MenuItem(Options.Adjudications) \n \
486 :Meta<Key>U: MenuItem(Options.CommonEngine) \n \
487 :Meta<Key>T: MenuItem(Options.TimeControl) \n \
488 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
489 #ifndef OPTIONSDIALOG
491 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
492 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
493 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
494 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
495 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
498 :<Key>F1: MenuItem(Help.ManXBoard) \n \
499 :<Key>F2: MenuItem(View.FlipView) \n \
500 :<KeyDown>Return: TempBackwardProc() \n \
501 :<KeyUp>Return: TempForwardProc() \n";
503 char ICSInputTranslations[] =
504 "<Key>Up: UpKeyProc() \n "
505 "<Key>Down: DownKeyProc() \n "
506 "<Key>Return: EnterKeyProc() \n";
508 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
509 // as the widget is destroyed before the up-click can call extend-end
510 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
512 String xboardResources[] = {
513 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
518 /* Max possible square size */
519 #define MAXSQSIZE 256
521 static int xpm_avail[MAXSQSIZE];
523 #ifdef HAVE_DIR_STRUCT
525 /* Extract piece size from filename */
527 xpm_getsize (char *name, int len, char *ext)
535 if ((p=strchr(name, '.')) == NULL ||
536 StrCaseCmp(p+1, ext) != 0)
542 while (*p && isdigit(*p))
549 /* Setup xpm_avail */
551 xpm_getavail (char *dirname, char *ext)
557 for (i=0; i<MAXSQSIZE; ++i)
560 if (appData.debugMode)
561 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
563 dir = opendir(dirname);
566 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
567 programName, dirname);
571 while ((ent=readdir(dir)) != NULL) {
572 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
573 if (i > 0 && i < MAXSQSIZE)
583 xpm_print_avail (FILE *fp, char *ext)
587 fprintf(fp, _("Available `%s' sizes:\n"), ext);
588 for (i=1; i<MAXSQSIZE; ++i) {
594 /* Return XPM piecesize closest to size */
596 xpm_closest_to (char *dirname, int size, char *ext)
599 int sm_diff = MAXSQSIZE;
603 xpm_getavail(dirname, ext);
605 if (appData.debugMode)
606 xpm_print_avail(stderr, ext);
608 for (i=1; i<MAXSQSIZE; ++i) {
611 diff = (diff<0) ? -diff : diff;
612 if (diff < sm_diff) {
620 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
626 #else /* !HAVE_DIR_STRUCT */
627 /* If we are on a system without a DIR struct, we can't
628 read the directory, so we can't collect a list of
629 filenames, etc., so we can't do any size-fitting. */
631 xpm_closest_to (char *dirname, int size, char *ext)
634 Warning: No DIR structure found on this system --\n\
635 Unable to autosize for XPM/XIM pieces.\n\
636 Please report this error to %s.\n\
637 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
640 #endif /* HAVE_DIR_STRUCT */
643 /* Arrange to catch delete-window events */
644 Atom wm_delete_window;
646 CatchDeleteWindow (Widget w, String procname)
649 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
650 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
651 XtAugmentTranslations(w, XtParseTranslationTable(buf));
658 XtSetArg(args[0], XtNiconic, False);
659 XtSetValues(shellWidget, args, 1);
661 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
664 //---------------------------------------------------------------------------------------------------------
665 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
668 #define CW_USEDEFAULT (1<<31)
669 #define ICS_TEXT_MENU_SIZE 90
670 #define DEBUG_FILE "xboard.debug"
671 #define SetCurrentDirectory chdir
672 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
676 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
679 // front-end part of option handling
681 // [HGM] This platform-dependent table provides the location for storing the color info
682 extern char *crWhite, * crBlack;
686 &appData.whitePieceColor,
687 &appData.blackPieceColor,
688 &appData.lightSquareColor,
689 &appData.darkSquareColor,
690 &appData.highlightSquareColor,
691 &appData.premoveHighlightColor,
692 &appData.lowTimeWarningColor,
703 // [HGM] font: keep a font for each square size, even non-stndard ones
706 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
707 char *fontTable[NUM_FONTS][MAX_SIZE];
710 ParseFont (char *name, int number)
711 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
713 if(sscanf(name, "size%d:", &size)) {
714 // [HGM] font: font is meant for specific boardSize (likely from settings file);
715 // defer processing it until we know if it matches our board size
716 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
717 fontTable[number][size] = strdup(strchr(name, ':')+1);
718 fontValid[number][size] = True;
723 case 0: // CLOCK_FONT
724 appData.clockFont = strdup(name);
726 case 1: // MESSAGE_FONT
727 appData.font = strdup(name);
729 case 2: // COORD_FONT
730 appData.coordFont = strdup(name);
735 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
740 { // only 2 fonts currently
741 appData.clockFont = CLOCK_FONT_NAME;
742 appData.coordFont = COORD_FONT_NAME;
743 appData.font = DEFAULT_FONT_NAME;
748 { // no-op, until we identify the code for this already in XBoard and move it here
752 ParseColor (int n, char *name)
753 { // in XBoard, just copy the color-name string
754 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
758 ParseTextAttribs (ColorClass cc, char *s)
760 (&appData.colorShout)[cc] = strdup(s);
764 ParseBoardSize (void *addr, char *name)
766 appData.boardSize = strdup(name);
771 { // In XBoard the sound-playing program takes care of obtaining the actual sound
775 SetCommPortDefaults ()
776 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
779 // [HGM] args: these three cases taken out to stay in front-end
781 SaveFontArg (FILE *f, ArgDescriptor *ad)
784 int i, n = (int)(intptr_t)ad->argLoc;
786 case 0: // CLOCK_FONT
787 name = appData.clockFont;
789 case 1: // MESSAGE_FONT
792 case 2: // COORD_FONT
793 name = appData.coordFont;
798 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
799 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
800 fontTable[n][squareSize] = strdup(name);
801 fontValid[n][squareSize] = True;
804 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
805 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
810 { // nothing to do, as the sounds are at all times represented by their text-string names already
814 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
815 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
816 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
820 SaveColor (FILE *f, ArgDescriptor *ad)
821 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
822 if(colorVariable[(int)(intptr_t)ad->argLoc])
823 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
827 SaveBoardSize (FILE *f, char *name, void *addr)
828 { // wrapper to shield back-end from BoardSize & sizeInfo
829 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
833 ParseCommPortSettings (char *s)
834 { // no such option in XBoard (yet)
840 GetActualPlacement (Widget wg, WindowPlacement *wp)
845 XWindowAttributes winAt;
852 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
853 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
854 wp->x = rx - winAt.x;
855 wp->y = ry - winAt.y;
856 wp->height = winAt.height;
857 wp->width = winAt.width;
858 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
863 { // wrapper to shield use of window handles from back-end (make addressible by number?)
864 // In XBoard this will have to wait until awareness of window parameters is implemented
865 GetActualPlacement(shellWidget, &wpMain);
866 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
867 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
868 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
869 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
870 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
871 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
875 PrintCommPortSettings (FILE *f, char *name)
876 { // This option does not exist in XBoard
880 EnsureOnScreen (int *x, int *y, int minX, int minY)
887 { // [HGM] args: allows testing if main window is realized from back-end
888 return xBoardWindow != 0;
893 extern Option dualOptions[];
895 Window tmp = xBoardWindow;
896 if(!dual) dual = XtWindow(dualOptions[3].handle); // must be first call
897 xBoardWindow = dual; // swap them
902 PopUpStartupDialog ()
903 { // start menu not implemented in XBoard
907 ConvertToLine (int argc, char **argv)
909 static char line[128*1024], buf[1024];
913 for(i=1; i<argc; i++)
915 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
916 && argv[i][0] != '{' )
917 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
919 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
920 strncat(line, buf, 128*1024 - strlen(line) - 1 );
923 line[strlen(line)-1] = NULLCHAR;
927 //--------------------------------------------------------------------------------------------
929 #define BoardSize int
931 InitDrawingSizes (BoardSize boardSize, int flags)
932 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
933 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
935 XtGeometryResult gres;
937 static Dimension oldWidth, oldHeight;
938 static VariantClass oldVariant;
939 static int oldMono = -1;
941 if(!formWidget) return;
943 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
944 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
945 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
947 if(boardWidth != oldWidth || boardHeight != oldHeight) { // do resizing stuff only if size actually changed
949 oldWidth = boardWidth; oldHeight = boardHeight;
953 * Inhibit shell resizing.
955 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW ;
956 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
957 shellArgs[4].value = shellArgs[2].value = w;
958 shellArgs[5].value = shellArgs[3].value = h;
959 XtSetValues(shellWidget, &shellArgs[0], 6);
961 XSync(xDisplay, False);
965 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
968 if(gameInfo.variant != oldVariant) { // and only if variant changed
973 for(p=0; p<=(int)WhiteKing; p++)
974 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
975 if(gameInfo.variant == VariantShogi) {
976 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
977 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
978 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
979 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
980 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
983 if(gameInfo.variant == VariantGothic) {
984 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
987 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
988 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
989 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
992 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
993 for(p=0; p<=(int)WhiteKing; p++)
994 ximMaskPm[p] = ximMaskPm2[p]; // defaults
995 if(gameInfo.variant == VariantShogi) {
996 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
997 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
998 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
999 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1000 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1003 if(gameInfo.variant == VariantGothic) {
1004 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1007 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1008 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1009 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1014 for(i=0; i<2; i++) {
1016 for(p=0; p<=(int)WhiteKing; p++)
1017 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1018 if(gameInfo.variant == VariantShogi) {
1019 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1020 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1021 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1022 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1023 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1026 if(gameInfo.variant == VariantGothic) {
1027 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1030 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1031 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1032 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1036 oldMono = -10; // kludge to force recreation of animation masks
1037 oldVariant = gameInfo.variant;
1040 if(appData.monoMode != oldMono)
1043 oldMono = appData.monoMode;
1047 MakeOneColor (char *name, Pixel *color)
1049 XrmValue vFrom, vTo;
1050 if (!appData.monoMode) {
1051 vFrom.addr = (caddr_t) name;
1052 vFrom.size = strlen(name);
1053 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1054 if (vTo.addr == NULL) {
1055 appData.monoMode = True;
1058 *color = *(Pixel *) vTo.addr;
1066 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1067 int forceMono = False;
1069 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1070 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1071 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1072 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1073 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1074 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1075 if(appData.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 main (int argc, char **argv)
1113 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1114 XSetWindowAttributes window_attributes;
1116 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1117 XrmValue vFrom, vTo;
1118 XtGeometryResult gres;
1121 int forceMono = False;
1123 srandom(time(0)); // [HGM] book: make random truly random
1125 setbuf(stdout, NULL);
1126 setbuf(stderr, NULL);
1129 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1130 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1134 programName = strrchr(argv[0], '/');
1135 if (programName == NULL)
1136 programName = argv[0];
1141 XtSetLanguageProc(NULL, NULL, NULL);
1142 bindtextdomain(PACKAGE, LOCALEDIR);
1143 textdomain(PACKAGE);
1147 XtAppInitialize(&appContext, "XBoard", shellOptions,
1148 XtNumber(shellOptions),
1149 &argc, argv, xboardResources, NULL, 0);
1150 appData.boardSize = "";
1151 InitAppData(ConvertToLine(argc, argv));
1153 if (p == NULL) p = "/tmp";
1154 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1155 gameCopyFilename = (char*) malloc(i);
1156 gamePasteFilename = (char*) malloc(i);
1157 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1158 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1160 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1161 clientResources, XtNumber(clientResources),
1164 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1165 static char buf[MSG_SIZ];
1166 EscapeExpand(buf, appData.firstInitString);
1167 appData.firstInitString = strdup(buf);
1168 EscapeExpand(buf, appData.secondInitString);
1169 appData.secondInitString = strdup(buf);
1170 EscapeExpand(buf, appData.firstComputerString);
1171 appData.firstComputerString = strdup(buf);
1172 EscapeExpand(buf, appData.secondComputerString);
1173 appData.secondComputerString = strdup(buf);
1176 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1179 if (chdir(chessDir) != 0) {
1180 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1186 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1187 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1188 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1189 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1192 setbuf(debugFP, NULL);
1196 if (appData.debugMode) {
1197 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1201 /* [HGM,HR] make sure board size is acceptable */
1202 if(appData.NrFiles > BOARD_FILES ||
1203 appData.NrRanks > BOARD_RANKS )
1204 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1207 /* This feature does not work; animation needs a rewrite */
1208 appData.highlightDragging = FALSE;
1212 xDisplay = XtDisplay(shellWidget);
1213 xScreen = DefaultScreen(xDisplay);
1214 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1216 gameInfo.variant = StringToVariant(appData.variant);
1217 InitPosition(FALSE);
1219 if (isdigit(appData.boardSize[0])) {
1220 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1221 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1222 &fontPxlSize, &smallLayout, &tinyLayout);
1224 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1225 programName, appData.boardSize);
1229 /* Find some defaults; use the nearest known size */
1230 SizeDefaults *szd, *nearest;
1231 int distance = 99999;
1232 nearest = szd = sizeDefaults;
1233 while (szd->name != NULL) {
1234 if (abs(szd->squareSize - squareSize) < distance) {
1236 distance = abs(szd->squareSize - squareSize);
1237 if (distance == 0) break;
1241 if (i < 2) lineGap = nearest->lineGap;
1242 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1243 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1244 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1245 if (i < 6) smallLayout = nearest->smallLayout;
1246 if (i < 7) tinyLayout = nearest->tinyLayout;
1249 SizeDefaults *szd = sizeDefaults;
1250 if (*appData.boardSize == NULLCHAR) {
1251 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1252 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1255 if (szd->name == NULL) szd--;
1256 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1258 while (szd->name != NULL &&
1259 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1260 if (szd->name == NULL) {
1261 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1262 programName, appData.boardSize);
1266 squareSize = szd->squareSize;
1267 lineGap = szd->lineGap;
1268 clockFontPxlSize = szd->clockFontPxlSize;
1269 coordFontPxlSize = szd->coordFontPxlSize;
1270 fontPxlSize = szd->fontPxlSize;
1271 smallLayout = szd->smallLayout;
1272 tinyLayout = szd->tinyLayout;
1273 // [HGM] font: use defaults from settings file if available and not overruled
1275 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1276 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1277 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1278 appData.font = fontTable[MESSAGE_FONT][squareSize];
1279 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1280 appData.coordFont = fontTable[COORD_FONT][squareSize];
1282 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1283 if (strlen(appData.pixmapDirectory) > 0) {
1284 p = ExpandPathName(appData.pixmapDirectory);
1286 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1287 appData.pixmapDirectory);
1290 if (appData.debugMode) {
1291 fprintf(stderr, _("\
1292 XBoard square size (hint): %d\n\
1293 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1295 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1296 if (appData.debugMode) {
1297 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1300 defaultLineGap = lineGap;
1301 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1303 /* [HR] height treated separately (hacked) */
1304 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1305 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1308 * Determine what fonts to use.
1311 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1312 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1313 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1314 fontSet = CreateFontSet(appData.font);
1315 clockFontSet = CreateFontSet(appData.clockFont);
1317 /* For the coordFont, use the 0th font of the fontset. */
1318 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1319 XFontStruct **font_struct_list;
1320 XFontSetExtents *fontSize;
1321 char **font_name_list;
1322 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1323 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1324 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1325 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1326 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1329 appData.font = FindFont(appData.font, fontPxlSize);
1330 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1331 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1332 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1333 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1334 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1335 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1336 // textHeight in !NLS mode!
1338 countFontID = coordFontID; // [HGM] holdings
1339 countFontStruct = coordFontStruct;
1341 xdb = XtDatabase(xDisplay);
1343 XrmPutLineResource(&xdb, "*international: True");
1344 vTo.size = sizeof(XFontSet);
1345 vTo.addr = (XtPointer) &fontSet;
1346 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1348 XrmPutStringResource(&xdb, "*font", appData.font);
1352 * Detect if there are not enough colors available and adapt.
1354 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1355 appData.monoMode = True;
1358 forceMono = MakeColors();
1361 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1363 appData.monoMode = True;
1366 if (appData.lowTimeWarning && !appData.monoMode) {
1367 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1368 vFrom.size = strlen(appData.lowTimeWarningColor);
1369 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1370 if (vTo.addr == NULL)
1371 appData.monoMode = True;
1373 lowTimeWarningColor = *(Pixel *) vTo.addr;
1376 if (appData.monoMode && appData.debugMode) {
1377 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1378 (unsigned long) XWhitePixel(xDisplay, xScreen),
1379 (unsigned long) XBlackPixel(xDisplay, xScreen));
1382 ParseIcsTextColors();
1384 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1390 layoutName = "tinyLayout";
1391 } else if (smallLayout) {
1392 layoutName = "smallLayout";
1394 layoutName = "normalLayout";
1397 optList = BoardPopUp(squareSize, lineGap, (void*)
1403 boardWidget = optList[22].handle;
1404 menuBarWidget = optList[ 0].handle;
1405 dropMenu = optList[25].handle;
1406 titleWidget = optList[optList[10].type != -1 ? 10 : 13].handle;
1407 formWidget = XtParent(boardWidget);
1408 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1409 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1410 XtGetValues(optList[11].handle, args, 2);
1411 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1412 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1413 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1414 XtGetValues(optList[18].handle, args, 2);
1416 AppendEnginesToMenu(appData.recentEngineList);
1418 xBoardWindow = XtWindow(boardWidget);
1420 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1421 // not need to go into InitDrawingSizes().
1424 * Create X checkmark bitmap and initialize option menu checks.
1426 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1427 checkmark_bits, checkmark_width, checkmark_height);
1433 ReadBitmap(&wIconPixmap, "icon_white.bm",
1434 icon_white_bits, icon_white_width, icon_white_height);
1435 ReadBitmap(&bIconPixmap, "icon_black.bm",
1436 icon_black_bits, icon_black_width, icon_black_height);
1437 iconPixmap = wIconPixmap;
1439 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1440 XtSetValues(shellWidget, args, i);
1443 * Create a cursor for the board widget.
1445 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1446 XChangeWindowAttributes(xDisplay, xBoardWindow,
1447 CWCursor, &window_attributes);
1450 * Inhibit shell resizing.
1452 shellArgs[0].value = (XtArgVal) &w;
1453 shellArgs[1].value = (XtArgVal) &h;
1454 XtGetValues(shellWidget, shellArgs, 2);
1455 shellArgs[4].value = shellArgs[2].value = w;
1456 shellArgs[5].value = shellArgs[3].value = h;
1457 XtSetValues(shellWidget, &shellArgs[2], 4);
1458 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1459 marginH = h - boardHeight;
1461 CatchDeleteWindow(shellWidget, "QuitProc");
1467 if (appData.animate || appData.animateDragging)
1470 XtAugmentTranslations(formWidget,
1471 XtParseTranslationTable(globalTranslations));
1473 XtAddEventHandler(formWidget, KeyPressMask, False,
1474 (XtEventHandler) MoveTypeInProc, NULL);
1475 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1476 (XtEventHandler) EventProc, NULL);
1478 /* [AS] Restore layout */
1479 if( wpMoveHistory.visible ) {
1483 if( wpEvalGraph.visible )
1488 if( wpEngineOutput.visible ) {
1489 EngineOutputPopUp();
1494 if (errorExitStatus == -1) {
1495 if (appData.icsActive) {
1496 /* We now wait until we see "login:" from the ICS before
1497 sending the logon script (problems with timestamp otherwise) */
1498 /*ICSInitScript();*/
1499 if (appData.icsInputBox) ICSInputBoxPopUp();
1503 signal(SIGWINCH, TermSizeSigHandler);
1505 signal(SIGINT, IntSigHandler);
1506 signal(SIGTERM, IntSigHandler);
1507 if (*appData.cmailGameName != NULLCHAR) {
1508 signal(SIGUSR1, CmailSigHandler);
1512 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1514 // XtSetKeyboardFocus(shellWidget, formWidget);
1515 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1517 XtAppMainLoop(appContext);
1518 if (appData.debugMode) fclose(debugFP); // [DM] debug
1523 TermSizeSigHandler (int sig)
1529 IntSigHandler (int sig)
1535 CmailSigHandler (int sig)
1540 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1542 /* Activate call-back function CmailSigHandlerCallBack() */
1543 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1545 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1549 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1552 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1554 /**** end signal code ****/
1557 #define Abs(n) ((n)<0 ? -(n) : (n))
1561 InsertPxlSize (char *pattern, int targetPxlSize)
1563 char *base_fnt_lst, strInt[12], *p, *q;
1564 int alternatives, i, len, strIntLen;
1567 * Replace the "*" (if present) in the pixel-size slot of each
1568 * alternative with the targetPxlSize.
1572 while ((p = strchr(p, ',')) != NULL) {
1576 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1577 strIntLen = strlen(strInt);
1578 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1582 while (alternatives--) {
1583 char *comma = strchr(p, ',');
1584 for (i=0; i<14; i++) {
1585 char *hyphen = strchr(p, '-');
1587 if (comma && hyphen > comma) break;
1588 len = hyphen + 1 - p;
1589 if (i == 7 && *p == '*' && len == 2) {
1591 memcpy(q, strInt, strIntLen);
1601 len = comma + 1 - p;
1608 return base_fnt_lst;
1612 CreateFontSet (char *base_fnt_lst)
1615 char **missing_list;
1619 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1620 &missing_list, &missing_count, &def_string);
1621 if (appData.debugMode) {
1623 XFontStruct **font_struct_list;
1624 char **font_name_list;
1625 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1627 fprintf(debugFP, " got list %s, locale %s\n",
1628 XBaseFontNameListOfFontSet(fntSet),
1629 XLocaleOfFontSet(fntSet));
1630 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1631 for (i = 0; i < count; i++) {
1632 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1635 for (i = 0; i < missing_count; i++) {
1636 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1639 if (fntSet == NULL) {
1640 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1645 #else // not ENABLE_NLS
1647 * Find a font that matches "pattern" that is as close as
1648 * possible to the targetPxlSize. Prefer fonts that are k
1649 * pixels smaller to fonts that are k pixels larger. The
1650 * pattern must be in the X Consortium standard format,
1651 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1652 * The return value should be freed with XtFree when no
1656 FindFont (char *pattern, int targetPxlSize)
1658 char **fonts, *p, *best, *scalable, *scalableTail;
1659 int i, j, nfonts, minerr, err, pxlSize;
1661 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1663 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1664 programName, pattern);
1671 for (i=0; i<nfonts; i++) {
1674 if (*p != '-') continue;
1676 if (*p == NULLCHAR) break;
1677 if (*p++ == '-') j++;
1679 if (j < 7) continue;
1682 scalable = fonts[i];
1685 err = pxlSize - targetPxlSize;
1686 if (Abs(err) < Abs(minerr) ||
1687 (minerr > 0 && err < 0 && -err == minerr)) {
1693 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1694 /* If the error is too big and there is a scalable font,
1695 use the scalable font. */
1696 int headlen = scalableTail - scalable;
1697 p = (char *) XtMalloc(strlen(scalable) + 10);
1698 while (isdigit(*scalableTail)) scalableTail++;
1699 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1701 p = (char *) XtMalloc(strlen(best) + 2);
1702 safeStrCpy(p, best, strlen(best)+1 );
1704 if (appData.debugMode) {
1705 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1706 pattern, targetPxlSize, p);
1708 XFreeFontNames(fonts);
1715 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
1716 // must be called before all non-first callse to CreateGCs()
1717 XtReleaseGC(shellWidget, highlineGC);
1718 XtReleaseGC(shellWidget, lightSquareGC);
1719 XtReleaseGC(shellWidget, darkSquareGC);
1720 XtReleaseGC(shellWidget, lineGC);
1721 if (appData.monoMode) {
1722 if (DefaultDepth(xDisplay, xScreen) == 1) {
1723 XtReleaseGC(shellWidget, wbPieceGC);
1725 XtReleaseGC(shellWidget, bwPieceGC);
1728 XtReleaseGC(shellWidget, prelineGC);
1729 XtReleaseGC(shellWidget, wdPieceGC);
1730 XtReleaseGC(shellWidget, wlPieceGC);
1731 XtReleaseGC(shellWidget, bdPieceGC);
1732 XtReleaseGC(shellWidget, blPieceGC);
1737 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
1739 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1740 | GCBackground | GCFunction | GCPlaneMask;
1741 gc_values->foreground = foreground;
1742 gc_values->background = background;
1743 return XtGetGC(shellWidget, value_mask, gc_values);
1747 CreateGCs (int redo)
1749 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1750 | GCBackground | GCFunction | GCPlaneMask;
1751 XGCValues gc_values;
1753 Pixel white = XWhitePixel(xDisplay, xScreen);
1754 Pixel black = XBlackPixel(xDisplay, xScreen);
1756 gc_values.plane_mask = AllPlanes;
1757 gc_values.line_width = lineGap;
1758 gc_values.line_style = LineSolid;
1759 gc_values.function = GXcopy;
1762 DeleteGCs(); // called a second time; clean up old GCs first
1763 } else { // [HGM] grid and font GCs created on first call only
1764 coordGC = CreateOneGC(&gc_values, black, white);
1765 XSetFont(xDisplay, coordGC, coordFontID);
1767 // [HGM] make font for holdings counts (white on black)
1768 countGC = CreateOneGC(&gc_values, white, black);
1769 XSetFont(xDisplay, countGC, countFontID);
1771 lineGC = CreateOneGC(&gc_values, black, black);
1773 if (appData.monoMode) {
1775 highlineGC = CreateOneGC(&gc_values, white, white);
1776 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
1777 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
1779 if (DefaultDepth(xDisplay, xScreen) == 1) {
1780 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
1781 gc_values.function = GXcopyInverted;
1782 copyInvertedGC = CreateOneGC(&gc_values, black, white);
1783 gc_values.function = GXcopy;
1784 if (XBlackPixel(xDisplay, xScreen) == 1) {
1785 bwPieceGC = darkSquareGC;
1786 wbPieceGC = copyInvertedGC;
1788 bwPieceGC = copyInvertedGC;
1789 wbPieceGC = lightSquareGC;
1794 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
1795 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
1796 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
1797 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
1798 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
1799 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
1800 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
1801 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
1806 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
1814 fp = fopen(filename, "rb");
1816 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
1823 for (y=0; y<h; ++y) {
1824 for (x=0; x<h; ++x) {
1829 XPutPixel(xim, x, y, blackPieceColor);
1831 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1834 XPutPixel(xim, x, y, darkSquareColor);
1836 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1839 XPutPixel(xim, x, y, whitePieceColor);
1841 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1844 XPutPixel(xim, x, y, lightSquareColor);
1846 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1854 /* create Pixmap of piece */
1855 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1857 XPutImage(xDisplay, *dest, lightSquareGC, xim,
1860 /* create Pixmap of clipmask
1861 Note: We assume the white/black pieces have the same
1862 outline, so we make only 6 masks. This is okay
1863 since the XPM clipmask routines do the same. */
1865 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1867 XPutImage(xDisplay, temp, lightSquareGC, xmask,
1870 /* now create the 1-bit version */
1871 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1874 values.foreground = 1;
1875 values.background = 0;
1877 /* Don't use XtGetGC, not read only */
1878 maskGC = XCreateGC(xDisplay, *mask,
1879 GCForeground | GCBackground, &values);
1880 XCopyPlane(xDisplay, temp, *mask, maskGC,
1881 0, 0, squareSize, squareSize, 0, 0, 1);
1882 XFreePixmap(xDisplay, temp);
1887 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
1895 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
1900 /* The XSynchronize calls were copied from CreatePieces.
1901 Not sure if needed, but can't hurt */
1902 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
1905 /* temp needed by loadXIM() */
1906 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1907 0, 0, ss, ss, AllPlanes, XYPixmap);
1909 if (strlen(appData.pixmapDirectory) == 0) {
1913 if (appData.monoMode) {
1914 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
1918 fprintf(stderr, _("\nLoading XIMs...\n"));
1920 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
1921 fprintf(stderr, "%d", piece+1);
1922 for (kind=0; kind<4; kind++) {
1923 fprintf(stderr, ".");
1924 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
1925 ExpandPathName(appData.pixmapDirectory),
1926 piece <= (int) WhiteKing ? "" : "w",
1927 pieceBitmapNames[piece],
1929 ximPieceBitmap[kind][piece] =
1930 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1931 0, 0, ss, ss, AllPlanes, XYPixmap);
1932 if (appData.debugMode)
1933 fprintf(stderr, _("(File:%s:) "), buf);
1934 loadXIM(ximPieceBitmap[kind][piece],
1936 &(xpmPieceBitmap2[kind][piece]),
1937 &(ximMaskPm2[piece]));
1938 if(piece <= (int)WhiteKing)
1939 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
1941 fprintf(stderr," ");
1943 /* Load light and dark squares */
1944 /* If the LSQ and DSQ pieces don't exist, we will
1945 draw them with solid squares. */
1946 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
1947 if (access(buf, 0) != 0) {
1951 fprintf(stderr, _("light square "));
1953 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1954 0, 0, ss, ss, AllPlanes, XYPixmap);
1955 if (appData.debugMode)
1956 fprintf(stderr, _("(File:%s:) "), buf);
1958 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
1959 fprintf(stderr, _("dark square "));
1960 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
1961 ExpandPathName(appData.pixmapDirectory), ss);
1962 if (appData.debugMode)
1963 fprintf(stderr, _("(File:%s:) "), buf);
1965 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1966 0, 0, ss, ss, AllPlanes, XYPixmap);
1967 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
1968 xpmJailSquare = xpmLightSquare;
1970 fprintf(stderr, _("Done.\n"));
1972 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
1975 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
1979 CreateXPMBoard (char *s, int kind)
1983 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
1984 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
1985 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
1991 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
1992 // thisroutine has to be called t free the old piece pixmaps
1994 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
1995 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
1997 XFreePixmap(xDisplay, xpmLightSquare);
1998 XFreePixmap(xDisplay, xpmDarkSquare);
2007 u_int ss = squareSize;
2009 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2010 XpmColorSymbol symbols[4];
2011 static int redo = False;
2013 if(redo) FreeXPMPieces(); else redo = 1;
2015 /* The XSynchronize calls were copied from CreatePieces.
2016 Not sure if needed, but can't hurt */
2017 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2019 /* Setup translations so piece colors match square colors */
2020 symbols[0].name = "light_piece";
2021 symbols[0].value = appData.whitePieceColor;
2022 symbols[1].name = "dark_piece";
2023 symbols[1].value = appData.blackPieceColor;
2024 symbols[2].name = "light_square";
2025 symbols[2].value = appData.lightSquareColor;
2026 symbols[3].name = "dark_square";
2027 symbols[3].value = appData.darkSquareColor;
2029 attr.valuemask = XpmColorSymbols;
2030 attr.colorsymbols = symbols;
2031 attr.numsymbols = 4;
2033 if (appData.monoMode) {
2034 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2038 if (strlen(appData.pixmapDirectory) == 0) {
2039 XpmPieces* pieces = builtInXpms;
2042 while (pieces->size != squareSize && pieces->size) pieces++;
2043 if (!pieces->size) {
2044 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2047 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2048 for (kind=0; kind<4; kind++) {
2050 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2051 pieces->xpm[piece][kind],
2052 &(xpmPieceBitmap2[kind][piece]),
2053 NULL, &attr)) != 0) {
2054 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2058 if(piece <= (int) WhiteKing)
2059 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2063 xpmJailSquare = xpmLightSquare;
2067 fprintf(stderr, _("\nLoading XPMs...\n"));
2070 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2071 fprintf(stderr, "%d ", piece+1);
2072 for (kind=0; kind<4; kind++) {
2073 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2074 ExpandPathName(appData.pixmapDirectory),
2075 piece > (int) WhiteKing ? "w" : "",
2076 pieceBitmapNames[piece],
2078 if (appData.debugMode) {
2079 fprintf(stderr, _("(File:%s:) "), buf);
2081 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2082 &(xpmPieceBitmap2[kind][piece]),
2083 NULL, &attr)) != 0) {
2084 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2085 // [HGM] missing: read of unorthodox piece failed; substitute King.
2086 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2087 ExpandPathName(appData.pixmapDirectory),
2089 if (appData.debugMode) {
2090 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2092 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2093 &(xpmPieceBitmap2[kind][piece]),
2097 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2102 if(piece <= (int) WhiteKing)
2103 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2106 /* Load light and dark squares */
2107 /* If the LSQ and DSQ pieces don't exist, we will
2108 draw them with solid squares. */
2109 fprintf(stderr, _("light square "));
2110 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2111 if (access(buf, 0) != 0) {
2115 if (appData.debugMode)
2116 fprintf(stderr, _("(File:%s:) "), buf);
2118 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2119 &xpmLightSquare, NULL, &attr)) != 0) {
2120 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2123 fprintf(stderr, _("dark square "));
2124 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2125 ExpandPathName(appData.pixmapDirectory), ss);
2126 if (appData.debugMode) {
2127 fprintf(stderr, _("(File:%s:) "), buf);
2129 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2130 &xpmDarkSquare, NULL, &attr)) != 0) {
2131 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2135 xpmJailSquare = xpmLightSquare;
2136 fprintf(stderr, _("Done.\n"));
2138 oldVariant = -1; // kludge to force re-makig of animation masks
2139 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2142 #endif /* HAVE_LIBXPM */
2145 /* No built-in bitmaps */
2150 u_int ss = squareSize;
2152 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2155 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2156 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2157 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2158 pieceBitmapNames[piece],
2159 ss, kind == SOLID ? 's' : 'o');
2160 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2161 if(piece <= (int)WhiteKing)
2162 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2166 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2170 /* With built-in bitmaps */
2174 BuiltInBits* bib = builtInBits;
2177 u_int ss = squareSize;
2179 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2182 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2184 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2185 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2186 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2187 pieceBitmapNames[piece],
2188 ss, kind == SOLID ? 's' : 'o');
2189 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2190 bib->bits[kind][piece], ss, ss);
2191 if(piece <= (int)WhiteKing)
2192 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2196 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2202 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2207 char msg[MSG_SIZ], fullname[MSG_SIZ];
2209 if (*appData.bitmapDirectory != NULLCHAR) {
2210 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2211 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2212 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2213 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2214 &w, &h, pm, &x_hot, &y_hot);
2215 fprintf(stderr, "load %s\n", name);
2216 if (errcode != BitmapSuccess) {
2218 case BitmapOpenFailed:
2219 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2221 case BitmapFileInvalid:
2222 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2224 case BitmapNoMemory:
2225 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2229 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2233 fprintf(stderr, _("%s: %s...using built-in\n"),
2235 } else if (w != wreq || h != hreq) {
2237 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2238 programName, fullname, w, h, wreq, hreq);
2244 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2254 if (lineGap == 0) return;
2256 /* [HR] Split this into 2 loops for non-square boards. */
2258 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2259 gridSegments[i].x1 = 0;
2260 gridSegments[i].x2 =
2261 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2262 gridSegments[i].y1 = gridSegments[i].y2
2263 = lineGap / 2 + (i * (squareSize + lineGap));
2266 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2267 gridSegments[j + i].y1 = 0;
2268 gridSegments[j + i].y2 =
2269 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2270 gridSegments[j + i].x1 = gridSegments[j + i].x2
2271 = lineGap / 2 + (j * (squareSize + lineGap));
2276 MarkMenuItem (char *menuRef, int state)
2278 MenuItem *item = MenuNameToItem(menuRef);
2282 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2283 XtSetValues(item->handle, args, 1);
2288 EnableMenuItem (char *menuRef, int state)
2290 MenuItem *item = MenuNameToItem(menuRef);
2292 if(item) XtSetSensitive(item->handle, state);
2296 EnableButtonBar (int state)
2298 XtSetSensitive(optList[15].handle, state);
2303 SetMenuEnables (Enables *enab)
2305 while (enab->name != NULL) {
2306 EnableMenuItem(enab->name, enab->value);
2312 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2313 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2317 if(*nprms == 0) return;
2318 item = MenuNameToItem(prms[0]);
2319 if(item) ((MenuProc *) item->proc) ();
2323 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
2325 MenuProc *proc = (MenuProc *) addr;
2331 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2333 RecentEngineEvent((int) (intptr_t) addr);
2337 AppendMenuItem (char *msg, int n)
2339 CreateMenuItem((Widget) optList[6].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
2351 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2352 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2353 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2354 dmEnables[i].piece);
2355 XtSetSensitive(entry, p != NULL || !appData.testLegality
2356 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2357 && !appData.icsActive));
2359 while (p && *p++ == dmEnables[i].piece) count++;
2360 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2362 XtSetArg(args[j], XtNlabel, label); j++;
2363 XtSetValues(entry, args, j);
2369 do_flash_delay (unsigned long msec)
2375 DrawBorder (int x, int y, int type)
2379 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
2381 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
2382 squareSize+lineGap, squareSize+lineGap);
2386 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
2388 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
2389 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
2391 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
2392 if(textureW[kind] < W*squareSize)
2393 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
2395 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
2396 if(textureH[kind] < H*squareSize)
2397 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
2399 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
2404 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
2405 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
2407 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
2408 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
2409 squareSize, squareSize, x*fac, y*fac);
2411 if (useImages && useImageSqs) {
2415 pm = xpmLightSquare;
2420 case 2: /* neutral */
2422 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
2425 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
2426 squareSize, squareSize, x*fac, y*fac);
2436 case 2: /* neutral */
2441 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
2446 I split out the routines to draw a piece so that I could
2447 make a generic flash routine.
2450 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2452 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2453 switch (square_color) {
2455 case 2: /* neutral */
2457 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2458 ? *pieceToOutline(piece)
2459 : *pieceToSolid(piece),
2460 dest, bwPieceGC, 0, 0,
2461 squareSize, squareSize, x, y);
2464 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2465 ? *pieceToSolid(piece)
2466 : *pieceToOutline(piece),
2467 dest, wbPieceGC, 0, 0,
2468 squareSize, squareSize, x, y);
2474 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2476 switch (square_color) {
2478 case 2: /* neutral */
2480 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2481 ? *pieceToOutline(piece)
2482 : *pieceToSolid(piece),
2483 dest, bwPieceGC, 0, 0,
2484 squareSize, squareSize, x, y, 1);
2487 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2488 ? *pieceToSolid(piece)
2489 : *pieceToOutline(piece),
2490 dest, wbPieceGC, 0, 0,
2491 squareSize, squareSize, x, y, 1);
2497 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2499 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
2500 switch (square_color) {
2502 XCopyPlane(xDisplay, *pieceToSolid(piece),
2503 dest, (int) piece < (int) BlackPawn
2504 ? wlPieceGC : blPieceGC, 0, 0,
2505 squareSize, squareSize, x, y, 1);
2508 XCopyPlane(xDisplay, *pieceToSolid(piece),
2509 dest, (int) piece < (int) BlackPawn
2510 ? wdPieceGC : bdPieceGC, 0, 0,
2511 squareSize, squareSize, x, y, 1);
2513 case 2: /* neutral */
2515 break; // should never contain pieces
2520 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2522 int kind, p = piece;
2524 switch (square_color) {
2526 case 2: /* neutral */
2528 if ((int)piece < (int) BlackPawn) {
2536 if ((int)piece < (int) BlackPawn) {
2544 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2545 if(useTexture & square_color+1) {
2546 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2547 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
2548 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
2549 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
2550 XSetClipMask(xDisplay, wlPieceGC, None);
2551 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
2553 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
2554 dest, wlPieceGC, 0, 0,
2555 squareSize, squareSize, x, y);
2558 typedef void (*DrawFunc)();
2563 if (appData.monoMode) {
2564 if (DefaultDepth(xDisplay, xScreen) == 1) {
2565 return monoDrawPiece_1bit;
2567 return monoDrawPiece;
2571 return colorDrawPieceImage;
2573 return colorDrawPiece;
2578 DrawDot (int marker, int x, int y, int r)
2580 if(appData.monoMode) {
2581 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
2582 x, y, r, r, 0, 64*360);
2583 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
2584 x, y, r, r, 0, 64*360);
2586 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
2587 x, y, r, r, 0, 64*360);
2591 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2592 { // basic front-end board-draw function: takes care of everything that can be in square:
2593 // piece, background, coordinate/count, marker dot
2594 int direction, font_ascent, font_descent;
2595 XCharStruct overall;
2598 if (piece == EmptySquare) {
2599 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2601 drawfunc = ChooseDrawFunc();
2602 drawfunc(piece, square_color, x, y, xBoardWindow);
2605 if(align) { // square carries inscription (coord or piece count)
2607 GC hGC = align < 3 ? coordGC : countGC;
2608 // first calculate where it goes
2609 XTextExtents(countFontStruct, string, 1, &direction,
2610 &font_ascent, &font_descent, &overall);
2612 xx += squareSize - overall.width - 2;
2613 yy += squareSize - font_descent - 1;
2614 } else if (align == 2) {
2615 xx += 2, yy += font_ascent + 1;
2616 } else if (align == 3) {
2617 xx += squareSize - overall.width - 2;
2618 yy += font_ascent + 1;
2619 } else if (align == 4) {
2620 xx += 2, yy += font_ascent + 1;
2623 if (appData.monoMode) {
2624 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2626 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2630 if(marker) { // print fat marker dot, if requested
2631 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2636 FlashDelay (int flash_delay)
2638 XSync(xDisplay, False);
2639 if(flash_delay) do_flash_delay(flash_delay);
2643 Fraction (int x, int start, int stop)
2645 double f = ((double) x - start)/(stop - start);
2646 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2650 static WindowPlacement wpNew;
2653 CoDrag (Widget sh, WindowPlacement *wp)
2656 int j=0, touch=0, fudge = 2;
2657 GetActualPlacement(sh, wp);
2658 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
2659 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
2660 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2661 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
2662 if(!touch ) return; // only windows that touch co-move
2663 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2664 int heightInc = wpNew.height - wpMain.height;
2665 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2666 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2667 wp->y += fracTop * heightInc;
2668 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2669 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2670 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2671 int widthInc = wpNew.width - wpMain.width;
2672 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2673 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2674 wp->y += fracLeft * widthInc;
2675 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2676 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2678 wp->x += wpNew.x - wpMain.x;
2679 wp->y += wpNew.y - wpMain.y;
2680 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2681 if(touch == 3) wp->y += wpNew.height - wpMain.height;
2682 XtSetArg(args[j], XtNx, wp->x); j++;
2683 XtSetArg(args[j], XtNy, wp->y); j++;
2684 XtSetValues(sh, args, j);
2687 static XtIntervalId delayedDragID = 0;
2692 GetActualPlacement(shellWidget, &wpNew);
2693 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
2694 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
2695 return; // false alarm
2696 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
2697 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
2698 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
2699 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
2701 DrawPosition(True, NULL);
2702 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
2709 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
2711 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
2715 EventProc (Widget widget, caddr_t unused, XEvent *event)
2717 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
2718 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
2721 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
2723 DrawSeekAxis (int x, int y, int xTo, int yTo)
2725 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
2729 DrawSeekBackground (int left, int top, int right, int bottom)
2731 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
2735 DrawSeekText (char *buf, int x, int y)
2737 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
2741 DrawSeekDot (int x, int y, int colorNr)
2743 int square = colorNr & 0x80;
2746 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
2748 XFillRectangle(xDisplay, xBoardWindow, color,
2749 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
2751 XFillArc(xDisplay, xBoardWindow, color,
2752 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
2758 XDrawSegments(xDisplay, xBoardWindow, lineGC,
2759 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
2764 * event handler for redrawing the board
2767 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2769 DrawPosition(True, NULL);
2774 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
2775 { // [HGM] pv: walk PV
2776 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
2779 static int savedIndex; /* gross that this is global */
2782 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
2785 XawTextPosition index, dummy;
2788 XawTextGetSelectionPos(w, &index, &dummy);
2789 XtSetArg(arg, XtNstring, &val);
2790 XtGetValues(w, &arg, 1);
2791 ReplaceComment(savedIndex, val);
2792 if(savedIndex != currentMove) ToNrEvent(savedIndex);
2793 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
2797 EditCommentPopUp (int index, char *title, char *text)
2800 if (text == NULL) text = "";
2801 NewCommentPopup(title, text, index);
2805 CommentPopUp (char *title, char *text)
2807 savedIndex = currentMove; // [HGM] vari
2808 NewCommentPopup(title, text, currentMove);
2814 PopDown(CommentDlg);
2817 static char *openName;
2823 (void) (*fileProc)(openFP, 0, openName);
2827 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
2829 fileProc = proc; /* I can't see a way not */
2830 fileOpenMode = openMode; /* to use globals here */
2831 { // [HGM] use file-selector dialog stolen from Ghostview
2832 int index; // this is not supported yet
2833 Browse(BoardWindow, label, (def[0] ? def : NULL), filter, False, openMode, &openName, &openFP);
2838 /* Disable all user input other than deleting the window */
2839 static int frozen = 0;
2845 /* Grab by a widget that doesn't accept input */
2846 XtAddGrab(optList[14].handle, TRUE, FALSE);
2850 /* Undo a FreezeUI */
2854 if (!frozen) return;
2855 XtRemoveGrab(optList[14].handle);
2863 static int oldPausing = FALSE;
2864 static GameMode oldmode = (GameMode) -1;
2867 if (!boardWidget || !XtIsRealized(boardWidget)) return;
2869 if (pausing != oldPausing) {
2870 oldPausing = pausing;
2871 MarkMenuItem("Mode.Pause", pausing);
2873 if (appData.showButtonBar) {
2874 /* Always toggle, don't set. Previous code messes up when
2875 invoked while the button is pressed, as releasing it
2876 toggles the state again. */
2879 XtSetArg(args[0], XtNbackground, &oldbg);
2880 XtSetArg(args[1], XtNforeground, &oldfg);
2881 XtGetValues(optList[18].handle,
2883 XtSetArg(args[0], XtNbackground, oldfg);
2884 XtSetArg(args[1], XtNforeground, oldbg);
2886 XtSetValues(optList[18].handle, args, 2);
2890 wname = ModeToWidgetName(oldmode);
2891 if (wname != NULL) {
2892 MarkMenuItem(wname, False);
2894 wname = ModeToWidgetName(gameMode);
2895 if (wname != NULL) {
2896 MarkMenuItem(wname, True);
2899 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
2901 /* Maybe all the enables should be handled here, not just this one */
2902 EnableMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
2907 * Button/menu procedures
2910 /* this variable is shared between CopyPositionProc and SendPositionSelection */
2911 char *selected_fen_position=NULL;
2914 SendPositionSelection (Widget w, Atom *selection, Atom *target,
2915 Atom *type_return, XtPointer *value_return,
2916 unsigned long *length_return, int *format_return)
2918 char *selection_tmp;
2920 // if (!selected_fen_position) return False; /* should never happen */
2921 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
2922 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
2923 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
2926 if (f == NULL) return False;
2930 selection_tmp = XtMalloc(len + 1);
2931 count = fread(selection_tmp, 1, len, f);
2934 XtFree(selection_tmp);
2937 selection_tmp[len] = NULLCHAR;
2939 /* note: since no XtSelectionDoneProc was registered, Xt will
2940 * automatically call XtFree on the value returned. So have to
2941 * make a copy of it allocated with XtMalloc */
2942 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
2943 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
2946 *value_return=selection_tmp;
2947 *length_return=strlen(selection_tmp);
2948 *type_return=*target;
2949 *format_return = 8; /* bits per byte */
2951 } else if (*target == XA_TARGETS(xDisplay)) {
2952 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
2953 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
2954 targets_tmp[1] = XA_STRING;
2955 *value_return = targets_tmp;
2956 *type_return = XA_ATOM;
2959 // This code leads to a read of value_return out of bounds on 64-bit systems.
2960 // Other code which I have seen always sets *format_return to 32 independent of
2961 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
2962 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
2963 *format_return = 8 * sizeof(Atom);
2964 if (*format_return > 32) {
2965 *length_return *= *format_return / 32;
2966 *format_return = 32;
2969 *format_return = 32;
2977 /* note: when called from menu all parameters are NULL, so no clue what the
2978 * Widget which was clicked on was, or what the click event was
2981 CopySomething (char *src)
2983 selected_fen_position = src;
2985 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
2986 * have a notion of a position that is selected but not copied.
2987 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
2989 XtOwnSelection(menuBarWidget, XA_PRIMARY,
2991 SendPositionSelection,
2992 NULL/* lose_ownership_proc */ ,
2993 NULL/* transfer_done_proc */);
2994 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
2996 SendPositionSelection,
2997 NULL/* lose_ownership_proc */ ,
2998 NULL/* transfer_done_proc */);
3001 /* function called when the data to Paste is ready */
3003 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3004 Atom *type, XtPointer value, unsigned long *len, int *format)
3007 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3008 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3009 EditPositionPasteFEN(fenstr);
3013 /* called when Paste Position button is pressed,
3014 * all parameters will be NULL */
3016 PastePositionProc ()
3018 XtGetSelectionValue(menuBarWidget,
3019 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3020 /* (XtSelectionCallbackProc) */ PastePositionCB,
3021 NULL, /* client_data passed to PastePositionCB */
3023 /* better to use the time field from the event that triggered the
3024 * call to this function, but that isn't trivial to get
3031 /* note: when called from menu all parameters are NULL, so no clue what the
3032 * Widget which was clicked on was, or what the click event was
3034 /* function called when the data to Paste is ready */
3036 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3037 Atom *type, XtPointer value, unsigned long *len, int *format)
3040 if (value == NULL || *len == 0) {
3041 return; /* nothing had been selected to copy */
3043 f = fopen(gamePasteFilename, "w");
3045 DisplayError(_("Can't open temp file"), errno);
3048 fwrite(value, 1, *len, f);
3051 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3054 /* called when Paste Game button is pressed,
3055 * all parameters will be NULL */
3059 XtGetSelectionValue(menuBarWidget,
3060 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3061 /* (XtSelectionCallbackProc) */ PasteGameCB,
3062 NULL, /* client_data passed to PasteGameCB */
3064 /* better to use the time field from the event that triggered the
3065 * call to this function, but that isn't trivial to get
3074 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3081 { // bassic primitive for determining if modifier keys are pressed
3082 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3085 XQueryKeymap(xDisplay,keys);
3086 for(i=0; i<6; i++) {
3088 j = XKeysymToKeycode(xDisplay, codes[i]);
3089 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3095 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3099 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3100 if ( n == 1 && *buf >= 32 // printable
3101 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3102 ) BoxAutoPopUp (buf);
3106 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3107 { // [HGM] input: let up-arrow recall previous line from history
3112 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3113 { // [HGM] input: let down-arrow recall next line from history
3118 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3124 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3126 if (!TempBackwardActive) {
3127 TempBackwardActive = True;
3133 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3135 /* Check to see if triggered by a key release event for a repeating key.
3136 * If so the next queued event will be a key press of the same key at the same time */
3137 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3139 XPeekEvent(xDisplay, &next);
3140 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3141 next.xkey.keycode == event->xkey.keycode)
3145 TempBackwardActive = False;
3149 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3150 { // called as key binding
3153 if (nprms && *nprms > 0)
3157 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3162 SetWindowTitle (char *text, char *title, char *icon)
3166 if (appData.titleInWindow) {
3168 XtSetArg(args[i], XtNlabel, text); i++;
3169 XtSetValues(titleWidget, args, i);
3172 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3173 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3174 XtSetValues(shellWidget, args, i);
3175 XSync(xDisplay, False);
3180 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3186 DisplayIcsInteractionTitle (String message)
3188 if (oldICSInteractionTitle == NULL) {
3189 /* Magic to find the old window title, adapted from vim */
3190 char *wina = getenv("WINDOWID");
3192 Window win = (Window) atoi(wina);
3193 Window root, parent, *children;
3194 unsigned int nchildren;
3195 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3197 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3198 if (!XQueryTree(xDisplay, win, &root, &parent,
3199 &children, &nchildren)) break;
3200 if (children) XFree((void *)children);
3201 if (parent == root || parent == 0) break;
3204 XSetErrorHandler(oldHandler);
3206 if (oldICSInteractionTitle == NULL) {
3207 oldICSInteractionTitle = "xterm";
3210 printf("\033]0;%s\007", message);
3215 XtIntervalId delayedEventTimerXID = 0;
3216 DelayedEventCallback delayedEventCallback = 0;
3221 delayedEventTimerXID = 0;
3222 delayedEventCallback();
3226 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3228 if(delayedEventTimerXID && delayedEventCallback == cb)
3229 // [HGM] alive: replace, rather than add or flush identical event
3230 XtRemoveTimeOut(delayedEventTimerXID);
3231 delayedEventCallback = cb;
3232 delayedEventTimerXID =
3233 XtAppAddTimeOut(appContext, millisec,
3234 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3237 DelayedEventCallback
3240 if (delayedEventTimerXID) {
3241 return delayedEventCallback;
3248 CancelDelayedEvent ()
3250 if (delayedEventTimerXID) {
3251 XtRemoveTimeOut(delayedEventTimerXID);
3252 delayedEventTimerXID = 0;
3256 XtIntervalId loadGameTimerXID = 0;
3259 LoadGameTimerRunning ()
3261 return loadGameTimerXID != 0;
3265 StopLoadGameTimer ()
3267 if (loadGameTimerXID != 0) {
3268 XtRemoveTimeOut(loadGameTimerXID);
3269 loadGameTimerXID = 0;
3277 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3279 loadGameTimerXID = 0;
3284 StartLoadGameTimer (long millisec)
3287 XtAppAddTimeOut(appContext, millisec,
3288 (XtTimerCallbackProc) LoadGameTimerCallback,
3292 XtIntervalId analysisClockXID = 0;
3295 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3297 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3298 || appData.icsEngineAnalyze) { // [DM]
3299 AnalysisPeriodicEvent(0);
3300 StartAnalysisClock();
3305 StartAnalysisClock ()
3308 XtAppAddTimeOut(appContext, 2000,
3309 (XtTimerCallbackProc) AnalysisClockCallback,
3313 XtIntervalId clockTimerXID = 0;
3316 ClockTimerRunning ()
3318 return clockTimerXID != 0;
3324 if (clockTimerXID != 0) {
3325 XtRemoveTimeOut(clockTimerXID);
3334 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3341 StartClockTimer (long millisec)
3344 XtAppAddTimeOut(appContext, millisec,
3345 (XtTimerCallbackProc) ClockTimerCallback,
3350 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
3354 Widget w = (Widget) opt->handle;
3356 /* check for low time warning */
3357 Pixel foregroundOrWarningColor = timerForegroundPixel;
3360 appData.lowTimeWarning &&
3361 (timer / 1000) < appData.icsAlarmTime)
3362 foregroundOrWarningColor = lowTimeWarningColor;
3364 if (appData.clockMode) {
3365 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
3366 XtSetArg(args[0], XtNlabel, buf);
3368 snprintf(buf, MSG_SIZ, "%s ", color);
3369 XtSetArg(args[0], XtNlabel, buf);
3374 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3375 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3377 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3378 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3381 XtSetValues(w, args, 3);
3384 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3387 SetClockIcon (int color)
3390 Pixmap pm = *clockIcons[color];
3391 if (iconPixmap != pm) {
3393 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3394 XtSetValues(shellWidget, args, 1);
3399 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3401 InputSource *is = (InputSource *) closure;
3406 if (is->lineByLine) {
3407 count = read(is->fd, is->unused,
3408 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3410 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3413 is->unused += count;
3415 while (p < is->unused) {
3416 q = memchr(p, '\n', is->unused - p);
3417 if (q == NULL) break;
3419 (is->func)(is, is->closure, p, q - p, 0);
3423 while (p < is->unused) {
3428 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3433 (is->func)(is, is->closure, is->buf, count, error);
3438 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3441 ChildProc *cp = (ChildProc *) pr;
3443 is = (InputSource *) calloc(1, sizeof(InputSource));
3444 is->lineByLine = lineByLine;
3448 is->fd = fileno(stdin);
3450 is->kind = cp->kind;
3451 is->fd = cp->fdFrom;
3454 is->unused = is->buf;
3457 is->xid = XtAppAddInput(appContext, is->fd,
3458 (XtPointer) (XtInputReadMask),
3459 (XtInputCallbackProc) DoInputCallback,
3461 is->closure = closure;
3462 return (InputSourceRef) is;
3466 RemoveInputSource (InputSourceRef isr)
3468 InputSource *is = (InputSource *) isr;
3470 if (is->xid == 0) return;
3471 XtRemoveInput(is->xid);
3475 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
3477 /* Masks for XPM pieces. Black and white pieces can have
3478 different shapes, but in the interest of retaining my
3479 sanity pieces must have the same outline on both light
3480 and dark squares, and all pieces must use the same
3481 background square colors/images. */
3483 static int xpmDone = 0;
3484 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
3485 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
3488 CreateAnimMasks (int pieceDepth)
3494 unsigned long plane;
3497 /* Need a bitmap just to get a GC with right depth */
3498 buf = XCreatePixmap(xDisplay, xBoardWindow,
3500 values.foreground = 1;
3501 values.background = 0;
3502 /* Don't use XtGetGC, not read only */
3503 maskGC = XCreateGC(xDisplay, buf,
3504 GCForeground | GCBackground, &values);
3505 XFreePixmap(xDisplay, buf);
3507 buf = XCreatePixmap(xDisplay, xBoardWindow,
3508 squareSize, squareSize, pieceDepth);
3509 values.foreground = XBlackPixel(xDisplay, xScreen);
3510 values.background = XWhitePixel(xDisplay, xScreen);
3511 bufGC = XCreateGC(xDisplay, buf,
3512 GCForeground | GCBackground, &values);
3514 for (piece = WhitePawn; piece <= BlackKing; piece++) {
3515 /* Begin with empty mask */
3516 if(!xpmDone) // [HGM] pieces: keep using existing
3517 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
3518 squareSize, squareSize, 1);
3519 XSetFunction(xDisplay, maskGC, GXclear);
3520 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
3521 0, 0, squareSize, squareSize);
3523 /* Take a copy of the piece */
3528 XSetFunction(xDisplay, bufGC, GXcopy);
3529 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
3531 0, 0, squareSize, squareSize, 0, 0);
3533 /* XOR the background (light) over the piece */
3534 XSetFunction(xDisplay, bufGC, GXxor);
3536 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
3537 0, 0, squareSize, squareSize, 0, 0);
3539 XSetForeground(xDisplay, bufGC, lightSquareColor);
3540 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
3543 /* We now have an inverted piece image with the background
3544 erased. Construct mask by just selecting all the non-zero
3545 pixels - no need to reconstruct the original image. */
3546 XSetFunction(xDisplay, maskGC, GXor);
3548 /* Might be quicker to download an XImage and create bitmap
3549 data from it rather than this N copies per piece, but it
3550 only takes a fraction of a second and there is a much
3551 longer delay for loading the pieces. */
3552 for (n = 0; n < pieceDepth; n ++) {
3553 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
3554 0, 0, squareSize, squareSize,
3560 XFreePixmap(xDisplay, buf);
3561 XFreeGC(xDisplay, bufGC);
3562 XFreeGC(xDisplay, maskGC);
3566 InitAnimState (AnimNr anr, XWindowAttributes *info)
3571 /* Each buffer is square size, same depth as window */
3572 animBufs[anr+4] = xBoardWindow;
3573 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
3574 squareSize, squareSize, info->depth);
3575 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
3576 squareSize, squareSize, info->depth);
3578 /* Create a plain GC for blitting */
3579 mask = GCForeground | GCBackground | GCFunction |
3580 GCPlaneMask | GCGraphicsExposures;
3581 values.foreground = XBlackPixel(xDisplay, xScreen);
3582 values.background = XWhitePixel(xDisplay, xScreen);
3583 values.function = GXcopy;
3584 values.plane_mask = AllPlanes;
3585 values.graphics_exposures = False;
3586 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
3588 /* Piece will be copied from an existing context at
3589 the start of each new animation/drag. */
3590 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
3592 /* Outline will be a read-only copy of an existing */
3593 animGCs[anr+4] = None;
3599 XWindowAttributes info;
3601 if (xpmDone && gameInfo.variant == oldVariant) return;
3602 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
3603 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
3605 InitAnimState(Game, &info);
3606 InitAnimState(Player, &info);
3608 /* For XPM pieces, we need bitmaps to use as masks. */
3610 CreateAnimMasks(info.depth), xpmDone = 1;
3615 static Boolean frameWaiting;
3618 FrameAlarm (int sig)
3620 frameWaiting = False;
3621 /* In case System-V style signals. Needed?? */
3622 signal(SIGALRM, FrameAlarm);
3626 FrameDelay (int time)
3628 struct itimerval delay;
3630 XSync(xDisplay, False);
3633 frameWaiting = True;
3634 signal(SIGALRM, FrameAlarm);
3635 delay.it_interval.tv_sec =
3636 delay.it_value.tv_sec = time / 1000;
3637 delay.it_interval.tv_usec =
3638 delay.it_value.tv_usec = (time % 1000) * 1000;
3639 setitimer(ITIMER_REAL, &delay, NULL);
3640 while (frameWaiting) pause();
3641 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
3642 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
3643 setitimer(ITIMER_REAL, &delay, NULL);
3650 FrameDelay (int time)
3652 XSync(xDisplay, False);
3654 usleep(time * 1000);
3660 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
3664 /* Bitmap for piece being moved. */
3665 if (appData.monoMode) {
3666 *mask = *pieceToSolid(piece);
3667 } else if (useImages) {
3669 *mask = xpmMask[piece];
3671 *mask = ximMaskPm[piece];
3674 *mask = *pieceToSolid(piece);
3677 /* GC for piece being moved. Square color doesn't matter, but
3678 since it gets modified we make a copy of the original. */
3680 if (appData.monoMode)
3685 if (appData.monoMode)
3690 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
3692 /* Outline only used in mono mode and is not modified */
3694 *outline = bwPieceGC;
3696 *outline = wbPieceGC;
3700 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
3705 /* Draw solid rectangle which will be clipped to shape of piece */
3706 XFillRectangle(xDisplay, dest, clip,
3707 0, 0, squareSize, squareSize);
3708 if (appData.monoMode)
3709 /* Also draw outline in contrasting color for black
3710 on black / white on white cases */
3711 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
3712 0, 0, squareSize, squareSize, 0, 0, 1);
3714 /* Copy the piece */
3719 if(appData.upsideDown && flipView) kind ^= 2;
3720 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3722 0, 0, squareSize, squareSize,
3728 InsertPiece (AnimNr anr, ChessSquare piece)
3730 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
3734 DrawBlank (AnimNr anr, int x, int y, int startColor)
3736 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
3739 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
3740 int srcX, int srcY, int width, int height, int destX, int destY)
3742 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
3743 srcX, srcY, width, height, destX, destY);
3747 SetDragPiece (AnimNr anr, ChessSquare piece)
3750 /* The piece will be drawn using its own bitmap as a matte */
3751 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
3752 XSetClipMask(xDisplay, animGCs[anr+2], mask);
3755 /* [AS] Arrow highlighting support */
3758 DrawPolygon (Pnt arrow[], int nr)
3762 for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
3763 XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
3764 if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
3768 UpdateLogos (int displ)
3770 return; // no logos in XBoard yet