2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
66 # if HAVE_SYS_SOCKET_H
67 # include <sys/socket.h>
68 # include <netinet/in.h>
70 # else /* not HAVE_SYS_SOCKET_H */
71 # if HAVE_LAN_SOCKET_H
72 # include <lan/socket.h>
74 # include <lan/netdb.h>
75 # else /* not HAVE_LAN_SOCKET_H */
76 # define OMIT_SOCKETS 1
77 # endif /* not HAVE_LAN_SOCKET_H */
78 # endif /* not HAVE_SYS_SOCKET_H */
79 #endif /* !OMIT_SOCKETS */
84 #else /* not STDC_HEADERS */
85 extern char *getenv();
88 # else /* not HAVE_STRING_H */
90 # endif /* not HAVE_STRING_H */
91 #endif /* not STDC_HEADERS */
94 # include <sys/fcntl.h>
95 #else /* not HAVE_SYS_FCNTL_H */
98 # endif /* HAVE_FCNTL_H */
99 #endif /* not HAVE_SYS_FCNTL_H */
101 #if HAVE_SYS_SYSTEMINFO_H
102 # include <sys/systeminfo.h>
103 #endif /* HAVE_SYS_SYSTEMINFO_H */
105 #if TIME_WITH_SYS_TIME
106 # include <sys/time.h>
110 # include <sys/time.h>
121 # include <sys/wait.h>
126 # define NAMLEN(dirent) strlen((dirent)->d_name)
127 # define HAVE_DIR_STRUCT
129 # define dirent direct
130 # define NAMLEN(dirent) (dirent)->d_namlen
132 # include <sys/ndir.h>
133 # define HAVE_DIR_STRUCT
136 # include <sys/dir.h>
137 # define HAVE_DIR_STRUCT
141 # define HAVE_DIR_STRUCT
149 #include <X11/Intrinsic.h>
150 #include <X11/StringDefs.h>
151 #include <X11/Shell.h>
152 #include <X11/cursorfont.h>
153 #include <X11/Xatom.h>
154 #include <X11/Xmu/Atoms.h>
156 #include <X11/Xaw3d/Dialog.h>
157 #include <X11/Xaw3d/Form.h>
158 #include <X11/Xaw3d/List.h>
159 #include <X11/Xaw3d/Label.h>
160 #include <X11/Xaw3d/SimpleMenu.h>
161 #include <X11/Xaw3d/SmeBSB.h>
162 #include <X11/Xaw3d/SmeLine.h>
163 #include <X11/Xaw3d/Box.h>
164 #include <X11/Xaw3d/MenuButton.h>
165 #include <X11/Xaw3d/Text.h>
166 #include <X11/Xaw3d/AsciiText.h>
168 #include <X11/Xaw/Dialog.h>
169 #include <X11/Xaw/Form.h>
170 #include <X11/Xaw/List.h>
171 #include <X11/Xaw/Label.h>
172 #include <X11/Xaw/SimpleMenu.h>
173 #include <X11/Xaw/SmeBSB.h>
174 #include <X11/Xaw/SmeLine.h>
175 #include <X11/Xaw/Box.h>
176 #include <X11/Xaw/MenuButton.h>
177 #include <X11/Xaw/Text.h>
178 #include <X11/Xaw/AsciiText.h>
181 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
186 #include "pixmaps/pixmaps.h"
187 #define IMAGE_EXT "xpm"
189 #define IMAGE_EXT "xim"
190 #include "bitmaps/bitmaps.h"
193 #include "bitmaps/icon_white.bm"
194 #include "bitmaps/icon_black.bm"
195 #include "bitmaps/checkmark.bm"
197 #include "frontend.h"
199 #include "backendz.h"
203 #include "xgamelist.h"
204 #include "xhistory.h"
205 #include "xedittags.h"
209 #include "engineoutput.h"
218 #define usleep(t) _sleep2(((t)+500)/1000)
222 # define _(s) gettext (s)
223 # define N_(s) gettext_noop (s)
229 int main P((int argc, char **argv));
230 RETSIGTYPE CmailSigHandler P((int sig));
231 RETSIGTYPE IntSigHandler P((int sig));
232 RETSIGTYPE TermSizeSigHandler P((int sig));
233 static void CreateGCs P((int redo));
234 static void CreateAnyPieces P((void));
235 void CreateXIMPieces P((void));
236 void CreateXPMPieces P((void));
237 void CreateXPMBoard P((char *s, int n));
238 void CreatePieces P((void));
239 Widget CreateMenuBar P((Menu *mb, int boardWidth));
241 char *InsertPxlSize P((char *pattern, int targetPxlSize));
242 XFontSet CreateFontSet P((char *base_fnt_lst));
244 char *FindFont P((char *pattern, int targetPxlSize));
246 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
247 u_int wreq, u_int hreq));
248 void CreateGrid P((void));
249 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
250 void DelayedDrag P((void));
251 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
252 void HandlePV P((Widget w, XEvent * event,
253 String * params, Cardinal * nParams));
254 void DrawPositionProc P((Widget w, XEvent *event,
255 String *prms, Cardinal *nprms));
256 void CommentClick P((Widget w, XEvent * event,
257 String * params, Cardinal * nParams));
258 void ICSInputBoxPopUp P((void));
259 void FileNamePopUp P((char *label, char *def, char *filter,
260 FileProc proc, char *openMode));
261 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
262 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
263 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
264 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
265 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
266 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
267 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
268 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
269 Boolean TempBackwardActive = False;
270 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
271 void DisplayMove P((int moveNumber));
272 void ICSInitScript P((void));
273 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
274 void update_ics_width P(());
275 int get_term_width P(());
276 int CopyMemoProc P(());
277 void SetupDropMenu P((void));
280 * XBoard depends on Xt R4 or higher
282 int xtVersion = XtSpecificationRelease;
287 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
288 highlightSquareColor, premoveHighlightColor, dialogColor, buttonColor;
289 Pixel lowTimeWarningColor;
290 GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
291 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
293 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
294 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
295 Option *optList; // contains all widgets of main window
296 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
298 XFontSet fontSet, clockFontSet;
301 XFontStruct *clockFontStruct;
303 Font coordFontID, countFontID;
304 XFontStruct *coordFontStruct, *countFontStruct;
305 XtAppContext appContext;
310 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
312 Position commentX = -1, commentY = -1;
313 Dimension commentW, commentH;
314 typedef unsigned int BoardSize;
316 Boolean chessProgram;
318 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
319 int smallLayout = 0, tinyLayout = 0,
320 marginW, marginH, // [HGM] for run-time resizing
321 fromX = -1, fromY = -1, toX, toY, commentUp = False,
322 errorExitStatus = -1, defaultLineGap;
323 Dimension textHeight;
324 Pixel timerForegroundPixel, timerBackgroundPixel;
325 Pixel buttonForegroundPixel, buttonBackgroundPixel;
326 char *chessDir, *programName, *programVersion;
327 Boolean alwaysOnTop = False;
328 char *icsTextMenuString;
330 char *firstChessProgramNames;
331 char *secondChessProgramNames;
333 WindowPlacement wpMain;
334 WindowPlacement wpConsole;
335 WindowPlacement wpComment;
336 WindowPlacement wpMoveHistory;
337 WindowPlacement wpEvalGraph;
338 WindowPlacement wpEngineOutput;
339 WindowPlacement wpGameList;
340 WindowPlacement wpTags;
345 Pixmap pieceBitmap[2][(int)BlackPawn];
346 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
347 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
348 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
349 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
350 Pixmap xpmBoardBitmap[2];
351 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
352 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
353 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
354 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
355 XImage *ximLightSquare, *ximDarkSquare;
358 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
359 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
361 #define White(piece) ((int)(piece) < (int)BlackPawn)
363 /* Bitmaps for use as masks when drawing XPM pieces.
364 Need one for each black and white piece. */
365 static Pixmap xpmMask[BlackKing + 1];
367 /* This magic number is the number of intermediate frames used
368 in each half of the animation. For short moves it's reduced
369 by 1. The total number of frames will be factor * 2 + 1. */
372 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
379 DropMenuEnables dmEnables[] = {
396 XtResource clientResources[] = {
397 { "flashCount", "flashCount", XtRInt, sizeof(int),
398 XtOffset(AppDataPtr, flashCount), XtRImmediate,
399 (XtPointer) FLASH_COUNT },
402 XrmOptionDescRec shellOptions[] = {
403 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
404 { "-flash", "flashCount", XrmoptionNoArg, "3" },
405 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
408 XtActionsRec boardActions[] = {
409 { "DrawPosition", DrawPositionProc },
410 { "HandlePV", HandlePV },
411 { "SelectPV", SelectPV },
412 { "StopPV", StopPV },
413 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
414 { "QuitProc", QuitWrapper },
415 { "ManProc", ManInner },
416 { "TempBackwardProc", TempBackwardProc },
417 { "TempForwardProc", TempForwardProc },
418 { "CommentClick", (XtActionProc) CommentClick },
419 { "GenericPopDown", (XtActionProc) GenericPopDown },
420 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
421 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
422 { "SelectMove", (XtActionProc) SelectMove },
423 { "LoadSelectedProc", LoadSelectedProc },
424 { "SetFilterProc", SetFilterProc },
425 { "TypeInProc", TypeInProc },
426 { "EnterKeyProc", EnterKeyProc },
427 { "UpKeyProc", UpKeyProc },
428 { "DownKeyProc", DownKeyProc },
429 { "WheelProc", WheelProc },
430 { "TabProc", TabProc },
433 char globalTranslations[] =
434 ":<Key>F9: MenuItem(Actions.Resign) \n \
435 :Ctrl<Key>n: MenuItem(File.NewGame) \n \
436 :Meta<Key>V: MenuItem(File.NewVariant) \n \
437 :Ctrl<Key>o: MenuItem(File.LoadGame) \n \
438 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
439 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
440 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
441 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
442 :Ctrl<Key>s: MenuItem(File.SaveGame) \n \
443 :Ctrl<Key>c: MenuItem(Edit.CopyGame) \n \
444 :Ctrl<Key>v: MenuItem(Edit.PasteGame) \n \
445 :Ctrl<Key>O: MenuItem(File.LoadPosition) \n \
446 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
447 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
448 :Ctrl<Key>S: MenuItem(File.SavePosition) \n \
449 :Ctrl<Key>C: MenuItem(Edit.CopyPosition) \n \
450 :Ctrl<Key>V: MenuItem(Edit.PastePosition) \n \
451 :Ctrl<Key>q: MenuItem(File.Quit) \n \
452 :Ctrl<Key>w: MenuItem(Mode.MachineWhite) \n \
453 :Ctrl<Key>b: MenuItem(Mode.MachineBlack) \n \
454 :Ctrl<Key>t: MenuItem(Mode.TwoMachines) \n \
455 :Ctrl<Key>a: MenuItem(Mode.AnalysisMode) \n \
456 :Ctrl<Key>g: MenuItem(Mode.AnalyzeFile) \n \
457 :Ctrl<Key>e: MenuItem(Mode.EditGame) \n \
458 :Ctrl<Key>E: MenuItem(Mode.EditPosition) \n \
459 :Meta<Key>O: MenuItem(View.EngineOutput) \n \
460 :Meta<Key>E: MenuItem(View.EvaluationGraph) \n \
461 :Meta<Key>G: MenuItem(View.GameList) \n \
462 :Meta<Key>H: MenuItem(View.MoveHistory) \n \
463 :<Key>Pause: MenuItem(Mode.Pause) \n \
464 :<Key>F3: MenuItem(Action.Accept) \n \
465 :<Key>F4: MenuItem(Action.Decline) \n \
466 :<Key>F12: MenuItem(Action.Rematch) \n \
467 :<Key>F5: MenuItem(Action.CallFlag) \n \
468 :<Key>F6: MenuItem(Action.Draw) \n \
469 :<Key>F7: MenuItem(Action.Adjourn) \n \
470 :<Key>F8: MenuItem(Action.Abort) \n \
471 :<Key>F10: MenuItem(Action.StopObserving) \n \
472 :<Key>F11: MenuItem(Action.StopExamining) \n \
473 :Ctrl<Key>d: MenuItem(DebugProc) \n \
474 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
475 :Meta<Key>End: MenuItem(Edit.ForwardtoEnd) \n \
476 :Meta<Key>Right: MenuItem(Edit.Forward) \n \
477 :Meta<Key>Home: MenuItem(Edit.BacktoStart) \n \
478 :Meta<Key>Left: MenuItem(Edit.Backward) \n \
479 :<Key>Left: MenuItem(Edit.Backward) \n \
480 :<Key>Right: MenuItem(Edit.Forward) \n \
481 :<Key>Home: MenuItem(Edit.Revert) \n \
482 :<Key>End: MenuItem(Edit.TruncateGame) \n \
483 :Ctrl<Key>m: MenuItem(Engine.MoveNow) \n \
484 :Ctrl<Key>x: MenuItem(Engine.RetractMove) \n \
485 :Meta<Key>J: MenuItem(Options.Adjudications) \n \
486 :Meta<Key>U: MenuItem(Options.CommonEngine) \n \
487 :Meta<Key>T: MenuItem(Options.TimeControl) \n \
488 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
489 #ifndef OPTIONSDIALOG
491 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
492 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
493 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
494 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
495 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
498 :<Key>F1: MenuItem(Help.ManXBoard) \n \
499 :<Key>F2: MenuItem(View.FlipView) \n \
500 :<KeyDown>Return: TempBackwardProc() \n \
501 :<KeyUp>Return: TempForwardProc() \n";
503 char ICSInputTranslations[] =
504 "<Key>Up: UpKeyProc() \n "
505 "<Key>Down: DownKeyProc() \n "
506 "<Key>Return: EnterKeyProc() \n";
508 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
509 // as the widget is destroyed before the up-click can call extend-end
510 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
512 String xboardResources[] = {
513 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
518 /* Max possible square size */
519 #define MAXSQSIZE 256
521 static int xpm_avail[MAXSQSIZE];
523 #ifdef HAVE_DIR_STRUCT
525 /* Extract piece size from filename */
527 xpm_getsize (char *name, int len, char *ext)
535 if ((p=strchr(name, '.')) == NULL ||
536 StrCaseCmp(p+1, ext) != 0)
542 while (*p && isdigit(*p))
549 /* Setup xpm_avail */
551 xpm_getavail (char *dirname, char *ext)
557 for (i=0; i<MAXSQSIZE; ++i)
560 if (appData.debugMode)
561 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
563 dir = opendir(dirname);
566 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
567 programName, dirname);
571 while ((ent=readdir(dir)) != NULL) {
572 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
573 if (i > 0 && i < MAXSQSIZE)
583 xpm_print_avail (FILE *fp, char *ext)
587 fprintf(fp, _("Available `%s' sizes:\n"), ext);
588 for (i=1; i<MAXSQSIZE; ++i) {
594 /* Return XPM piecesize closest to size */
596 xpm_closest_to (char *dirname, int size, char *ext)
599 int sm_diff = MAXSQSIZE;
603 xpm_getavail(dirname, ext);
605 if (appData.debugMode)
606 xpm_print_avail(stderr, ext);
608 for (i=1; i<MAXSQSIZE; ++i) {
611 diff = (diff<0) ? -diff : diff;
612 if (diff < sm_diff) {
620 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
626 #else /* !HAVE_DIR_STRUCT */
627 /* If we are on a system without a DIR struct, we can't
628 read the directory, so we can't collect a list of
629 filenames, etc., so we can't do any size-fitting. */
631 xpm_closest_to (char *dirname, int size, char *ext)
634 Warning: No DIR structure found on this system --\n\
635 Unable to autosize for XPM/XIM pieces.\n\
636 Please report this error to %s.\n\
637 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
640 #endif /* HAVE_DIR_STRUCT */
643 /* Arrange to catch delete-window events */
644 Atom wm_delete_window;
646 CatchDeleteWindow (Widget w, String procname)
649 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
650 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
651 XtAugmentTranslations(w, XtParseTranslationTable(buf));
658 XtSetArg(args[0], XtNiconic, False);
659 XtSetValues(shellWidget, args, 1);
661 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
664 //---------------------------------------------------------------------------------------------------------
665 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
668 #define CW_USEDEFAULT (1<<31)
669 #define ICS_TEXT_MENU_SIZE 90
670 #define DEBUG_FILE "xboard.debug"
671 #define SetCurrentDirectory chdir
672 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
676 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
679 // front-end part of option handling
681 // [HGM] This platform-dependent table provides the location for storing the color info
682 extern char *crWhite, * crBlack;
686 &appData.whitePieceColor,
687 &appData.blackPieceColor,
688 &appData.lightSquareColor,
689 &appData.darkSquareColor,
690 &appData.highlightSquareColor,
691 &appData.premoveHighlightColor,
692 &appData.lowTimeWarningColor,
703 // [HGM] font: keep a font for each square size, even non-stndard ones
706 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
707 char *fontTable[NUM_FONTS][MAX_SIZE];
710 ParseFont (char *name, int number)
711 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
713 if(sscanf(name, "size%d:", &size)) {
714 // [HGM] font: font is meant for specific boardSize (likely from settings file);
715 // defer processing it until we know if it matches our board size
716 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
717 fontTable[number][size] = strdup(strchr(name, ':')+1);
718 fontValid[number][size] = True;
723 case 0: // CLOCK_FONT
724 appData.clockFont = strdup(name);
726 case 1: // MESSAGE_FONT
727 appData.font = strdup(name);
729 case 2: // COORD_FONT
730 appData.coordFont = strdup(name);
735 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
740 { // only 2 fonts currently
741 appData.clockFont = CLOCK_FONT_NAME;
742 appData.coordFont = COORD_FONT_NAME;
743 appData.font = DEFAULT_FONT_NAME;
748 { // no-op, until we identify the code for this already in XBoard and move it here
752 ParseColor (int n, char *name)
753 { // in XBoard, just copy the color-name string
754 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
758 ParseTextAttribs (ColorClass cc, char *s)
760 (&appData.colorShout)[cc] = strdup(s);
764 ParseBoardSize (void *addr, char *name)
766 appData.boardSize = strdup(name);
771 { // In XBoard the sound-playing program takes care of obtaining the actual sound
775 SetCommPortDefaults ()
776 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
779 // [HGM] args: these three cases taken out to stay in front-end
781 SaveFontArg (FILE *f, ArgDescriptor *ad)
784 int i, n = (int)(intptr_t)ad->argLoc;
786 case 0: // CLOCK_FONT
787 name = appData.clockFont;
789 case 1: // MESSAGE_FONT
792 case 2: // COORD_FONT
793 name = appData.coordFont;
798 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
799 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
800 fontTable[n][squareSize] = strdup(name);
801 fontValid[n][squareSize] = True;
804 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
805 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
810 { // nothing to do, as the sounds are at all times represented by their text-string names already
814 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
815 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
816 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
820 SaveColor (FILE *f, ArgDescriptor *ad)
821 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
822 if(colorVariable[(int)(intptr_t)ad->argLoc])
823 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
827 SaveBoardSize (FILE *f, char *name, void *addr)
828 { // wrapper to shield back-end from BoardSize & sizeInfo
829 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
833 ParseCommPortSettings (char *s)
834 { // no such option in XBoard (yet)
840 GetActualPlacement (Widget wg, WindowPlacement *wp)
842 XWindowAttributes winAt;
849 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
850 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
851 wp->x = rx - winAt.x;
852 wp->y = ry - winAt.y;
853 wp->height = winAt.height;
854 wp->width = winAt.width;
855 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
860 { // wrapper to shield use of window handles from back-end (make addressible by number?)
861 // In XBoard this will have to wait until awareness of window parameters is implemented
862 GetActualPlacement(shellWidget, &wpMain);
863 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
864 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
865 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
866 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
867 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
868 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
872 PrintCommPortSettings (FILE *f, char *name)
873 { // This option does not exist in XBoard
877 EnsureOnScreen (int *x, int *y, int minX, int minY)
884 { // [HGM] args: allows testing if main window is realized from back-end
885 return xBoardWindow != 0;
890 extern Option dualOptions[];
892 Window tmp = xBoardWindow;
893 if(!dual) dual = XtWindow(dualOptions[3].handle); // must be first call
894 xBoardWindow = dual; // swap them
899 PopUpStartupDialog ()
900 { // start menu not implemented in XBoard
904 ConvertToLine (int argc, char **argv)
906 static char line[128*1024], buf[1024];
910 for(i=1; i<argc; i++)
912 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
913 && argv[i][0] != '{' )
914 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
916 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
917 strncat(line, buf, 128*1024 - strlen(line) - 1 );
920 line[strlen(line)-1] = NULLCHAR;
924 //--------------------------------------------------------------------------------------------
926 #define BoardSize int
928 InitDrawingSizes (BoardSize boardSize, int flags)
929 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
930 Dimension boardWidth, boardHeight, w, h;
932 static Dimension oldWidth, oldHeight;
933 static VariantClass oldVariant;
934 static int oldMono = -1, oldTwoBoards = 0;
936 if(!formWidget) return;
938 if(oldTwoBoards && !twoBoards) PopDown(DummyDlg);
939 oldTwoBoards = twoBoards;
941 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
942 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
943 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
945 if(boardWidth != oldWidth || boardHeight != oldHeight) { // do resizing stuff only if size actually changed
947 oldWidth = boardWidth; oldHeight = boardHeight;
951 * Inhibit shell resizing.
953 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW ;
954 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
955 shellArgs[4].value = shellArgs[2].value = w;
956 shellArgs[5].value = shellArgs[3].value = h;
957 XtSetValues(shellWidget, &shellArgs[0], 6);
959 XSync(xDisplay, False);
963 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
966 if(gameInfo.variant != oldVariant) { // and only if variant changed
971 for(p=0; p<=(int)WhiteKing; p++)
972 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
973 if(gameInfo.variant == VariantShogi) {
974 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
975 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
976 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
977 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
978 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
981 if(gameInfo.variant == VariantGothic) {
982 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
985 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
986 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
987 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
990 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
991 for(p=0; p<=(int)WhiteKing; p++)
992 ximMaskPm[p] = ximMaskPm2[p]; // defaults
993 if(gameInfo.variant == VariantShogi) {
994 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
995 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
996 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
997 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
998 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1001 if(gameInfo.variant == VariantGothic) {
1002 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1005 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1006 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1007 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1012 for(i=0; i<2; i++) {
1014 for(p=0; p<=(int)WhiteKing; p++)
1015 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1016 if(gameInfo.variant == VariantShogi) {
1017 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1018 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1019 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1020 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1021 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1024 if(gameInfo.variant == VariantGothic) {
1025 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1028 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1029 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1030 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1034 oldMono = -10; // kludge to force recreation of animation masks
1035 oldVariant = gameInfo.variant;
1038 if(appData.monoMode != oldMono)
1041 oldMono = appData.monoMode;
1045 MakeOneColor (char *name, Pixel *color)
1047 XrmValue vFrom, vTo;
1048 if (!appData.monoMode) {
1049 vFrom.addr = (caddr_t) name;
1050 vFrom.size = strlen(name);
1051 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1052 if (vTo.addr == NULL) {
1053 appData.monoMode = True;
1056 *color = *(Pixel *) vTo.addr;
1064 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1065 int forceMono = False;
1067 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1068 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1069 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1070 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1071 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1072 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1073 if (appData.lowTimeWarning)
1074 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
1075 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1076 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1083 { // [HGM] taken out of main
1085 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1086 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1087 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1089 if (appData.bitmapDirectory[0] != NULLCHAR) {
1093 CreateXPMBoard(appData.liteBackTextureFile, 1);
1094 CreateXPMBoard(appData.darkBackTextureFile, 0);
1098 /* Create regular pieces */
1099 if (!useImages) CreatePieces();
1104 InitDrawingParams ()
1106 MakeColors(); CreateGCs(True);
1111 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
1112 { // detervtomine what fonts to use, and create them
1116 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1117 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1118 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1119 appData.font = fontTable[MESSAGE_FONT][squareSize];
1120 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1121 appData.coordFont = fontTable[COORD_FONT][squareSize];
1124 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1125 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1126 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1127 fontSet = CreateFontSet(appData.font);
1128 clockFontSet = CreateFontSet(appData.clockFont);
1130 /* For the coordFont, use the 0th font of the fontset. */
1131 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1132 XFontStruct **font_struct_list;
1133 XFontSetExtents *fontSize;
1134 char **font_name_list;
1135 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1136 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1137 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1138 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1139 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1142 appData.font = FindFont(appData.font, fontPxlSize);
1143 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1144 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1145 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1146 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1147 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1148 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1149 // textHeight in !NLS mode!
1151 countFontID = coordFontID; // [HGM] holdings
1152 countFontStruct = coordFontStruct;
1154 xdb = XtDatabase(xDisplay);
1156 XrmPutLineResource(&xdb, "*international: True");
1157 vTo.size = sizeof(XFontSet);
1158 vTo.addr = (XtPointer) &fontSet;
1159 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1161 XrmPutStringResource(&xdb, "*font", appData.font);
1166 main (int argc, char **argv)
1168 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1169 XSetWindowAttributes window_attributes;
1171 Dimension boardWidth, boardHeight, w, h;
1173 int forceMono = False;
1175 srandom(time(0)); // [HGM] book: make random truly random
1177 setbuf(stdout, NULL);
1178 setbuf(stderr, NULL);
1181 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1182 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1186 programName = strrchr(argv[0], '/');
1187 if (programName == NULL)
1188 programName = argv[0];
1193 XtSetLanguageProc(NULL, NULL, NULL);
1194 if (appData.debugMode) {
1195 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1198 bindtextdomain(PACKAGE, LOCALEDIR);
1199 textdomain(PACKAGE);
1202 appData.boardSize = "";
1203 InitAppData(ConvertToLine(argc, argv));
1205 if (p == NULL) p = "/tmp";
1206 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1207 gameCopyFilename = (char*) malloc(i);
1208 gamePasteFilename = (char*) malloc(i);
1209 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1210 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1212 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1213 static char buf[MSG_SIZ];
1214 EscapeExpand(buf, appData.firstInitString);
1215 appData.firstInitString = strdup(buf);
1216 EscapeExpand(buf, appData.secondInitString);
1217 appData.secondInitString = strdup(buf);
1218 EscapeExpand(buf, appData.firstComputerString);
1219 appData.firstComputerString = strdup(buf);
1220 EscapeExpand(buf, appData.secondComputerString);
1221 appData.secondComputerString = strdup(buf);
1224 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1227 if (chdir(chessDir) != 0) {
1228 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1234 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1235 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1236 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1237 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1240 setbuf(debugFP, NULL);
1243 /* [HGM,HR] make sure board size is acceptable */
1244 if(appData.NrFiles > BOARD_FILES ||
1245 appData.NrRanks > BOARD_RANKS )
1246 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1249 /* This feature does not work; animation needs a rewrite */
1250 appData.highlightDragging = FALSE;
1254 gameInfo.variant = StringToVariant(appData.variant);
1255 InitPosition(FALSE);
1258 XtAppInitialize(&appContext, "XBoard", shellOptions,
1259 XtNumber(shellOptions),
1260 &argc, argv, xboardResources, NULL, 0);
1262 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1263 clientResources, XtNumber(clientResources),
1266 xDisplay = XtDisplay(shellWidget);
1267 xScreen = DefaultScreen(xDisplay);
1268 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1271 * determine size, based on supplied or remembered -size, or screen size
1273 if (isdigit(appData.boardSize[0])) {
1274 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1275 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1276 &fontPxlSize, &smallLayout, &tinyLayout);
1278 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1279 programName, appData.boardSize);
1283 /* Find some defaults; use the nearest known size */
1284 SizeDefaults *szd, *nearest;
1285 int distance = 99999;
1286 nearest = szd = sizeDefaults;
1287 while (szd->name != NULL) {
1288 if (abs(szd->squareSize - squareSize) < distance) {
1290 distance = abs(szd->squareSize - squareSize);
1291 if (distance == 0) break;
1295 if (i < 2) lineGap = nearest->lineGap;
1296 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1297 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1298 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1299 if (i < 6) smallLayout = nearest->smallLayout;
1300 if (i < 7) tinyLayout = nearest->tinyLayout;
1303 SizeDefaults *szd = sizeDefaults;
1304 if (*appData.boardSize == NULLCHAR) {
1305 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1306 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1309 if (szd->name == NULL) szd--;
1310 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1312 while (szd->name != NULL &&
1313 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1314 if (szd->name == NULL) {
1315 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1316 programName, appData.boardSize);
1320 squareSize = szd->squareSize;
1321 lineGap = szd->lineGap;
1322 clockFontPxlSize = szd->clockFontPxlSize;
1323 coordFontPxlSize = szd->coordFontPxlSize;
1324 fontPxlSize = szd->fontPxlSize;
1325 smallLayout = szd->smallLayout;
1326 tinyLayout = szd->tinyLayout;
1327 // [HGM] font: use defaults from settings file if available and not overruled
1330 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1331 if (strlen(appData.pixmapDirectory) > 0) {
1332 p = ExpandPathName(appData.pixmapDirectory);
1334 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1335 appData.pixmapDirectory);
1338 if (appData.debugMode) {
1339 fprintf(stderr, _("\
1340 XBoard square size (hint): %d\n\
1341 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1343 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1344 if (appData.debugMode) {
1345 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1348 defaultLineGap = lineGap;
1349 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1351 /* [HR] height treated separately (hacked) */
1352 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1353 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1356 * Determine what fonts to use.
1358 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1361 * Detect if there are not enough colors available and adapt.
1363 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1364 appData.monoMode = True;
1367 forceMono = MakeColors();
1370 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1372 appData.monoMode = True;
1375 if (appData.monoMode && appData.debugMode) {
1376 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1377 (unsigned long) XWhitePixel(xDisplay, xScreen),
1378 (unsigned long) XBlackPixel(xDisplay, xScreen));
1381 ParseIcsTextColors();
1383 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1389 layoutName = "tinyLayout";
1390 } else if (smallLayout) {
1391 layoutName = "smallLayout";
1393 layoutName = "normalLayout";
1396 optList = BoardPopUp(squareSize, lineGap, (void*)
1402 boardWidget = optList[W_BOARD].handle;
1403 menuBarWidget = optList[W_MENU].handle;
1404 dropMenu = optList[W_DROP].handle;
1405 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1406 formWidget = XtParent(boardWidget);
1407 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1408 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1409 XtGetValues(optList[W_WHITE].handle, args, 2);
1410 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1411 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1412 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1413 XtGetValues(optList[W_PAUSE].handle, args, 2);
1415 AppendEnginesToMenu(appData.recentEngineList);
1417 xBoardWindow = XtWindow(boardWidget);
1419 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1420 // not need to go into InitDrawingSizes().
1423 * Create X checkmark bitmap and initialize option menu checks.
1425 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1426 checkmark_bits, checkmark_width, checkmark_height);
1432 ReadBitmap(&wIconPixmap, "icon_white.bm",
1433 icon_white_bits, icon_white_width, icon_white_height);
1434 ReadBitmap(&bIconPixmap, "icon_black.bm",
1435 icon_black_bits, icon_black_width, icon_black_height);
1436 iconPixmap = wIconPixmap;
1438 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1439 XtSetValues(shellWidget, args, i);
1442 * Create a cursor for the board widget.
1444 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1445 XChangeWindowAttributes(xDisplay, xBoardWindow,
1446 CWCursor, &window_attributes);
1449 * Inhibit shell resizing.
1451 shellArgs[0].value = (XtArgVal) &w;
1452 shellArgs[1].value = (XtArgVal) &h;
1453 XtGetValues(shellWidget, shellArgs, 2);
1454 shellArgs[4].value = shellArgs[2].value = w;
1455 shellArgs[5].value = shellArgs[3].value = h;
1456 XtSetValues(shellWidget, &shellArgs[2], 4);
1457 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1458 marginH = h - boardHeight;
1460 CatchDeleteWindow(shellWidget, "QuitProc");
1466 if (appData.animate || appData.animateDragging)
1469 XtAugmentTranslations(formWidget,
1470 XtParseTranslationTable(globalTranslations));
1472 XtAddEventHandler(formWidget, KeyPressMask, False,
1473 (XtEventHandler) MoveTypeInProc, NULL);
1474 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1475 (XtEventHandler) EventProc, NULL);
1477 /* [AS] Restore layout */
1478 if( wpMoveHistory.visible ) {
1482 if( wpEvalGraph.visible )
1487 if( wpEngineOutput.visible ) {
1488 EngineOutputPopUp();
1493 if (errorExitStatus == -1) {
1494 if (appData.icsActive) {
1495 /* We now wait until we see "login:" from the ICS before
1496 sending the logon script (problems with timestamp otherwise) */
1497 /*ICSInitScript();*/
1498 if (appData.icsInputBox) ICSInputBoxPopUp();
1502 signal(SIGWINCH, TermSizeSigHandler);
1504 signal(SIGINT, IntSigHandler);
1505 signal(SIGTERM, IntSigHandler);
1506 if (*appData.cmailGameName != NULLCHAR) {
1507 signal(SIGUSR1, CmailSigHandler);
1511 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1513 // XtSetKeyboardFocus(shellWidget, formWidget);
1514 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1516 XtAppMainLoop(appContext);
1517 if (appData.debugMode) fclose(debugFP); // [DM] debug
1522 TermSizeSigHandler (int sig)
1528 IntSigHandler (int sig)
1534 CmailSigHandler (int sig)
1539 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1541 /* Activate call-back function CmailSigHandlerCallBack() */
1542 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1544 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1548 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1551 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1553 /**** end signal code ****/
1556 #define Abs(n) ((n)<0 ? -(n) : (n))
1560 InsertPxlSize (char *pattern, int targetPxlSize)
1562 char *base_fnt_lst, strInt[12], *p, *q;
1563 int alternatives, i, len, strIntLen;
1566 * Replace the "*" (if present) in the pixel-size slot of each
1567 * alternative with the targetPxlSize.
1571 while ((p = strchr(p, ',')) != NULL) {
1575 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1576 strIntLen = strlen(strInt);
1577 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1581 while (alternatives--) {
1582 char *comma = strchr(p, ',');
1583 for (i=0; i<14; i++) {
1584 char *hyphen = strchr(p, '-');
1586 if (comma && hyphen > comma) break;
1587 len = hyphen + 1 - p;
1588 if (i == 7 && *p == '*' && len == 2) {
1590 memcpy(q, strInt, strIntLen);
1600 len = comma + 1 - p;
1607 return base_fnt_lst;
1611 CreateFontSet (char *base_fnt_lst)
1614 char **missing_list;
1618 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1619 &missing_list, &missing_count, &def_string);
1620 if (appData.debugMode) {
1622 XFontStruct **font_struct_list;
1623 char **font_name_list;
1624 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1626 fprintf(debugFP, " got list %s, locale %s\n",
1627 XBaseFontNameListOfFontSet(fntSet),
1628 XLocaleOfFontSet(fntSet));
1629 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1630 for (i = 0; i < count; i++) {
1631 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1634 for (i = 0; i < missing_count; i++) {
1635 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1638 if (fntSet == NULL) {
1639 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1644 #else // not ENABLE_NLS
1646 * Find a font that matches "pattern" that is as close as
1647 * possible to the targetPxlSize. Prefer fonts that are k
1648 * pixels smaller to fonts that are k pixels larger. The
1649 * pattern must be in the X Consortium standard format,
1650 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1651 * The return value should be freed with XtFree when no
1655 FindFont (char *pattern, int targetPxlSize)
1657 char **fonts, *p, *best, *scalable, *scalableTail;
1658 int i, j, nfonts, minerr, err, pxlSize;
1660 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1662 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1663 programName, pattern);
1670 for (i=0; i<nfonts; i++) {
1673 if (*p != '-') continue;
1675 if (*p == NULLCHAR) break;
1676 if (*p++ == '-') j++;
1678 if (j < 7) continue;
1681 scalable = fonts[i];
1684 err = pxlSize - targetPxlSize;
1685 if (Abs(err) < Abs(minerr) ||
1686 (minerr > 0 && err < 0 && -err == minerr)) {
1692 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1693 /* If the error is too big and there is a scalable font,
1694 use the scalable font. */
1695 int headlen = scalableTail - scalable;
1696 p = (char *) XtMalloc(strlen(scalable) + 10);
1697 while (isdigit(*scalableTail)) scalableTail++;
1698 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1700 p = (char *) XtMalloc(strlen(best) + 2);
1701 safeStrCpy(p, best, strlen(best)+1 );
1703 if (appData.debugMode) {
1704 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1705 pattern, targetPxlSize, p);
1707 XFreeFontNames(fonts);
1714 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
1715 // must be called before all non-first callse to CreateGCs()
1716 XtReleaseGC(shellWidget, highlineGC);
1717 XtReleaseGC(shellWidget, lightSquareGC);
1718 XtReleaseGC(shellWidget, darkSquareGC);
1719 XtReleaseGC(shellWidget, lineGC);
1720 if (appData.monoMode) {
1721 if (DefaultDepth(xDisplay, xScreen) == 1) {
1722 XtReleaseGC(shellWidget, wbPieceGC);
1724 XtReleaseGC(shellWidget, bwPieceGC);
1727 XtReleaseGC(shellWidget, prelineGC);
1728 XtReleaseGC(shellWidget, wdPieceGC);
1729 XtReleaseGC(shellWidget, wlPieceGC);
1730 XtReleaseGC(shellWidget, bdPieceGC);
1731 XtReleaseGC(shellWidget, blPieceGC);
1736 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
1738 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1739 | GCBackground | GCFunction | GCPlaneMask;
1740 gc_values->foreground = foreground;
1741 gc_values->background = background;
1742 return XtGetGC(shellWidget, value_mask, gc_values);
1746 CreateGCs (int redo)
1748 XGCValues gc_values;
1750 Pixel white = XWhitePixel(xDisplay, xScreen);
1751 Pixel black = XBlackPixel(xDisplay, xScreen);
1753 gc_values.plane_mask = AllPlanes;
1754 gc_values.line_width = lineGap;
1755 gc_values.line_style = LineSolid;
1756 gc_values.function = GXcopy;
1759 DeleteGCs(); // called a second time; clean up old GCs first
1760 } else { // [HGM] grid and font GCs created on first call only
1761 coordGC = CreateOneGC(&gc_values, black, white);
1762 XSetFont(xDisplay, coordGC, coordFontID);
1764 // [HGM] make font for holdings counts (white on black)
1765 countGC = CreateOneGC(&gc_values, white, black);
1766 XSetFont(xDisplay, countGC, countFontID);
1768 lineGC = CreateOneGC(&gc_values, black, black);
1770 if (appData.monoMode) {
1772 highlineGC = CreateOneGC(&gc_values, white, white);
1773 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
1774 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
1776 if (DefaultDepth(xDisplay, xScreen) == 1) {
1777 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
1778 gc_values.function = GXcopyInverted;
1779 copyInvertedGC = CreateOneGC(&gc_values, black, white);
1780 gc_values.function = GXcopy;
1781 if (XBlackPixel(xDisplay, xScreen) == 1) {
1782 bwPieceGC = darkSquareGC;
1783 wbPieceGC = copyInvertedGC;
1785 bwPieceGC = copyInvertedGC;
1786 wbPieceGC = lightSquareGC;
1791 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
1792 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
1793 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
1794 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
1795 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
1796 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
1797 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
1798 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
1803 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
1811 fp = fopen(filename, "rb");
1813 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
1820 for (y=0; y<h; ++y) {
1821 for (x=0; x<h; ++x) {
1826 XPutPixel(xim, x, y, blackPieceColor);
1828 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1831 XPutPixel(xim, x, y, darkSquareColor);
1833 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1836 XPutPixel(xim, x, y, whitePieceColor);
1838 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1841 XPutPixel(xim, x, y, lightSquareColor);
1843 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1851 /* create Pixmap of piece */
1852 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1854 XPutImage(xDisplay, *dest, lightSquareGC, xim,
1857 /* create Pixmap of clipmask
1858 Note: We assume the white/black pieces have the same
1859 outline, so we make only 6 masks. This is okay
1860 since the XPM clipmask routines do the same. */
1862 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1864 XPutImage(xDisplay, temp, lightSquareGC, xmask,
1867 /* now create the 1-bit version */
1868 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1871 values.foreground = 1;
1872 values.background = 0;
1874 /* Don't use XtGetGC, not read only */
1875 maskGC = XCreateGC(xDisplay, *mask,
1876 GCForeground | GCBackground, &values);
1877 XCopyPlane(xDisplay, temp, *mask, maskGC,
1878 0, 0, squareSize, squareSize, 0, 0, 1);
1879 XFreePixmap(xDisplay, temp);
1884 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
1892 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
1897 /* The XSynchronize calls were copied from CreatePieces.
1898 Not sure if needed, but can't hurt */
1899 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
1902 /* temp needed by loadXIM() */
1903 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1904 0, 0, ss, ss, AllPlanes, XYPixmap);
1906 if (strlen(appData.pixmapDirectory) == 0) {
1910 if (appData.monoMode) {
1911 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
1915 fprintf(stderr, _("\nLoading XIMs...\n"));
1917 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
1918 fprintf(stderr, "%d", piece+1);
1919 for (kind=0; kind<4; kind++) {
1920 fprintf(stderr, ".");
1921 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
1922 ExpandPathName(appData.pixmapDirectory),
1923 piece <= (int) WhiteKing ? "" : "w",
1924 pieceBitmapNames[piece],
1926 ximPieceBitmap[kind][piece] =
1927 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1928 0, 0, ss, ss, AllPlanes, XYPixmap);
1929 if (appData.debugMode)
1930 fprintf(stderr, _("(File:%s:) "), buf);
1931 loadXIM(ximPieceBitmap[kind][piece],
1933 &(xpmPieceBitmap2[kind][piece]),
1934 &(ximMaskPm2[piece]));
1935 if(piece <= (int)WhiteKing)
1936 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
1938 fprintf(stderr," ");
1940 /* Load light and dark squares */
1941 /* If the LSQ and DSQ pieces don't exist, we will
1942 draw them with solid squares. */
1943 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
1944 if (access(buf, 0) != 0) {
1948 fprintf(stderr, _("light square "));
1950 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1951 0, 0, ss, ss, AllPlanes, XYPixmap);
1952 if (appData.debugMode)
1953 fprintf(stderr, _("(File:%s:) "), buf);
1955 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
1956 fprintf(stderr, _("dark square "));
1957 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
1958 ExpandPathName(appData.pixmapDirectory), ss);
1959 if (appData.debugMode)
1960 fprintf(stderr, _("(File:%s:) "), buf);
1962 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1963 0, 0, ss, ss, AllPlanes, XYPixmap);
1964 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
1965 xpmJailSquare = xpmLightSquare;
1967 fprintf(stderr, _("Done.\n"));
1969 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
1972 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
1976 CreateXPMBoard (char *s, int kind)
1980 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
1981 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
1982 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
1988 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
1989 // thisroutine has to be called t free the old piece pixmaps
1991 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
1992 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
1994 XFreePixmap(xDisplay, xpmLightSquare);
1995 XFreePixmap(xDisplay, xpmDarkSquare);
2004 u_int ss = squareSize;
2006 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2007 XpmColorSymbol symbols[4];
2008 static int redo = False;
2010 if(redo) FreeXPMPieces(); else redo = 1;
2012 /* The XSynchronize calls were copied from CreatePieces.
2013 Not sure if needed, but can't hurt */
2014 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2016 /* Setup translations so piece colors match square colors */
2017 symbols[0].name = "light_piece";
2018 symbols[0].value = appData.whitePieceColor;
2019 symbols[1].name = "dark_piece";
2020 symbols[1].value = appData.blackPieceColor;
2021 symbols[2].name = "light_square";
2022 symbols[2].value = appData.lightSquareColor;
2023 symbols[3].name = "dark_square";
2024 symbols[3].value = appData.darkSquareColor;
2026 attr.valuemask = XpmColorSymbols;
2027 attr.colorsymbols = symbols;
2028 attr.numsymbols = 4;
2030 if (appData.monoMode) {
2031 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2035 if (strlen(appData.pixmapDirectory) == 0) {
2036 XpmPieces* pieces = builtInXpms;
2039 while (pieces->size != squareSize && pieces->size) pieces++;
2040 if (!pieces->size) {
2041 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2044 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2045 for (kind=0; kind<4; kind++) {
2047 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2048 pieces->xpm[piece][kind],
2049 &(xpmPieceBitmap2[kind][piece]),
2050 NULL, &attr)) != 0) {
2051 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2055 if(piece <= (int) WhiteKing)
2056 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2060 xpmJailSquare = xpmLightSquare;
2064 fprintf(stderr, _("\nLoading XPMs...\n"));
2067 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2068 fprintf(stderr, "%d ", piece+1);
2069 for (kind=0; kind<4; kind++) {
2070 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2071 ExpandPathName(appData.pixmapDirectory),
2072 piece > (int) WhiteKing ? "w" : "",
2073 pieceBitmapNames[piece],
2075 if (appData.debugMode) {
2076 fprintf(stderr, _("(File:%s:) "), buf);
2078 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2079 &(xpmPieceBitmap2[kind][piece]),
2080 NULL, &attr)) != 0) {
2081 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2082 // [HGM] missing: read of unorthodox piece failed; substitute King.
2083 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2084 ExpandPathName(appData.pixmapDirectory),
2086 if (appData.debugMode) {
2087 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2089 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2090 &(xpmPieceBitmap2[kind][piece]),
2094 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2099 if(piece <= (int) WhiteKing)
2100 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2103 /* Load light and dark squares */
2104 /* If the LSQ and DSQ pieces don't exist, we will
2105 draw them with solid squares. */
2106 fprintf(stderr, _("light square "));
2107 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2108 if (access(buf, 0) != 0) {
2112 if (appData.debugMode)
2113 fprintf(stderr, _("(File:%s:) "), buf);
2115 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2116 &xpmLightSquare, NULL, &attr)) != 0) {
2117 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2120 fprintf(stderr, _("dark square "));
2121 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2122 ExpandPathName(appData.pixmapDirectory), ss);
2123 if (appData.debugMode) {
2124 fprintf(stderr, _("(File:%s:) "), buf);
2126 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2127 &xpmDarkSquare, NULL, &attr)) != 0) {
2128 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2132 xpmJailSquare = xpmLightSquare;
2133 fprintf(stderr, _("Done.\n"));
2135 oldVariant = -1; // kludge to force re-makig of animation masks
2136 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2139 #endif /* HAVE_LIBXPM */
2142 /* No built-in bitmaps */
2147 u_int ss = squareSize;
2149 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2152 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2153 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2154 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2155 pieceBitmapNames[piece],
2156 ss, kind == SOLID ? 's' : 'o');
2157 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2158 if(piece <= (int)WhiteKing)
2159 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2163 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2167 /* With built-in bitmaps */
2171 BuiltInBits* bib = builtInBits;
2174 u_int ss = squareSize;
2176 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2179 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2181 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2182 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2183 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2184 pieceBitmapNames[piece],
2185 ss, kind == SOLID ? 's' : 'o');
2186 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2187 bib->bits[kind][piece], ss, ss);
2188 if(piece <= (int)WhiteKing)
2189 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2193 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2199 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2204 char msg[MSG_SIZ], fullname[MSG_SIZ];
2206 if (*appData.bitmapDirectory != NULLCHAR) {
2207 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2208 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2209 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2210 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2211 &w, &h, pm, &x_hot, &y_hot);
2212 fprintf(stderr, "load %s\n", name);
2213 if (errcode != BitmapSuccess) {
2215 case BitmapOpenFailed:
2216 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2218 case BitmapFileInvalid:
2219 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2221 case BitmapNoMemory:
2222 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2226 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2230 fprintf(stderr, _("%s: %s...using built-in\n"),
2232 } else if (w != wreq || h != hreq) {
2234 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2235 programName, fullname, w, h, wreq, hreq);
2241 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2251 if (lineGap == 0) return;
2253 /* [HR] Split this into 2 loops for non-square boards. */
2255 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2256 gridSegments[i].x1 = 0;
2257 gridSegments[i].x2 =
2258 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2259 gridSegments[i].y1 = gridSegments[i].y2
2260 = lineGap / 2 + (i * (squareSize + lineGap));
2263 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2264 gridSegments[j + i].y1 = 0;
2265 gridSegments[j + i].y2 =
2266 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2267 gridSegments[j + i].x1 = gridSegments[j + i].x2
2268 = lineGap / 2 + (j * (squareSize + lineGap));
2273 MarkMenuItem (char *menuRef, int state)
2275 MenuItem *item = MenuNameToItem(menuRef);
2279 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2280 XtSetValues(item->handle, args, 1);
2285 EnableMenuItem (char *menuRef, int state)
2287 MenuItem *item = MenuNameToItem(menuRef);
2289 if(item) XtSetSensitive(item->handle, state);
2293 EnableButtonBar (int state)
2295 XtSetSensitive(optList[W_BUTTON].handle, state);
2300 SetMenuEnables (Enables *enab)
2302 while (enab->name != NULL) {
2303 EnableMenuItem(enab->name, enab->value);
2309 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2310 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2312 if(*nprms == 0) return;
2313 item = MenuNameToItem(prms[0]);
2314 if(item) ((MenuProc *) item->proc) ();
2318 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2320 RecentEngineEvent((int) (intptr_t) addr);
2324 AppendMenuItem (char *msg, int n)
2326 CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
2338 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2339 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2340 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2341 dmEnables[i].piece);
2342 XtSetSensitive(entry, p != NULL || !appData.testLegality
2343 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2344 && !appData.icsActive));
2346 while (p && *p++ == dmEnables[i].piece) count++;
2347 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2349 XtSetArg(args[j], XtNlabel, label); j++;
2350 XtSetValues(entry, args, j);
2356 do_flash_delay (unsigned long msec)
2362 DrawBorder (int x, int y, int type)
2366 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
2368 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
2369 squareSize+lineGap, squareSize+lineGap);
2373 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
2375 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
2376 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
2378 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
2379 if(textureW[kind] < W*squareSize)
2380 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
2382 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
2383 if(textureH[kind] < H*squareSize)
2384 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
2386 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
2391 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
2392 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
2394 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
2395 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
2396 squareSize, squareSize, x*fac, y*fac);
2398 if (useImages && useImageSqs) {
2402 pm = xpmLightSquare;
2407 case 2: /* neutral */
2409 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
2412 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
2413 squareSize, squareSize, x*fac, y*fac);
2423 case 2: /* neutral */
2428 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
2433 I split out the routines to draw a piece so that I could
2434 make a generic flash routine.
2437 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2439 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2440 switch (square_color) {
2442 case 2: /* neutral */
2444 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2445 ? *pieceToOutline(piece)
2446 : *pieceToSolid(piece),
2447 dest, bwPieceGC, 0, 0,
2448 squareSize, squareSize, x, y);
2451 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2452 ? *pieceToSolid(piece)
2453 : *pieceToOutline(piece),
2454 dest, wbPieceGC, 0, 0,
2455 squareSize, squareSize, x, y);
2461 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2463 switch (square_color) {
2465 case 2: /* neutral */
2467 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2468 ? *pieceToOutline(piece)
2469 : *pieceToSolid(piece),
2470 dest, bwPieceGC, 0, 0,
2471 squareSize, squareSize, x, y, 1);
2474 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2475 ? *pieceToSolid(piece)
2476 : *pieceToOutline(piece),
2477 dest, wbPieceGC, 0, 0,
2478 squareSize, squareSize, x, y, 1);
2484 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2486 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
2487 switch (square_color) {
2489 XCopyPlane(xDisplay, *pieceToSolid(piece),
2490 dest, (int) piece < (int) BlackPawn
2491 ? wlPieceGC : blPieceGC, 0, 0,
2492 squareSize, squareSize, x, y, 1);
2495 XCopyPlane(xDisplay, *pieceToSolid(piece),
2496 dest, (int) piece < (int) BlackPawn
2497 ? wdPieceGC : bdPieceGC, 0, 0,
2498 squareSize, squareSize, x, y, 1);
2500 case 2: /* neutral */
2502 break; // should never contain pieces
2507 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2509 int kind, p = piece;
2511 switch (square_color) {
2513 case 2: /* neutral */
2515 if ((int)piece < (int) BlackPawn) {
2523 if ((int)piece < (int) BlackPawn) {
2531 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2532 if(useTexture & square_color+1) {
2533 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2534 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
2535 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
2536 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
2537 XSetClipMask(xDisplay, wlPieceGC, None);
2538 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
2540 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
2541 dest, wlPieceGC, 0, 0,
2542 squareSize, squareSize, x, y);
2545 typedef void (*DrawFunc)();
2550 if (appData.monoMode) {
2551 if (DefaultDepth(xDisplay, xScreen) == 1) {
2552 return monoDrawPiece_1bit;
2554 return monoDrawPiece;
2558 return colorDrawPieceImage;
2560 return colorDrawPiece;
2565 DrawDot (int marker, int x, int y, int r)
2567 if(appData.monoMode) {
2568 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
2569 x, y, r, r, 0, 64*360);
2570 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
2571 x, y, r, r, 0, 64*360);
2573 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
2574 x, y, r, r, 0, 64*360);
2578 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2579 { // basic front-end board-draw function: takes care of everything that can be in square:
2580 // piece, background, coordinate/count, marker dot
2581 int direction, font_ascent, font_descent;
2582 XCharStruct overall;
2585 if (piece == EmptySquare) {
2586 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2588 drawfunc = ChooseDrawFunc();
2589 drawfunc(piece, square_color, x, y, xBoardWindow);
2592 if(align) { // square carries inscription (coord or piece count)
2594 GC hGC = align < 3 ? coordGC : countGC;
2595 // first calculate where it goes
2596 XTextExtents(countFontStruct, string, 1, &direction,
2597 &font_ascent, &font_descent, &overall);
2599 xx += squareSize - overall.width - 2;
2600 yy += squareSize - font_descent - 1;
2601 } else if (align == 2) {
2602 xx += 2, yy += font_ascent + 1;
2603 } else if (align == 3) {
2604 xx += squareSize - overall.width - 2;
2605 yy += font_ascent + 1;
2606 } else if (align == 4) {
2607 xx += 2, yy += font_ascent + 1;
2610 if (appData.monoMode) {
2611 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2613 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2617 if(marker) { // print fat marker dot, if requested
2618 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2623 FlashDelay (int flash_delay)
2625 XSync(xDisplay, False);
2626 if(flash_delay) do_flash_delay(flash_delay);
2630 Fraction (int x, int start, int stop)
2632 double f = ((double) x - start)/(stop - start);
2633 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2637 static WindowPlacement wpNew;
2640 CoDrag (Widget sh, WindowPlacement *wp)
2643 int j=0, touch=0, fudge = 2;
2644 GetActualPlacement(sh, wp);
2645 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
2646 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
2647 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2648 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
2649 if(!touch ) return; // only windows that touch co-move
2650 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2651 int heightInc = wpNew.height - wpMain.height;
2652 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2653 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2654 wp->y += fracTop * heightInc;
2655 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2656 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2657 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2658 int widthInc = wpNew.width - wpMain.width;
2659 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2660 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2661 wp->y += fracLeft * widthInc;
2662 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2663 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2665 wp->x += wpNew.x - wpMain.x;
2666 wp->y += wpNew.y - wpMain.y;
2667 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2668 if(touch == 3) wp->y += wpNew.height - wpMain.height;
2669 XtSetArg(args[j], XtNx, wp->x); j++;
2670 XtSetArg(args[j], XtNy, wp->y); j++;
2671 XtSetValues(sh, args, j);
2674 static XtIntervalId delayedDragID = 0;
2679 GetActualPlacement(shellWidget, &wpNew);
2680 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
2681 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
2682 return; // false alarm
2683 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
2684 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
2685 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
2686 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
2688 DrawPosition(True, NULL);
2689 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
2696 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
2698 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
2702 EventProc (Widget widget, caddr_t unused, XEvent *event)
2704 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
2705 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
2708 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
2710 DrawSeekAxis (int x, int y, int xTo, int yTo)
2712 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
2716 DrawSeekBackground (int left, int top, int right, int bottom)
2718 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
2722 DrawSeekText (char *buf, int x, int y)
2724 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
2728 DrawSeekDot (int x, int y, int colorNr)
2730 int square = colorNr & 0x80;
2733 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
2735 XFillRectangle(xDisplay, xBoardWindow, color,
2736 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
2738 XFillArc(xDisplay, xBoardWindow, color,
2739 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
2745 XDrawSegments(xDisplay, xBoardWindow, lineGC,
2746 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
2751 * event handler for redrawing the board
2754 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2756 DrawPosition(True, NULL);
2761 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
2762 { // [HGM] pv: walk PV
2763 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
2766 static int savedIndex; /* gross that this is global */
2769 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
2772 XawTextPosition index, dummy;
2775 XawTextGetSelectionPos(w, &index, &dummy);
2776 XtSetArg(arg, XtNstring, &val);
2777 XtGetValues(w, &arg, 1);
2778 ReplaceComment(savedIndex, val);
2779 if(savedIndex != currentMove) ToNrEvent(savedIndex);
2780 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
2784 EditCommentPopUp (int index, char *title, char *text)
2787 if (text == NULL) text = "";
2788 NewCommentPopup(title, text, index);
2792 CommentPopUp (char *title, char *text)
2794 savedIndex = currentMove; // [HGM] vari
2795 NewCommentPopup(title, text, currentMove);
2801 PopDown(CommentDlg);
2804 static char *openName;
2810 (void) (*fileProc)(openFP, 0, openName);
2814 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
2816 fileProc = proc; /* I can't see a way not */
2817 fileOpenMode = openMode; /* to use globals here */
2818 { // [HGM] use file-selector dialog stolen from Ghostview
2819 // int index; // this is not supported yet
2820 Browse(BoardWindow, label, (def[0] ? def : NULL), filter, False, openMode, &openName, &openFP);
2825 /* Disable all user input other than deleting the window */
2826 static int frozen = 0;
2832 /* Grab by a widget that doesn't accept input */
2833 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
2837 /* Undo a FreezeUI */
2841 if (!frozen) return;
2842 XtRemoveGrab(optList[W_MESSG].handle);
2850 static int oldPausing = FALSE;
2851 static GameMode oldmode = (GameMode) -1;
2854 if (!boardWidget || !XtIsRealized(boardWidget)) return;
2856 if (pausing != oldPausing) {
2857 oldPausing = pausing;
2858 MarkMenuItem("Mode.Pause", pausing);
2860 if (appData.showButtonBar) {
2861 /* Always toggle, don't set. Previous code messes up when
2862 invoked while the button is pressed, as releasing it
2863 toggles the state again. */
2866 XtSetArg(args[0], XtNbackground, &oldbg);
2867 XtSetArg(args[1], XtNforeground, &oldfg);
2868 XtGetValues(optList[W_PAUSE].handle,
2870 XtSetArg(args[0], XtNbackground, oldfg);
2871 XtSetArg(args[1], XtNforeground, oldbg);
2873 XtSetValues(optList[W_PAUSE].handle, args, 2);
2877 wname = ModeToWidgetName(oldmode);
2878 if (wname != NULL) {
2879 MarkMenuItem(wname, False);
2881 wname = ModeToWidgetName(gameMode);
2882 if (wname != NULL) {
2883 MarkMenuItem(wname, True);
2886 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
2888 /* Maybe all the enables should be handled here, not just this one */
2889 EnableMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
2894 * Button/menu procedures
2897 /* this variable is shared between CopyPositionProc and SendPositionSelection */
2898 char *selected_fen_position=NULL;
2901 SendPositionSelection (Widget w, Atom *selection, Atom *target,
2902 Atom *type_return, XtPointer *value_return,
2903 unsigned long *length_return, int *format_return)
2905 char *selection_tmp;
2907 // if (!selected_fen_position) return False; /* should never happen */
2908 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
2909 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
2910 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
2913 if (f == NULL) return False;
2917 selection_tmp = XtMalloc(len + 1);
2918 count = fread(selection_tmp, 1, len, f);
2921 XtFree(selection_tmp);
2924 selection_tmp[len] = NULLCHAR;
2926 /* note: since no XtSelectionDoneProc was registered, Xt will
2927 * automatically call XtFree on the value returned. So have to
2928 * make a copy of it allocated with XtMalloc */
2929 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
2930 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
2933 *value_return=selection_tmp;
2934 *length_return=strlen(selection_tmp);
2935 *type_return=*target;
2936 *format_return = 8; /* bits per byte */
2938 } else if (*target == XA_TARGETS(xDisplay)) {
2939 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
2940 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
2941 targets_tmp[1] = XA_STRING;
2942 *value_return = targets_tmp;
2943 *type_return = XA_ATOM;
2946 // This code leads to a read of value_return out of bounds on 64-bit systems.
2947 // Other code which I have seen always sets *format_return to 32 independent of
2948 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
2949 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
2950 *format_return = 8 * sizeof(Atom);
2951 if (*format_return > 32) {
2952 *length_return *= *format_return / 32;
2953 *format_return = 32;
2956 *format_return = 32;
2964 /* note: when called from menu all parameters are NULL, so no clue what the
2965 * Widget which was clicked on was, or what the click event was
2968 CopySomething (char *src)
2970 selected_fen_position = src;
2972 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
2973 * have a notion of a position that is selected but not copied.
2974 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
2976 XtOwnSelection(menuBarWidget, XA_PRIMARY,
2978 SendPositionSelection,
2979 NULL/* lose_ownership_proc */ ,
2980 NULL/* transfer_done_proc */);
2981 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
2983 SendPositionSelection,
2984 NULL/* lose_ownership_proc */ ,
2985 NULL/* transfer_done_proc */);
2988 /* function called when the data to Paste is ready */
2990 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
2991 Atom *type, XtPointer value, unsigned long *len, int *format)
2994 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
2995 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
2996 EditPositionPasteFEN(fenstr);
3000 /* called when Paste Position button is pressed,
3001 * all parameters will be NULL */
3003 PastePositionProc ()
3005 XtGetSelectionValue(menuBarWidget,
3006 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3007 /* (XtSelectionCallbackProc) */ PastePositionCB,
3008 NULL, /* client_data passed to PastePositionCB */
3010 /* better to use the time field from the event that triggered the
3011 * call to this function, but that isn't trivial to get
3018 /* note: when called from menu all parameters are NULL, so no clue what the
3019 * Widget which was clicked on was, or what the click event was
3021 /* function called when the data to Paste is ready */
3023 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3024 Atom *type, XtPointer value, unsigned long *len, int *format)
3027 if (value == NULL || *len == 0) {
3028 return; /* nothing had been selected to copy */
3030 f = fopen(gamePasteFilename, "w");
3032 DisplayError(_("Can't open temp file"), errno);
3035 fwrite(value, 1, *len, f);
3038 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3041 /* called when Paste Game button is pressed,
3042 * all parameters will be NULL */
3046 XtGetSelectionValue(menuBarWidget,
3047 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3048 /* (XtSelectionCallbackProc) */ PasteGameCB,
3049 NULL, /* client_data passed to PasteGameCB */
3051 /* better to use the time field from the event that triggered the
3052 * call to this function, but that isn't trivial to get
3061 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3068 { // bassic primitive for determining if modifier keys are pressed
3069 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3072 XQueryKeymap(xDisplay,keys);
3073 for(i=0; i<6; i++) {
3075 j = XKeysymToKeycode(xDisplay, codes[i]);
3076 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3082 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3086 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3087 if ( n == 1 && *buf >= 32 // printable
3088 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3089 ) BoxAutoPopUp (buf);
3093 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3094 { // [HGM] input: let up-arrow recall previous line from history
3099 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3100 { // [HGM] input: let down-arrow recall next line from history
3105 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3111 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3113 if (!TempBackwardActive) {
3114 TempBackwardActive = True;
3120 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3122 /* Check to see if triggered by a key release event for a repeating key.
3123 * If so the next queued event will be a key press of the same key at the same time */
3124 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3126 XPeekEvent(xDisplay, &next);
3127 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3128 next.xkey.keycode == event->xkey.keycode)
3132 TempBackwardActive = False;
3136 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3137 { // called as key binding
3140 if (nprms && *nprms > 0)
3144 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3149 SetWindowTitle (char *text, char *title, char *icon)
3153 if (appData.titleInWindow) {
3155 XtSetArg(args[i], XtNlabel, text); i++;
3156 XtSetValues(titleWidget, args, i);
3159 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3160 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3161 XtSetValues(shellWidget, args, i);
3162 XSync(xDisplay, False);
3167 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3173 DisplayIcsInteractionTitle (String message)
3175 if (oldICSInteractionTitle == NULL) {
3176 /* Magic to find the old window title, adapted from vim */
3177 char *wina = getenv("WINDOWID");
3179 Window win = (Window) atoi(wina);
3180 Window root, parent, *children;
3181 unsigned int nchildren;
3182 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3184 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3185 if (!XQueryTree(xDisplay, win, &root, &parent,
3186 &children, &nchildren)) break;
3187 if (children) XFree((void *)children);
3188 if (parent == root || parent == 0) break;
3191 XSetErrorHandler(oldHandler);
3193 if (oldICSInteractionTitle == NULL) {
3194 oldICSInteractionTitle = "xterm";
3197 printf("\033]0;%s\007", message);
3202 XtIntervalId delayedEventTimerXID = 0;
3203 DelayedEventCallback delayedEventCallback = 0;
3208 delayedEventTimerXID = 0;
3209 delayedEventCallback();
3213 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3215 if(delayedEventTimerXID && delayedEventCallback == cb)
3216 // [HGM] alive: replace, rather than add or flush identical event
3217 XtRemoveTimeOut(delayedEventTimerXID);
3218 delayedEventCallback = cb;
3219 delayedEventTimerXID =
3220 XtAppAddTimeOut(appContext, millisec,
3221 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3224 DelayedEventCallback
3227 if (delayedEventTimerXID) {
3228 return delayedEventCallback;
3235 CancelDelayedEvent ()
3237 if (delayedEventTimerXID) {
3238 XtRemoveTimeOut(delayedEventTimerXID);
3239 delayedEventTimerXID = 0;
3243 XtIntervalId loadGameTimerXID = 0;
3246 LoadGameTimerRunning ()
3248 return loadGameTimerXID != 0;
3252 StopLoadGameTimer ()
3254 if (loadGameTimerXID != 0) {
3255 XtRemoveTimeOut(loadGameTimerXID);
3256 loadGameTimerXID = 0;
3264 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3266 loadGameTimerXID = 0;
3271 StartLoadGameTimer (long millisec)
3274 XtAppAddTimeOut(appContext, millisec,
3275 (XtTimerCallbackProc) LoadGameTimerCallback,
3279 XtIntervalId analysisClockXID = 0;
3282 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3284 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3285 || appData.icsEngineAnalyze) { // [DM]
3286 AnalysisPeriodicEvent(0);
3287 StartAnalysisClock();
3292 StartAnalysisClock ()
3295 XtAppAddTimeOut(appContext, 2000,
3296 (XtTimerCallbackProc) AnalysisClockCallback,
3300 XtIntervalId clockTimerXID = 0;
3303 ClockTimerRunning ()
3305 return clockTimerXID != 0;
3311 if (clockTimerXID != 0) {
3312 XtRemoveTimeOut(clockTimerXID);
3321 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3328 StartClockTimer (long millisec)
3331 XtAppAddTimeOut(appContext, millisec,
3332 (XtTimerCallbackProc) ClockTimerCallback,
3337 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
3341 Widget w = (Widget) opt->handle;
3343 /* check for low time warning */
3344 Pixel foregroundOrWarningColor = timerForegroundPixel;
3347 appData.lowTimeWarning &&
3348 (timer / 1000) < appData.icsAlarmTime)
3349 foregroundOrWarningColor = lowTimeWarningColor;
3351 if (appData.clockMode) {
3352 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
3353 XtSetArg(args[0], XtNlabel, buf);
3355 snprintf(buf, MSG_SIZ, "%s ", color);
3356 XtSetArg(args[0], XtNlabel, buf);
3361 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3362 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3364 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3365 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3368 XtSetValues(w, args, 3);
3371 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3374 SetClockIcon (int color)
3377 Pixmap pm = *clockIcons[color];
3378 if (iconPixmap != pm) {
3380 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3381 XtSetValues(shellWidget, args, 1);
3386 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3388 InputSource *is = (InputSource *) closure;
3393 if (is->lineByLine) {
3394 count = read(is->fd, is->unused,
3395 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3397 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3400 is->unused += count;
3402 while (p < is->unused) {
3403 q = memchr(p, '\n', is->unused - p);
3404 if (q == NULL) break;
3406 (is->func)(is, is->closure, p, q - p, 0);
3410 while (p < is->unused) {
3415 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3420 (is->func)(is, is->closure, is->buf, count, error);
3425 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3428 ChildProc *cp = (ChildProc *) pr;
3430 is = (InputSource *) calloc(1, sizeof(InputSource));
3431 is->lineByLine = lineByLine;
3435 is->fd = fileno(stdin);
3437 is->kind = cp->kind;
3438 is->fd = cp->fdFrom;
3441 is->unused = is->buf;
3444 is->xid = XtAppAddInput(appContext, is->fd,
3445 (XtPointer) (XtInputReadMask),
3446 (XtInputCallbackProc) DoInputCallback,
3448 is->closure = closure;
3449 return (InputSourceRef) is;
3453 RemoveInputSource (InputSourceRef isr)
3455 InputSource *is = (InputSource *) isr;
3457 if (is->xid == 0) return;
3458 XtRemoveInput(is->xid);
3462 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
3464 /* Masks for XPM pieces. Black and white pieces can have
3465 different shapes, but in the interest of retaining my
3466 sanity pieces must have the same outline on both light
3467 and dark squares, and all pieces must use the same
3468 background square colors/images. */
3470 static int xpmDone = 0;
3471 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
3472 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
3475 CreateAnimMasks (int pieceDepth)
3481 unsigned long plane;
3484 /* Need a bitmap just to get a GC with right depth */
3485 buf = XCreatePixmap(xDisplay, xBoardWindow,
3487 values.foreground = 1;
3488 values.background = 0;
3489 /* Don't use XtGetGC, not read only */
3490 maskGC = XCreateGC(xDisplay, buf,
3491 GCForeground | GCBackground, &values);
3492 XFreePixmap(xDisplay, buf);
3494 buf = XCreatePixmap(xDisplay, xBoardWindow,
3495 squareSize, squareSize, pieceDepth);
3496 values.foreground = XBlackPixel(xDisplay, xScreen);
3497 values.background = XWhitePixel(xDisplay, xScreen);
3498 bufGC = XCreateGC(xDisplay, buf,
3499 GCForeground | GCBackground, &values);
3501 for (piece = WhitePawn; piece <= BlackKing; piece++) {
3502 /* Begin with empty mask */
3503 if(!xpmDone) // [HGM] pieces: keep using existing
3504 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
3505 squareSize, squareSize, 1);
3506 XSetFunction(xDisplay, maskGC, GXclear);
3507 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
3508 0, 0, squareSize, squareSize);
3510 /* Take a copy of the piece */
3515 XSetFunction(xDisplay, bufGC, GXcopy);
3516 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
3518 0, 0, squareSize, squareSize, 0, 0);
3520 /* XOR the background (light) over the piece */
3521 XSetFunction(xDisplay, bufGC, GXxor);
3523 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
3524 0, 0, squareSize, squareSize, 0, 0);
3526 XSetForeground(xDisplay, bufGC, lightSquareColor);
3527 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
3530 /* We now have an inverted piece image with the background
3531 erased. Construct mask by just selecting all the non-zero
3532 pixels - no need to reconstruct the original image. */
3533 XSetFunction(xDisplay, maskGC, GXor);
3535 /* Might be quicker to download an XImage and create bitmap
3536 data from it rather than this N copies per piece, but it
3537 only takes a fraction of a second and there is a much
3538 longer delay for loading the pieces. */
3539 for (n = 0; n < pieceDepth; n ++) {
3540 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
3541 0, 0, squareSize, squareSize,
3547 XFreePixmap(xDisplay, buf);
3548 XFreeGC(xDisplay, bufGC);
3549 XFreeGC(xDisplay, maskGC);
3553 InitAnimState (AnimNr anr, XWindowAttributes *info)
3558 /* Each buffer is square size, same depth as window */
3559 animBufs[anr+4] = xBoardWindow;
3560 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
3561 squareSize, squareSize, info->depth);
3562 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
3563 squareSize, squareSize, info->depth);
3565 /* Create a plain GC for blitting */
3566 mask = GCForeground | GCBackground | GCFunction |
3567 GCPlaneMask | GCGraphicsExposures;
3568 values.foreground = XBlackPixel(xDisplay, xScreen);
3569 values.background = XWhitePixel(xDisplay, xScreen);
3570 values.function = GXcopy;
3571 values.plane_mask = AllPlanes;
3572 values.graphics_exposures = False;
3573 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
3575 /* Piece will be copied from an existing context at
3576 the start of each new animation/drag. */
3577 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
3579 /* Outline will be a read-only copy of an existing */
3580 animGCs[anr+4] = None;
3586 XWindowAttributes info;
3588 if (xpmDone && gameInfo.variant == oldVariant) return;
3589 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
3590 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
3592 InitAnimState(Game, &info);
3593 InitAnimState(Player, &info);
3595 /* For XPM pieces, we need bitmaps to use as masks. */
3597 CreateAnimMasks(info.depth), xpmDone = 1;
3602 static Boolean frameWaiting;
3605 FrameAlarm (int sig)
3607 frameWaiting = False;
3608 /* In case System-V style signals. Needed?? */
3609 signal(SIGALRM, FrameAlarm);
3613 FrameDelay (int time)
3615 struct itimerval delay;
3617 XSync(xDisplay, False);
3620 frameWaiting = True;
3621 signal(SIGALRM, FrameAlarm);
3622 delay.it_interval.tv_sec =
3623 delay.it_value.tv_sec = time / 1000;
3624 delay.it_interval.tv_usec =
3625 delay.it_value.tv_usec = (time % 1000) * 1000;
3626 setitimer(ITIMER_REAL, &delay, NULL);
3627 while (frameWaiting) pause();
3628 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
3629 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
3630 setitimer(ITIMER_REAL, &delay, NULL);
3637 FrameDelay (int time)
3639 XSync(xDisplay, False);
3641 usleep(time * 1000);
3647 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
3651 /* Bitmap for piece being moved. */
3652 if (appData.monoMode) {
3653 *mask = *pieceToSolid(piece);
3654 } else if (useImages) {
3656 *mask = xpmMask[piece];
3658 *mask = ximMaskPm[piece];
3661 *mask = *pieceToSolid(piece);
3664 /* GC for piece being moved. Square color doesn't matter, but
3665 since it gets modified we make a copy of the original. */
3667 if (appData.monoMode)
3672 if (appData.monoMode)
3677 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
3679 /* Outline only used in mono mode and is not modified */
3681 *outline = bwPieceGC;
3683 *outline = wbPieceGC;
3687 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
3692 /* Draw solid rectangle which will be clipped to shape of piece */
3693 XFillRectangle(xDisplay, dest, clip,
3694 0, 0, squareSize, squareSize);
3695 if (appData.monoMode)
3696 /* Also draw outline in contrasting color for black
3697 on black / white on white cases */
3698 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
3699 0, 0, squareSize, squareSize, 0, 0, 1);
3701 /* Copy the piece */
3706 if(appData.upsideDown && flipView) kind ^= 2;
3707 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3709 0, 0, squareSize, squareSize,
3715 InsertPiece (AnimNr anr, ChessSquare piece)
3717 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
3721 DrawBlank (AnimNr anr, int x, int y, int startColor)
3723 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
3726 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
3727 int srcX, int srcY, int width, int height, int