2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
66 # if HAVE_SYS_SOCKET_H
67 # include <sys/socket.h>
68 # include <netinet/in.h>
70 # else /* not HAVE_SYS_SOCKET_H */
71 # if HAVE_LAN_SOCKET_H
72 # include <lan/socket.h>
74 # include <lan/netdb.h>
75 # else /* not HAVE_LAN_SOCKET_H */
76 # define OMIT_SOCKETS 1
77 # endif /* not HAVE_LAN_SOCKET_H */
78 # endif /* not HAVE_SYS_SOCKET_H */
79 #endif /* !OMIT_SOCKETS */
84 #else /* not STDC_HEADERS */
85 extern char *getenv();
88 # else /* not HAVE_STRING_H */
90 # endif /* not HAVE_STRING_H */
91 #endif /* not STDC_HEADERS */
94 # include <sys/fcntl.h>
95 #else /* not HAVE_SYS_FCNTL_H */
98 # endif /* HAVE_FCNTL_H */
99 #endif /* not HAVE_SYS_FCNTL_H */
101 #if HAVE_SYS_SYSTEMINFO_H
102 # include <sys/systeminfo.h>
103 #endif /* HAVE_SYS_SYSTEMINFO_H */
105 #if TIME_WITH_SYS_TIME
106 # include <sys/time.h>
110 # include <sys/time.h>
121 # include <sys/wait.h>
126 # define NAMLEN(dirent) strlen((dirent)->d_name)
127 # define HAVE_DIR_STRUCT
129 # define dirent direct
130 # define NAMLEN(dirent) (dirent)->d_namlen
132 # include <sys/ndir.h>
133 # define HAVE_DIR_STRUCT
136 # include <sys/dir.h>
137 # define HAVE_DIR_STRUCT
141 # define HAVE_DIR_STRUCT
149 #include <X11/Intrinsic.h>
150 #include <X11/StringDefs.h>
151 #include <X11/Shell.h>
152 #include <X11/cursorfont.h>
153 #include <X11/Xatom.h>
154 #include <X11/Xmu/Atoms.h>
156 #include <X11/Xaw3d/Dialog.h>
157 #include <X11/Xaw3d/Form.h>
158 #include <X11/Xaw3d/List.h>
159 #include <X11/Xaw3d/Label.h>
160 #include <X11/Xaw3d/SimpleMenu.h>
161 #include <X11/Xaw3d/SmeBSB.h>
162 #include <X11/Xaw3d/SmeLine.h>
163 #include <X11/Xaw3d/Box.h>
164 #include <X11/Xaw3d/MenuButton.h>
165 #include <X11/Xaw3d/Text.h>
166 #include <X11/Xaw3d/AsciiText.h>
168 #include <X11/Xaw/Dialog.h>
169 #include <X11/Xaw/Form.h>
170 #include <X11/Xaw/List.h>
171 #include <X11/Xaw/Label.h>
172 #include <X11/Xaw/SimpleMenu.h>
173 #include <X11/Xaw/SmeBSB.h>
174 #include <X11/Xaw/SmeLine.h>
175 #include <X11/Xaw/Box.h>
176 #include <X11/Xaw/MenuButton.h>
177 #include <X11/Xaw/Text.h>
178 #include <X11/Xaw/AsciiText.h>
181 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
186 #include "pixmaps/pixmaps.h"
187 #define IMAGE_EXT "xpm"
189 #define IMAGE_EXT "xim"
190 #include "bitmaps/bitmaps.h"
193 #include "bitmaps/icon_white.bm"
194 #include "bitmaps/icon_black.bm"
195 #include "bitmaps/checkmark.bm"
197 #include "frontend.h"
199 #include "backendz.h"
203 #include "xgamelist.h"
204 #include "xhistory.h"
205 #include "xedittags.h"
209 #include "engineoutput.h"
218 #define usleep(t) _sleep2(((t)+500)/1000)
222 # define _(s) gettext (s)
223 # define N_(s) gettext_noop (s)
229 int main P((int argc, char **argv));
230 RETSIGTYPE CmailSigHandler P((int sig));
231 RETSIGTYPE IntSigHandler P((int sig));
232 RETSIGTYPE TermSizeSigHandler P((int sig));
233 static void CreateGCs P((int redo));
234 static void CreateAnyPieces P((void));
235 void CreateXIMPieces P((void));
236 void CreateXPMPieces P((void));
237 void CreateXPMBoard P((char *s, int n));
238 void CreatePieces P((void));
239 Widget CreateMenuBar P((Menu *mb, int boardWidth));
241 char *InsertPxlSize P((char *pattern, int targetPxlSize));
242 XFontSet CreateFontSet P((char *base_fnt_lst));
244 char *FindFont P((char *pattern, int targetPxlSize));
246 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
247 u_int wreq, u_int hreq));
248 void CreateGrid P((void));
249 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
250 void DelayedDrag P((void));
251 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
252 void HandlePV P((Widget w, XEvent * event,
253 String * params, Cardinal * nParams));
254 void DrawPositionProc P((Widget w, XEvent *event,
255 String *prms, Cardinal *nprms));
256 void CommentClick P((Widget w, XEvent * event,
257 String * params, Cardinal * nParams));
258 void ICSInputBoxPopUp P((void));
259 void FileNamePopUp P((char *label, char *def, char *filter,
260 FileProc proc, char *openMode));
261 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
262 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
263 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
264 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
265 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
266 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
267 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
268 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
269 Boolean TempBackwardActive = False;
270 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
271 void DisplayMove P((int moveNumber));
272 void ICSInitScript P((void));
273 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
274 void update_ics_width P(());
275 int CopyMemoProc P(());
278 * XBoard depends on Xt R4 or higher
280 int xtVersion = XtSpecificationRelease;
285 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
286 highlightSquareColor, premoveHighlightColor, dialogColor, buttonColor;
287 Pixel lowTimeWarningColor;
288 GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
289 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
291 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
292 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
293 Option *optList; // contains all widgets of main window
294 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
296 XFontSet fontSet, clockFontSet;
299 XFontStruct *clockFontStruct;
301 Font coordFontID, countFontID;
302 XFontStruct *coordFontStruct, *countFontStruct;
303 XtAppContext appContext;
308 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
310 Position commentX = -1, commentY = -1;
311 Dimension commentW, commentH;
312 typedef unsigned int BoardSize;
314 Boolean chessProgram;
316 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
317 int smallLayout = 0, tinyLayout = 0,
318 marginW, marginH, // [HGM] for run-time resizing
319 fromX = -1, fromY = -1, toX, toY, commentUp = False,
320 errorExitStatus = -1, defaultLineGap;
321 Dimension textHeight;
322 Pixel timerForegroundPixel, timerBackgroundPixel;
323 Pixel buttonForegroundPixel, buttonBackgroundPixel;
324 char *chessDir, *programName, *programVersion;
325 Boolean alwaysOnTop = False;
326 char *icsTextMenuString;
328 char *firstChessProgramNames;
329 char *secondChessProgramNames;
331 WindowPlacement wpMain;
332 WindowPlacement wpConsole;
333 WindowPlacement wpComment;
334 WindowPlacement wpMoveHistory;
335 WindowPlacement wpEvalGraph;
336 WindowPlacement wpEngineOutput;
337 WindowPlacement wpGameList;
338 WindowPlacement wpTags;
343 Pixmap pieceBitmap[2][(int)BlackPawn];
344 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
345 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
346 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
347 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
348 Pixmap xpmBoardBitmap[2];
349 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
350 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
351 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
352 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
353 XImage *ximLightSquare, *ximDarkSquare;
356 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
357 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
359 #define White(piece) ((int)(piece) < (int)BlackPawn)
361 /* Bitmaps for use as masks when drawing XPM pieces.
362 Need one for each black and white piece. */
363 static Pixmap xpmMask[BlackKing + 1];
365 /* This magic number is the number of intermediate frames used
366 in each half of the animation. For short moves it's reduced
367 by 1. The total number of frames will be factor * 2 + 1. */
370 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
377 DropMenuEnables dmEnables[] = {
394 XtResource clientResources[] = {
395 { "flashCount", "flashCount", XtRInt, sizeof(int),
396 XtOffset(AppDataPtr, flashCount), XtRImmediate,
397 (XtPointer) FLASH_COUNT },
400 XrmOptionDescRec shellOptions[] = {
401 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
402 { "-flash", "flashCount", XrmoptionNoArg, "3" },
403 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
406 XtActionsRec boardActions[] = {
407 { "DrawPosition", DrawPositionProc },
408 { "HandlePV", HandlePV },
409 { "SelectPV", SelectPV },
410 { "StopPV", StopPV },
411 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
412 { "QuitProc", QuitWrapper },
413 { "ManProc", ManInner },
414 { "TempBackwardProc", TempBackwardProc },
415 { "TempForwardProc", TempForwardProc },
416 { "CommentClick", (XtActionProc) CommentClick },
417 { "GenericPopDown", (XtActionProc) GenericPopDown },
418 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
419 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
420 { "SelectMove", (XtActionProc) SelectMove },
421 { "LoadSelectedProc", LoadSelectedProc },
422 { "SetFilterProc", SetFilterProc },
423 { "TypeInProc", TypeInProc },
424 { "EnterKeyProc", EnterKeyProc },
425 { "UpKeyProc", UpKeyProc },
426 { "DownKeyProc", DownKeyProc },
427 { "WheelProc", WheelProc },
428 { "TabProc", TabProc },
431 char globalTranslations[] =
432 ":<Key>F9: MenuItem(Actions.Resign) \n \
433 :Ctrl<Key>n: MenuItem(File.NewGame) \n \
434 :Meta<Key>V: MenuItem(File.NewVariant) \n \
435 :Ctrl<Key>o: MenuItem(File.LoadGame) \n \
436 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
437 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
438 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
439 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
440 :Ctrl<Key>s: MenuItem(File.SaveGame) \n \
441 :Ctrl<Key>c: MenuItem(Edit.CopyGame) \n \
442 :Ctrl<Key>v: MenuItem(Edit.PasteGame) \n \
443 :Ctrl<Key>O: MenuItem(File.LoadPosition) \n \
444 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
445 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
446 :Ctrl<Key>S: MenuItem(File.SavePosition) \n \
447 :Ctrl<Key>C: MenuItem(Edit.CopyPosition) \n \
448 :Ctrl<Key>V: MenuItem(Edit.PastePosition) \n \
449 :Ctrl<Key>q: MenuItem(File.Quit) \n \
450 :Ctrl<Key>w: MenuItem(Mode.MachineWhite) \n \
451 :Ctrl<Key>b: MenuItem(Mode.MachineBlack) \n \
452 :Ctrl<Key>t: MenuItem(Mode.TwoMachines) \n \
453 :Ctrl<Key>a: MenuItem(Mode.AnalysisMode) \n \
454 :Ctrl<Key>g: MenuItem(Mode.AnalyzeFile) \n \
455 :Ctrl<Key>e: MenuItem(Mode.EditGame) \n \
456 :Ctrl<Key>E: MenuItem(Mode.EditPosition) \n \
457 :Meta<Key>O: MenuItem(View.EngineOutput) \n \
458 :Meta<Key>E: MenuItem(View.EvaluationGraph) \n \
459 :Meta<Key>G: MenuItem(View.GameList) \n \
460 :Meta<Key>H: MenuItem(View.MoveHistory) \n \
461 :<Key>Pause: MenuItem(Mode.Pause) \n \
462 :<Key>F3: MenuItem(Action.Accept) \n \
463 :<Key>F4: MenuItem(Action.Decline) \n \
464 :<Key>F12: MenuItem(Action.Rematch) \n \
465 :<Key>F5: MenuItem(Action.CallFlag) \n \
466 :<Key>F6: MenuItem(Action.Draw) \n \
467 :<Key>F7: MenuItem(Action.Adjourn) \n \
468 :<Key>F8: MenuItem(Action.Abort) \n \
469 :<Key>F10: MenuItem(Action.StopObserving) \n \
470 :<Key>F11: MenuItem(Action.StopExamining) \n \
471 :Ctrl<Key>d: MenuItem(DebugProc) \n \
472 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
473 :Meta<Key>End: MenuItem(Edit.ForwardtoEnd) \n \
474 :Meta<Key>Right: MenuItem(Edit.Forward) \n \
475 :Meta<Key>Home: MenuItem(Edit.BacktoStart) \n \
476 :Meta<Key>Left: MenuItem(Edit.Backward) \n \
477 :<Key>Left: MenuItem(Edit.Backward) \n \
478 :<Key>Right: MenuItem(Edit.Forward) \n \
479 :<Key>Home: MenuItem(Edit.Revert) \n \
480 :<Key>End: MenuItem(Edit.TruncateGame) \n \
481 :Ctrl<Key>m: MenuItem(Engine.MoveNow) \n \
482 :Ctrl<Key>x: MenuItem(Engine.RetractMove) \n \
483 :Meta<Key>J: MenuItem(Options.Adjudications) \n \
484 :Meta<Key>U: MenuItem(Options.CommonEngine) \n \
485 :Meta<Key>T: MenuItem(Options.TimeControl) \n \
486 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
487 #ifndef OPTIONSDIALOG
489 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
490 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
491 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
492 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
493 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
496 :<Key>F1: MenuItem(Help.ManXBoard) \n \
497 :<Key>F2: MenuItem(View.FlipView) \n \
498 :<KeyDown>Return: TempBackwardProc() \n \
499 :<KeyUp>Return: TempForwardProc() \n";
501 char ICSInputTranslations[] =
502 "<Key>Up: UpKeyProc() \n "
503 "<Key>Down: DownKeyProc() \n "
504 "<Key>Return: EnterKeyProc() \n";
506 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
507 // as the widget is destroyed before the up-click can call extend-end
508 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
510 String xboardResources[] = {
511 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
516 /* Max possible square size */
517 #define MAXSQSIZE 256
519 static int xpm_avail[MAXSQSIZE];
521 #ifdef HAVE_DIR_STRUCT
523 /* Extract piece size from filename */
525 xpm_getsize (char *name, int len, char *ext)
533 if ((p=strchr(name, '.')) == NULL ||
534 StrCaseCmp(p+1, ext) != 0)
540 while (*p && isdigit(*p))
547 /* Setup xpm_avail */
549 xpm_getavail (char *dirname, char *ext)
555 for (i=0; i<MAXSQSIZE; ++i)
558 if (appData.debugMode)
559 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
561 dir = opendir(dirname);
564 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
565 programName, dirname);
569 while ((ent=readdir(dir)) != NULL) {
570 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
571 if (i > 0 && i < MAXSQSIZE)
581 xpm_print_avail (FILE *fp, char *ext)
585 fprintf(fp, _("Available `%s' sizes:\n"), ext);
586 for (i=1; i<MAXSQSIZE; ++i) {
592 /* Return XPM piecesize closest to size */
594 xpm_closest_to (char *dirname, int size, char *ext)
597 int sm_diff = MAXSQSIZE;
601 xpm_getavail(dirname, ext);
603 if (appData.debugMode)
604 xpm_print_avail(stderr, ext);
606 for (i=1; i<MAXSQSIZE; ++i) {
609 diff = (diff<0) ? -diff : diff;
610 if (diff < sm_diff) {
618 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
624 #else /* !HAVE_DIR_STRUCT */
625 /* If we are on a system without a DIR struct, we can't
626 read the directory, so we can't collect a list of
627 filenames, etc., so we can't do any size-fitting. */
629 xpm_closest_to (char *dirname, int size, char *ext)
632 Warning: No DIR structure found on this system --\n\
633 Unable to autosize for XPM/XIM pieces.\n\
634 Please report this error to %s.\n\
635 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
638 #endif /* HAVE_DIR_STRUCT */
641 /* Arrange to catch delete-window events */
642 Atom wm_delete_window;
644 CatchDeleteWindow (Widget w, String procname)
647 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
648 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
649 XtAugmentTranslations(w, XtParseTranslationTable(buf));
656 XtSetArg(args[0], XtNiconic, False);
657 XtSetValues(shellWidget, args, 1);
659 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
662 //---------------------------------------------------------------------------------------------------------
663 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
666 #define CW_USEDEFAULT (1<<31)
667 #define ICS_TEXT_MENU_SIZE 90
668 #define DEBUG_FILE "xboard.debug"
669 #define SetCurrentDirectory chdir
670 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
674 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
677 // front-end part of option handling
679 // [HGM] This platform-dependent table provides the location for storing the color info
680 extern char *crWhite, * crBlack;
684 &appData.whitePieceColor,
685 &appData.blackPieceColor,
686 &appData.lightSquareColor,
687 &appData.darkSquareColor,
688 &appData.highlightSquareColor,
689 &appData.premoveHighlightColor,
690 &appData.lowTimeWarningColor,
701 // [HGM] font: keep a font for each square size, even non-stndard ones
704 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
705 char *fontTable[NUM_FONTS][MAX_SIZE];
708 ParseFont (char *name, int number)
709 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
711 if(sscanf(name, "size%d:", &size)) {
712 // [HGM] font: font is meant for specific boardSize (likely from settings file);
713 // defer processing it until we know if it matches our board size
714 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
715 fontTable[number][size] = strdup(strchr(name, ':')+1);
716 fontValid[number][size] = True;
721 case 0: // CLOCK_FONT
722 appData.clockFont = strdup(name);
724 case 1: // MESSAGE_FONT
725 appData.font = strdup(name);
727 case 2: // COORD_FONT
728 appData.coordFont = strdup(name);
733 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
738 { // only 2 fonts currently
739 appData.clockFont = CLOCK_FONT_NAME;
740 appData.coordFont = COORD_FONT_NAME;
741 appData.font = DEFAULT_FONT_NAME;
746 { // no-op, until we identify the code for this already in XBoard and move it here
750 ParseColor (int n, char *name)
751 { // in XBoard, just copy the color-name string
752 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
756 ParseTextAttribs (ColorClass cc, char *s)
758 (&appData.colorShout)[cc] = strdup(s);
762 ParseBoardSize (void *addr, char *name)
764 appData.boardSize = strdup(name);
769 { // In XBoard the sound-playing program takes care of obtaining the actual sound
773 SetCommPortDefaults ()
774 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
777 // [HGM] args: these three cases taken out to stay in front-end
779 SaveFontArg (FILE *f, ArgDescriptor *ad)
782 int i, n = (int)(intptr_t)ad->argLoc;
784 case 0: // CLOCK_FONT
785 name = appData.clockFont;
787 case 1: // MESSAGE_FONT
790 case 2: // COORD_FONT
791 name = appData.coordFont;
796 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
797 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
798 fontTable[n][squareSize] = strdup(name);
799 fontValid[n][squareSize] = True;
802 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
803 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
808 { // nothing to do, as the sounds are at all times represented by their text-string names already
812 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
813 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
814 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
818 SaveColor (FILE *f, ArgDescriptor *ad)
819 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
820 if(colorVariable[(int)(intptr_t)ad->argLoc])
821 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
825 SaveBoardSize (FILE *f, char *name, void *addr)
826 { // wrapper to shield back-end from BoardSize & sizeInfo
827 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
831 ParseCommPortSettings (char *s)
832 { // no such option in XBoard (yet)
838 GetActualPlacement (Widget wg, WindowPlacement *wp)
840 XWindowAttributes winAt;
847 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
848 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
849 wp->x = rx - winAt.x;
850 wp->y = ry - winAt.y;
851 wp->height = winAt.height;
852 wp->width = winAt.width;
853 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
858 { // wrapper to shield use of window handles from back-end (make addressible by number?)
859 // In XBoard this will have to wait until awareness of window parameters is implemented
860 GetActualPlacement(shellWidget, &wpMain);
861 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
862 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
863 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
864 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
865 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
866 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
870 PrintCommPortSettings (FILE *f, char *name)
871 { // This option does not exist in XBoard
875 EnsureOnScreen (int *x, int *y, int minX, int minY)
882 { // [HGM] args: allows testing if main window is realized from back-end
883 return xBoardWindow != 0;
889 extern Option dualOptions[];
891 Window tmp = xBoardWindow;
892 if(!dual) dual = XtWindow(dualOptions[3].handle); // must be first call
893 xBoardWindow = dual; // swap them
898 PopUpStartupDialog ()
899 { // start menu not implemented in XBoard
903 ConvertToLine (int argc, char **argv)
905 static char line[128*1024], buf[1024];
909 for(i=1; i<argc; i++)
911 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
912 && argv[i][0] != '{' )
913 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
915 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
916 strncat(line, buf, 128*1024 - strlen(line) - 1 );
919 line[strlen(line)-1] = NULLCHAR;
923 //--------------------------------------------------------------------------------------------
925 #define BoardSize int
927 InitDrawingSizes (BoardSize boardSize, int flags)
928 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
929 Dimension boardWidth, boardHeight, w, h;
931 static Dimension oldWidth, oldHeight;
932 static VariantClass oldVariant;
933 static int oldMono = -1, oldTwoBoards = 0;
935 if(!formWidget) return;
937 if(oldTwoBoards && !twoBoards) PopDown(DummyDlg);
938 oldTwoBoards = twoBoards;
940 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
941 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
942 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
944 if(boardWidth != oldWidth || boardHeight != oldHeight) { // do resizing stuff only if size actually changed
946 oldWidth = boardWidth; oldHeight = boardHeight;
950 * Inhibit shell resizing.
952 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW ;
953 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
954 shellArgs[4].value = shellArgs[2].value = w;
955 shellArgs[5].value = shellArgs[3].value = h;
956 XtSetValues(shellWidget, &shellArgs[0], 6);
958 XSync(xDisplay, False);
962 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
965 if(gameInfo.variant != oldVariant) { // and only if variant changed
970 for(p=0; p<=(int)WhiteKing; p++)
971 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
972 if(gameInfo.variant == VariantShogi) {
973 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
974 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
975 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
976 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
977 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
980 if(gameInfo.variant == VariantGothic) {
981 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
984 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
985 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
986 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
989 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
990 for(p=0; p<=(int)WhiteKing; p++)
991 ximMaskPm[p] = ximMaskPm2[p]; // defaults
992 if(gameInfo.variant == VariantShogi) {
993 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
994 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
995 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
996 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
997 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1000 if(gameInfo.variant == VariantGothic) {
1001 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1004 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1005 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1006 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1011 for(i=0; i<2; i++) {
1013 for(p=0; p<=(int)WhiteKing; p++)
1014 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1015 if(gameInfo.variant == VariantShogi) {
1016 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1017 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1018 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1019 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1020 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1023 if(gameInfo.variant == VariantGothic) {
1024 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1027 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1028 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1029 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1033 oldMono = -10; // kludge to force recreation of animation masks
1034 oldVariant = gameInfo.variant;
1037 if(appData.monoMode != oldMono)
1040 oldMono = appData.monoMode;
1044 MakeOneColor (char *name, Pixel *color)
1046 XrmValue vFrom, vTo;
1047 if (!appData.monoMode) {
1048 vFrom.addr = (caddr_t) name;
1049 vFrom.size = strlen(name);
1050 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1051 if (vTo.addr == NULL) {
1052 appData.monoMode = True;
1055 *color = *(Pixel *) vTo.addr;
1063 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1064 int forceMono = False;
1066 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1067 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1068 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1069 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1070 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1071 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1072 if (appData.lowTimeWarning)
1073 forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
1074 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1075 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1082 { // [HGM] taken out of main
1084 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1085 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1086 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1088 if (appData.bitmapDirectory[0] != NULLCHAR) {
1092 CreateXPMBoard(appData.liteBackTextureFile, 1);
1093 CreateXPMBoard(appData.darkBackTextureFile, 0);
1097 /* Create regular pieces */
1098 if (!useImages) CreatePieces();
1103 InitDrawingParams ()
1105 MakeColors(); CreateGCs(True);
1110 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
1111 { // detervtomine what fonts to use, and create them
1115 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1116 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1117 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1118 appData.font = fontTable[MESSAGE_FONT][squareSize];
1119 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1120 appData.coordFont = fontTable[COORD_FONT][squareSize];
1123 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1124 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1125 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1126 fontSet = CreateFontSet(appData.font);
1127 clockFontSet = CreateFontSet(appData.clockFont);
1129 /* For the coordFont, use the 0th font of the fontset. */
1130 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1131 XFontStruct **font_struct_list;
1132 XFontSetExtents *fontSize;
1133 char **font_name_list;
1134 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1135 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1136 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1137 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1138 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1141 appData.font = FindFont(appData.font, fontPxlSize);
1142 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1143 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1144 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1145 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1146 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1147 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1148 // textHeight in !NLS mode!
1150 countFontID = coordFontID; // [HGM] holdings
1151 countFontStruct = coordFontStruct;
1153 xdb = XtDatabase(xDisplay);
1155 XrmPutLineResource(&xdb, "*international: True");
1156 vTo.size = sizeof(XFontSet);
1157 vTo.addr = (XtPointer) &fontSet;
1158 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1160 XrmPutStringResource(&xdb, "*font", appData.font);
1165 main (int argc, char **argv)
1167 int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1168 XSetWindowAttributes window_attributes;
1170 Dimension boardWidth, boardHeight, w, h;
1172 int forceMono = False;
1174 srandom(time(0)); // [HGM] book: make random truly random
1176 setbuf(stdout, NULL);
1177 setbuf(stderr, NULL);
1180 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1181 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1185 programName = strrchr(argv[0], '/');
1186 if (programName == NULL)
1187 programName = argv[0];
1192 XtSetLanguageProc(NULL, NULL, NULL);
1193 if (appData.debugMode) {
1194 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1197 bindtextdomain(PACKAGE, LOCALEDIR);
1198 textdomain(PACKAGE);
1201 appData.boardSize = "";
1202 InitAppData(ConvertToLine(argc, argv));
1204 if (p == NULL) p = "/tmp";
1205 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1206 gameCopyFilename = (char*) malloc(i);
1207 gamePasteFilename = (char*) malloc(i);
1208 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1209 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1211 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1212 static char buf[MSG_SIZ];
1213 EscapeExpand(buf, appData.firstInitString);
1214 appData.firstInitString = strdup(buf);
1215 EscapeExpand(buf, appData.secondInitString);
1216 appData.secondInitString = strdup(buf);
1217 EscapeExpand(buf, appData.firstComputerString);
1218 appData.firstComputerString = strdup(buf);
1219 EscapeExpand(buf, appData.secondComputerString);
1220 appData.secondComputerString = strdup(buf);
1223 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1226 if (chdir(chessDir) != 0) {
1227 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1233 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1234 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1235 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1236 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1239 setbuf(debugFP, NULL);
1242 /* [HGM,HR] make sure board size is acceptable */
1243 if(appData.NrFiles > BOARD_FILES ||
1244 appData.NrRanks > BOARD_RANKS )
1245 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1248 /* This feature does not work; animation needs a rewrite */
1249 appData.highlightDragging = FALSE;
1253 gameInfo.variant = StringToVariant(appData.variant);
1254 InitPosition(FALSE);
1257 XtAppInitialize(&appContext, "XBoard", shellOptions,
1258 XtNumber(shellOptions),
1259 &argc, argv, xboardResources, NULL, 0);
1261 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1262 clientResources, XtNumber(clientResources),
1265 xDisplay = XtDisplay(shellWidget);
1266 xScreen = DefaultScreen(xDisplay);
1267 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1270 * determine size, based on supplied or remembered -size, or screen size
1272 if (isdigit(appData.boardSize[0])) {
1273 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1274 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1275 &fontPxlSize, &smallLayout, &tinyLayout);
1277 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1278 programName, appData.boardSize);
1282 /* Find some defaults; use the nearest known size */
1283 SizeDefaults *szd, *nearest;
1284 int distance = 99999;
1285 nearest = szd = sizeDefaults;
1286 while (szd->name != NULL) {
1287 if (abs(szd->squareSize - squareSize) < distance) {
1289 distance = abs(szd->squareSize - squareSize);
1290 if (distance == 0) break;
1294 if (i < 2) lineGap = nearest->lineGap;
1295 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1296 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1297 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1298 if (i < 6) smallLayout = nearest->smallLayout;
1299 if (i < 7) tinyLayout = nearest->tinyLayout;
1302 SizeDefaults *szd = sizeDefaults;
1303 if (*appData.boardSize == NULLCHAR) {
1304 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1305 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1308 if (szd->name == NULL) szd--;
1309 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1311 while (szd->name != NULL &&
1312 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1313 if (szd->name == NULL) {
1314 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1315 programName, appData.boardSize);
1319 squareSize = szd->squareSize;
1320 lineGap = szd->lineGap;
1321 clockFontPxlSize = szd->clockFontPxlSize;
1322 coordFontPxlSize = szd->coordFontPxlSize;
1323 fontPxlSize = szd->fontPxlSize;
1324 smallLayout = szd->smallLayout;
1325 tinyLayout = szd->tinyLayout;
1326 // [HGM] font: use defaults from settings file if available and not overruled
1329 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1330 if (strlen(appData.pixmapDirectory) > 0) {
1331 p = ExpandPathName(appData.pixmapDirectory);
1333 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1334 appData.pixmapDirectory);
1337 if (appData.debugMode) {
1338 fprintf(stderr, _("\
1339 XBoard square size (hint): %d\n\
1340 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1342 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1343 if (appData.debugMode) {
1344 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1347 defaultLineGap = lineGap;
1348 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1350 /* [HR] height treated separately (hacked) */
1351 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1352 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1355 * Determine what fonts to use.
1357 InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1360 * Detect if there are not enough colors available and adapt.
1362 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1363 appData.monoMode = True;
1366 forceMono = MakeColors();
1369 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1371 appData.monoMode = True;
1374 if (appData.monoMode && appData.debugMode) {
1375 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1376 (unsigned long) XWhitePixel(xDisplay, xScreen),
1377 (unsigned long) XBlackPixel(xDisplay, xScreen));
1380 ParseIcsTextColors();
1382 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1388 layoutName = "tinyLayout";
1389 } else if (smallLayout) {
1390 layoutName = "smallLayout";
1392 layoutName = "normalLayout";
1395 optList = BoardPopUp(squareSize, lineGap, (void*)
1401 boardWidget = optList[W_BOARD].handle;
1402 menuBarWidget = optList[W_MENU].handle;
1403 dropMenu = optList[W_DROP].handle;
1404 titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1405 formWidget = XtParent(boardWidget);
1406 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1407 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1408 XtGetValues(optList[W_WHITE].handle, args, 2);
1409 if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1410 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1411 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1412 XtGetValues(optList[W_PAUSE].handle, args, 2);
1414 AppendEnginesToMenu(appData.recentEngineList);
1416 xBoardWindow = XtWindow(boardWidget);
1418 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1419 // not need to go into InitDrawingSizes().
1422 * Create X checkmark bitmap and initialize option menu checks.
1424 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1425 checkmark_bits, checkmark_width, checkmark_height);
1431 ReadBitmap(&wIconPixmap, "icon_white.bm",
1432 icon_white_bits, icon_white_width, icon_white_height);
1433 ReadBitmap(&bIconPixmap, "icon_black.bm",
1434 icon_black_bits, icon_black_width, icon_black_height);
1435 iconPixmap = wIconPixmap;
1437 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1438 XtSetValues(shellWidget, args, i);
1441 * Create a cursor for the board widget.
1443 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1444 XChangeWindowAttributes(xDisplay, xBoardWindow,
1445 CWCursor, &window_attributes);
1448 * Inhibit shell resizing.
1450 shellArgs[0].value = (XtArgVal) &w;
1451 shellArgs[1].value = (XtArgVal) &h;
1452 XtGetValues(shellWidget, shellArgs, 2);
1453 shellArgs[4].value = shellArgs[2].value = w;
1454 shellArgs[5].value = shellArgs[3].value = h;
1455 XtSetValues(shellWidget, &shellArgs[2], 4);
1456 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1457 marginH = h - boardHeight;
1459 CatchDeleteWindow(shellWidget, "QuitProc");
1465 if (appData.animate || appData.animateDragging)
1468 XtAugmentTranslations(formWidget,
1469 XtParseTranslationTable(globalTranslations));
1471 XtAddEventHandler(formWidget, KeyPressMask, False,
1472 (XtEventHandler) MoveTypeInProc, NULL);
1473 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1474 (XtEventHandler) EventProc, NULL);
1476 /* [AS] Restore layout */
1477 if( wpMoveHistory.visible ) {
1481 if( wpEvalGraph.visible )
1486 if( wpEngineOutput.visible ) {
1487 EngineOutputPopUp();
1492 if (errorExitStatus == -1) {
1493 if (appData.icsActive) {
1494 /* We now wait until we see "login:" from the ICS before
1495 sending the logon script (problems with timestamp otherwise) */
1496 /*ICSInitScript();*/
1497 if (appData.icsInputBox) ICSInputBoxPopUp();
1501 signal(SIGWINCH, TermSizeSigHandler);
1503 signal(SIGINT, IntSigHandler);
1504 signal(SIGTERM, IntSigHandler);
1505 if (*appData.cmailGameName != NULLCHAR) {
1506 signal(SIGUSR1, CmailSigHandler);
1510 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1512 // XtSetKeyboardFocus(shellWidget, formWidget);
1513 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1515 XtAppMainLoop(appContext);
1516 if (appData.debugMode) fclose(debugFP); // [DM] debug
1521 TermSizeSigHandler (int sig)
1527 IntSigHandler (int sig)
1533 CmailSigHandler (int sig)
1538 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1540 /* Activate call-back function CmailSigHandlerCallBack() */
1541 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1543 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1547 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1550 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1552 /**** end signal code ****/
1555 #define Abs(n) ((n)<0 ? -(n) : (n))
1559 InsertPxlSize (char *pattern, int targetPxlSize)
1561 char *base_fnt_lst, strInt[12], *p, *q;
1562 int alternatives, i, len, strIntLen;
1565 * Replace the "*" (if present) in the pixel-size slot of each
1566 * alternative with the targetPxlSize.
1570 while ((p = strchr(p, ',')) != NULL) {
1574 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1575 strIntLen = strlen(strInt);
1576 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1580 while (alternatives--) {
1581 char *comma = strchr(p, ',');
1582 for (i=0; i<14; i++) {
1583 char *hyphen = strchr(p, '-');
1585 if (comma && hyphen > comma) break;
1586 len = hyphen + 1 - p;
1587 if (i == 7 && *p == '*' && len == 2) {
1589 memcpy(q, strInt, strIntLen);
1599 len = comma + 1 - p;
1606 return base_fnt_lst;
1610 CreateFontSet (char *base_fnt_lst)
1613 char **missing_list;
1617 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1618 &missing_list, &missing_count, &def_string);
1619 if (appData.debugMode) {
1621 XFontStruct **font_struct_list;
1622 char **font_name_list;
1623 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1625 fprintf(debugFP, " got list %s, locale %s\n",
1626 XBaseFontNameListOfFontSet(fntSet),
1627 XLocaleOfFontSet(fntSet));
1628 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1629 for (i = 0; i < count; i++) {
1630 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1633 for (i = 0; i < missing_count; i++) {
1634 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1637 if (fntSet == NULL) {
1638 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1643 #else // not ENABLE_NLS
1645 * Find a font that matches "pattern" that is as close as
1646 * possible to the targetPxlSize. Prefer fonts that are k
1647 * pixels smaller to fonts that are k pixels larger. The
1648 * pattern must be in the X Consortium standard format,
1649 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1650 * The return value should be freed with XtFree when no
1654 FindFont (char *pattern, int targetPxlSize)
1656 char **fonts, *p, *best, *scalable, *scalableTail;
1657 int i, j, nfonts, minerr, err, pxlSize;
1659 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1661 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1662 programName, pattern);
1669 for (i=0; i<nfonts; i++) {
1672 if (*p != '-') continue;
1674 if (*p == NULLCHAR) break;
1675 if (*p++ == '-') j++;
1677 if (j < 7) continue;
1680 scalable = fonts[i];
1683 err = pxlSize - targetPxlSize;
1684 if (Abs(err) < Abs(minerr) ||
1685 (minerr > 0 && err < 0 && -err == minerr)) {
1691 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1692 /* If the error is too big and there is a scalable font,
1693 use the scalable font. */
1694 int headlen = scalableTail - scalable;
1695 p = (char *) XtMalloc(strlen(scalable) + 10);
1696 while (isdigit(*scalableTail)) scalableTail++;
1697 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1699 p = (char *) XtMalloc(strlen(best) + 2);
1700 safeStrCpy(p, best, strlen(best)+1 );
1702 if (appData.debugMode) {
1703 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
1704 pattern, targetPxlSize, p);
1706 XFreeFontNames(fonts);
1713 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
1714 // must be called before all non-first callse to CreateGCs()
1715 XtReleaseGC(shellWidget, highlineGC);
1716 XtReleaseGC(shellWidget, lightSquareGC);
1717 XtReleaseGC(shellWidget, darkSquareGC);
1718 XtReleaseGC(shellWidget, lineGC);
1719 if (appData.monoMode) {
1720 if (DefaultDepth(xDisplay, xScreen) == 1) {
1721 XtReleaseGC(shellWidget, wbPieceGC);
1723 XtReleaseGC(shellWidget, bwPieceGC);
1726 XtReleaseGC(shellWidget, prelineGC);
1727 XtReleaseGC(shellWidget, wdPieceGC);
1728 XtReleaseGC(shellWidget, wlPieceGC);
1729 XtReleaseGC(shellWidget, bdPieceGC);
1730 XtReleaseGC(shellWidget, blPieceGC);
1735 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
1737 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1738 | GCBackground | GCFunction | GCPlaneMask;
1739 gc_values->foreground = foreground;
1740 gc_values->background = background;
1741 return XtGetGC(shellWidget, value_mask, gc_values);
1745 CreateGCs (int redo)
1747 XGCValues gc_values;
1749 Pixel white = XWhitePixel(xDisplay, xScreen);
1750 Pixel black = XBlackPixel(xDisplay, xScreen);
1752 gc_values.plane_mask = AllPlanes;
1753 gc_values.line_width = lineGap;
1754 gc_values.line_style = LineSolid;
1755 gc_values.function = GXcopy;
1758 DeleteGCs(); // called a second time; clean up old GCs first
1759 } else { // [HGM] grid and font GCs created on first call only
1760 coordGC = CreateOneGC(&gc_values, black, white);
1761 XSetFont(xDisplay, coordGC, coordFontID);
1763 // [HGM] make font for holdings counts (white on black)
1764 countGC = CreateOneGC(&gc_values, white, black);
1765 XSetFont(xDisplay, countGC, countFontID);
1767 lineGC = CreateOneGC(&gc_values, black, black);
1769 if (appData.monoMode) {
1771 highlineGC = CreateOneGC(&gc_values, white, white);
1772 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
1773 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
1775 if (DefaultDepth(xDisplay, xScreen) == 1) {
1776 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
1777 gc_values.function = GXcopyInverted;
1778 copyInvertedGC = CreateOneGC(&gc_values, black, white);
1779 gc_values.function = GXcopy;
1780 if (XBlackPixel(xDisplay, xScreen) == 1) {
1781 bwPieceGC = darkSquareGC;
1782 wbPieceGC = copyInvertedGC;
1784 bwPieceGC = copyInvertedGC;
1785 wbPieceGC = lightSquareGC;
1790 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
1791 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
1792 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
1793 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
1794 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
1795 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
1796 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
1797 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
1802 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
1810 fp = fopen(filename, "rb");
1812 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
1819 for (y=0; y<h; ++y) {
1820 for (x=0; x<h; ++x) {
1825 XPutPixel(xim, x, y, blackPieceColor);
1827 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1830 XPutPixel(xim, x, y, darkSquareColor);
1832 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1835 XPutPixel(xim, x, y, whitePieceColor);
1837 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
1840 XPutPixel(xim, x, y, lightSquareColor);
1842 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
1850 /* create Pixmap of piece */
1851 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1853 XPutImage(xDisplay, *dest, lightSquareGC, xim,
1856 /* create Pixmap of clipmask
1857 Note: We assume the white/black pieces have the same
1858 outline, so we make only 6 masks. This is okay
1859 since the XPM clipmask routines do the same. */
1861 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1863 XPutImage(xDisplay, temp, lightSquareGC, xmask,
1866 /* now create the 1-bit version */
1867 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
1870 values.foreground = 1;
1871 values.background = 0;
1873 /* Don't use XtGetGC, not read only */
1874 maskGC = XCreateGC(xDisplay, *mask,
1875 GCForeground | GCBackground, &values);
1876 XCopyPlane(xDisplay, temp, *mask, maskGC,
1877 0, 0, squareSize, squareSize, 0, 0, 1);
1878 XFreePixmap(xDisplay, temp);
1883 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
1891 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
1896 /* The XSynchronize calls were copied from CreatePieces.
1897 Not sure if needed, but can't hurt */
1898 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
1901 /* temp needed by loadXIM() */
1902 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1903 0, 0, ss, ss, AllPlanes, XYPixmap);
1905 if (strlen(appData.pixmapDirectory) == 0) {
1909 if (appData.monoMode) {
1910 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
1914 fprintf(stderr, _("\nLoading XIMs...\n"));
1916 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
1917 fprintf(stderr, "%d", piece+1);
1918 for (kind=0; kind<4; kind++) {
1919 fprintf(stderr, ".");
1920 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
1921 ExpandPathName(appData.pixmapDirectory),
1922 piece <= (int) WhiteKing ? "" : "w",
1923 pieceBitmapNames[piece],
1925 ximPieceBitmap[kind][piece] =
1926 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1927 0, 0, ss, ss, AllPlanes, XYPixmap);
1928 if (appData.debugMode)
1929 fprintf(stderr, _("(File:%s:) "), buf);
1930 loadXIM(ximPieceBitmap[kind][piece],
1932 &(xpmPieceBitmap2[kind][piece]),
1933 &(ximMaskPm2[piece]));
1934 if(piece <= (int)WhiteKing)
1935 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
1937 fprintf(stderr," ");
1939 /* Load light and dark squares */
1940 /* If the LSQ and DSQ pieces don't exist, we will
1941 draw them with solid squares. */
1942 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
1943 if (access(buf, 0) != 0) {
1947 fprintf(stderr, _("light square "));
1949 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1950 0, 0, ss, ss, AllPlanes, XYPixmap);
1951 if (appData.debugMode)
1952 fprintf(stderr, _("(File:%s:) "), buf);
1954 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
1955 fprintf(stderr, _("dark square "));
1956 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
1957 ExpandPathName(appData.pixmapDirectory), ss);
1958 if (appData.debugMode)
1959 fprintf(stderr, _("(File:%s:) "), buf);
1961 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
1962 0, 0, ss, ss, AllPlanes, XYPixmap);
1963 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
1964 xpmJailSquare = xpmLightSquare;
1966 fprintf(stderr, _("Done.\n"));
1968 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
1971 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
1975 CreateXPMBoard (char *s, int kind)
1979 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
1980 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
1981 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
1987 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
1988 // thisroutine has to be called t free the old piece pixmaps
1990 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
1991 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
1993 XFreePixmap(xDisplay, xpmLightSquare);
1994 XFreePixmap(xDisplay, xpmDarkSquare);
2003 u_int ss = squareSize;
2005 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2006 XpmColorSymbol symbols[4];
2007 static int redo = False;
2009 if(redo) FreeXPMPieces(); else redo = 1;
2011 /* The XSynchronize calls were copied from CreatePieces.
2012 Not sure if needed, but can't hurt */
2013 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2015 /* Setup translations so piece colors match square colors */
2016 symbols[0].name = "light_piece";
2017 symbols[0].value = appData.whitePieceColor;
2018 symbols[1].name = "dark_piece";
2019 symbols[1].value = appData.blackPieceColor;
2020 symbols[2].name = "light_square";
2021 symbols[2].value = appData.lightSquareColor;
2022 symbols[3].name = "dark_square";
2023 symbols[3].value = appData.darkSquareColor;
2025 attr.valuemask = XpmColorSymbols;
2026 attr.colorsymbols = symbols;
2027 attr.numsymbols = 4;
2029 if (appData.monoMode) {
2030 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2034 if (strlen(appData.pixmapDirectory) == 0) {
2035 XpmPieces* pieces = builtInXpms;
2038 while (pieces->size != squareSize && pieces->size) pieces++;
2039 if (!pieces->size) {
2040 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2043 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2044 for (kind=0; kind<4; kind++) {
2046 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2047 pieces->xpm[piece][kind],
2048 &(xpmPieceBitmap2[kind][piece]),
2049 NULL, &attr)) != 0) {
2050 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2054 if(piece <= (int) WhiteKing)
2055 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2059 xpmJailSquare = xpmLightSquare;
2063 fprintf(stderr, _("\nLoading XPMs...\n"));
2066 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2067 fprintf(stderr, "%d ", piece+1);
2068 for (kind=0; kind<4; kind++) {
2069 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2070 ExpandPathName(appData.pixmapDirectory),
2071 piece > (int) WhiteKing ? "w" : "",
2072 pieceBitmapNames[piece],
2074 if (appData.debugMode) {
2075 fprintf(stderr, _("(File:%s:) "), buf);
2077 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2078 &(xpmPieceBitmap2[kind][piece]),
2079 NULL, &attr)) != 0) {
2080 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2081 // [HGM] missing: read of unorthodox piece failed; substitute King.
2082 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2083 ExpandPathName(appData.pixmapDirectory),
2085 if (appData.debugMode) {
2086 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2088 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2089 &(xpmPieceBitmap2[kind][piece]),
2093 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2098 if(piece <= (int) WhiteKing)
2099 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2102 /* Load light and dark squares */
2103 /* If the LSQ and DSQ pieces don't exist, we will
2104 draw them with solid squares. */
2105 fprintf(stderr, _("light square "));
2106 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2107 if (access(buf, 0) != 0) {
2111 if (appData.debugMode)
2112 fprintf(stderr, _("(File:%s:) "), buf);
2114 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2115 &xpmLightSquare, NULL, &attr)) != 0) {
2116 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2119 fprintf(stderr, _("dark square "));
2120 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2121 ExpandPathName(appData.pixmapDirectory), ss);
2122 if (appData.debugMode) {
2123 fprintf(stderr, _("(File:%s:) "), buf);
2125 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2126 &xpmDarkSquare, NULL, &attr)) != 0) {
2127 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2131 xpmJailSquare = xpmLightSquare;
2132 fprintf(stderr, _("Done.\n"));
2134 oldVariant = -1; // kludge to force re-makig of animation masks
2135 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2138 #endif /* HAVE_LIBXPM */
2141 /* No built-in bitmaps */
2146 u_int ss = squareSize;
2148 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2151 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2152 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2153 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2154 pieceBitmapNames[piece],
2155 ss, kind == SOLID ? 's' : 'o');
2156 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2157 if(piece <= (int)WhiteKing)
2158 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2162 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2166 /* With built-in bitmaps */
2170 BuiltInBits* bib = builtInBits;
2173 u_int ss = squareSize;
2175 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2178 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2180 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2181 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2182 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2183 pieceBitmapNames[piece],
2184 ss, kind == SOLID ? 's' : 'o');
2185 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2186 bib->bits[kind][piece], ss, ss);
2187 if(piece <= (int)WhiteKing)
2188 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2192 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2198 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2203 char msg[MSG_SIZ], fullname[MSG_SIZ];
2205 if (*appData.bitmapDirectory != NULLCHAR) {
2206 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2207 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2208 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2209 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2210 &w, &h, pm, &x_hot, &y_hot);
2211 fprintf(stderr, "load %s\n", name);
2212 if (errcode != BitmapSuccess) {
2214 case BitmapOpenFailed:
2215 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2217 case BitmapFileInvalid:
2218 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2220 case BitmapNoMemory:
2221 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2225 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2229 fprintf(stderr, _("%s: %s...using built-in\n"),
2231 } else if (w != wreq || h != hreq) {
2233 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2234 programName, fullname, w, h, wreq, hreq);
2240 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2250 if (lineGap == 0) return;
2252 /* [HR] Split this into 2 loops for non-square boards. */
2254 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2255 gridSegments[i].x1 = 0;
2256 gridSegments[i].x2 =
2257 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2258 gridSegments[i].y1 = gridSegments[i].y2
2259 = lineGap / 2 + (i * (squareSize + lineGap));
2262 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2263 gridSegments[j + i].y1 = 0;
2264 gridSegments[j + i].y2 =
2265 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2266 gridSegments[j + i].x1 = gridSegments[j + i].x2
2267 = lineGap / 2 + (j * (squareSize + lineGap));
2272 MarkMenuItem (char *menuRef, int state)
2274 MenuItem *item = MenuNameToItem(menuRef);
2278 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2279 XtSetValues(item->handle, args, 1);
2284 EnableMenuItem (char *menuRef, int state)
2286 MenuItem *item = MenuNameToItem(menuRef);
2288 if(item) XtSetSensitive(item->handle, state);
2292 EnableButtonBar (int state)
2294 XtSetSensitive(optList[W_BUTTON].handle, state);
2299 SetMenuEnables (Enables *enab)
2301 while (enab->name != NULL) {
2302 EnableMenuItem(enab->name, enab->value);
2308 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2309 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2311 if(*nprms == 0) return;
2312 item = MenuNameToItem(prms[0]);
2313 if(item) ((MenuProc *) item->proc) ();
2317 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2319 RecentEngineEvent((int) (intptr_t) addr);
2323 AppendMenuItem (char *msg, int n)
2325 CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
2337 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2338 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2339 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2340 dmEnables[i].piece);
2341 XtSetSensitive(entry, p != NULL || !appData.testLegality
2342 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2343 && !appData.icsActive));
2345 while (p && *p++ == dmEnables[i].piece) count++;
2346 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2348 XtSetArg(args[j], XtNlabel, label); j++;
2349 XtSetValues(entry, args, j);
2355 do_flash_delay (unsigned long msec)
2361 DrawBorder (int x, int y, int type)
2365 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
2367 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
2368 squareSize+lineGap, squareSize+lineGap);
2372 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
2374 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
2375 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
2377 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
2378 if(textureW[kind] < W*squareSize)
2379 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
2381 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
2382 if(textureH[kind] < H*squareSize)
2383 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
2385 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
2390 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
2391 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
2393 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
2394 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
2395 squareSize, squareSize, x*fac, y*fac);
2397 if (useImages && useImageSqs) {
2401 pm = xpmLightSquare;
2406 case 2: /* neutral */
2408 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
2411 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
2412 squareSize, squareSize, x*fac, y*fac);
2422 case 2: /* neutral */
2427 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
2432 I split out the routines to draw a piece so that I could
2433 make a generic flash routine.
2436 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2438 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2439 switch (square_color) {
2441 case 2: /* neutral */
2443 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2444 ? *pieceToOutline(piece)
2445 : *pieceToSolid(piece),
2446 dest, bwPieceGC, 0, 0,
2447 squareSize, squareSize, x, y);
2450 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
2451 ? *pieceToSolid(piece)
2452 : *pieceToOutline(piece),
2453 dest, wbPieceGC, 0, 0,
2454 squareSize, squareSize, x, y);
2460 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2462 switch (square_color) {
2464 case 2: /* neutral */
2466 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2467 ? *pieceToOutline(piece)
2468 : *pieceToSolid(piece),
2469 dest, bwPieceGC, 0, 0,
2470 squareSize, squareSize, x, y, 1);
2473 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
2474 ? *pieceToSolid(piece)
2475 : *pieceToOutline(piece),
2476 dest, wbPieceGC, 0, 0,
2477 squareSize, squareSize, x, y, 1);
2483 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2485 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
2486 switch (square_color) {
2488 XCopyPlane(xDisplay, *pieceToSolid(piece),
2489 dest, (int) piece < (int) BlackPawn
2490 ? wlPieceGC : blPieceGC, 0, 0,
2491 squareSize, squareSize, x, y, 1);
2494 XCopyPlane(xDisplay, *pieceToSolid(piece),
2495 dest, (int) piece < (int) BlackPawn
2496 ? wdPieceGC : bdPieceGC, 0, 0,
2497 squareSize, squareSize, x, y, 1);
2499 case 2: /* neutral */
2501 break; // should never contain pieces
2506 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
2508 int kind, p = piece;
2510 switch (square_color) {
2512 case 2: /* neutral */
2514 if ((int)piece < (int) BlackPawn) {
2522 if ((int)piece < (int) BlackPawn) {
2530 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
2531 if(useTexture & square_color+1) {
2532 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
2533 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
2534 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
2535 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
2536 XSetClipMask(xDisplay, wlPieceGC, None);
2537 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
2539 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
2540 dest, wlPieceGC, 0, 0,
2541 squareSize, squareSize, x, y);
2544 typedef void (*DrawFunc)();
2549 if (appData.monoMode) {
2550 if (DefaultDepth(xDisplay, xScreen) == 1) {
2551 return monoDrawPiece_1bit;
2553 return monoDrawPiece;
2557 return colorDrawPieceImage;
2559 return colorDrawPiece;
2564 DrawDot (int marker, int x, int y, int r)
2566 if(appData.monoMode) {
2567 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
2568 x, y, r, r, 0, 64*360);
2569 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
2570 x, y, r, r, 0, 64*360);
2572 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
2573 x, y, r, r, 0, 64*360);
2577 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
2578 { // basic front-end board-draw function: takes care of everything that can be in square:
2579 // piece, background, coordinate/count, marker dot
2580 int direction, font_ascent, font_descent;
2581 XCharStruct overall;
2584 if (piece == EmptySquare) {
2585 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
2587 drawfunc = ChooseDrawFunc();
2588 drawfunc(piece, square_color, x, y, xBoardWindow);
2591 if(align) { // square carries inscription (coord or piece count)
2593 GC hGC = align < 3 ? coordGC : countGC;
2594 // first calculate where it goes
2595 XTextExtents(countFontStruct, string, 1, &direction,
2596 &font_ascent, &font_descent, &overall);
2598 xx += squareSize - overall.width - 2;
2599 yy += squareSize - font_descent - 1;
2600 } else if (align == 2) {
2601 xx += 2, yy += font_ascent + 1;
2602 } else if (align == 3) {
2603 xx += squareSize - overall.width - 2;
2604 yy += font_ascent + 1;
2605 } else if (align == 4) {
2606 xx += 2, yy += font_ascent + 1;
2609 if (appData.monoMode) {
2610 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2612 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
2616 if(marker) { // print fat marker dot, if requested
2617 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
2622 FlashDelay (int flash_delay)
2624 XSync(xDisplay, False);
2625 if(flash_delay) do_flash_delay(flash_delay);
2629 Fraction (int x, int start, int stop)
2631 double f = ((double) x - start)/(stop - start);
2632 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
2636 static WindowPlacement wpNew;
2639 CoDrag (Widget sh, WindowPlacement *wp)
2642 int j=0, touch=0, fudge = 2;
2643 GetActualPlacement(sh, wp);
2644 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
2645 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
2646 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
2647 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
2648 if(!touch ) return; // only windows that touch co-move
2649 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
2650 int heightInc = wpNew.height - wpMain.height;
2651 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2652 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
2653 wp->y += fracTop * heightInc;
2654 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
2655 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
2656 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
2657 int widthInc = wpNew.width - wpMain.width;
2658 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2659 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
2660 wp->y += fracLeft * widthInc;
2661 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
2662 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
2664 wp->x += wpNew.x - wpMain.x;
2665 wp->y += wpNew.y - wpMain.y;
2666 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
2667 if(touch == 3) wp->y += wpNew.height - wpMain.height;
2668 XtSetArg(args[j], XtNx, wp->x); j++;
2669 XtSetArg(args[j], XtNy, wp->y); j++;
2670 XtSetValues(sh, args, j);
2673 static XtIntervalId delayedDragID = 0;
2678 GetActualPlacement(shellWidget, &wpNew);
2679 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
2680 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
2681 return; // false alarm
2682 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
2683 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
2684 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
2685 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
2687 DrawPosition(True, NULL);
2688 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
2695 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
2697 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
2701 EventProc (Widget widget, caddr_t unused, XEvent *event)
2703 if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
2704 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
2707 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
2709 DrawSeekAxis (int x, int y, int xTo, int yTo)
2711 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
2715 DrawSeekBackground (int left, int top, int right, int bottom)
2717 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
2721 DrawSeekText (char *buf, int x, int y)
2723 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
2727 DrawSeekDot (int x, int y, int colorNr)
2729 int square = colorNr & 0x80;
2732 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
2734 XFillRectangle(xDisplay, xBoardWindow, color,
2735 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
2737 XFillArc(xDisplay, xBoardWindow, color,
2738 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
2744 XDrawSegments(xDisplay, xBoardWindow, lineGC,
2745 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
2750 * event handler for redrawing the board
2753 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2755 DrawPosition(True, NULL);
2760 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
2761 { // [HGM] pv: walk PV
2762 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
2765 static int savedIndex; /* gross that this is global */
2768 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
2771 XawTextPosition index, dummy;
2774 XawTextGetSelectionPos(w, &index, &dummy);
2775 XtSetArg(arg, XtNstring, &val);
2776 XtGetValues(w, &arg, 1);
2777 ReplaceComment(savedIndex, val);
2778 if(savedIndex != currentMove) ToNrEvent(savedIndex);
2779 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
2783 EditCommentPopUp (int index, char *title, char *text)
2786 if (text == NULL) text = "";
2787 NewCommentPopup(title, text, index);
2791 CommentPopUp (char *title, char *text)
2793 savedIndex = currentMove; // [HGM] vari
2794 NewCommentPopup(title, text, currentMove);
2800 PopDown(CommentDlg);
2803 static char *openName;
2809 (void) (*fileProc)(openFP, 0, openName);
2813 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
2815 fileProc = proc; /* I can't see a way not */
2816 fileOpenMode = openMode; /* to use globals here */
2817 { // [HGM] use file-selector dialog stolen from Ghostview
2818 // int index; // this is not supported yet
2819 Browse(BoardWindow, label, (def[0] ? def : NULL), filter, False, openMode, &openName, &openFP);
2824 /* Disable all user input other than deleting the window */
2825 static int frozen = 0;
2831 /* Grab by a widget that doesn't accept input */
2832 XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
2836 /* Undo a FreezeUI */
2840 if (!frozen) return;
2841 XtRemoveGrab(optList[W_MESSG].handle);
2849 static int oldPausing = FALSE;
2850 static GameMode oldmode = (GameMode) -1;
2853 if (!boardWidget || !XtIsRealized(boardWidget)) return;
2855 if (pausing != oldPausing) {
2856 oldPausing = pausing;
2857 MarkMenuItem("Mode.Pause", pausing);
2859 if (appData.showButtonBar) {
2860 /* Always toggle, don't set. Previous code messes up when
2861 invoked while the button is pressed, as releasing it
2862 toggles the state again. */
2865 XtSetArg(args[0], XtNbackground, &oldbg);
2866 XtSetArg(args[1], XtNforeground, &oldfg);
2867 XtGetValues(optList[W_PAUSE].handle,
2869 XtSetArg(args[0], XtNbackground, oldfg);
2870 XtSetArg(args[1], XtNforeground, oldbg);
2872 XtSetValues(optList[W_PAUSE].handle, args, 2);
2876 wname = ModeToWidgetName(oldmode);
2877 if (wname != NULL) {
2878 MarkMenuItem(wname, False);
2880 wname = ModeToWidgetName(gameMode);
2881 if (wname != NULL) {
2882 MarkMenuItem(wname, True);
2885 MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
2887 /* Maybe all the enables should be handled here, not just this one */
2888 EnableMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
2893 * Button/menu procedures
2896 /* this variable is shared between CopyPositionProc and SendPositionSelection */
2897 char *selected_fen_position=NULL;
2900 SendPositionSelection (Widget w, Atom *selection, Atom *target,
2901 Atom *type_return, XtPointer *value_return,
2902 unsigned long *length_return, int *format_return)
2904 char *selection_tmp;
2906 // if (!selected_fen_position) return False; /* should never happen */
2907 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
2908 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
2909 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
2912 if (f == NULL) return False;
2916 selection_tmp = XtMalloc(len + 1);
2917 count = fread(selection_tmp, 1, len, f);
2920 XtFree(selection_tmp);
2923 selection_tmp[len] = NULLCHAR;
2925 /* note: since no XtSelectionDoneProc was registered, Xt will
2926 * automatically call XtFree on the value returned. So have to
2927 * make a copy of it allocated with XtMalloc */
2928 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
2929 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
2932 *value_return=selection_tmp;
2933 *length_return=strlen(selection_tmp);
2934 *type_return=*target;
2935 *format_return = 8; /* bits per byte */
2937 } else if (*target == XA_TARGETS(xDisplay)) {
2938 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
2939 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
2940 targets_tmp[1] = XA_STRING;
2941 *value_return = targets_tmp;
2942 *type_return = XA_ATOM;
2945 // This code leads to a read of value_return out of bounds on 64-bit systems.
2946 // Other code which I have seen always sets *format_return to 32 independent of
2947 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
2948 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
2949 *format_return = 8 * sizeof(Atom);
2950 if (*format_return > 32) {
2951 *length_return *= *format_return / 32;
2952 *format_return = 32;
2955 *format_return = 32;
2963 /* note: when called from menu all parameters are NULL, so no clue what the
2964 * Widget which was clicked on was, or what the click event was
2967 CopySomething (char *src)
2969 selected_fen_position = src;
2971 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
2972 * have a notion of a position that is selected but not copied.
2973 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
2975 XtOwnSelection(menuBarWidget, XA_PRIMARY,
2977 SendPositionSelection,
2978 NULL/* lose_ownership_proc */ ,
2979 NULL/* transfer_done_proc */);
2980 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
2982 SendPositionSelection,
2983 NULL/* lose_ownership_proc */ ,
2984 NULL/* transfer_done_proc */);
2987 /* function called when the data to Paste is ready */
2989 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
2990 Atom *type, XtPointer value, unsigned long *len, int *format)
2993 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
2994 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
2995 EditPositionPasteFEN(fenstr);
2999 /* called when Paste Position button is pressed,
3000 * all parameters will be NULL */
3002 PastePositionProc ()
3004 XtGetSelectionValue(menuBarWidget,
3005 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3006 /* (XtSelectionCallbackProc) */ PastePositionCB,
3007 NULL, /* client_data passed to PastePositionCB */
3009 /* better to use the time field from the event that triggered the
3010 * call to this function, but that isn't trivial to get
3017 /* note: when called from menu all parameters are NULL, so no clue what the
3018 * Widget which was clicked on was, or what the click event was
3020 /* function called when the data to Paste is ready */
3022 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3023 Atom *type, XtPointer value, unsigned long *len, int *format)
3026 if (value == NULL || *len == 0) {
3027 return; /* nothing had been selected to copy */
3029 f = fopen(gamePasteFilename, "w");
3031 DisplayError(_("Can't open temp file"), errno);
3034 fwrite(value, 1, *len, f);
3037 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3040 /* called when Paste Game button is pressed,
3041 * all parameters will be NULL */
3045 XtGetSelectionValue(menuBarWidget,
3046 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3047 /* (XtSelectionCallbackProc) */ PasteGameCB,
3048 NULL, /* client_data passed to PasteGameCB */
3050 /* better to use the time field from the event that triggered the
3051 * call to this function, but that isn't trivial to get
3060 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3067 { // bassic primitive for determining if modifier keys are pressed
3068 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3071 XQueryKeymap(xDisplay,keys);
3072 for(i=0; i<6; i++) {
3074 j = XKeysymToKeycode(xDisplay, codes[i]);
3075 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3081 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3085 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3086 if ( n == 1 && *buf >= 32 // printable
3087 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3088 ) BoxAutoPopUp (buf);
3092 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3093 { // [HGM] input: let up-arrow recall previous line from history
3098 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3099 { // [HGM] input: let down-arrow recall next line from history
3104 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3110 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3112 if (!TempBackwardActive) {
3113 TempBackwardActive = True;
3119 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3121 /* Check to see if triggered by a key release event for a repeating key.
3122 * If so the next queued event will be a key press of the same key at the same time */
3123 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3125 XPeekEvent(xDisplay, &next);
3126 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3127 next.xkey.keycode == event->xkey.keycode)
3131 TempBackwardActive = False;
3135 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3136 { // called as key binding
3139 if (nprms && *nprms > 0)
3143 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3148 SetWindowTitle (char *text, char *title, char *icon)
3152 if (appData.titleInWindow) {
3154 XtSetArg(args[i], XtNlabel, text); i++;
3155 XtSetValues(titleWidget, args, i);
3158 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3159 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3160 XtSetValues(shellWidget, args, i);
3161 XSync(xDisplay, False);
3166 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3172 DisplayIcsInteractionTitle (String message)
3174 if (oldICSInteractionTitle == NULL) {
3175 /* Magic to find the old window title, adapted from vim */
3176 char *wina = getenv("WINDOWID");
3178 Window win = (Window) atoi(wina);
3179 Window root, parent, *children;
3180 unsigned int nchildren;
3181 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
3183 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
3184 if (!XQueryTree(xDisplay, win, &root, &parent,
3185 &children, &nchildren)) break;
3186 if (children) XFree((void *)children);
3187 if (parent == root || parent == 0) break;
3190 XSetErrorHandler(oldHandler);
3192 if (oldICSInteractionTitle == NULL) {
3193 oldICSInteractionTitle = "xterm";
3196 printf("\033]0;%s\007", message);
3201 XtIntervalId delayedEventTimerXID = 0;
3202 DelayedEventCallback delayedEventCallback = 0;
3207 delayedEventTimerXID = 0;
3208 delayedEventCallback();
3212 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
3214 if(delayedEventTimerXID && delayedEventCallback == cb)
3215 // [HGM] alive: replace, rather than add or flush identical event
3216 XtRemoveTimeOut(delayedEventTimerXID);
3217 delayedEventCallback = cb;
3218 delayedEventTimerXID =
3219 XtAppAddTimeOut(appContext, millisec,
3220 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
3223 DelayedEventCallback
3226 if (delayedEventTimerXID) {
3227 return delayedEventCallback;
3234 CancelDelayedEvent ()
3236 if (delayedEventTimerXID) {
3237 XtRemoveTimeOut(delayedEventTimerXID);
3238 delayedEventTimerXID = 0;
3242 XtIntervalId loadGameTimerXID = 0;
3245 LoadGameTimerRunning ()
3247 return loadGameTimerXID != 0;
3251 StopLoadGameTimer ()
3253 if (loadGameTimerXID != 0) {
3254 XtRemoveTimeOut(loadGameTimerXID);
3255 loadGameTimerXID = 0;
3263 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
3265 loadGameTimerXID = 0;
3270 StartLoadGameTimer (long millisec)
3273 XtAppAddTimeOut(appContext, millisec,
3274 (XtTimerCallbackProc) LoadGameTimerCallback,
3278 XtIntervalId analysisClockXID = 0;
3281 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
3283 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
3284 || appData.icsEngineAnalyze) { // [DM]
3285 AnalysisPeriodicEvent(0);
3286 StartAnalysisClock();
3291 StartAnalysisClock ()
3294 XtAppAddTimeOut(appContext, 2000,
3295 (XtTimerCallbackProc) AnalysisClockCallback,
3299 XtIntervalId clockTimerXID = 0;
3302 ClockTimerRunning ()
3304 return clockTimerXID != 0;
3310 if (clockTimerXID != 0) {
3311 XtRemoveTimeOut(clockTimerXID);
3320 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
3327 StartClockTimer (long millisec)
3330 XtAppAddTimeOut(appContext, millisec,
3331 (XtTimerCallbackProc) ClockTimerCallback,
3336 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
3340 Widget w = (Widget) opt->handle;
3342 /* check for low time warning */
3343 Pixel foregroundOrWarningColor = timerForegroundPixel;
3346 appData.lowTimeWarning &&
3347 (timer / 1000) < appData.icsAlarmTime)
3348 foregroundOrWarningColor = lowTimeWarningColor;
3350 if (appData.clockMode) {
3351 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
3352 XtSetArg(args[0], XtNlabel, buf);
3354 snprintf(buf, MSG_SIZ, "%s ", color);
3355 XtSetArg(args[0], XtNlabel, buf);
3360 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
3361 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
3363 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
3364 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
3367 XtSetValues(w, args, 3);
3370 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
3373 SetClockIcon (int color)
3376 Pixmap pm = *clockIcons[color];
3377 if (iconPixmap != pm) {
3379 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
3380 XtSetValues(shellWidget, args, 1);
3385 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
3387 InputSource *is = (InputSource *) closure;
3392 if (is->lineByLine) {
3393 count = read(is->fd, is->unused,
3394 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
3396 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
3399 is->unused += count;
3401 while (p < is->unused) {
3402 q = memchr(p, '\n', is->unused - p);
3403 if (q == NULL) break;
3405 (is->func)(is, is->closure, p, q - p, 0);
3409 while (p < is->unused) {
3414 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
3419 (is->func)(is, is->closure, is->buf, count, error);
3424 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
3427 ChildProc *cp = (ChildProc *) pr;
3429 is = (InputSource *) calloc(1, sizeof(InputSource));
3430 is->lineByLine = lineByLine;
3434 is->fd = fileno(stdin);
3436 is->kind = cp->kind;
3437 is->fd = cp->fdFrom;
3440 is->unused = is->buf;
3443 is->xid = XtAppAddInput(appContext, is->fd,
3444 (XtPointer) (XtInputReadMask),
3445 (XtInputCallbackProc) DoInputCallback,
3447 is->closure = closure;
3448 return (InputSourceRef) is;
3452 RemoveInputSource (InputSourceRef isr)
3454 InputSource *is = (InputSource *) isr;
3456 if (is->xid == 0) return;
3457 XtRemoveInput(is->xid);
3461 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
3463 /* Masks for XPM pieces. Black and white pieces can have
3464 different shapes, but in the interest of retaining my
3465 sanity pieces must have the same outline on both light
3466 and dark squares, and all pieces must use the same
3467 background square colors/images. */
3469 static int xpmDone = 0;
3470 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
3471 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
3474 CreateAnimMasks (int pieceDepth)
3480 unsigned long plane;
3483 /* Need a bitmap just to get a GC with right depth */
3484 buf = XCreatePixmap(xDisplay, xBoardWindow,
3486 values.foreground = 1;
3487 values.background = 0;
3488 /* Don't use XtGetGC, not read only */
3489 maskGC = XCreateGC(xDisplay, buf,
3490 GCForeground | GCBackground, &values);
3491 XFreePixmap(xDisplay, buf);
3493 buf = XCreatePixmap(xDisplay, xBoardWindow,
3494 squareSize, squareSize, pieceDepth);
3495 values.foreground = XBlackPixel(xDisplay, xScreen);
3496 values.background = XWhitePixel(xDisplay, xScreen);
3497 bufGC = XCreateGC(xDisplay, buf,
3498 GCForeground | GCBackground, &values);
3500 for (piece = WhitePawn; piece <= BlackKing; piece++) {
3501 /* Begin with empty mask */
3502 if(!xpmDone) // [HGM] pieces: keep using existing
3503 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
3504 squareSize, squareSize, 1);
3505 XSetFunction(xDisplay, maskGC, GXclear);
3506 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
3507 0, 0, squareSize, squareSize);
3509 /* Take a copy of the piece */
3514 XSetFunction(xDisplay, bufGC, GXcopy);
3515 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
3517 0, 0, squareSize, squareSize, 0, 0);
3519 /* XOR the background (light) over the piece */
3520 XSetFunction(xDisplay, bufGC, GXxor);
3522 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
3523 0, 0, squareSize, squareSize, 0, 0);
3525 XSetForeground(xDisplay, bufGC, lightSquareColor);
3526 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
3529 /* We now have an inverted piece image with the background
3530 erased. Construct mask by just selecting all the non-zero
3531 pixels - no need to reconstruct the original image. */
3532 XSetFunction(xDisplay, maskGC, GXor);
3534 /* Might be quicker to download an XImage and create bitmap
3535 data from it rather than this N copies per piece, but it
3536 only takes a fraction of a second and there is a much
3537 longer delay for loading the pieces. */
3538 for (n = 0; n < pieceDepth; n ++) {
3539 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
3540 0, 0, squareSize, squareSize,
3546 XFreePixmap(xDisplay, buf);
3547 XFreeGC(xDisplay, bufGC);
3548 XFreeGC(xDisplay, maskGC);
3552 InitAnimState (AnimNr anr, XWindowAttributes *info)
3557 /* Each buffer is square size, same depth as window */
3558 animBufs[anr+4] = xBoardWindow;
3559 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
3560 squareSize, squareSize, info->depth);
3561 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
3562 squareSize, squareSize, info->depth);
3564 /* Create a plain GC for blitting */
3565 mask = GCForeground | GCBackground | GCFunction |
3566 GCPlaneMask | GCGraphicsExposures;
3567 values.foreground = XBlackPixel(xDisplay, xScreen);
3568 values.background = XWhitePixel(xDisplay, xScreen);
3569 values.function = GXcopy;
3570 values.plane_mask = AllPlanes;
3571 values.graphics_exposures = False;
3572 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
3574 /* Piece will be copied from an existing context at
3575 the start of each new animation/drag. */
3576 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
3578 /* Outline will be a read-only copy of an existing */
3579 animGCs[anr+4] = None;
3585 XWindowAttributes info;
3587 if (xpmDone && gameInfo.variant == oldVariant) return;
3588 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
3589 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
3591 InitAnimState(Game, &info);
3592 InitAnimState(Player, &info);
3594 /* For XPM pieces, we need bitmaps to use as masks. */
3596 CreateAnimMasks(info.depth), xpmDone = 1;
3601 static Boolean frameWaiting;
3604 FrameAlarm (int sig)
3606 frameWaiting = False;
3607 /* In case System-V style signals. Needed?? */
3608 signal(SIGALRM, FrameAlarm);
3612 FrameDelay (int time)
3614 struct itimerval delay;
3616 XSync(xDisplay, False);
3619 frameWaiting = True;
3620 signal(SIGALRM, FrameAlarm);
3621 delay.it_interval.tv_sec =
3622 delay.it_value.tv_sec = time / 1000;
3623 delay.it_interval.tv_usec =
3624 delay.it_value.tv_usec = (time % 1000) * 1000;
3625 setitimer(ITIMER_REAL, &delay, NULL);
3626 while (frameWaiting) pause();
3627 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
3628 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
3629 setitimer(ITIMER_REAL, &delay, NULL);
3636 FrameDelay (int time)
3638 XSync(xDisplay, False);
3640 usleep(time * 1000);
3646 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
3650 /* Bitmap for piece being moved. */
3651 if (appData.monoMode) {
3652 *mask = *pieceToSolid(piece);
3653 } else if (useImages) {
3655 *mask = xpmMask[piece];
3657 *mask = ximMaskPm[piece];
3660 *mask = *pieceToSolid(piece);
3663 /* GC for piece being moved. Square color doesn't matter, but
3664 since it gets modified we make a copy of the original. */
3666 if (appData.monoMode)
3671 if (appData.monoMode)
3676 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
3678 /* Outline only used in mono mode and is not modified */
3680 *outline = bwPieceGC;
3682 *outline = wbPieceGC;
3686 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
3691 /* Draw solid rectangle which will be clipped to shape of piece */
3692 XFillRectangle(xDisplay, dest, clip,
3693 0, 0, squareSize, squareSize);
3694 if (appData.monoMode)
3695 /* Also draw outline in contrasting color for black
3696 on black / white on white cases */
3697 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
3698 0, 0, squareSize, squareSize, 0, 0, 1);
3700 /* Copy the piece */
3705 if(appData.upsideDown && flipView) kind ^= 2;
3706 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3708 0, 0, squareSize, squareSize,
3714 InsertPiece (AnimNr anr, ChessSquare piece)
3716 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
3720 DrawBlank (AnimNr anr, int x, int y, int startColor)
3722 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
3725 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
3726 int srcX, int srcY, int width, int height, int destX, int destY)
3728 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
3729 srcX, srcY, width, height, destX, destY);
3733 SetDragPiece (AnimNr anr, ChessSquare piece)
3736 /* The piece will be drawn using its own bitmap as a matte */
3737 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
3738 XSetClipMask(xDisplay, animGCs[anr+2], mask);
3741 /* [AS] Arrow highlighting support */
3744 DrawPolygon (Pnt arrow[], int nr)
3748 for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
3749 XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
3750 if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
3754 UpdateLogos (int displ)
3756 return; // no logos in XBoard yet