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, oldTwoBoards = 0;
941 if(!formWidget) return;
943 if(oldTwoBoards && !twoBoards) PopDown(DummyDlg);
944 oldTwoBoards = twoBoards;
946 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
947 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
948 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
949 fprintf(debugFP, "BOARD %d x %d\n",boardWidth, boardHeight);
950 if(boardWidth != oldWidth || boardHeight != oldHeight) { // do resizing stuff only if size actually changed
952 oldWidth = boardWidth; oldHeight = boardHeight;
956 * Inhibit shell resizing.
958 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW ;
959 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
960 shellArgs[4].value = shellArgs[2].value = w;
961 shellArgs[5].value = shellArgs[3].value = h;
962 XtSetValues(shellWidget, &shellArgs[0], 6);
964 XSync(xDisplay, False);
968 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
971 if(gameInfo.variant != oldVariant) { // and only if variant changed
976 for(p=0; p<=(int)WhiteKing; p++)
977 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
978 if(gameInfo.variant == VariantShogi) {
979 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
980 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
981 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
982 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
983 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
986 if(gameInfo.variant == VariantGothic) {
987 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
990 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
991 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
992 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
995 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
996 for(p=0; p<=(int)WhiteKing; p++)
997 ximMaskPm[p] = ximMaskPm2[p]; // defaults
998 if(gameInfo.variant == VariantShogi) {
999 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1000 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1001 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1002 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1003 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1006 if(gameInfo.variant == VariantGothic) {
1007 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1010 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1011 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1012 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1017 for(i=0; i<2; i++) {
1019 for(p=0; p<=(int)WhiteKing; p++)
1020 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1021 if(gameInfo.variant == VariantShogi) {
1022 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1023 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1024 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1025 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1026 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1029 if(gameInfo.variant == VariantGothic) {
1030 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1033 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1034 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1035 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1039 oldMono = -10; // kludge to force recreation of animation masks
1040 oldVariant = gameInfo.variant;
1043 if(appData.monoMode != oldMono)
1046 oldMono = appData.monoMode;
1050 MakeOneColor (char *name, Pixel *color)
1052 XrmValue vFrom, vTo;
1053 if (!appData.monoMode) {
1054 vFrom.addr = (caddr_t) name;
1055 vFrom.size = strlen(name);
1056 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1057 if (vTo.addr == NULL) {
1058 appData.monoMode = True;
1061 *color = *(Pixel *) vTo.addr;
1069 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1070 int forceMono = False;
1072 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1073 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1074 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1075 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1076 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1077 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1078 if (appData.lowTimeWarning)
1079 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
1080 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1081 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1088 { // [HGM] taken out of main
1090 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1091 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1092 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1094 if (appData.bitmapDirectory[0] != NULLCHAR) {
1098 CreateXPMBoard(appData.liteBackTextureFile, 1);
1099 CreateXPMBoard(appData.darkBackTextureFile, 0);
1103 /* Create regular pieces */
1104 if (!useImages) CreatePieces();
1109 InitDrawingParams ()
1111 MakeColors(); CreateGCs(True);
1116 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
1117 { // determine what fonts to use, and create them
1118 XrmValue vFrom, vTo;
1121 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1122 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1123 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1124 appData.font = fontTable[MESSAGE_FONT][squareSize];
1125 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1126 appData.coordFont = fontTable[COORD_FONT][squareSize];
1129 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1130 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1131 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1132 fontSet = CreateFontSet(appData.font);
1133 clockFontSet = CreateFontSet(appData.clockFont);
1135 /* For the coordFont, use the 0th font of the fontset. */
1136 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1137 XFontStruct **font_struct_list;
1138 XFontSetExtents *fontSize;
1139 char **font_name_list;
1140 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1141 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1142 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1143 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1144 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1147 appData.font = FindFont(appData.font, fontPxlSize);
1148 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1149 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1150 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1151 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1152 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1153 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1154 // textHeight in !NLS mode!
1156 countFontID = coordFontID; // [HGM] holdings
1157 countFontStruct = coordFontStruct;
1159 xdb = XtDatabase(xDisplay);
1161 XrmPutLineResource(&xdb, "*international: True");
1162 vTo.size = sizeof(XFontSet);
1163 vTo.addr = (XtPointer) &fontSet;
1164 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1166 XrmPutStringResource(&xdb, "*font", appData.font);
1171 main (int argc, char **argv)
1173 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1174 XSetWindowAttributes window_attributes;
1176 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1177 XtGeometryResult gres;
1179 int forceMono = False;
1181 srandom(time(0)); // [HGM] book: make random truly random
1183 setbuf(stdout, NULL);
1184 setbuf(stderr, NULL);
1187 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1188 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1192 programName = strrchr(argv[0], '/');
1193 if (programName == NULL)
1194 programName = argv[0];
1199 XtSetLanguageProc(NULL, NULL, NULL);
1200 if (appData.debugMode) {
1201 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1204 bindtextdomain(PACKAGE, LOCALEDIR);
1205 textdomain(PACKAGE);
1208 appData.boardSize = "";
1209 InitAppData(ConvertToLine(argc, argv));
1211 if (p == NULL) p = "/tmp";
1212 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1213 gameCopyFilename = (char*) malloc(i);
1214 gamePasteFilename = (char*) malloc(i);
1215 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1216 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1218 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1219 static char buf[MSG_SIZ];
1220 EscapeExpand(buf, appData.firstInitString);
1221 appData.firstInitString = strdup(buf);
1222 EscapeExpand(buf, appData.secondInitString);
1223 appData.secondInitString = strdup(buf);
1224 EscapeExpand(buf, appData.firstComputerString);
1225 appData.firstComputerString = strdup(buf);
1226 EscapeExpand(buf, appData.secondComputerString);
1227 appData.secondComputerString = strdup(buf);
1230 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1233 if (chdir(chessDir) != 0) {
1234 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1240 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1241 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1242 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1243 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1246 setbuf(debugFP, NULL);
1249 /* [HGM,HR] make sure board size is acceptable */
1250 if(appData.NrFiles > BOARD_FILES ||
1251 appData.NrRanks > BOARD_RANKS )
1252 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1255 /* This feature does not work; animation needs a rewrite */
1256 appData.highlightDragging = FALSE;
1260 gameInfo.variant = StringToVariant(appData.variant);
1261 InitPosition(FALSE);
1264 XtAppInitialize(&appContext, "XBoard", shellOptions,
1265 XtNumber(shellOptions),
1266 &argc, argv, xboardResources, NULL, 0);
1268 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1269 clientResources, XtNumber(clientResources),
1272 xDisplay = XtDisplay(shellWidget);
1273 xScreen = DefaultScreen(xDisplay);
1274 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1277 * determine size, based on supplied or remembered -size, or screen size
1279 if (isdigit(appData.boardSize[0])) {
1280 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1281 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1282 &fontPxlSize, &smallLayout, &tinyLayout);
1284 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1285 programName, appData.boardSize);
1289 /* Find some defaults; use the nearest known size */
1290 SizeDefaults *szd, *nearest;
1291 int distance = 99999;
1292 nearest = szd = sizeDefaults;
1293 while (szd->name != NULL) {
1294 if (abs(szd->squareSize - squareSize) < distance) {
1296 distance = abs(szd->squareSize - squareSize);
1297 if (distance == 0) break;
1301 if (i < 2) lineGap = nearest->lineGap;
1302 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1303 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1304 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1305 if (i < 6) smallLayout = nearest->smallLayout;
1306 if (i < 7) tinyLayout = nearest->tinyLayout;
1309 SizeDefaults *szd = sizeDefaults;
1310 if (*appData.boardSize == NULLCHAR) {
1311 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1312 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1315 if (szd->name == NULL) szd--;
1316 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1318 while (szd->name != NULL &&
1319 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1320 if (szd->name == NULL) {
1321 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1322 programName, appData.boardSize);
1326 squareSize = szd->squareSize;
1327 lineGap = szd->lineGap;
1328 clockFontPxlSize = szd->clockFontPxlSize;
1329 coordFontPxlSize = szd->coordFontPxlSize;
1330 fontPxlSize = szd->fontPxlSize;
1331 smallLayout = szd->smallLayout;
1332 tinyLayout = szd->tinyLayout;
1333 // [HGM] font: use defaults from settings file if available and not overruled
1336 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1337 if (strlen(appData.pixmapDirectory) > 0) {
1338 p = ExpandPathName(appData.pixmapDirectory);
1340 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1341 appData.pixmapDirectory);
1344 if (appData.debugMode) {
1345 fprintf(stderr, _("\
1346 XBoard square size (hint): %d\n\
1347 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1349 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1350 if (appData.debugMode) {
1351 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1354 defaultLineGap = lineGap;
1355 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1357 /* [HR] height treated separately (hacked) */
1358 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1359 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1362 * Determine what fonts to use.
1364 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1367 * Detect if there are not enough colors available and adapt.
1369 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1370 appData.monoMode = True;
1373 forceMono = MakeColors();
1376 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1378 appData.monoMode = True;
1381 if (appData.monoMode && appData.debugMode) {
1382 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1383 (unsigned long) XWhitePixel(xDisplay, xScreen),
1384 (unsigned long) XBlackPixel(xDisplay, xScreen));
1387 ParseIcsTextColors();
1389 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1395 layoutName = "tinyLayout";
1396 } else if (smallLayout) {
1397 layoutName = "smallLayout";
1399 layoutName = "normalLayout";
1402 optList = BoardPopUp(squareSize, lineGap, (void*)
1408 boardWidget = optList[W_BOARD].handle;
1409 menuBarWidget = optList[W_MENU].handle;
1410 dropMenu = optList[W_DROP].handle;
1411 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1412 formWidget = XtParent(boardWidget);
1413 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1414 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1415 XtGetValues(optList[W_WHITE].handle, args, 2);
1416 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1417 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1418 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1419 XtGetValues(optList[W_PAUSE].handle, args, 2);
1421 AppendEnginesToMenu(appData.recentEngineList);
1423 xBoardWindow = XtWindow(boardWidget);
1425 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1426 // not need to go into InitDrawingSizes().
1429 * Create X checkmark bitmap and initialize option menu checks.
1431 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1432 checkmark_bits, checkmark_width, checkmark_height);
1438 ReadBitmap(&wIconPixmap, "icon_white.bm",
1439 icon_white_bits, icon_white_width, icon_white_height);
1440 ReadBitmap(&bIconPixmap, "icon_black.bm",
1441 icon_black_bits, icon_black_width, icon_black_height);
1442 iconPixmap = wIconPixmap;
1444 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1445 XtSetValues(shellWidget, args, i);
1448 * Create a cursor for the board widget.
1450 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1451 XChangeWindowAttributes(xDisplay, xBoardWindow,
1452 CWCursor, &window_attributes);
1455 * Inhibit shell resizing.
1457 shellArgs[0].value = (XtArgVal) &w;
1458 shellArgs[1].value = (XtArgVal) &h;
1459 XtGetValues(shellWidget, shellArgs, 2);
1460 shellArgs[4].value = shellArgs[2].value = w;
1461 shellArgs[5].value = shellArgs[3].value = h;
1462 XtSetValues(shellWidget, &shellArgs[2], 4);
1463 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1464 marginH = h - boardHeight;
1466 CatchDeleteWindow(shellWidget, "QuitProc");
1472 if (appData.animate || appData.animateDragging)
1475 XtAugmentTranslations(formWidget,
1476 XtParseTranslationTable(globalTranslations));
1478 XtAddEventHandler(formWidget, KeyPressMask, False,
1479 (XtEventHandler) MoveTypeInProc, NULL);
1480 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1481 (XtEventHandler) EventProc, NULL);
1483 /* [AS] Restore layout */
1484 if( wpMoveHistory.visible ) {
1488 if( wpEvalGraph.visible )
1493 if( wpEngineOutput.visible ) {
1494 EngineOutputPopUp();
1499 if (errorExitStatus == -1) {
1500 if (appData.icsActive) {
1501 /* We now wait until we see "login:" from the ICS before
1502 sending the logon script (problems with timestamp otherwise) */
1503 /*ICSInitScript();*/
1504 if (appData.icsInputBox) ICSInputBoxPopUp();
1508 signal(SIGWINCH, TermSizeSigHandler);
1510 signal(SIGINT, IntSigHandler);
1511 signal(SIGTERM, IntSigHandler);
1512 if (*appData.cmailGameName != NULLCHAR) {
1513 signal(SIGUSR1, CmailSigHandler);
1517 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1519 // XtSetKeyboardFocus(shellWidget, formWidget);
1520 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1522 XtAppMainLoop(appContext);
1523 if (appData.debugMode) fclose(debugFP); // [DM] debug
1528 TermSizeSigHandler (int sig)
1534 IntSigHandler (int sig)
1540 CmailSigHandler (int sig)
1545 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1547 /* Activate call-back function CmailSigHandlerCallBack() */
1548 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1550 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1554 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1557 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1559 /**** end signal code ****/
1562 #define Abs(n) ((n)<0 ? -(n) : (n))
1566 InsertPxlSize (char *pattern, int targetPxlSize)
1568 char *base_fnt_lst, strInt[12], *p, *q;
1569 int alternatives, i, len, strIntLen;
1572 * Replace the "*" (if present) in the pixel-size slot of each
1573 * alternative with the targetPxlSize.
1577 while ((p = strchr(p, ',')) != NULL) {
1581 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1582 strIntLen = strlen(strInt);
1583 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1587 while (alternatives--) {
1588 char *comma = strchr(p, ',');
1589 for (i=0; i<14; i++) {
1590 char *hyphen = strchr(p, '-');
1592 if (comma && hyphen > comma) break;
1593 len = hyphen + 1 - p;
1594 if (i == 7 && *p == '*' && len == 2) {
1596 memcpy(q, strInt, strIntLen);
1606 len = comma + 1 - p;
1613 return base_fnt_lst;
1617 CreateFontSet (char *base_fnt_lst)
1620 char **missing_list;
1624 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1625 &missing_list, &missing_count, &def_string);
1626 if (appData.debugMode) {
1628 XFontStruct **font_struct_list;
1629 char **font_name_list;
1630 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1632 fprintf(debugFP, " got list %s, locale %s\n",
1633 XBaseFontNameListOfFontSet(fntSet),
1634 XLocaleOfFontSet(fntSet));
1635 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1636 for (i = 0; i < count; i++) {
1637 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1640 for (i = 0; i < missing_count; i++) {
1641 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1644 if (fntSet == NULL) {
1645 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1650 #else // not ENABLE_NLS
1652 * Find a font that matches "pattern" that is as close as
1653 * possible to the targetPxlSize. Prefer fonts that are k
1654 * pixels smaller to fonts that are k pixels larger. The
1655 * pattern must be in the X Consortium standard format,
1656 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1657 * The return value should be freed with XtFree when no
1661 FindFont (char *pattern, int targetPxlSize)
1663 char **fonts, *p, *best, *scalable, *scalableTail;
1664 int i, j, nfonts, minerr, err, pxlSize;
1666 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1668 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1669 programName, pattern);
1676 for (i=0; i<nfonts; i++) {
1679 if (*p != '-') continue;
1681 if (*p == NULLCHAR) break;
1682 if (*p++ == '-') j++;
1684 if (j < 7) continue;
1687 scalable = fonts[i];
1690 err = pxlSize - targetPxlSize;
1691 if (Abs(err) < Abs(minerr) ||
1692 (minerr > 0 && err < 0 && -err == minerr)) {
1698 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1699 /* If the error is too big and there is a scalable font,
1700 use the scalable font. */
1701 int headlen = scalableTail - scalable;
1702 p = (char *) XtMalloc(strlen(scalable) + 10);
1703 while (isdigit(*scalableTail)) scalableTail++;
1704 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1706 p = (char *) XtMalloc(strlen(best) + 2);
1707 safeStrCpy(p, best, strlen(best)+1 );
1709 if (appData.debugMode) {
1710 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1711 pattern, targetPxlSize, p);
1713 XFreeFontNames(fonts);
1720 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
1721 // must be called before all non-first callse to CreateGCs()
1722 XtReleaseGC(shellWidget, highlineGC);
1723 XtReleaseGC(shellWidget, lightSquareGC);
1724 XtReleaseGC(shellWidget, darkSquareGC);
1725 XtReleaseGC(shellWidget, lineGC);
1726 if (appData.monoMode) {
1727 if (DefaultDepth(xDisplay, xScreen) == 1) {
1728 XtReleaseGC(shellWidget, wbPieceGC);
1730 XtReleaseGC(shellWidget, bwPieceGC);
1733 XtReleaseGC(shellWidget, prelineGC);
1734 XtReleaseGC(shellWidget, wdPieceGC);
1735 XtReleaseGC(shellWidget, wlPieceGC);
1736 XtReleaseGC(shellWidget, bdPieceGC);
1737 XtReleaseGC(shellWidget, blPieceGC);
1742 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
1744 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1745 | GCBackground | GCFunction | GCPlaneMask;
1746 gc_values->foreground = foreground;
1747 gc_values->background = background;
1748 return XtGetGC(shellWidget, value_mask, gc_values);
1752 CreateGCs (int redo)
1754 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1755 | GCBackground | GCFunction | GCPlaneMask;
1756 XGCValues gc_values;
1758 Pixel white = XWhitePixel(xDisplay, xScreen);
1759 Pixel black = XBlackPixel(xDisplay, xScreen);
1761 gc_values.plane_mask = AllPlanes;
1762 gc_values.line_width = lineGap;
1763 gc_values.line_style = LineSolid;
1764 gc_values.function = GXcopy;
1767 DeleteGCs(); // called a second time; clean up old GCs first
1768 } else { // [HGM] grid and font GCs created on first call only
1769 coordGC = CreateOneGC(&gc_values, black, white);
1770 XSetFont(xDisplay, coordGC, coordFontID);
1772 // [HGM] make font for holdings counts (white on black)
1773 countGC = CreateOneGC(&gc_values, white, black);
1774 XSetFont(xDisplay, countGC, countFontID);
1776 lineGC = CreateOneGC(&gc_values, black, black);
1778 if (appData.monoMode) {
1780 highlineGC = CreateOneGC(&gc_values, white, white);
1781 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
1782 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
1784 if (DefaultDepth(xDisplay, xScreen) == 1) {
1785 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
1786 gc_values.function = GXcopyInverted;
1787 copyInvertedGC = CreateOneGC(&gc_values, black, white);
1788 gc_values.function = GXcopy;
1789 if (XBlackPixel(xDisplay, xScreen) == 1) {
1790 bwPieceGC = darkSquareGC;
1791 wbPieceGC = copyInvertedGC;
1793 bwPieceGC = copyInvertedGC;
1794 wbPieceGC = lightSquareGC;
1799 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
1800 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
1801 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
1802 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
1803 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
1804 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
1805 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
1806 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
1811 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
1819 fp = fopen(filename, "rb");
1821 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
1828 for (y=0; y<h; ++y) {
1829 for (x=0; x<h; ++x) {
1834 XPutPixel(xim, x, y, blackPieceColor);
1836 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1839 XPutPixel(xim, x, y, darkSquareColor);
1841 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1844 XPutPixel(xim, x, y, whitePieceColor);
1846 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1849 XPutPixel(xim, x, y, lightSquareColor);
1851 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1859 /* create Pixmap of piece */
1860 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1862 XPutImage(xDisplay, *dest, lightSquareGC, xim,
1865 /* create Pixmap of clipmask
1866 Note: We assume the white/black pieces have the same
1867 outline, so we make only 6 masks. This is okay
1868 since the XPM clipmask routines do the same. */
1870 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1872 XPutImage(xDisplay, temp, lightSquareGC, xmask,
1875 /* now create the 1-bit version */
1876 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1879 values.foreground = 1;
1880 values.background = 0;
1882 /* Don't use XtGetGC, not read only */
1883 maskGC = XCreateGC(xDisplay, *mask,
1884 GCForeground | GCBackground, &values);
1885 XCopyPlane(xDisplay, temp, *mask, maskGC,
1886 0, 0, squareSize, squareSize, 0, 0, 1);
1887 XFreePixmap(xDisplay, temp);
1892 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
1900 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
1905 /* The XSynchronize calls were copied from CreatePieces.
1906 Not sure if needed, but can't hurt */
1907 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
1910 /* temp needed by loadXIM() */
1911 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1912 0, 0, ss, ss, AllPlanes, XYPixmap);
1914 if (strlen(appData.pixmapDirectory) == 0) {
1918 if (appData.monoMode) {
1919 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
1923 fprintf(stderr, _("\nLoading XIMs...\n"));
1925 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
1926 fprintf(stderr, "%d", piece+1);
1927 for (kind=0; kind<4; kind++) {
1928 fprintf(stderr, ".");
1929 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
1930 ExpandPathName(appData.pixmapDirectory),
1931 piece <= (int) WhiteKing ? "" : "w",
1932 pieceBitmapNames[piece],
1934 ximPieceBitmap[kind][piece] =
1935 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1936 0, 0, ss, ss, AllPlanes, XYPixmap);
1937 if (appData.debugMode)
1938 fprintf(stderr, _("(File:%s:) "), buf);
1939 loadXIM(ximPieceBitmap[kind][piece],
1941 &(xpmPieceBitmap2[kind][piece]),
1942 &(ximMaskPm2[piece]));
1943 if(piece <= (int)WhiteKing)
1944 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
1946 fprintf(stderr," ");
1948 /* Load light and dark squares */
1949 /* If the LSQ and DSQ pieces don't exist, we will
1950 draw them with solid squares. */
1951 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
1952 if (access(buf, 0) != 0) {
1956 fprintf(stderr, _("light square "));
1958 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1959 0, 0, ss, ss, AllPlanes, XYPixmap);
1960 if (appData.debugMode)
1961 fprintf(stderr, _("(File:%s:) "), buf);
1963 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
1964 fprintf(stderr, _("dark square "));
1965 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
1966 ExpandPathName(appData.pixmapDirectory), ss);
1967 if (appData.debugMode)
1968 fprintf(stderr, _("(File:%s:) "), buf);
1970 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1971 0, 0, ss, ss, AllPlanes, XYPixmap);
1972 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
1973 xpmJailSquare = xpmLightSquare;
1975 fprintf(stderr, _("Done.\n"));
1977 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
1980 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
1984 CreateXPMBoard (char *s, int kind)
1988 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
1989 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
1990 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
1996 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
1997 // thisroutine has to be called t free the old piece pixmaps
1999 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2000 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2002 XFreePixmap(xDisplay, xpmLightSquare);
2003 XFreePixmap(xDisplay, xpmDarkSquare);
2012 u_int ss = squareSize;
2014 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2015 XpmColorSymbol symbols[4];
2016 static int redo = False;
2018 if(redo) FreeXPMPieces(); else redo = 1;
2020 /* The XSynchronize calls were copied from CreatePieces.
2021 Not sure if needed, but can't hurt */
2022 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2024 /* Setup translations so piece colors match square colors */
2025 symbols[0].name = "light_piece";
2026 symbols[0].value = appData.whitePieceColor;
2027 symbols[1].name = "dark_piece";
2028 symbols[1].value = appData.blackPieceColor;
2029 symbols[2].name = "light_square";
2030 symbols[2].value = appData.lightSquareColor;
2031 symbols[3].name = "dark_square";
2032 symbols[3].value = appData.darkSquareColor;
2034 attr.valuemask = XpmColorSymbols;
2035 attr.colorsymbols = symbols;
2036 attr.numsymbols = 4;
2038 if (appData.monoMode) {
2039 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2043 if (strlen(appData.pixmapDirectory) == 0) {
2044 XpmPieces* pieces = builtInXpms;
2047 while (pieces->size != squareSize && pieces->size) pieces++;
2048 if (!pieces->size) {
2049 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2052 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2053 for (kind=0; kind<4; kind++) {
2055 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2056 pieces->xpm[piece][kind],
2057 &(xpmPieceBitmap2[kind][piece]),
2058 NULL, &attr)) != 0) {
2059 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2063 if(piece <= (int) WhiteKing)
2064 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2068 xpmJailSquare = xpmLightSquare;
2072 fprintf(stderr, _("\nLoading XPMs...\n"));
2075 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2076 fprintf(stderr, "%d ", piece+1);
2077 for (kind=0; kind<4; kind++) {
2078 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2079 ExpandPathName(appData.pixmapDirectory),
2080 piece > (int) WhiteKing ? "w" : "",
2081 pieceBitmapNames[piece],
2083 if (appData.debugMode) {
2084 fprintf(stderr, _("(File:%s:) "), buf);
2086 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2087 &(xpmPieceBitmap2[kind][piece]),
2088 NULL, &attr)) != 0) {
2089 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2090 // [HGM] missing: read of unorthodox piece failed; substitute King.
2091 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2092 ExpandPathName(appData.pixmapDirectory),
2094 if (appData.debugMode) {
2095 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2097 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2098 &(xpmPieceBitmap2[kind][piece]),
2102 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2107 if(piece <= (int) WhiteKing)
2108 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2111 /* Load light and dark squares */
2112 /* If the LSQ and DSQ pieces don't exist, we will
2113 draw them with solid squares. */
2114 fprintf(stderr, _("light square "));
2115 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2116 if (access(buf, 0) != 0) {
2120 if (appData.debugMode)
2121 fprintf(stderr, _("(File:%s:) "), buf);
2123 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2124 &xpmLightSquare, NULL, &attr)) != 0) {
2125 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2128 fprintf(stderr, _("dark square "));
2129 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2130 ExpandPathName(appData.pixmapDirectory), ss);
2131 if (appData.debugMode) {
2132 fprintf(stderr, _("(File:%s:) "), buf);
2134 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2135 &xpmDarkSquare, NULL, &attr)) != 0) {
2136 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2140 xpmJailSquare = xpmLightSquare;
2141 fprintf(stderr, _("Done.\n"));
2143 oldVariant = -1; // kludge to force re-makig of animation masks
2144 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2147 #endif /* HAVE_LIBXPM */
2150 /* No built-in bitmaps */
2155 u_int ss = squareSize;
2157 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2160 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2161 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2162 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2163 pieceBitmapNames[piece],
2164 ss, kind == SOLID ? 's' : 'o');
2165 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2166 if(piece <= (int)WhiteKing)
2167 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2171 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2175 /* With built-in bitmaps */
2179 BuiltInBits* bib = builtInBits;
2182 u_int ss = squareSize;
2184 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2187 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2189 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2190 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2191 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2192 pieceBitmapNames[piece],
2193 ss, kind == SOLID ? 's' : 'o');
2194 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2195 bib->bits[kind][piece], ss, ss);
2196 if(piece <= (int)WhiteKing)
2197 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2201 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2207 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2212 char msg[MSG_SIZ], fullname[MSG_SIZ];
2214 if (*appData.bitmapDirectory != NULLCHAR) {
2215 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2216 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2217 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2218 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2219 &w, &h, pm, &x_hot, &y_hot);
2220 fprintf(stderr, "load %s\n", name);
2221 if (errcode != BitmapSuccess) {
2223 case BitmapOpenFailed:
2224 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2226 case BitmapFileInvalid:
2227 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2229 case BitmapNoMemory:
2230 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2234 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2238 fprintf(stderr, _("%s: %s...using built-in\n"),
2240 } else if (w != wreq || h != hreq) {
2242 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2243 programName, fullname, w, h, wreq, hreq);
2249 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2259 if (lineGap == 0) return;
2261 /* [HR] Split this into 2 loops for non-square boards. */
2263 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2264 gridSegments[i].x1 = 0;
2265 gridSegments[i].x2 =
2266 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2267 gridSegments[i].y1 = gridSegments[i].y2
2268 = lineGap / 2 + (i * (squareSize + lineGap));
2271 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2272 gridSegments[j + i].y1 = 0;
2273 gridSegments[j + i].y2 =
2274 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2275 gridSegments[j + i].x1 = gridSegments[j + i].x2
2276 = lineGap / 2 + (j * (squareSize + lineGap));
2281 MarkMenuItem (char *menuRef, int state)
2283 MenuItem *item = MenuNameToItem(menuRef);
2287 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2288 XtSetValues(item->handle, args, 1);
2293 EnableMenuItem (char *menuRef, int state)
2295 MenuItem *item = MenuNameToItem(menuRef);
2297 if(item) XtSetSensitive(item->handle, state);
2301 EnableButtonBar (int state)
2303 XtSetSensitive(optList[W_BUTTON].handle, state);
2308 SetMenuEnables (Enables *enab)
2310 while (enab->name != NULL) {
2311 EnableMenuItem(enab->name, enab->value);
2317 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2318 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2322 if(*nprms == 0) return;
2323 item = MenuNameToItem(prms[0]);
2324 if(item) ((MenuProc *) item->proc) ();
2328 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
2330 MenuProc *proc = (MenuProc *) addr;
2336 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2338 RecentEngineEvent((int) (intptr_t) addr);
2342 AppendMenuItem (char *msg, int n)
2344 CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
2356 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2357 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2358 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2359 dmEnables[i].piece);
2360 XtSetSensitive(entry, p != NULL || !appData.testLegality
2361 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2362 && !appData.icsActive));
2364 while (p && *p++ == dmEnables[i].piece) count++;
2365 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2367 XtSetArg(args[j], XtNlabel, label); j++;
2368 XtSetValues(entry, args, j);
2374 do_flash_delay (unsigned long msec)
2380 DrawBorder (int x, int y, int type)
2384 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
2386 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
2387 squareSize+lineGap, squareSize+lineGap);
2391 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
2393 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
2394 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
2396 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
2397 if(textureW[kind] < W*squareSize)
2398 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
2400 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
2401 if(textureH[kind] < H*squareSize)
2402 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
2404 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
2409 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
2410 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
2412 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
2413 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
2414 squareSize, squareSize, x*fac, y*fac);
2416 if (useImages && useImageSqs) {
2420 pm = xpmLightSquare;
2425 case 2: /* neutral */
2427 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
2430 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
2431 squareSize, squareSize, x*fac, y*fac);
2441 case 2: /* neutral */
2446 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
2451 I split out the routines to draw a piece so that I could
2452 make a generic flash routine.
2455 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2457 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2458 switch (square_color) {
2460 case 2: /* neutral */
2462 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2463 ? *pieceToOutline(piece)
2464 : *pieceToSolid(piece),
2465 dest, bwPieceGC, 0, 0,
2466 squareSize, squareSize, x, y);
2469 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2470 ? *pieceToSolid(piece)
2471 : *pieceToOutline(piece),
2472 dest, wbPieceGC, 0, 0,
2473 squareSize, squareSize, x, y);
2479 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2481 switch (square_color) {
2483 case 2: /* neutral */
2485 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2486 ? *pieceToOutline(piece)
2487 : *pieceToSolid(piece),
2488 dest, bwPieceGC, 0, 0,
2489 squareSize, squareSize, x, y, 1);
2492 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2493 ? *pieceToSolid(piece)
2494 : *pieceToOutline(piece),
2495 dest, wbPieceGC, 0, 0,
2496 squareSize, squareSize, x, y, 1);
2502 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2504 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
2505 switch (square_color) {
2507 XCopyPlane(xDisplay, *pieceToSolid(piece),
2508 dest, (int) piece < (int) BlackPawn
2509 ? wlPieceGC : blPieceGC, 0, 0,
2510 squareSize, squareSize, x, y, 1);
2513 XCopyPlane(xDisplay, *pieceToSolid(piece),
2514 dest, (int) piece < (int) BlackPawn
2515 ? wdPieceGC : bdPieceGC, 0, 0,
2516 squareSize, squareSize, x, y, 1);
2518 case 2: /* neutral */
2520 break; // should never contain pieces
2525 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2527 int kind, p = piece;
2529 switch (square_color) {
2531 case 2: /* neutral */
2533 if ((int)piece < (int) BlackPawn) {
2541 if ((int)piece < (int) BlackPawn) {
2549 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2550 if(useTexture & square_color+1) {
2551 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2552 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
2553 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
2554 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
2555 XSetClipMask(xDisplay, wlPieceGC, None);
2556 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
2558 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
2559 dest, wlPieceGC, 0, 0,
2560 squareSize, squareSize, x, y);
2563 typedef void (*DrawFunc)();
2568 if (appData.monoMode) {
2569 if (DefaultDepth(xDisplay, xScreen) == 1) {
2570 return monoDrawPiece_1bit;
2572 return monoDrawPiece;
2576 return colorDrawPieceImage;
2578 return colorDrawPiece;
2583 DrawDot (int marker, int x, int y, int r)
2585 if(appData.monoMode) {
2586 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
2587 x, y, r, r, 0, 64*360);
2588 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
2589 x, y, r, r, 0, 64*360);
2591 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
2592 x, y, r, r, 0, 64*360);
2596 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2597 { // basic front-end board-draw function: takes care of everything that can be in square:
2598 // piece, background, coordinate/count, marker dot
2599 int direction, font_ascent, font_descent;
2600 XCharStruct overall;
2603 if (piece == EmptySquare) {
2604 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2606 drawfunc = ChooseDrawFunc();
2607 drawfunc(piece, square_color, x, y, xBoardWindow);
2610 if(align) { // square carries inscription (coord or piece count)
2612 GC hGC = align < 3 ? coordGC : countGC;
2613 // first calculate where it goes
2614 XTextExtents(countFontStruct, string, 1, &direction,
2615 &font_ascent, &font_descent, &overall);
2617 xx += squareSize - overall.width - 2;
2618 yy += squareSize - font_descent - 1;
2619 } else if (align == 2) {
2620 xx += 2, yy += font_ascent + 1;
2621 } else if (align == 3) {
2622 xx += squareSize - overall.width - 2;
2623 yy += font_ascent + 1;
2624 } else if (align == 4) {
2625 xx += 2, yy += font_ascent + 1;
2628 if (appData.monoMode) {
2629 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2631 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2635 if(marker) { // print fat marker dot, if requested
2636 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2641 FlashDelay (int flash_delay)
2643 XSync(xDisplay, False);
2644 if(flash_delay) do_flash_delay(flash_delay);
2648 Fraction (int x, int start, int stop)
2650 double f = ((double) x - start)/(stop - start);
2651 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2655 static WindowPlacement wpNew;
2658 CoDrag (Widget sh, WindowPlacement *wp)
2661 int j=0, touch=0, fudge = 2;
2662 GetActualPlacement(sh, wp);
2663 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
2664 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
2665 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2666 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
2667 if(!touch ) return; // only windows that touch co-move
2668 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2669 int heightInc = wpNew.height - wpMain.height;
2670 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2671 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2672 wp->y += fracTop * heightInc;
2673 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2674 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2675 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2676 int widthInc = wpNew.width - wpMain.width;
2677 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2678 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2679 wp->y += fracLeft * widthInc;
2680 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2681 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2683 wp->x += wpNew.x - wpMain.x;
2684 wp->y += wpNew.y - wpMain.y;
2685 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2686 if(touch == 3) wp->y += wpNew.height - wpMain.height;
2687 XtSetArg(args[j], XtNx, wp->x); j++;
2688 XtSetArg(args[j], XtNy, wp->y); j++;
2689 XtSetValues(sh, args, j);
2692 static XtIntervalId delayedDragID = 0;
2697 GetActualPlacement(shellWidget, &wpNew);
2698 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
2699 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
2700 return; // false alarm
2701 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
2702 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
2703 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
2704 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
2706 DrawPosition(True, NULL);
2707 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
2714 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
2716 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
2720 EventProc (Widget widget, caddr_t unused, XEvent *event)
2722 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
2723 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
2726 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
2728 DrawSeekAxis (int x, int y, int xTo, int yTo)
2730 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
2734 DrawSeekBackground (int left, int top, int right, int bottom)
2736 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
2740 DrawSeekText (char *buf, int x, int y)
2742 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
2746 DrawSeekDot (int x, int y, int colorNr)
2748 int square = colorNr & 0x80;
2751 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
2753 XFillRectangle(xDisplay, xBoardWindow, color,
2754 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
2756 XFillArc(xDisplay, xBoardWindow, color,
2757 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
2763 XDrawSegments(xDisplay, xBoardWindow, lineGC,
2764 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
2769 * event handler for redrawing the board
2772 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2774 DrawPosition(True, NULL);
2779 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
2780 { // [HGM] pv: walk PV
2781 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
2784 static int savedIndex; /* gross that this is global */
2787 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
2790 XawTextPosition index, dummy;
2793 XawTextGetSelectionPos(w, &index, &dummy);
2794 XtSetArg(arg, XtNstring, &val);
2795 XtGetValues(w, &arg, 1);
2796 ReplaceComment(savedIndex, val);
2797 if(savedIndex != currentMove) ToNrEvent(savedIndex);
2798 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
2802 EditCommentPopUp (int index, char *title, char *text)
2805 if (text == NULL) text = "";
2806 NewCommentPopup(title, text, index);
2810 CommentPopUp (char *title, char *text)
2812 savedIndex = currentMove; // [HGM] vari
2813 NewCommentPopup(title, text, currentMove);
2819 PopDown(CommentDlg);
2822 static char *openName;
2828 (void) (*fileProc)(openFP, 0, openName);
2832 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
2834 fileProc = proc; /* I can't see a way not */
2835 fileOpenMode = openMode; /* to use globals here */
2836 { // [HGM] use file-selector dialog stolen from Ghostview
2837 int index; // this is not supported yet
2838 Browse(BoardWindow, label, (def[0] ? def : NULL), filter, False, openMode, &openName, &openFP);
2843 /* Disable all user input other than deleting the window */
2844 static int frozen = 0;
2850 /* Grab by a widget that doesn't accept input */
2851 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
2855 /* Undo a FreezeUI */
2859 if (!frozen) return;
2860 XtRemoveGrab(optList[W_MESSG].handle);
2868 static int oldPausing = FALSE;
2869 static GameMode oldmode = (GameMode) -1;
2872 if (!boardWidget || !XtIsRealized(boardWidget)) return;
2874 if (pausing != oldPausing) {
2875 oldPausing = pausing;
2876 MarkMenuItem("Mode.Pause", pausing);
2878 if (appData.showButtonBar) {
2879 /* Always toggle, don't set. Previous code messes up when
2880 invoked while the button is pressed, as releasing it
2881 toggles the state again. */
2884 XtSetArg(args[0], XtNbackground, &oldbg);
2885 XtSetArg(args[1], XtNforeground, &oldfg);
2886 XtGetValues(optList[W_PAUSE].handle,
2888 XtSetArg(args[0], XtNbackground, oldfg);
2889 XtSetArg(args[1], XtNforeground, oldbg);
2891 XtSetValues(optList[W_PAUSE].handle, args, 2);
2895 wname = ModeToWidgetName(oldmode);
2896 if (wname != NULL) {
2897 MarkMenuItem(wname, False);
2899 wname = ModeToWidgetName(gameMode);
2900 if (wname != NULL) {
2901 MarkMenuItem(wname, True);
2904 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
2906 /* Maybe all the enables should be handled here, not just this one */
2907 EnableMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
2912 * Button/menu procedures
2915 /* this variable is shared between CopyPositionProc and SendPositionSelection */
2916 char *selected_fen_position=NULL;
2919 SendPositionSelection (Widget w, Atom *selection, Atom *target,
2920 Atom *type_return, XtPointer *value_return,
2921 unsigned long *length_return, int *format_return)
2923 char *selection_tmp;
2925 // if (!selected_fen_position) return False; /* should never happen */
2926 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
2927 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
2928 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
2931 if (f == NULL) return False;
2935 selection_tmp = XtMalloc(len + 1);
2936 count = fread(selection_tmp, 1, len, f);
2939 XtFree(selection_tmp);
2942 selection_tmp[len] = NULLCHAR;
2944 /* note: since no XtSelectionDoneProc was registered, Xt will
2945 * automatically call XtFree on the value returned. So have to
2946 * make a copy of it allocated with XtMalloc */
2947 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
2948 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
2951 *value_return=selection_tmp;
2952 *length_return=strlen(selection_tmp);
2953 *type_return=*target;
2954 *format_return = 8; /* bits per byte */
2956 } else if (*target == XA_TARGETS(xDisplay)) {
2957 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
2958 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
2959 targets_tmp[1] = XA_STRING;
2960 *value_return = targets_tmp;
2961 *type_return = XA_ATOM;
2964 // This code leads to a read of value_return out of bounds on 64-bit systems.
2965 // Other code which I have seen always sets *format_return to 32 independent of
2966 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
2967 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
2968 *format_return = 8 * sizeof(Atom);
2969 if (*format_return > 32) {
2970 *length_return *= *format_return / 32;
2971 *format_return = 32;
2974 *format_return = 32;
2982 /* note: when called from menu all parameters are NULL, so no clue what the
2983 * Widget which was clicked on was, or what the click event was
2986 CopySomething (char *src)
2988 selected_fen_position = src;
2990 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
2991 * have a notion of a position that is selected but not copied.
2992 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
2994 XtOwnSelection(menuBarWidget, XA_PRIMARY,
2996 SendPositionSelection,
2997 NULL/* lose_ownership_proc */ ,
2998 NULL/* transfer_done_proc */);
2999 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3001 SendPositionSelection,
3002 NULL/* lose_ownership_proc */ ,
3003 NULL/* transfer_done_proc */);
3006 /* function called when the data to Paste is ready */
3008 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3009 Atom *type, XtPointer value, unsigned long *len, int *format)
3012 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3013 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3014 EditPositionPasteFEN(fenstr);
3018 /* called when Paste Position button is pressed,
3019 * all parameters will be NULL */
3021 PastePositionProc ()
3023 XtGetSelectionValue(menuBarWidget,
3024 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3025 /* (XtSelectionCallbackProc) */ PastePositionCB,
3026 NULL, /* client_data passed to PastePositionCB */
3028 /* better to use the time field from the event that triggered the
3029 * call to this function, but that isn't trivial to get
3036 /* note: when called from menu all parameters are NULL, so no clue what the
3037 * Widget which was clicked on was, or what the click event was
3039 /* function called when the data to Paste is ready */
3041 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3042 Atom *type, XtPointer value, unsigned long *len, int *format)
3045 if (value == NULL || *len == 0) {
3046 return; /* nothing had been selected to copy */
3048 f = fopen(gamePasteFilename, "w");
3050 DisplayError(_("Can't open temp file"), errno);
3053 fwrite(value, 1, *len, f);
3056 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3059 /* called when Paste Game button is pressed,
3060 * all parameters will be NULL */
3064 XtGetSelectionValue(menuBarWidget,
3065 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3066 /* (XtSelectionCallbackProc) */ PasteGameCB,
3067 NULL, /* client_data passed to PasteGameCB */
3069 /* better to use the time field from the event that triggered the
3070 * call to this function, but that isn't trivial to get
3079 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3086 { // bassic primitive for determining if modifier keys are pressed
3087 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3090 XQueryKeymap(xDisplay,keys);
3091 for(i=0; i<6; i++) {
3093 j = XKeysymToKeycode(xDisplay, codes[i]);
3094 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3100 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3104 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3105 if ( n == 1 && *buf >= 32 // printable
3106 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3107 ) BoxAutoPopUp (buf);
3111 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3112 { // [HGM] input: let up-arrow recall previous line from history
3117 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3118 { // [HGM] input: let down-arrow recall next line from history
3123 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3129 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3131 if (!TempBackwardActive) {
3132 TempBackwardActive = True;
3138 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3140 /* Check to see if triggered by a key release event for a repeating key.
3141 * If so the next queued event will be a key press of the same key at the same time */
3142 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3144 XPeekEvent(xDisplay, &next);
3145 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3146 next.xkey.keycode == event->xkey.keycode)
3150 TempBackwardActive = False;
3154 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3155 { // called as key binding
3158 if (nprms && *nprms > 0)
3162 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3167 SetWindowTitle (char *text, char *title, char *icon)
3171 if (appData.titleInWindow) {
3173 XtSetArg(args[i], XtNlabel, text); i++;
3174 XtSetValues(titleWidget, args, i);
3177 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3178 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3179 XtSetValues(shellWidget, args, i);
3180 XSync(xDisplay, False);
3185 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3191 DisplayIcsInteractionTitle (String message)
3193 if (oldICSInteractionTitle == NULL) {
3194 /* Magic to find the old window title, adapted from vim */
3195 char *wina = getenv("WINDOWID");
3197 Window win = (Window) atoi(wina);
3198 Window root, parent, *children;
3199 unsigned int nchildren;
3200 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3202 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3203 if (!XQueryTree(xDisplay, win, &root, &parent,
3204 &children, &nchildren)) break;
3205 if (children) XFree((void *)children);
3206 if (parent == root || parent == 0) break;
3209 XSetErrorHandler(oldHandler);
3211 if (oldICSInteractionTitle == NULL) {
3212 oldICSInteractionTitle = "xterm";
3215 printf("\033]0;%s\007", message);
3220 XtIntervalId delayedEventTimerXID = 0;
3221 DelayedEventCallback delayedEventCallback = 0;
3226 delayedEventTimerXID = 0;
3227 delayedEventCallback();
3231 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3233 if(delayedEventTimerXID && delayedEventCallback == cb)
3234 // [HGM] alive: replace, rather than add or flush identical event
3235 XtRemoveTimeOut(delayedEventTimerXID);
3236 delayedEventCallback = cb;
3237 delayedEventTimerXID =
3238 XtAppAddTimeOut(appContext, millisec,
3239 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3242 DelayedEventCallback
3245 if (delayedEventTimerXID) {
3246 return delayedEventCallback;
3253 CancelDelayedEvent ()
3255 if (delayedEventTimerXID) {
3256 XtRemoveTimeOut(delayedEventTimerXID);
3257 delayedEventTimerXID = 0;
3261 XtIntervalId loadGameTimerXID = 0;
3264 LoadGameTimerRunning ()
3266 return loadGameTimerXID != 0;
3270 StopLoadGameTimer ()
3272 if (loadGameTimerXID != 0) {
3273 XtRemoveTimeOut(loadGameTimerXID);
3274 loadGameTimerXID = 0;
3282 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3284 loadGameTimerXID = 0;
3289 StartLoadGameTimer (long millisec)
3292 XtAppAddTimeOut(appContext, millisec,
3293 (XtTimerCallbackProc) LoadGameTimerCallback,
3297 XtIntervalId analysisClockXID = 0;
3300 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3302 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3303 || appData.icsEngineAnalyze) { // [DM]
3304 AnalysisPeriodicEvent(0);
3305 StartAnalysisClock();
3310 StartAnalysisClock ()
3313 XtAppAddTimeOut(appContext, 2000,
3314 (XtTimerCallbackProc) AnalysisClockCallback,
3318 XtIntervalId clockTimerXID = 0;
3321 ClockTimerRunning ()
3323 return clockTimerXID != 0;
3329 if (clockTimerXID != 0) {
3330 XtRemoveTimeOut(clockTimerXID);
3339 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3346 StartClockTimer (long millisec)
3349 XtAppAddTimeOut(appContext, millisec,
3350 (XtTimerCallbackProc) ClockTimerCallback,
3355 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
3359 Widget w = (Widget) opt->handle;
3361 /* check for low time warning */
3362 Pixel foregroundOrWarningColor = timerForegroundPixel;
3365 appData.lowTimeWarning &&
3366 (timer / 1000) < appData.icsAlarmTime)
3367 foregroundOrWarningColor = lowTimeWarningColor;
3369 if (appData.clockMode) {
3370 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
3371 XtSetArg(args[0], XtNlabel, buf);
3373 snprintf(buf, MSG_SIZ, "%s ", color);
3374 XtSetArg(args[0], XtNlabel, buf);
3379 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3380 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3382 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3383 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3386 XtSetValues(w, args, 3);
3389 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3392 SetClockIcon (int color)
3395 Pixmap pm = *clockIcons[color];
3396 if (iconPixmap != pm) {
3398 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3399 XtSetValues(shellWidget, args, 1);
3404 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3406 InputSource *is = (InputSource *) closure;
3411 if (is->lineByLine) {
3412 count = read(is->fd, is->unused,
3413 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3415 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3418 is->unused += count;
3420 while (p < is->unused) {
3421 q = memchr(p, '\n', is->unused - p);
3422 if (q == NULL) break;
3424 (is->func)(is, is->closure, p, q - p, 0);
3428 while (p < is->unused) {
3433 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3438 (is->func)(is, is->closure, is->buf, count, error);
3443 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3446 ChildProc *cp = (ChildProc *) pr;
3448 is = (InputSource *) calloc(1, sizeof(InputSource));
3449 is->lineByLine = lineByLine;
3453 is->fd = fileno(stdin);
3455 is->kind = cp->kind;
3456 is->fd = cp->fdFrom;
3459 is->unused = is->buf;
3462 is->xid = XtAppAddInput(appContext, is->fd,
3463 (XtPointer) (XtInputReadMask),
3464 (XtInputCallbackProc) DoInputCallback,
3466 is->closure = closure;
3467 return (InputSourceRef) is;
3471 RemoveInputSource (InputSourceRef isr)
3473 InputSource *is = (InputSource *) isr;
3475 if (is->xid == 0) return;
3476 XtRemoveInput(is->xid);
3480 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
3482 /* Masks for XPM pieces. Black and white pieces can have
3483 different shapes, but in the interest of retaining my
3484 sanity pieces must have the same outline on both light
3485 and dark squares, and all pieces must use the same
3486 background square colors/images. */
3488 static int xpmDone = 0;
3489 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
3490 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
3493 CreateAnimMasks (int pieceDepth)
3499 unsigned long plane;
3502 /* Need a bitmap just to get a GC with right depth */
3503 buf = XCreatePixmap(xDisplay, xBoardWindow,
3505 values.foreground = 1;
3506 values.background = 0;
3507 /* Don't use XtGetGC, not read only */
3508 maskGC = XCreateGC(xDisplay, buf,
3509 GCForeground | GCBackground, &values);
3510 XFreePixmap(xDisplay, buf);
3512 buf = XCreatePixmap(xDisplay, xBoardWindow,
3513 squareSize, squareSize, pieceDepth);
3514 values.foreground = XBlackPixel(xDisplay, xScreen);
3515 values.background = XWhitePixel(xDisplay, xScreen);
3516 bufGC = XCreateGC(xDisplay, buf,
3517 GCForeground | GCBackground, &values);
3519 for (piece = WhitePawn; piece <= BlackKing; piece++) {
3520 /* Begin with empty mask */
3521 if(!xpmDone) // [HGM] pieces: keep using existing
3522 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
3523 squareSize, squareSize, 1);
3524 XSetFunction(xDisplay, maskGC, GXclear);
3525 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
3526 0, 0, squareSize, squareSize);
3528 /* Take a copy of the piece */
3533 XSetFunction(xDisplay, bufGC, GXcopy);
3534 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
3536 0, 0, squareSize, squareSize, 0, 0);
3538 /* XOR the background (light) over the piece */
3539 XSetFunction(xDisplay, bufGC, GXxor);
3541 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
3542 0, 0, squareSize, squareSize, 0, 0);
3544 XSetForeground(xDisplay, bufGC, lightSquareColor);
3545 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
3548 /* We now have an inverted piece image with the background
3549 erased. Construct mask by just selecting all the non-zero
3550 pixels - no need to reconstruct the original image. */
3551 XSetFunction(xDisplay, maskGC, GXor);
3553 /* Might be quicker to download an XImage and create bitmap
3554 data from it rather than this N copies per piece, but it
3555 only takes a fraction of a second and there is a much
3556 longer delay for loading the pieces. */
3557 for (n = 0; n < pieceDepth; n ++) {
3558 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
3559 0, 0, squareSize, squareSize,
3565 XFreePixmap(xDisplay, buf);
3566 XFreeGC(xDisplay, bufGC);
3567 XFreeGC(xDisplay, maskGC);
3571 InitAnimState (AnimNr anr, XWindowAttributes *info)
3576 /* Each buffer is square size, same depth as window */
3577 animBufs[anr+4] = xBoardWindow;
3578 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
3579 squareSize, squareSize, info->depth);
3580 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
3581 squareSize, squareSize, info->depth);
3583 /* Create a plain GC for blitting */
3584 mask = GCForeground | GCBackground | GCFunction |
3585 GCPlaneMask | GCGraphicsExposures;
3586 values.foreground = XBlackPixel(xDisplay, xScreen);
3587 values.background = XWhitePixel(xDisplay, xScreen);
3588 values.function = GXcopy;
3589 values.plane_mask = AllPlanes;
3590 values.graphics_exposures = False;
3591 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
3593 /* Piece will be copied from an existing context at
3594 the start of each new animation/drag. */
3595 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
3597 /* Outline will be a read-only copy of an existing */
3598 animGCs[anr+4] = None;
3604 XWindowAttributes info;
3606 if (xpmDone && gameInfo.variant == oldVariant) return;
3607 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
3608 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
3610 InitAnimState(Game, &info);
3611 InitAnimState(Player, &info);
3613 /* For XPM pieces, we need bitmaps to use as masks. */
3615 CreateAnimMasks(info.depth), xpmDone = 1;
3620 static Boolean frameWaiting;
3623 FrameAlarm (int sig)
3625 frameWaiting = False;
3626 /* In case System-V style signals. Needed?? */
3627 signal(SIGALRM, FrameAlarm);
3631 FrameDelay (int time)
3633 struct itimerval delay;
3635 XSync(xDisplay, False);
3638 frameWaiting = True;
3639 signal(SIGALRM, FrameAlarm);
3640 delay.it_interval.tv_sec =
3641 delay.it_value.tv_sec = time / 1000;
3642 delay.it_interval.tv_usec =
3643 delay.it_value.tv_usec = (time % 1000) * 1000;
3644 setitimer(ITIMER_REAL, &delay, NULL);
3645 while (frameWaiting) pause();
3646 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
3647 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
3648 setitimer(ITIMER_REAL, &delay, NULL);
3655 FrameDelay (int time)
3657 XSync(xDisplay, False);
3659 usleep(time * 1000);
3665 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
3669 /* Bitmap for piece being moved. */
3670 if (appData.monoMode) {
3671 *mask = *pieceToSolid(piece);
3672 } else if (useImages) {
3674 *mask = xpmMask[piece];
3676 *mask = ximMaskPm[piece];
3679 *mask = *pieceToSolid(piece);
3682 /* GC for piece being moved. Square color doesn't matter, but
3683 since it gets modified we make a copy of the original. */
3685 if (appData.monoMode)
3690 if (appData.monoMode)
3695 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
3697 /* Outline only used in mono mode and is not modified */
3699 *outline = bwPieceGC;
3701 *outline = wbPieceGC;
3705 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
3710 /* Draw solid rectangle which will be clipped to shape of piece */
3711 XFillRectangle(xDisplay, dest, clip,
3712 0, 0, squareSize, squareSize);
3713 if (appData.monoMode)
3714 /* Also draw outline in contrasting color for black
3715 on black / white on white cases */
3716 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
3717 0, 0, squareSize, squareSize, 0, 0, 1);
3719 /* Copy the piece */
3724 if(appData.upsideDown && flipView) kind ^= 2;
3725 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],