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 SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
260 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
261 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
262 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
263 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
264 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
265 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
266 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
267 Boolean TempBackwardActive = False;
268 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
269 void DisplayMove P((int moveNumber));
270 void ICSInitScript P((void));
271 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
272 void update_ics_width P(());
273 int CopyMemoProc P(());
276 * XBoard depends on Xt R4 or higher
278 int xtVersion = XtSpecificationRelease;
283 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
284 highlightSquareColor, premoveHighlightColor, dialogColor, buttonColor;
285 Pixel lowTimeWarningColor;
286 GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
287 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
289 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
290 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
291 Option *optList; // contains all widgets of main window
292 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
294 XFontSet fontSet, clockFontSet;
297 XFontStruct *clockFontStruct;
299 Font coordFontID, countFontID;
300 XFontStruct *coordFontStruct, *countFontStruct;
301 XtAppContext appContext;
304 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
306 Position commentX = -1, commentY = -1;
307 Dimension commentW, commentH;
308 typedef unsigned int BoardSize;
310 Boolean chessProgram;
312 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
313 int smallLayout = 0, tinyLayout = 0,
314 marginW, marginH, // [HGM] for run-time resizing
315 fromX = -1, fromY = -1, toX, toY, commentUp = False,
316 errorExitStatus = -1, defaultLineGap;
317 Dimension textHeight;
318 Pixel timerForegroundPixel, timerBackgroundPixel;
319 Pixel buttonForegroundPixel, buttonBackgroundPixel;
320 char *chessDir, *programName, *programVersion;
321 Boolean alwaysOnTop = False;
322 char *icsTextMenuString;
324 char *firstChessProgramNames;
325 char *secondChessProgramNames;
327 WindowPlacement wpMain;
328 WindowPlacement wpConsole;
329 WindowPlacement wpComment;
330 WindowPlacement wpMoveHistory;
331 WindowPlacement wpEvalGraph;
332 WindowPlacement wpEngineOutput;
333 WindowPlacement wpGameList;
334 WindowPlacement wpTags;
339 Pixmap pieceBitmap[2][(int)BlackPawn];
340 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
341 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
342 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
343 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
344 Pixmap xpmBoardBitmap[2];
345 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
346 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
347 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
348 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
349 XImage *ximLightSquare, *ximDarkSquare;
352 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
353 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
355 #define White(piece) ((int)(piece) < (int)BlackPawn)
357 /* Bitmaps for use as masks when drawing XPM pieces.
358 Need one for each black and white piece. */
359 static Pixmap xpmMask[BlackKing + 1];
361 /* This magic number is the number of intermediate frames used
362 in each half of the animation. For short moves it's reduced
363 by 1. The total number of frames will be factor * 2 + 1. */
366 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
373 DropMenuEnables dmEnables[] = {
390 XtResource clientResources[] = {
391 { "flashCount", "flashCount", XtRInt, sizeof(int),
392 XtOffset(AppDataPtr, flashCount), XtRImmediate,
393 (XtPointer) FLASH_COUNT },
396 XrmOptionDescRec shellOptions[] = {
397 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
398 { "-flash", "flashCount", XrmoptionNoArg, "3" },
399 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
402 XtActionsRec boardActions[] = {
403 { "DrawPosition", DrawPositionProc },
404 { "HandlePV", HandlePV },
405 { "SelectPV", SelectPV },
406 { "StopPV", StopPV },
407 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
408 { "QuitProc", QuitWrapper },
409 { "ManProc", ManInner },
410 { "TempBackwardProc", TempBackwardProc },
411 { "TempForwardProc", TempForwardProc },
412 { "CommentClick", (XtActionProc) CommentClick },
413 { "GenericPopDown", (XtActionProc) GenericPopDown },
414 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
415 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
416 { "SelectMove", (XtActionProc) SelectMove },
417 { "LoadSelectedProc", LoadSelectedProc },
418 { "SetFilterProc", SetFilterProc },
419 { "TypeInProc", TypeInProc },
420 { "EnterKeyProc", EnterKeyProc },
421 { "UpKeyProc", UpKeyProc },
422 { "DownKeyProc", DownKeyProc },
423 { "WheelProc", WheelProc },
424 { "TabProc", TabProc },
427 char globalTranslations[] =
428 ":<Key>F9: MenuItem(Actions.Resign) \n \
429 :Ctrl<Key>n: MenuItem(File.NewGame) \n \
430 :Meta<Key>V: MenuItem(File.NewVariant) \n \
431 :Ctrl<Key>o: MenuItem(File.LoadGame) \n \
432 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
433 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
434 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
435 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
436 :Ctrl<Key>s: MenuItem(File.SaveGame) \n \
437 :Ctrl<Key>c: MenuItem(Edit.CopyGame) \n \
438 :Ctrl<Key>v: MenuItem(Edit.PasteGame) \n \
439 :Ctrl<Key>O: MenuItem(File.LoadPosition) \n \
440 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
441 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
442 :Ctrl<Key>S: MenuItem(File.SavePosition) \n \
443 :Ctrl<Key>C: MenuItem(Edit.CopyPosition) \n \
444 :Ctrl<Key>V: MenuItem(Edit.PastePosition) \n \
445 :Ctrl<Key>q: MenuItem(File.Quit) \n \
446 :Ctrl<Key>w: MenuItem(Mode.MachineWhite) \n \
447 :Ctrl<Key>b: MenuItem(Mode.MachineBlack) \n \
448 :Ctrl<Key>t: MenuItem(Mode.TwoMachines) \n \
449 :Ctrl<Key>a: MenuItem(Mode.AnalysisMode) \n \
450 :Ctrl<Key>g: MenuItem(Mode.AnalyzeFile) \n \
451 :Ctrl<Key>e: MenuItem(Mode.EditGame) \n \
452 :Ctrl<Key>E: MenuItem(Mode.EditPosition) \n \
453 :Meta<Key>O: MenuItem(View.EngineOutput) \n \
454 :Meta<Key>E: MenuItem(View.EvaluationGraph) \n \
455 :Meta<Key>G: MenuItem(View.GameList) \n \
456 :Meta<Key>H: MenuItem(View.MoveHistory) \n \
457 :<Key>Pause: MenuItem(Mode.Pause) \n \
458 :<Key>F3: MenuItem(Action.Accept) \n \
459 :<Key>F4: MenuItem(Action.Decline) \n \
460 :<Key>F12: MenuItem(Action.Rematch) \n \
461 :<Key>F5: MenuItem(Action.CallFlag) \n \
462 :<Key>F6: MenuItem(Action.Draw) \n \
463 :<Key>F7: MenuItem(Action.Adjourn) \n \
464 :<Key>F8: MenuItem(Action.Abort) \n \
465 :<Key>F10: MenuItem(Action.StopObserving) \n \
466 :<Key>F11: MenuItem(Action.StopExamining) \n \
467 :Ctrl<Key>d: MenuItem(DebugProc) \n \
468 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
469 :Meta<Key>End: MenuItem(Edit.ForwardtoEnd) \n \
470 :Meta<Key>Right: MenuItem(Edit.Forward) \n \
471 :Meta<Key>Home: MenuItem(Edit.BacktoStart) \n \
472 :Meta<Key>Left: MenuItem(Edit.Backward) \n \
473 :<Key>Left: MenuItem(Edit.Backward) \n \
474 :<Key>Right: MenuItem(Edit.Forward) \n \
475 :<Key>Home: MenuItem(Edit.Revert) \n \
476 :<Key>End: MenuItem(Edit.TruncateGame) \n \
477 :Ctrl<Key>m: MenuItem(Engine.MoveNow) \n \
478 :Ctrl<Key>x: MenuItem(Engine.RetractMove) \n \
479 :Meta<Key>J: MenuItem(Options.Adjudications) \n \
480 :Meta<Key>U: MenuItem(Options.CommonEngine) \n \
481 :Meta<Key>T: MenuItem(Options.TimeControl) \n \
482 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
483 #ifndef OPTIONSDIALOG
485 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
486 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
487 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
488 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
489 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
492 :<Key>F1: MenuItem(Help.ManXBoard) \n \
493 :<Key>F2: MenuItem(View.FlipView) \n \
494 :<KeyDown>Return: TempBackwardProc() \n \
495 :<KeyUp>Return: TempForwardProc() \n";
497 char ICSInputTranslations[] =
498 "<Key>Up: UpKeyProc() \n "
499 "<Key>Down: DownKeyProc() \n "
500 "<Key>Return: EnterKeyProc() \n";
502 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
503 // as the widget is destroyed before the up-click can call extend-end
504 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
506 String xboardResources[] = {
507 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
512 /* Max possible square size */
513 #define MAXSQSIZE 256
515 static int xpm_avail[MAXSQSIZE];
517 #ifdef HAVE_DIR_STRUCT
519 /* Extract piece size from filename */
521 xpm_getsize (char *name, int len, char *ext)
529 if ((p=strchr(name, '.')) == NULL ||
530 StrCaseCmp(p+1, ext) != 0)
536 while (*p && isdigit(*p))
543 /* Setup xpm_avail */
545 xpm_getavail (char *dirname, char *ext)
551 for (i=0; i<MAXSQSIZE; ++i)
554 if (appData.debugMode)
555 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
557 dir = opendir(dirname);
560 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
561 programName, dirname);
565 while ((ent=readdir(dir)) != NULL) {
566 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
567 if (i > 0 && i < MAXSQSIZE)
577 xpm_print_avail (FILE *fp, char *ext)
581 fprintf(fp, _("Available `%s' sizes:\n"), ext);
582 for (i=1; i<MAXSQSIZE; ++i) {
588 /* Return XPM piecesize closest to size */
590 xpm_closest_to (char *dirname, int size, char *ext)
593 int sm_diff = MAXSQSIZE;
597 xpm_getavail(dirname, ext);
599 if (appData.debugMode)
600 xpm_print_avail(stderr, ext);
602 for (i=1; i<MAXSQSIZE; ++i) {
605 diff = (diff<0) ? -diff : diff;
606 if (diff < sm_diff) {
614 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
620 #else /* !HAVE_DIR_STRUCT */
621 /* If we are on a system without a DIR struct, we can't
622 read the directory, so we can't collect a list of
623 filenames, etc., so we can't do any size-fitting. */
625 xpm_closest_to (char *dirname, int size, char *ext)
628 Warning: No DIR structure found on this system --\n\
629 Unable to autosize for XPM/XIM pieces.\n\
630 Please report this error to %s.\n\
631 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
634 #endif /* HAVE_DIR_STRUCT */
637 /* Arrange to catch delete-window events */
638 Atom wm_delete_window;
640 CatchDeleteWindow (Widget w, String procname)
643 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
644 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
645 XtAugmentTranslations(w, XtParseTranslationTable(buf));
652 XtSetArg(args[0], XtNiconic, False);
653 XtSetValues(shellWidget, args, 1);
655 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
658 //---------------------------------------------------------------------------------------------------------
659 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
662 #define CW_USEDEFAULT (1<<31)
663 #define ICS_TEXT_MENU_SIZE 90
664 #define DEBUG_FILE "xboard.debug"
665 #define SetCurrentDirectory chdir
666 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
670 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
673 // front-end part of option handling
675 // [HGM] This platform-dependent table provides the location for storing the color info
676 extern char *crWhite, * crBlack;
680 &appData.whitePieceColor,
681 &appData.blackPieceColor,
682 &appData.lightSquareColor,
683 &appData.darkSquareColor,
684 &appData.highlightSquareColor,
685 &appData.premoveHighlightColor,
686 &appData.lowTimeWarningColor,
697 // [HGM] font: keep a font for each square size, even non-stndard ones
700 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
701 char *fontTable[NUM_FONTS][MAX_SIZE];
704 ParseFont (char *name, int number)
705 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
707 if(sscanf(name, "size%d:", &size)) {
708 // [HGM] font: font is meant for specific boardSize (likely from settings file);
709 // defer processing it until we know if it matches our board size
710 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
711 fontTable[number][size] = strdup(strchr(name, ':')+1);
712 fontValid[number][size] = True;
717 case 0: // CLOCK_FONT
718 appData.clockFont = strdup(name);
720 case 1: // MESSAGE_FONT
721 appData.font = strdup(name);
723 case 2: // COORD_FONT
724 appData.coordFont = strdup(name);
729 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
734 { // only 2 fonts currently
735 appData.clockFont = CLOCK_FONT_NAME;
736 appData.coordFont = COORD_FONT_NAME;
737 appData.font = DEFAULT_FONT_NAME;
742 { // no-op, until we identify the code for this already in XBoard and move it here
746 ParseColor (int n, char *name)
747 { // in XBoard, just copy the color-name string
748 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
752 ParseTextAttribs (ColorClass cc, char *s)
754 (&appData.colorShout)[cc] = strdup(s);
758 ParseBoardSize (void *addr, char *name)
760 appData.boardSize = strdup(name);
765 { // In XBoard the sound-playing program takes care of obtaining the actual sound
769 SetCommPortDefaults ()
770 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
773 // [HGM] args: these three cases taken out to stay in front-end
775 SaveFontArg (FILE *f, ArgDescriptor *ad)
778 int i, n = (int)(intptr_t)ad->argLoc;
780 case 0: // CLOCK_FONT
781 name = appData.clockFont;
783 case 1: // MESSAGE_FONT
786 case 2: // COORD_FONT
787 name = appData.coordFont;
792 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
793 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
794 fontTable[n][squareSize] = strdup(name);
795 fontValid[n][squareSize] = True;
798 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
799 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
804 { // nothing to do, as the sounds are at all times represented by their text-string names already
808 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
809 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
810 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
814 SaveColor (FILE *f, ArgDescriptor *ad)
815 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
816 if(colorVariable[(int)(intptr_t)ad->argLoc])
817 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
821 SaveBoardSize (FILE *f, char *name, void *addr)
822 { // wrapper to shield back-end from BoardSize & sizeInfo
823 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
827 ParseCommPortSettings (char *s)
828 { // no such option in XBoard (yet)
834 GetActualPlacement (Widget wg, WindowPlacement *wp)
836 XWindowAttributes winAt;
843 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
844 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
845 wp->x = rx - winAt.x;
846 wp->y = ry - winAt.y;
847 wp->height = winAt.height;
848 wp->width = winAt.width;
849 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
854 { // wrapper to shield use of window handles from back-end (make addressible by number?)
855 // In XBoard this will have to wait until awareness of window parameters is implemented
856 GetActualPlacement(shellWidget, &wpMain);
857 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
858 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
859 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
860 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
861 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
862 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
866 PrintCommPortSettings (FILE *f, char *name)
867 { // This option does not exist in XBoard
871 EnsureOnScreen (int *x, int *y, int minX, int minY)
878 { // [HGM] args: allows testing if main window is realized from back-end
879 return xBoardWindow != 0;
885 extern Option dualOptions[];
887 Window tmp = xBoardWindow;
888 if(!dual) dual = XtWindow(dualOptions[3].handle); // must be first call
889 xBoardWindow = dual; // swap them
894 PopUpStartupDialog ()
895 { // start menu not implemented in XBoard
899 ConvertToLine (int argc, char **argv)
901 static char line[128*1024], buf[1024];
905 for(i=1; i<argc; i++)
907 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
908 && argv[i][0] != '{' )
909 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
911 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
912 strncat(line, buf, 128*1024 - strlen(line) - 1 );
915 line[strlen(line)-1] = NULLCHAR;
919 //--------------------------------------------------------------------------------------------
921 #define BoardSize int
923 InitDrawingSizes (BoardSize boardSize, int flags)
924 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
925 Dimension boardWidth, boardHeight, w, h;
927 static Dimension oldWidth, oldHeight;
928 static VariantClass oldVariant;
929 static int oldMono = -1, oldTwoBoards = 0;
931 if(!formWidget) return;
933 if(oldTwoBoards && !twoBoards) PopDown(DummyDlg);
934 oldTwoBoards = twoBoards;
936 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
937 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
938 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
940 if(boardWidth != oldWidth || boardHeight != oldHeight) { // do resizing stuff only if size actually changed
942 oldWidth = boardWidth; oldHeight = boardHeight;
946 * Inhibit shell resizing.
948 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW ;
949 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
950 shellArgs[4].value = shellArgs[2].value = w;
951 shellArgs[5].value = shellArgs[3].value = h;
952 XtSetValues(shellWidget, &shellArgs[0], 6);
954 XSync(xDisplay, False);
958 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
961 if(gameInfo.variant != oldVariant) { // and only if variant changed
966 for(p=0; p<=(int)WhiteKing; p++)
967 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
968 if(gameInfo.variant == VariantShogi) {
969 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
970 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
971 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
972 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
973 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
976 if(gameInfo.variant == VariantGothic) {
977 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
980 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
981 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
982 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
985 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
986 for(p=0; p<=(int)WhiteKing; p++)
987 ximMaskPm[p] = ximMaskPm2[p]; // defaults
988 if(gameInfo.variant == VariantShogi) {
989 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
990 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
991 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
992 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
993 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
996 if(gameInfo.variant == VariantGothic) {
997 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1000 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1001 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1002 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1007 for(i=0; i<2; i++) {
1009 for(p=0; p<=(int)WhiteKing; p++)
1010 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1011 if(gameInfo.variant == VariantShogi) {
1012 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1013 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1014 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1015 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1016 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1019 if(gameInfo.variant == VariantGothic) {
1020 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1023 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1024 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1025 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1029 oldMono = -10; // kludge to force recreation of animation masks
1030 oldVariant = gameInfo.variant;
1033 if(appData.monoMode != oldMono)
1036 oldMono = appData.monoMode;
1040 MakeOneColor (char *name, Pixel *color)
1042 XrmValue vFrom, vTo;
1043 if (!appData.monoMode) {
1044 vFrom.addr = (caddr_t) name;
1045 vFrom.size = strlen(name);
1046 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1047 if (vTo.addr == NULL) {
1048 appData.monoMode = True;
1051 *color = *(Pixel *) vTo.addr;
1059 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1060 int forceMono = False;
1062 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1063 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1064 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1065 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1066 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1067 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1068 if (appData.lowTimeWarning)
1069 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
1070 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1071 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1078 { // [HGM] taken out of main
1080 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1081 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1082 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1084 if (appData.bitmapDirectory[0] != NULLCHAR) {
1088 CreateXPMBoard(appData.liteBackTextureFile, 1);
1089 CreateXPMBoard(appData.darkBackTextureFile, 0);
1093 /* Create regular pieces */
1094 if (!useImages) CreatePieces();
1099 InitDrawingParams ()
1101 MakeColors(); CreateGCs(True);
1106 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
1107 { // detervtomine what fonts to use, and create them
1111 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1112 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1113 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1114 appData.font = fontTable[MESSAGE_FONT][squareSize];
1115 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1116 appData.coordFont = fontTable[COORD_FONT][squareSize];
1119 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1120 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1121 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1122 fontSet = CreateFontSet(appData.font);
1123 clockFontSet = CreateFontSet(appData.clockFont);
1125 /* For the coordFont, use the 0th font of the fontset. */
1126 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1127 XFontStruct **font_struct_list;
1128 XFontSetExtents *fontSize;
1129 char **font_name_list;
1130 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1131 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1132 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1133 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1134 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1137 appData.font = FindFont(appData.font, fontPxlSize);
1138 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1139 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1140 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1141 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1142 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1143 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1144 // textHeight in !NLS mode!
1146 countFontID = coordFontID; // [HGM] holdings
1147 countFontStruct = coordFontStruct;
1149 xdb = XtDatabase(xDisplay);
1151 XrmPutLineResource(&xdb, "*international: True");
1152 vTo.size = sizeof(XFontSet);
1153 vTo.addr = (XtPointer) &fontSet;
1154 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1156 XrmPutStringResource(&xdb, "*font", appData.font);
1161 main (int argc, char **argv)
1163 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1164 XSetWindowAttributes window_attributes;
1166 Dimension boardWidth, boardHeight, w, h;
1168 int forceMono = False;
1170 srandom(time(0)); // [HGM] book: make random truly random
1172 setbuf(stdout, NULL);
1173 setbuf(stderr, NULL);
1176 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1177 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1181 programName = strrchr(argv[0], '/');
1182 if (programName == NULL)
1183 programName = argv[0];
1188 XtSetLanguageProc(NULL, NULL, NULL);
1189 if (appData.debugMode) {
1190 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1193 bindtextdomain(PACKAGE, LOCALEDIR);
1194 textdomain(PACKAGE);
1197 appData.boardSize = "";
1198 InitAppData(ConvertToLine(argc, argv));
1200 if (p == NULL) p = "/tmp";
1201 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1202 gameCopyFilename = (char*) malloc(i);
1203 gamePasteFilename = (char*) malloc(i);
1204 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1205 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1207 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1208 static char buf[MSG_SIZ];
1209 EscapeExpand(buf, appData.firstInitString);
1210 appData.firstInitString = strdup(buf);
1211 EscapeExpand(buf, appData.secondInitString);
1212 appData.secondInitString = strdup(buf);
1213 EscapeExpand(buf, appData.firstComputerString);
1214 appData.firstComputerString = strdup(buf);
1215 EscapeExpand(buf, appData.secondComputerString);
1216 appData.secondComputerString = strdup(buf);
1219 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1222 if (chdir(chessDir) != 0) {
1223 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1229 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1230 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1231 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1232 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1235 setbuf(debugFP, NULL);
1238 /* [HGM,HR] make sure board size is acceptable */
1239 if(appData.NrFiles > BOARD_FILES ||
1240 appData.NrRanks > BOARD_RANKS )
1241 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1244 /* This feature does not work; animation needs a rewrite */
1245 appData.highlightDragging = FALSE;
1249 gameInfo.variant = StringToVariant(appData.variant);
1250 InitPosition(FALSE);
1253 XtAppInitialize(&appContext, "XBoard", shellOptions,
1254 XtNumber(shellOptions),
1255 &argc, argv, xboardResources, NULL, 0);
1257 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1258 clientResources, XtNumber(clientResources),
1261 xDisplay = XtDisplay(shellWidget);
1262 xScreen = DefaultScreen(xDisplay);
1263 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1266 * determine size, based on supplied or remembered -size, or screen size
1268 if (isdigit(appData.boardSize[0])) {
1269 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1270 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1271 &fontPxlSize, &smallLayout, &tinyLayout);
1273 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1274 programName, appData.boardSize);
1278 /* Find some defaults; use the nearest known size */
1279 SizeDefaults *szd, *nearest;
1280 int distance = 99999;
1281 nearest = szd = sizeDefaults;
1282 while (szd->name != NULL) {
1283 if (abs(szd->squareSize - squareSize) < distance) {
1285 distance = abs(szd->squareSize - squareSize);
1286 if (distance == 0) break;
1290 if (i < 2) lineGap = nearest->lineGap;
1291 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1292 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1293 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1294 if (i < 6) smallLayout = nearest->smallLayout;
1295 if (i < 7) tinyLayout = nearest->tinyLayout;
1298 SizeDefaults *szd = sizeDefaults;
1299 if (*appData.boardSize == NULLCHAR) {
1300 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1301 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1304 if (szd->name == NULL) szd--;
1305 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1307 while (szd->name != NULL &&
1308 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1309 if (szd->name == NULL) {
1310 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1311 programName, appData.boardSize);
1315 squareSize = szd->squareSize;
1316 lineGap = szd->lineGap;
1317 clockFontPxlSize = szd->clockFontPxlSize;
1318 coordFontPxlSize = szd->coordFontPxlSize;
1319 fontPxlSize = szd->fontPxlSize;
1320 smallLayout = szd->smallLayout;
1321 tinyLayout = szd->tinyLayout;
1322 // [HGM] font: use defaults from settings file if available and not overruled
1325 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1326 if (strlen(appData.pixmapDirectory) > 0) {
1327 p = ExpandPathName(appData.pixmapDirectory);
1329 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1330 appData.pixmapDirectory);
1333 if (appData.debugMode) {
1334 fprintf(stderr, _("\
1335 XBoard square size (hint): %d\n\
1336 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1338 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1339 if (appData.debugMode) {
1340 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1343 defaultLineGap = lineGap;
1344 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1346 /* [HR] height treated separately (hacked) */
1347 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1348 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1351 * Determine what fonts to use.
1353 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1356 * Detect if there are not enough colors available and adapt.
1358 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1359 appData.monoMode = True;
1362 forceMono = MakeColors();
1365 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1367 appData.monoMode = True;
1370 if (appData.monoMode && appData.debugMode) {
1371 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1372 (unsigned long) XWhitePixel(xDisplay, xScreen),
1373 (unsigned long) XBlackPixel(xDisplay, xScreen));
1376 ParseIcsTextColors();
1378 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1384 layoutName = "tinyLayout";
1385 } else if (smallLayout) {
1386 layoutName = "smallLayout";
1388 layoutName = "normalLayout";
1391 optList = BoardPopUp(squareSize, lineGap, (void*)
1397 boardWidget = optList[W_BOARD].handle;
1398 menuBarWidget = optList[W_MENU].handle;
1399 dropMenu = optList[W_DROP].handle;
1400 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1401 formWidget = XtParent(boardWidget);
1402 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1403 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1404 XtGetValues(optList[W_WHITE].handle, args, 2);
1405 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1406 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1407 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1408 XtGetValues(optList[W_PAUSE].handle, args, 2);
1410 AppendEnginesToMenu(appData.recentEngineList);
1412 xBoardWindow = XtWindow(boardWidget);
1414 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1415 // not need to go into InitDrawingSizes().
1418 * Create X checkmark bitmap and initialize option menu checks.
1420 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1421 checkmark_bits, checkmark_width, checkmark_height);
1427 ReadBitmap(&wIconPixmap, "icon_white.bm",
1428 icon_white_bits, icon_white_width, icon_white_height);
1429 ReadBitmap(&bIconPixmap, "icon_black.bm",
1430 icon_black_bits, icon_black_width, icon_black_height);
1431 iconPixmap = wIconPixmap;
1433 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1434 XtSetValues(shellWidget, args, i);
1437 * Create a cursor for the board widget.
1439 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1440 XChangeWindowAttributes(xDisplay, xBoardWindow,
1441 CWCursor, &window_attributes);
1444 * Inhibit shell resizing.
1446 shellArgs[0].value = (XtArgVal) &w;
1447 shellArgs[1].value = (XtArgVal) &h;
1448 XtGetValues(shellWidget, shellArgs, 2);
1449 shellArgs[4].value = shellArgs[2].value = w;
1450 shellArgs[5].value = shellArgs[3].value = h;
1451 XtSetValues(shellWidget, &shellArgs[2], 4);
1452 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1453 marginH = h - boardHeight;
1455 CatchDeleteWindow(shellWidget, "QuitProc");
1461 if (appData.animate || appData.animateDragging)
1464 XtAugmentTranslations(formWidget,
1465 XtParseTranslationTable(globalTranslations));
1467 XtAddEventHandler(formWidget, KeyPressMask, False,
1468 (XtEventHandler) MoveTypeInProc, NULL);
1469 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1470 (XtEventHandler) EventProc, NULL);
1472 /* [AS] Restore layout */
1473 if( wpMoveHistory.visible ) {
1477 if( wpEvalGraph.visible )
1482 if( wpEngineOutput.visible ) {
1483 EngineOutputPopUp();
1488 if (errorExitStatus == -1) {
1489 if (appData.icsActive) {
1490 /* We now wait until we see "login:" from the ICS before
1491 sending the logon script (problems with timestamp otherwise) */
1492 /*ICSInitScript();*/
1493 if (appData.icsInputBox) ICSInputBoxPopUp();
1497 signal(SIGWINCH, TermSizeSigHandler);
1499 signal(SIGINT, IntSigHandler);
1500 signal(SIGTERM, IntSigHandler);
1501 if (*appData.cmailGameName != NULLCHAR) {
1502 signal(SIGUSR1, CmailSigHandler);
1506 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1508 // XtSetKeyboardFocus(shellWidget, formWidget);
1509 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1511 XtAppMainLoop(appContext);
1512 if (appData.debugMode) fclose(debugFP); // [DM] debug
1517 TermSizeSigHandler (int sig)
1523 IntSigHandler (int sig)
1529 CmailSigHandler (int sig)
1534 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1536 /* Activate call-back function CmailSigHandlerCallBack() */
1537 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1539 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1543 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1546 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1548 /**** end signal code ****/
1551 #define Abs(n) ((n)<0 ? -(n) : (n))
1555 InsertPxlSize (char *pattern, int targetPxlSize)
1557 char *base_fnt_lst, strInt[12], *p, *q;
1558 int alternatives, i, len, strIntLen;
1561 * Replace the "*" (if present) in the pixel-size slot of each
1562 * alternative with the targetPxlSize.
1566 while ((p = strchr(p, ',')) != NULL) {
1570 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1571 strIntLen = strlen(strInt);
1572 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1576 while (alternatives--) {
1577 char *comma = strchr(p, ',');
1578 for (i=0; i<14; i++) {
1579 char *hyphen = strchr(p, '-');
1581 if (comma && hyphen > comma) break;
1582 len = hyphen + 1 - p;
1583 if (i == 7 && *p == '*' && len == 2) {
1585 memcpy(q, strInt, strIntLen);
1595 len = comma + 1 - p;
1602 return base_fnt_lst;
1606 CreateFontSet (char *base_fnt_lst)
1609 char **missing_list;
1613 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1614 &missing_list, &missing_count, &def_string);
1615 if (appData.debugMode) {
1617 XFontStruct **font_struct_list;
1618 char **font_name_list;
1619 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1621 fprintf(debugFP, " got list %s, locale %s\n",
1622 XBaseFontNameListOfFontSet(fntSet),
1623 XLocaleOfFontSet(fntSet));
1624 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1625 for (i = 0; i < count; i++) {
1626 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1629 for (i = 0; i < missing_count; i++) {
1630 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1633 if (fntSet == NULL) {
1634 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1639 #else // not ENABLE_NLS
1641 * Find a font that matches "pattern" that is as close as
1642 * possible to the targetPxlSize. Prefer fonts that are k
1643 * pixels smaller to fonts that are k pixels larger. The
1644 * pattern must be in the X Consortium standard format,
1645 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1646 * The return value should be freed with XtFree when no
1650 FindFont (char *pattern, int targetPxlSize)
1652 char **fonts, *p, *best, *scalable, *scalableTail;
1653 int i, j, nfonts, minerr, err, pxlSize;
1655 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1657 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1658 programName, pattern);
1665 for (i=0; i<nfonts; i++) {
1668 if (*p != '-') continue;
1670 if (*p == NULLCHAR) break;
1671 if (*p++ == '-') j++;
1673 if (j < 7) continue;
1676 scalable = fonts[i];
1679 err = pxlSize - targetPxlSize;
1680 if (Abs(err) < Abs(minerr) ||
1681 (minerr > 0 && err < 0 && -err == minerr)) {
1687 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1688 /* If the error is too big and there is a scalable font,
1689 use the scalable font. */
1690 int headlen = scalableTail - scalable;
1691 p = (char *) XtMalloc(strlen(scalable) + 10);
1692 while (isdigit(*scalableTail)) scalableTail++;
1693 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1695 p = (char *) XtMalloc(strlen(best) + 2);
1696 safeStrCpy(p, best, strlen(best)+1 );
1698 if (appData.debugMode) {
1699 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1700 pattern, targetPxlSize, p);
1702 XFreeFontNames(fonts);
1709 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
1710 // must be called before all non-first callse to CreateGCs()
1711 XtReleaseGC(shellWidget, highlineGC);
1712 XtReleaseGC(shellWidget, lightSquareGC);
1713 XtReleaseGC(shellWidget, darkSquareGC);
1714 XtReleaseGC(shellWidget, lineGC);
1715 if (appData.monoMode) {
1716 if (DefaultDepth(xDisplay, xScreen) == 1) {
1717 XtReleaseGC(shellWidget, wbPieceGC);
1719 XtReleaseGC(shellWidget, bwPieceGC);
1722 XtReleaseGC(shellWidget, prelineGC);
1723 XtReleaseGC(shellWidget, wdPieceGC);
1724 XtReleaseGC(shellWidget, wlPieceGC);
1725 XtReleaseGC(shellWidget, bdPieceGC);
1726 XtReleaseGC(shellWidget, blPieceGC);
1731 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
1733 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1734 | GCBackground | GCFunction | GCPlaneMask;
1735 gc_values->foreground = foreground;
1736 gc_values->background = background;
1737 return XtGetGC(shellWidget, value_mask, gc_values);
1741 CreateGCs (int redo)
1743 XGCValues gc_values;
1745 Pixel white = XWhitePixel(xDisplay, xScreen);
1746 Pixel black = XBlackPixel(xDisplay, xScreen);
1748 gc_values.plane_mask = AllPlanes;
1749 gc_values.line_width = lineGap;
1750 gc_values.line_style = LineSolid;
1751 gc_values.function = GXcopy;
1754 DeleteGCs(); // called a second time; clean up old GCs first
1755 } else { // [HGM] grid and font GCs created on first call only
1756 coordGC = CreateOneGC(&gc_values, black, white);
1757 XSetFont(xDisplay, coordGC, coordFontID);
1759 // [HGM] make font for holdings counts (white on black)
1760 countGC = CreateOneGC(&gc_values, white, black);
1761 XSetFont(xDisplay, countGC, countFontID);
1763 lineGC = CreateOneGC(&gc_values, black, black);
1765 if (appData.monoMode) {
1767 highlineGC = CreateOneGC(&gc_values, white, white);
1768 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
1769 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
1771 if (DefaultDepth(xDisplay, xScreen) == 1) {
1772 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
1773 gc_values.function = GXcopyInverted;
1774 copyInvertedGC = CreateOneGC(&gc_values, black, white);
1775 gc_values.function = GXcopy;
1776 if (XBlackPixel(xDisplay, xScreen) == 1) {
1777 bwPieceGC = darkSquareGC;
1778 wbPieceGC = copyInvertedGC;
1780 bwPieceGC = copyInvertedGC;
1781 wbPieceGC = lightSquareGC;
1786 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
1787 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
1788 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
1789 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
1790 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
1791 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
1792 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
1793 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
1798 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
1806 fp = fopen(filename, "rb");
1808 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
1815 for (y=0; y<h; ++y) {
1816 for (x=0; x<h; ++x) {
1821 XPutPixel(xim, x, y, blackPieceColor);
1823 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1826 XPutPixel(xim, x, y, darkSquareColor);
1828 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1831 XPutPixel(xim, x, y, whitePieceColor);
1833 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1836 XPutPixel(xim, x, y, lightSquareColor);
1838 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1846 /* create Pixmap of piece */
1847 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1849 XPutImage(xDisplay, *dest, lightSquareGC, xim,
1852 /* create Pixmap of clipmask
1853 Note: We assume the white/black pieces have the same
1854 outline, so we make only 6 masks. This is okay
1855 since the XPM clipmask routines do the same. */
1857 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1859 XPutImage(xDisplay, temp, lightSquareGC, xmask,
1862 /* now create the 1-bit version */
1863 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1866 values.foreground = 1;
1867 values.background = 0;
1869 /* Don't use XtGetGC, not read only */
1870 maskGC = XCreateGC(xDisplay, *mask,
1871 GCForeground | GCBackground, &values);
1872 XCopyPlane(xDisplay, temp, *mask, maskGC,
1873 0, 0, squareSize, squareSize, 0, 0, 1);
1874 XFreePixmap(xDisplay, temp);
1879 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
1887 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
1892 /* The XSynchronize calls were copied from CreatePieces.
1893 Not sure if needed, but can't hurt */
1894 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
1897 /* temp needed by loadXIM() */
1898 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1899 0, 0, ss, ss, AllPlanes, XYPixmap);
1901 if (strlen(appData.pixmapDirectory) == 0) {
1905 if (appData.monoMode) {
1906 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
1910 fprintf(stderr, _("\nLoading XIMs...\n"));
1912 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
1913 fprintf(stderr, "%d", piece+1);
1914 for (kind=0; kind<4; kind++) {
1915 fprintf(stderr, ".");
1916 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
1917 ExpandPathName(appData.pixmapDirectory),
1918 piece <= (int) WhiteKing ? "" : "w",
1919 pieceBitmapNames[piece],
1921 ximPieceBitmap[kind][piece] =
1922 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1923 0, 0, ss, ss, AllPlanes, XYPixmap);
1924 if (appData.debugMode)
1925 fprintf(stderr, _("(File:%s:) "), buf);
1926 loadXIM(ximPieceBitmap[kind][piece],
1928 &(xpmPieceBitmap2[kind][piece]),
1929 &(ximMaskPm2[piece]));
1930 if(piece <= (int)WhiteKing)
1931 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
1933 fprintf(stderr," ");
1935 /* Load light and dark squares */
1936 /* If the LSQ and DSQ pieces don't exist, we will
1937 draw them with solid squares. */
1938 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
1939 if (access(buf, 0) != 0) {
1943 fprintf(stderr, _("light square "));
1945 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1946 0, 0, ss, ss, AllPlanes, XYPixmap);
1947 if (appData.debugMode)
1948 fprintf(stderr, _("(File:%s:) "), buf);
1950 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
1951 fprintf(stderr, _("dark square "));
1952 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
1953 ExpandPathName(appData.pixmapDirectory), ss);
1954 if (appData.debugMode)
1955 fprintf(stderr, _("(File:%s:) "), buf);
1957 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1958 0, 0, ss, ss, AllPlanes, XYPixmap);
1959 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
1960 xpmJailSquare = xpmLightSquare;
1962 fprintf(stderr, _("Done.\n"));
1964 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
1967 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
1971 CreateXPMBoard (char *s, int kind)
1975 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
1976 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
1977 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
1983 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
1984 // thisroutine has to be called t free the old piece pixmaps
1986 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
1987 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
1989 XFreePixmap(xDisplay, xpmLightSquare);
1990 XFreePixmap(xDisplay, xpmDarkSquare);
1999 u_int ss = squareSize;
2001 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2002 XpmColorSymbol symbols[4];
2003 static int redo = False;
2005 if(redo) FreeXPMPieces(); else redo = 1;
2007 /* The XSynchronize calls were copied from CreatePieces.
2008 Not sure if needed, but can't hurt */
2009 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2011 /* Setup translations so piece colors match square colors */
2012 symbols[0].name = "light_piece";
2013 symbols[0].value = appData.whitePieceColor;
2014 symbols[1].name = "dark_piece";
2015 symbols[1].value = appData.blackPieceColor;
2016 symbols[2].name = "light_square";
2017 symbols[2].value = appData.lightSquareColor;
2018 symbols[3].name = "dark_square";
2019 symbols[3].value = appData.darkSquareColor;
2021 attr.valuemask = XpmColorSymbols;
2022 attr.colorsymbols = symbols;
2023 attr.numsymbols = 4;
2025 if (appData.monoMode) {
2026 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2030 if (strlen(appData.pixmapDirectory) == 0) {
2031 XpmPieces* pieces = builtInXpms;
2034 while (pieces->size != squareSize && pieces->size) pieces++;
2035 if (!pieces->size) {
2036 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2039 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2040 for (kind=0; kind<4; kind++) {
2042 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2043 pieces->xpm[piece][kind],
2044 &(xpmPieceBitmap2[kind][piece]),
2045 NULL, &attr)) != 0) {
2046 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2050 if(piece <= (int) WhiteKing)
2051 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2055 xpmJailSquare = xpmLightSquare;
2059 fprintf(stderr, _("\nLoading XPMs...\n"));
2062 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2063 fprintf(stderr, "%d ", piece+1);
2064 for (kind=0; kind<4; kind++) {
2065 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2066 ExpandPathName(appData.pixmapDirectory),
2067 piece > (int) WhiteKing ? "w" : "",
2068 pieceBitmapNames[piece],
2070 if (appData.debugMode) {
2071 fprintf(stderr, _("(File:%s:) "), buf);
2073 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2074 &(xpmPieceBitmap2[kind][piece]),
2075 NULL, &attr)) != 0) {
2076 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2077 // [HGM] missing: read of unorthodox piece failed; substitute King.
2078 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2079 ExpandPathName(appData.pixmapDirectory),
2081 if (appData.debugMode) {
2082 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2084 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2085 &(xpmPieceBitmap2[kind][piece]),
2089 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2094 if(piece <= (int) WhiteKing)
2095 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2098 /* Load light and dark squares */
2099 /* If the LSQ and DSQ pieces don't exist, we will
2100 draw them with solid squares. */
2101 fprintf(stderr, _("light square "));
2102 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2103 if (access(buf, 0) != 0) {
2107 if (appData.debugMode)
2108 fprintf(stderr, _("(File:%s:) "), buf);
2110 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2111 &xpmLightSquare, NULL, &attr)) != 0) {
2112 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2115 fprintf(stderr, _("dark square "));
2116 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2117 ExpandPathName(appData.pixmapDirectory), ss);
2118 if (appData.debugMode) {
2119 fprintf(stderr, _("(File:%s:) "), buf);
2121 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2122 &xpmDarkSquare, NULL, &attr)) != 0) {
2123 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2127 xpmJailSquare = xpmLightSquare;
2128 fprintf(stderr, _("Done.\n"));
2130 oldVariant = -1; // kludge to force re-makig of animation masks
2131 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2134 #endif /* HAVE_LIBXPM */
2137 /* No built-in bitmaps */
2142 u_int ss = squareSize;
2144 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2147 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2148 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2149 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2150 pieceBitmapNames[piece],
2151 ss, kind == SOLID ? 's' : 'o');
2152 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2153 if(piece <= (int)WhiteKing)
2154 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2158 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2162 /* With built-in bitmaps */
2166 BuiltInBits* bib = builtInBits;
2169 u_int ss = squareSize;
2171 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2174 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2176 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2177 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2178 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2179 pieceBitmapNames[piece],
2180 ss, kind == SOLID ? 's' : 'o');
2181 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2182 bib->bits[kind][piece], ss, ss);
2183 if(piece <= (int)WhiteKing)
2184 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2188 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2194 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2199 char msg[MSG_SIZ], fullname[MSG_SIZ];
2201 if (*appData.bitmapDirectory != NULLCHAR) {
2202 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2203 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2204 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2205 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2206 &w, &h, pm, &x_hot, &y_hot);
2207 fprintf(stderr, "load %s\n", name);
2208 if (errcode != BitmapSuccess) {
2210 case BitmapOpenFailed:
2211 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2213 case BitmapFileInvalid:
2214 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2216 case BitmapNoMemory:
2217 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2221 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2225 fprintf(stderr, _("%s: %s...using built-in\n"),
2227 } else if (w != wreq || h != hreq) {
2229 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2230 programName, fullname, w, h, wreq, hreq);
2236 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2246 if (lineGap == 0) return;
2248 /* [HR] Split this into 2 loops for non-square boards. */
2250 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2251 gridSegments[i].x1 = 0;
2252 gridSegments[i].x2 =
2253 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2254 gridSegments[i].y1 = gridSegments[i].y2
2255 = lineGap / 2 + (i * (squareSize + lineGap));
2258 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2259 gridSegments[j + i].y1 = 0;
2260 gridSegments[j + i].y2 =
2261 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2262 gridSegments[j + i].x1 = gridSegments[j + i].x2
2263 = lineGap / 2 + (j * (squareSize + lineGap));
2268 MarkMenuItem (char *menuRef, int state)
2270 MenuItem *item = MenuNameToItem(menuRef);
2274 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2275 XtSetValues(item->handle, args, 1);
2280 EnableMenuItem (char *menuRef, int state)
2282 MenuItem *item = MenuNameToItem(menuRef);
2284 if(item) XtSetSensitive(item->handle, state);
2288 EnableButtonBar (int state)
2290 XtSetSensitive(optList[W_BUTTON].handle, state);
2295 SetMenuEnables (Enables *enab)
2297 while (enab->name != NULL) {
2298 EnableMenuItem(enab->name, enab->value);
2304 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2305 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2307 if(*nprms == 0) return;
2308 item = MenuNameToItem(prms[0]);
2309 if(item) ((MenuProc *) item->proc) ();
2313 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2315 RecentEngineEvent((int) (intptr_t) addr);
2319 AppendMenuItem (char *msg, int n)
2321 CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
2333 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2334 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2335 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2336 dmEnables[i].piece);
2337 XtSetSensitive(entry, p != NULL || !appData.testLegality
2338 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2339 && !appData.icsActive));
2341 while (p && *p++ == dmEnables[i].piece) count++;
2342 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2344 XtSetArg(args[j], XtNlabel, label); j++;
2345 XtSetValues(entry, args, j);
2351 do_flash_delay (unsigned long msec)
2357 DrawBorder (int x, int y, int type)
2361 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
2363 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
2364 squareSize+lineGap, squareSize+lineGap);
2368 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
2370 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
2371 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
2373 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
2374 if(textureW[kind] < W*squareSize)
2375 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
2377 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
2378 if(textureH[kind] < H*squareSize)
2379 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
2381 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
2386 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
2387 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
2389 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
2390 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
2391 squareSize, squareSize, x*fac, y*fac);
2393 if (useImages && useImageSqs) {
2397 pm = xpmLightSquare;
2402 case 2: /* neutral */
2404 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
2407 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
2408 squareSize, squareSize, x*fac, y*fac);
2418 case 2: /* neutral */
2423 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
2428 I split out the routines to draw a piece so that I could
2429 make a generic flash routine.
2432 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2434 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2435 switch (square_color) {
2437 case 2: /* neutral */
2439 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2440 ? *pieceToOutline(piece)
2441 : *pieceToSolid(piece),
2442 dest, bwPieceGC, 0, 0,
2443 squareSize, squareSize, x, y);
2446 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2447 ? *pieceToSolid(piece)
2448 : *pieceToOutline(piece),
2449 dest, wbPieceGC, 0, 0,
2450 squareSize, squareSize, x, y);
2456 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2458 switch (square_color) {
2460 case 2: /* neutral */
2462 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2463 ? *pieceToOutline(piece)
2464 : *pieceToSolid(piece),
2465 dest, bwPieceGC, 0, 0,
2466 squareSize, squareSize, x, y, 1);
2469 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2470 ? *pieceToSolid(piece)
2471 : *pieceToOutline(piece),
2472 dest, wbPieceGC, 0, 0,
2473 squareSize, squareSize, x, y, 1);
2479 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2481 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
2482 switch (square_color) {
2484 XCopyPlane(xDisplay, *pieceToSolid(piece),
2485 dest, (int) piece < (int) BlackPawn
2486 ? wlPieceGC : blPieceGC, 0, 0,
2487 squareSize, squareSize, x, y, 1);
2490 XCopyPlane(xDisplay, *pieceToSolid(piece),
2491 dest, (int) piece < (int) BlackPawn
2492 ? wdPieceGC : bdPieceGC, 0, 0,
2493 squareSize, squareSize, x, y, 1);
2495 case 2: /* neutral */
2497 break; // should never contain pieces
2502 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2504 int kind, p = piece;
2506 switch (square_color) {
2508 case 2: /* neutral */
2510 if ((int)piece < (int) BlackPawn) {
2518 if ((int)piece < (int) BlackPawn) {
2526 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2527 if(useTexture & square_color+1) {
2528 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2529 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
2530 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
2531 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
2532 XSetClipMask(xDisplay, wlPieceGC, None);
2533 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
2535 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
2536 dest, wlPieceGC, 0, 0,
2537 squareSize, squareSize, x, y);
2540 typedef void (*DrawFunc)();
2545 if (appData.monoMode) {
2546 if (DefaultDepth(xDisplay, xScreen) == 1) {
2547 return monoDrawPiece_1bit;
2549 return monoDrawPiece;
2553 return colorDrawPieceImage;
2555 return colorDrawPiece;
2560 DrawDot (int marker, int x, int y, int r)
2562 if(appData.monoMode) {
2563 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
2564 x, y, r, r, 0, 64*360);
2565 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
2566 x, y, r, r, 0, 64*360);
2568 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
2569 x, y, r, r, 0, 64*360);
2573 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2574 { // basic front-end board-draw function: takes care of everything that can be in square:
2575 // piece, background, coordinate/count, marker dot
2576 int direction, font_ascent, font_descent;
2577 XCharStruct overall;
2580 if (piece == EmptySquare) {
2581 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2583 drawfunc = ChooseDrawFunc();
2584 drawfunc(piece, square_color, x, y, xBoardWindow);
2587 if(align) { // square carries inscription (coord or piece count)
2589 GC hGC = align < 3 ? coordGC : countGC;
2590 // first calculate where it goes
2591 XTextExtents(countFontStruct, string, 1, &direction,
2592 &font_ascent, &font_descent, &overall);
2594 xx += squareSize - overall.width - 2;
2595 yy += squareSize - font_descent - 1;
2596 } else if (align == 2) {
2597 xx += 2, yy += font_ascent + 1;
2598 } else if (align == 3) {
2599 xx += squareSize - overall.width - 2;
2600 yy += font_ascent + 1;
2601 } else if (align == 4) {
2602 xx += 2, yy += font_ascent + 1;
2605 if (appData.monoMode) {
2606 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2608 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2612 if(marker) { // print fat marker dot, if requested
2613 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2618 FlashDelay (int flash_delay)
2620 XSync(xDisplay, False);
2621 if(flash_delay) do_flash_delay(flash_delay);
2625 Fraction (int x, int start, int stop)
2627 double f = ((double) x - start)/(stop - start);
2628 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2632 static WindowPlacement wpNew;
2635 CoDrag (Widget sh, WindowPlacement *wp)
2638 int j=0, touch=0, fudge = 2;
2639 GetActualPlacement(sh, wp);
2640 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
2641 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
2642 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2643 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
2644 if(!touch ) return; // only windows that touch co-move
2645 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2646 int heightInc = wpNew.height - wpMain.height;
2647 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2648 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2649 wp->y += fracTop * heightInc;
2650 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2651 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2652 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2653 int widthInc = wpNew.width - wpMain.width;
2654 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2655 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2656 wp->y += fracLeft * widthInc;
2657 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2658 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2660 wp->x += wpNew.x - wpMain.x;
2661 wp->y += wpNew.y - wpMain.y;
2662 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2663 if(touch == 3) wp->y += wpNew.height - wpMain.height;
2664 XtSetArg(args[j], XtNx, wp->x); j++;
2665 XtSetArg(args[j], XtNy, wp->y); j++;
2666 XtSetValues(sh, args, j);
2669 static XtIntervalId delayedDragID = 0;
2674 GetActualPlacement(shellWidget, &wpNew);
2675 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
2676 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
2677 return; // false alarm
2678 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
2679 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
2680 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
2681 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
2683 DrawPosition(True, NULL);
2684 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
2691 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
2693 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
2697 EventProc (Widget widget, caddr_t unused, XEvent *event)
2699 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
2700 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
2703 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
2705 DrawSeekAxis (int x, int y, int xTo, int yTo)
2707 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
2711 DrawSeekBackground (int left, int top, int right, int bottom)
2713 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
2717 DrawSeekText (char *buf, int x, int y)
2719 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
2723 DrawSeekDot (int x, int y, int colorNr)
2725 int square = colorNr & 0x80;
2728 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
2730 XFillRectangle(xDisplay, xBoardWindow, color,
2731 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
2733 XFillArc(xDisplay, xBoardWindow, color,
2734 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
2740 XDrawSegments(xDisplay, xBoardWindow, lineGC,
2741 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
2746 * event handler for redrawing the board
2749 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2751 DrawPosition(True, NULL);
2756 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
2757 { // [HGM] pv: walk PV
2758 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
2761 static int savedIndex; /* gross that this is global */
2764 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
2767 XawTextPosition index, dummy;
2770 XawTextGetSelectionPos(w, &index, &dummy);
2771 XtSetArg(arg, XtNstring, &val);
2772 XtGetValues(w, &arg, 1);
2773 ReplaceComment(savedIndex, val);
2774 if(savedIndex != currentMove) ToNrEvent(savedIndex);
2775 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
2779 EditCommentPopUp (int index, char *title, char *text)
2782 if (text == NULL) text = "";
2783 NewCommentPopup(title, text, index);
2787 CommentPopUp (char *title, char *text)
2789 savedIndex = currentMove; // [HGM] vari
2790 NewCommentPopup(title, text, currentMove);
2796 PopDown(CommentDlg);
2800 /* Disable all user input other than deleting the window */
2801 static int frozen = 0;
2807 /* Grab by a widget that doesn't accept input */
2808 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
2812 /* Undo a FreezeUI */
2816 if (!frozen) return;
2817 XtRemoveGrab(optList[W_MESSG].handle);
2825 static int oldPausing = FALSE;
2826 static GameMode oldmode = (GameMode) -1;
2829 if (!boardWidget || !XtIsRealized(boardWidget)) return;
2831 if (pausing != oldPausing) {
2832 oldPausing = pausing;
2833 MarkMenuItem("Mode.Pause", pausing);
2835 if (appData.showButtonBar) {
2836 /* Always toggle, don't set. Previous code messes up when
2837 invoked while the button is pressed, as releasing it
2838 toggles the state again. */
2841 XtSetArg(args[0], XtNbackground, &oldbg);
2842 XtSetArg(args[1], XtNforeground, &oldfg);
2843 XtGetValues(optList[W_PAUSE].handle,
2845 XtSetArg(args[0], XtNbackground, oldfg);
2846 XtSetArg(args[1], XtNforeground, oldbg);
2848 XtSetValues(optList[W_PAUSE].handle, args, 2);
2852 wname = ModeToWidgetName(oldmode);
2853 if (wname != NULL) {
2854 MarkMenuItem(wname, False);
2856 wname = ModeToWidgetName(gameMode);
2857 if (wname != NULL) {
2858 MarkMenuItem(wname, True);
2861 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
2863 /* Maybe all the enables should be handled here, not just this one */
2864 EnableMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
2869 * Button/menu procedures
2872 /* this variable is shared between CopyPositionProc and SendPositionSelection */
2873 char *selected_fen_position=NULL;
2876 SendPositionSelection (Widget w, Atom *selection, Atom *target,
2877 Atom *type_return, XtPointer *value_return,
2878 unsigned long *length_return, int *format_return)
2880 char *selection_tmp;
2882 // if (!selected_fen_position) return False; /* should never happen */
2883 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
2884 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
2885 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
2888 if (f == NULL) return False;
2892 selection_tmp = XtMalloc(len + 1);
2893 count = fread(selection_tmp, 1, len, f);
2896 XtFree(selection_tmp);
2899 selection_tmp[len] = NULLCHAR;
2901 /* note: since no XtSelectionDoneProc was registered, Xt will
2902 * automatically call XtFree on the value returned. So have to
2903 * make a copy of it allocated with XtMalloc */
2904 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
2905 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
2908 *value_return=selection_tmp;
2909 *length_return=strlen(selection_tmp);
2910 *type_return=*target;
2911 *format_return = 8; /* bits per byte */
2913 } else if (*target == XA_TARGETS(xDisplay)) {
2914 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
2915 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
2916 targets_tmp[1] = XA_STRING;
2917 *value_return = targets_tmp;
2918 *type_return = XA_ATOM;
2921 // This code leads to a read of value_return out of bounds on 64-bit systems.
2922 // Other code which I have seen always sets *format_return to 32 independent of
2923 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
2924 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
2925 *format_return = 8 * sizeof(Atom);
2926 if (*format_return > 32) {
2927 *length_return *= *format_return / 32;
2928 *format_return = 32;
2931 *format_return = 32;
2939 /* note: when called from menu all parameters are NULL, so no clue what the
2940 * Widget which was clicked on was, or what the click event was
2943 CopySomething (char *src)
2945 selected_fen_position = src;
2947 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
2948 * have a notion of a position that is selected but not copied.
2949 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
2951 XtOwnSelection(menuBarWidget, XA_PRIMARY,
2953 SendPositionSelection,
2954 NULL/* lose_ownership_proc */ ,
2955 NULL/* transfer_done_proc */);
2956 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
2958 SendPositionSelection,
2959 NULL/* lose_ownership_proc */ ,
2960 NULL/* transfer_done_proc */);
2963 /* function called when the data to Paste is ready */
2965 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
2966 Atom *type, XtPointer value, unsigned long *len, int *format)
2969 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
2970 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
2971 EditPositionPasteFEN(fenstr);
2975 /* called when Paste Position button is pressed,
2976 * all parameters will be NULL */
2978 PastePositionProc ()
2980 XtGetSelectionValue(menuBarWidget,
2981 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2982 /* (XtSelectionCallbackProc) */ PastePositionCB,
2983 NULL, /* client_data passed to PastePositionCB */
2985 /* better to use the time field from the event that triggered the
2986 * call to this function, but that isn't trivial to get
2993 /* note: when called from menu all parameters are NULL, so no clue what the
2994 * Widget which was clicked on was, or what the click event was
2996 /* function called when the data to Paste is ready */
2998 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
2999 Atom *type, XtPointer value, unsigned long *len, int *format)
3002 if (value == NULL || *len == 0) {
3003 return; /* nothing had been selected to copy */
3005 f = fopen(gamePasteFilename, "w");
3007 DisplayError(_("Can't open temp file"), errno);
3010 fwrite(value, 1, *len, f);
3013 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3016 /* called when Paste Game button is pressed,
3017 * all parameters will be NULL */
3021 XtGetSelectionValue(menuBarWidget,
3022 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3023 /* (XtSelectionCallbackProc) */ PasteGameCB,
3024 NULL, /* client_data passed to PasteGameCB */
3026 /* better to use the time field from the event that triggered the
3027 * call to this function, but that isn't trivial to get
3036 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3043 { // bassic primitive for determining if modifier keys are pressed
3044 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3047 XQueryKeymap(xDisplay,keys);
3048 for(i=0; i<6; i++) {
3050 j = XKeysymToKeycode(xDisplay, codes[i]);
3051 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3057 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3061 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3062 if ( n == 1 && *buf >= 32 // printable
3063 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3064 ) BoxAutoPopUp (buf);
3068 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3069 { // [HGM] input: let up-arrow recall previous line from history
3074 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3075 { // [HGM] input: let down-arrow recall next line from history
3080 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3086 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3088 if (!TempBackwardActive) {
3089 TempBackwardActive = True;
3095 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3097 /* Check to see if triggered by a key release event for a repeating key.
3098 * If so the next queued event will be a key press of the same key at the same time */
3099 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3101 XPeekEvent(xDisplay, &next);
3102 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3103 next.xkey.keycode == event->xkey.keycode)
3107 TempBackwardActive = False;
3111 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3112 { // called as key binding
3115 if (nprms && *nprms > 0)
3119 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3125 { // called from menu
3126 ManInner(NULL, NULL, NULL, NULL);
3130 SetWindowTitle (char *text, char *title, char *icon)
3134 if (appData.titleInWindow) {
3136 XtSetArg(args[i], XtNlabel, text); i++;
3137 XtSetValues(titleWidget, args, i);
3140 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3141 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3142 XtSetValues(shellWidget, args, i);
3143 XSync(xDisplay, False);
3148 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3154 DisplayIcsInteractionTitle (String message)
3156 if (oldICSInteractionTitle == NULL) {
3157 /* Magic to find the old window title, adapted from vim */
3158 char *wina = getenv("WINDOWID");
3160 Window win = (Window) atoi(wina);
3161 Window root, parent, *children;
3162 unsigned int nchildren;
3163 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3165 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3166 if (!XQueryTree(xDisplay, win, &root, &parent,
3167 &children, &nchildren)) break;
3168 if (children) XFree((void *)children);
3169 if (parent == root || parent == 0) break;
3172 XSetErrorHandler(oldHandler);
3174 if (oldICSInteractionTitle == NULL) {
3175 oldICSInteractionTitle = "xterm";
3178 printf("\033]0;%s\007", message);
3183 XtIntervalId delayedEventTimerXID = 0;
3184 DelayedEventCallback delayedEventCallback = 0;
3189 delayedEventTimerXID = 0;
3190 delayedEventCallback();
3194 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3196 if(delayedEventTimerXID && delayedEventCallback == cb)
3197 // [HGM] alive: replace, rather than add or flush identical event
3198 XtRemoveTimeOut(delayedEventTimerXID);
3199 delayedEventCallback = cb;
3200 delayedEventTimerXID =
3201 XtAppAddTimeOut(appContext, millisec,
3202 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3205 DelayedEventCallback
3208 if (delayedEventTimerXID) {
3209 return delayedEventCallback;
3216 CancelDelayedEvent ()
3218 if (delayedEventTimerXID) {
3219 XtRemoveTimeOut(delayedEventTimerXID);
3220 delayedEventTimerXID = 0;
3224 XtIntervalId loadGameTimerXID = 0;
3227 LoadGameTimerRunning ()
3229 return loadGameTimerXID != 0;
3233 StopLoadGameTimer ()
3235 if (loadGameTimerXID != 0) {
3236 XtRemoveTimeOut(loadGameTimerXID);
3237 loadGameTimerXID = 0;
3245 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3247 loadGameTimerXID = 0;
3252 StartLoadGameTimer (long millisec)
3255 XtAppAddTimeOut(appContext, millisec,
3256 (XtTimerCallbackProc) LoadGameTimerCallback,
3260 XtIntervalId analysisClockXID = 0;
3263 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3265 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3266 || appData.icsEngineAnalyze) { // [DM]
3267 AnalysisPeriodicEvent(0);
3268 StartAnalysisClock();
3273 StartAnalysisClock ()
3276 XtAppAddTimeOut(appContext, 2000,
3277 (XtTimerCallbackProc) AnalysisClockCallback,
3281 XtIntervalId clockTimerXID = 0;
3284 ClockTimerRunning ()
3286 return clockTimerXID != 0;
3292 if (clockTimerXID != 0) {
3293 XtRemoveTimeOut(clockTimerXID);
3302 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3309 StartClockTimer (long millisec)
3312 XtAppAddTimeOut(appContext, millisec,
3313 (XtTimerCallbackProc) ClockTimerCallback,
3318 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
3322 Widget w = (Widget) opt->handle;
3324 /* check for low time warning */
3325 Pixel foregroundOrWarningColor = timerForegroundPixel;
3328 appData.lowTimeWarning &&
3329 (timer / 1000) < appData.icsAlarmTime)
3330 foregroundOrWarningColor = lowTimeWarningColor;
3332 if (appData.clockMode) {
3333 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
3334 XtSetArg(args[0], XtNlabel, buf);
3336 snprintf(buf, MSG_SIZ, "%s ", color);
3337 XtSetArg(args[0], XtNlabel, buf);
3342 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3343 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3345 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3346 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3349 XtSetValues(w, args, 3);
3352 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3355 SetClockIcon (int color)
3358 Pixmap pm = *clockIcons[color];
3359 if (iconPixmap != pm) {
3361 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3362 XtSetValues(shellWidget, args, 1);
3367 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3369 InputSource *is = (InputSource *) closure;
3374 if (is->lineByLine) {
3375 count = read(is->fd, is->unused,
3376 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3378 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3381 is->unused += count;
3383 while (p < is->unused) {
3384 q = memchr(p, '\n', is->unused - p);
3385 if (q == NULL) break;
3387 (is->func)(is, is->closure, p, q - p, 0);
3391 while (p < is->unused) {
3396 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3401 (is->func)(is, is->closure, is->buf, count, error);
3406 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3409 ChildProc *cp = (ChildProc *) pr;
3411 is = (InputSource *) calloc(1, sizeof(InputSource));
3412 is->lineByLine = lineByLine;
3416 is->fd = fileno(stdin);
3418 is->kind = cp->kind;
3419 is->fd = cp->fdFrom;
3422 is->unused = is->buf;
3425 is->xid = XtAppAddInput(appContext, is->fd,
3426 (XtPointer) (XtInputReadMask),
3427 (XtInputCallbackProc) DoInputCallback,
3429 is->closure = closure;
3430 return (InputSourceRef) is;
3434 RemoveInputSource (InputSourceRef isr)
3436 InputSource *is = (InputSource *) isr;
3438 if (is->xid == 0) return;
3439 XtRemoveInput(is->xid);
3443 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
3445 /* Masks for XPM pieces. Black and white pieces can have
3446 different shapes, but in the interest of retaining my
3447 sanity pieces must have the same outline on both light
3448 and dark squares, and all pieces must use the same
3449 background square colors/images. */
3451 static int xpmDone = 0;
3452 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
3453 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
3456 CreateAnimMasks (int pieceDepth)
3462 unsigned long plane;
3465 /* Need a bitmap just to get a GC with right depth */
3466 buf = XCreatePixmap(xDisplay, xBoardWindow,
3468 values.foreground = 1;
3469 values.background = 0;
3470 /* Don't use XtGetGC, not read only */
3471 maskGC = XCreateGC(xDisplay, buf,
3472 GCForeground | GCBackground, &values);
3473 XFreePixmap(xDisplay, buf);
3475 buf = XCreatePixmap(xDisplay, xBoardWindow,
3476 squareSize, squareSize, pieceDepth);
3477 values.foreground = XBlackPixel(xDisplay, xScreen);
3478 values.background = XWhitePixel(xDisplay, xScreen);
3479 bufGC = XCreateGC(xDisplay, buf,
3480 GCForeground | GCBackground, &values);
3482 for (piece = WhitePawn; piece <= BlackKing; piece++) {
3483 /* Begin with empty mask */
3484 if(!xpmDone) // [HGM] pieces: keep using existing
3485 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
3486 squareSize, squareSize, 1);
3487 XSetFunction(xDisplay, maskGC, GXclear);
3488 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
3489 0, 0, squareSize, squareSize);
3491 /* Take a copy of the piece */
3496 XSetFunction(xDisplay, bufGC, GXcopy);
3497 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
3499 0, 0, squareSize, squareSize, 0, 0);
3501 /* XOR the background (light) over the piece */
3502 XSetFunction(xDisplay, bufGC, GXxor);
3504 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
3505 0, 0, squareSize, squareSize, 0, 0);
3507 XSetForeground(xDisplay, bufGC, lightSquareColor);
3508 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
3511 /* We now have an inverted piece image with the background
3512 erased. Construct mask by just selecting all the non-zero
3513 pixels - no need to reconstruct the original image. */
3514 XSetFunction(xDisplay, maskGC, GXor);
3516 /* Might be quicker to download an XImage and create bitmap
3517 data from it rather than this N copies per piece, but it
3518 only takes a fraction of a second and there is a much
3519 longer delay for loading the pieces. */
3520 for (n = 0; n < pieceDepth; n ++) {
3521 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
3522 0, 0, squareSize, squareSize,
3528 XFreePixmap(xDisplay, buf);
3529 XFreeGC(xDisplay, bufGC);
3530 XFreeGC(xDisplay, maskGC);
3534 InitAnimState (AnimNr anr, XWindowAttributes *info)
3539 /* Each buffer is square size, same depth as window */
3540 animBufs[anr+4] = xBoardWindow;
3541 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
3542 squareSize, squareSize, info->depth);
3543 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
3544 squareSize, squareSize, info->depth);
3546 /* Create a plain GC for blitting */
3547 mask = GCForeground | GCBackground | GCFunction |
3548 GCPlaneMask | GCGraphicsExposures;
3549 values.foreground = XBlackPixel(xDisplay, xScreen);
3550 values.background = XWhitePixel(xDisplay, xScreen);
3551 values.function = GXcopy;
3552 values.plane_mask = AllPlanes;
3553 values.graphics_exposures = False;
3554 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
3556 /* Piece will be copied from an existing context at
3557 the start of each new animation/drag. */
3558 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
3560 /* Outline will be a read-only copy of an existing */
3561 animGCs[anr+4] = None;
3567 XWindowAttributes info;
3569 if (xpmDone && gameInfo.variant == oldVariant) return;
3570 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
3571 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
3573 InitAnimState(Game, &info);
3574 InitAnimState(Player, &info);
3576 /* For XPM pieces, we need bitmaps to use as masks. */
3578 CreateAnimMasks(info.depth), xpmDone = 1;
3583 static Boolean frameWaiting;
3586 FrameAlarm (int sig)
3588 frameWaiting = False;
3589 /* In case System-V style signals. Needed?? */
3590 signal(SIGALRM, FrameAlarm);
3594 FrameDelay (int time)
3596 struct itimerval delay;
3598 XSync(xDisplay, False);
3601 frameWaiting = True;
3602 signal(SIGALRM, FrameAlarm);
3603 delay.it_interval.tv_sec =
3604 delay.it_value.tv_sec = time / 1000;
3605 delay.it_interval.tv_usec =
3606 delay.it_value.tv_usec = (time % 1000) * 1000;
3607 setitimer(ITIMER_REAL, &delay, NULL);
3608 while (frameWaiting) pause();
3609 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
3610 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
3611 setitimer(ITIMER_REAL, &delay, NULL);
3618 FrameDelay (int time)
3620 XSync(xDisplay, False);
3622 usleep(time * 1000);
3628 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
3632 /* Bitmap for piece being moved. */
3633 if (appData.monoMode) {
3634 *mask = *pieceToSolid(piece);
3635 } else if (useImages) {
3637 *mask = xpmMask[piece];
3639 *mask = ximMaskPm[piece];
3642 *mask = *pieceToSolid(piece);
3645 /* GC for piece being moved. Square color doesn't matter, but
3646 since it gets modified we make a copy of the original. */
3648 if (appData.monoMode)
3653 if (appData.monoMode)
3658 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
3660 /* Outline only used in mono mode and is not modified */
3662 *outline = bwPieceGC;
3664 *outline = wbPieceGC;
3668 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
3673 /* Draw solid rectangle which will be clipped to shape of piece */
3674 XFillRectangle(xDisplay, dest, clip,
3675 0, 0, squareSize, squareSize);
3676 if (appData.monoMode)
3677 /* Also draw outline in contrasting color for black
3678 on black / white on white cases */
3679 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
3680 0, 0, squareSize, squareSize, 0, 0, 1);
3682 /* Copy the piece */
3687 if(appData.upsideDown && flipView) kind ^= 2;
3688 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3690 0, 0, squareSize, squareSize,
3696 InsertPiece (AnimNr anr, ChessSquare piece)
3698 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
3702 DrawBlank (AnimNr anr, int x, int y, int startColor)
3704 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
3707 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
3708 int srcX, int srcY, int width, int height, int destX, int destY)
3710 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
3711 srcX, srcY, width, height, destX, destY);
3715 SetDragPiece (AnimNr anr, ChessSquare piece)
3718 /* The piece will be drawn using its own bitmap as a matte */
3719 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
3720 XSetClipMask(xDisplay, animGCs[anr+2], mask);
3723 /* [AS] Arrow highlighting support */
3726 DrawPolygon (Pnt arrow[], int nr)
3730 for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
3731 XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
3732 if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
3736 UpdateLogos (int displ)
3738 return; // no logos in XBoard yet