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 void CreatePieceMenus P((void));
240 Widget CreateMenuBar P((Menu *mb, int boardWidth));
241 Widget CreateButtonBar P ((MenuItem *mi));
243 char *InsertPxlSize P((char *pattern, int targetPxlSize));
244 XFontSet CreateFontSet P((char *base_fnt_lst));
246 char *FindFont P((char *pattern, int targetPxlSize));
248 void PieceMenuPopup P((Widget w, XEvent *event,
249 String *params, Cardinal *num_params));
250 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
251 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
252 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
253 u_int wreq, u_int hreq));
254 void CreateGrid P((void));
255 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
256 void DelayedDrag P((void));
257 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
258 void HandleUserMove P((Widget w, XEvent *event,
259 String *prms, Cardinal *nprms));
260 void AnimateUserMove P((Widget w, XEvent * event,
261 String * params, Cardinal * nParams));
262 void HandlePV P((Widget w, XEvent * event,
263 String * params, Cardinal * nParams));
264 void WhiteClock P((Widget w, XEvent *event,
265 String *prms, Cardinal *nprms));
266 void BlackClock P((Widget w, XEvent *event,
267 String *prms, Cardinal *nprms));
268 void DrawPositionProc P((Widget w, XEvent *event,
269 String *prms, Cardinal *nprms));
270 void CommentClick P((Widget w, XEvent * event,
271 String * params, Cardinal * nParams));
272 void ICSInputBoxPopUp P((void));
273 void FileNamePopUp P((char *label, char *def, char *filter,
274 FileProc proc, char *openMode));
275 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
276 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
277 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
278 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
279 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
280 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
281 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
282 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
283 Boolean TempBackwardActive = False;
284 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
285 void DisplayMove P((int moveNumber));
286 void ICSInitScript P((void));
287 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
288 void update_ics_width P(());
289 int get_term_width P(());
290 int CopyMemoProc P(());
293 * XBoard depends on Xt R4 or higher
295 int xtVersion = XtSpecificationRelease;
300 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
301 highlightSquareColor, premoveHighlightColor, dialogColor, buttonColor;
302 Pixel lowTimeWarningColor;
303 GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
304 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
306 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
307 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
308 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
309 commentShell, whitePieceMenu, blackPieceMenu, dropMenu,
310 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
311 ICSInputShell, fileNameShell;
313 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
314 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
316 XFontSet fontSet, clockFontSet;
319 XFontStruct *clockFontStruct;
321 Font coordFontID, countFontID;
322 XFontStruct *coordFontStruct, *countFontStruct;
323 XtAppContext appContext;
328 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
330 Position commentX = -1, commentY = -1;
331 Dimension commentW, commentH;
332 typedef unsigned int BoardSize;
334 Boolean chessProgram;
336 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
337 int smallLayout = 0, tinyLayout = 0,
338 marginW, marginH, // [HGM] for run-time resizing
339 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
340 ICSInputBoxUp = False,
341 filenameUp = False, pmFromX = -1, pmFromY = -1,
342 errorExitStatus = -1, defaultLineGap;
343 Dimension textHeight;
344 Pixel timerForegroundPixel, timerBackgroundPixel;
345 Pixel buttonForegroundPixel, buttonBackgroundPixel;
346 char *chessDir, *programName, *programVersion;
347 Boolean alwaysOnTop = False;
348 char *icsTextMenuString;
350 char *firstChessProgramNames;
351 char *secondChessProgramNames;
353 WindowPlacement wpMain;
354 WindowPlacement wpConsole;
355 WindowPlacement wpComment;
356 WindowPlacement wpMoveHistory;
357 WindowPlacement wpEvalGraph;
358 WindowPlacement wpEngineOutput;
359 WindowPlacement wpGameList;
360 WindowPlacement wpTags;
365 Pixmap pieceBitmap[2][(int)BlackPawn];
366 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
367 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
368 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
369 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
370 Pixmap xpmBoardBitmap[2];
371 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
372 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
373 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
374 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
375 XImage *ximLightSquare, *ximDarkSquare;
378 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
379 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
381 #define White(piece) ((int)(piece) < (int)BlackPawn)
383 /* Bitmaps for use as masks when drawing XPM pieces.
384 Need one for each black and white piece. */
385 static Pixmap xpmMask[BlackKing + 1];
387 /* This magic number is the number of intermediate frames used
388 in each half of the animation. For short moves it's reduced
389 by 1. The total number of frames will be factor * 2 + 1. */
392 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
394 #define PAUSE_BUTTON "P"
395 MenuItem buttonBar[] = {
396 {"<<", "<<", ToStartEvent},
397 {"<", "<", BackwardEvent},
398 {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseEvent},
399 {">", ">", ForwardEvent},
400 {">>", ">>", ToEndEvent},
404 #define PIECE_MENU_SIZE 18
405 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
406 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
407 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
408 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
409 N_("Empty square"), N_("Clear board") },
410 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
411 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
412 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
413 N_("Empty square"), N_("Clear board") }
415 /* must be in same order as pieceMenuStrings! */
416 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
417 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
418 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
419 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
420 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
421 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
422 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
423 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
424 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
427 #define DROP_MENU_SIZE 6
428 String dropMenuStrings[DROP_MENU_SIZE] = {
429 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
431 /* must be in same order as dropMenuStrings! */
432 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
433 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
434 WhiteRook, WhiteQueen
442 DropMenuEnables dmEnables[] = {
460 { XtNborderWidth, 0 },
461 { XtNdefaultDistance, 0 },
465 { XtNborderWidth, 0 },
466 { XtNresizable, (XtArgVal) True },
470 { XtNborderWidth, 0 },
476 { XtNjustify, (XtArgVal) XtJustifyRight },
477 { XtNlabel, (XtArgVal) "..." },
478 { XtNresizable, (XtArgVal) True },
479 { XtNresize, (XtArgVal) False }
482 Arg messageArgs[] = {
483 { XtNjustify, (XtArgVal) XtJustifyLeft },
484 { XtNlabel, (XtArgVal) "..." },
485 { XtNresizable, (XtArgVal) True },
486 { XtNresize, (XtArgVal) False }
490 { XtNborderWidth, 0 },
491 { XtNjustify, (XtArgVal) XtJustifyLeft }
494 XtResource clientResources[] = {
495 { "flashCount", "flashCount", XtRInt, sizeof(int),
496 XtOffset(AppDataPtr, flashCount), XtRImmediate,
497 (XtPointer) FLASH_COUNT },
500 XrmOptionDescRec shellOptions[] = {
501 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
502 { "-flash", "flashCount", XrmoptionNoArg, "3" },
503 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
506 XtActionsRec boardActions[] = {
507 { "DrawPosition", DrawPositionProc },
508 { "HandleUserMove", HandleUserMove },
509 { "AnimateUserMove", AnimateUserMove },
510 { "HandlePV", HandlePV },
511 { "SelectPV", SelectPV },
512 { "StopPV", StopPV },
513 { "PieceMenuPopup", PieceMenuPopup },
514 { "WhiteClock", WhiteClock },
515 { "BlackClock", BlackClock },
516 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
517 { "QuitProc", QuitWrapper },
518 { "ManProc", ManInner },
519 { "TempBackwardProc", TempBackwardProc },
520 { "TempForwardProc", TempForwardProc },
521 { "CommentClick", (XtActionProc) CommentClick },
522 { "GenericPopDown", (XtActionProc) GenericPopDown },
523 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
524 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
525 { "SelectMove", (XtActionProc) SelectMove },
526 { "LoadSelectedProc", LoadSelectedProc },
527 { "SetFilterProc", SetFilterProc },
528 { "TypeInProc", TypeInProc },
529 { "EnterKeyProc", EnterKeyProc },
530 { "UpKeyProc", UpKeyProc },
531 { "DownKeyProc", DownKeyProc },
532 { "WheelProc", WheelProc },
533 { "TabProc", TabProc },
536 char globalTranslations[] =
537 ":<Key>F9: MenuItem(ResignProc) \n \
538 :Ctrl<Key>n: MenuItem(NewGame) \n \
539 :Meta<Key>V: MenuItem(NewVariant) \n \
540 :Ctrl<Key>o: MenuItem(LoadGame) \n \
541 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
542 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
543 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
544 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
545 :Ctrl<Key>s: MenuItem(SaveGame) \n \
546 :Ctrl<Key>c: MenuItem(CopyGame) \n \
547 :Ctrl<Key>v: MenuItem(PasteGame) \n \
548 :Ctrl<Key>O: MenuItem(LoadPosition) \n \
549 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
550 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
551 :Ctrl<Key>S: MenuItem(SavePosition) \n \
552 :Ctrl<Key>C: MenuItem(CopyPosition) \n \
553 :Ctrl<Key>V: MenuItem(PastePosition) \n \
554 :Ctrl<Key>q: MenuItem(Exit) \n \
555 :Ctrl<Key>w: MenuItem(MachineWhite) \n \
556 :Ctrl<Key>b: MenuItem(MachineBlack) \n \
557 :Ctrl<Key>t: MenuItem(TwoMachines) \n \
558 :Ctrl<Key>a: MenuItem(AnalysisMode) \n \
559 :Ctrl<Key>g: MenuItem(AnalyzeFile) \n \
560 :Ctrl<Key>e: MenuItem(EditGame) \n \
561 :Ctrl<Key>E: MenuItem(EditPosition) \n \
562 :Meta<Key>O: MenuItem(ShowEngineOutput) \n \
563 :Meta<Key>E: MenuItem(ShowEvaluationGraph) \n \
564 :Meta<Key>G: MenuItem(ShowGameList) \n \
565 :Meta<Key>H: MenuItem(ShowMoveHistory) \n \
566 :<Key>Pause: MenuItem(Pause) \n \
567 :<Key>F3: MenuItem(Accept) \n \
568 :<Key>F4: MenuItem(Decline) \n \
569 :<Key>F12: MenuItem(Rematch) \n \
570 :<Key>F5: MenuItem(CallFlag) \n \
571 :<Key>F6: MenuItem(Draw) \n \
572 :<Key>F7: MenuItem(Adjourn) \n \
573 :<Key>F8: MenuItem(Abort) \n \
574 :<Key>F10: MenuItem(StopObserving) \n \
575 :<Key>F11: MenuItem(StopExamining) \n \
576 :Ctrl<Key>d: MenuItem(DebugProc) \n \
577 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
578 :Meta<Key>End: MenuItem(ToEnd) \n \
579 :Meta<Key>Right: MenuItem(Forward) \n \
580 :Meta<Key>Home: MenuItem(ToStart) \n \
581 :Meta<Key>Left: MenuItem(Backward) \n \
582 :<Key>Left: MenuItem(Backward) \n \
583 :<Key>Right: MenuItem(Forward) \n \
584 :<Key>Home: MenuItem(Revert) \n \
585 :<Key>End: MenuItem(TruncateGame) \n \
586 :Ctrl<Key>m: MenuItem(MoveNow) \n \
587 :Ctrl<Key>x: MenuItem(RetractMove) \n \
588 :Meta<Key>J: MenuItem(Adjudications) \n \
589 :Meta<Key>U: MenuItem(CommonEngine) \n \
590 :Meta<Key>T: MenuItem(TimeControl) \n \
591 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
592 #ifndef OPTIONSDIALOG
594 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
595 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
596 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
597 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
598 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
601 :<Key>F1: MenuItem(Manual) \n \
602 :<Key>F2: MenuItem(FlipView) \n \
603 :<KeyDown>Return: TempBackwardProc() \n \
604 :<KeyUp>Return: TempForwardProc() \n";
606 char boardTranslations[] =
607 "<Btn1Down>: HandleUserMove(0) \n \
608 Shift<Btn1Up>: HandleUserMove(1) \n \
609 <Btn1Up>: HandleUserMove(0) \n \
610 <Btn1Motion>: AnimateUserMove() \n \
611 <Btn3Motion>: HandlePV() \n \
612 <Btn2Motion>: HandlePV() \n \
613 <Btn3Up>: PieceMenuPopup(menuB) \n \
614 <Btn2Up>: PieceMenuPopup(menuB) \n \
615 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
616 PieceMenuPopup(menuB) \n \
617 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
618 PieceMenuPopup(menuW) \n \
619 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
620 PieceMenuPopup(menuW) \n \
621 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
622 PieceMenuPopup(menuB) \n";
624 char whiteTranslations[] =
625 "Shift<BtnDown>: WhiteClock(1)\n \
626 <BtnDown>: WhiteClock(0)\n";
627 char blackTranslations[] =
628 "Shift<BtnDown>: BlackClock(1)\n \
629 <BtnDown>: BlackClock(0)\n";
631 char ICSInputTranslations[] =
632 "<Key>Up: UpKeyProc() \n "
633 "<Key>Down: DownKeyProc() \n "
634 "<Key>Return: EnterKeyProc() \n";
636 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
637 // as the widget is destroyed before the up-click can call extend-end
638 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
640 String xboardResources[] = {
641 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
646 /* Max possible square size */
647 #define MAXSQSIZE 256
649 static int xpm_avail[MAXSQSIZE];
651 #ifdef HAVE_DIR_STRUCT
653 /* Extract piece size from filename */
655 xpm_getsize (char *name, int len, char *ext)
663 if ((p=strchr(name, '.')) == NULL ||
664 StrCaseCmp(p+1, ext) != 0)
670 while (*p && isdigit(*p))
677 /* Setup xpm_avail */
679 xpm_getavail (char *dirname, char *ext)
685 for (i=0; i<MAXSQSIZE; ++i)
688 if (appData.debugMode)
689 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
691 dir = opendir(dirname);
694 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
695 programName, dirname);
699 while ((ent=readdir(dir)) != NULL) {
700 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
701 if (i > 0 && i < MAXSQSIZE)
711 xpm_print_avail (FILE *fp, char *ext)
715 fprintf(fp, _("Available `%s' sizes:\n"), ext);
716 for (i=1; i<MAXSQSIZE; ++i) {
722 /* Return XPM piecesize closest to size */
724 xpm_closest_to (char *dirname, int size, char *ext)
727 int sm_diff = MAXSQSIZE;
731 xpm_getavail(dirname, ext);
733 if (appData.debugMode)
734 xpm_print_avail(stderr, ext);
736 for (i=1; i<MAXSQSIZE; ++i) {
739 diff = (diff<0) ? -diff : diff;
740 if (diff < sm_diff) {
748 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
754 #else /* !HAVE_DIR_STRUCT */
755 /* If we are on a system without a DIR struct, we can't
756 read the directory, so we can't collect a list of
757 filenames, etc., so we can't do any size-fitting. */
759 xpm_closest_to (char *dirname, int size, char *ext)
762 Warning: No DIR structure found on this system --\n\
763 Unable to autosize for XPM/XIM pieces.\n\
764 Please report this error to %s.\n\
765 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
768 #endif /* HAVE_DIR_STRUCT */
771 /* Arrange to catch delete-window events */
772 Atom wm_delete_window;
774 CatchDeleteWindow (Widget w, String procname)
777 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
778 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
779 XtAugmentTranslations(w, XtParseTranslationTable(buf));
786 XtSetArg(args[0], XtNiconic, False);
787 XtSetValues(shellWidget, args, 1);
789 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
792 //---------------------------------------------------------------------------------------------------------
793 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
796 #define CW_USEDEFAULT (1<<31)
797 #define ICS_TEXT_MENU_SIZE 90
798 #define DEBUG_FILE "xboard.debug"
799 #define SetCurrentDirectory chdir
800 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
804 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
807 // front-end part of option handling
809 // [HGM] This platform-dependent table provides the location for storing the color info
810 extern char *crWhite, * crBlack;
814 &appData.whitePieceColor,
815 &appData.blackPieceColor,
816 &appData.lightSquareColor,
817 &appData.darkSquareColor,
818 &appData.highlightSquareColor,
819 &appData.premoveHighlightColor,
820 &appData.lowTimeWarningColor,
831 // [HGM] font: keep a font for each square size, even non-stndard ones
834 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
835 char *fontTable[NUM_FONTS][MAX_SIZE];
838 ParseFont (char *name, int number)
839 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
841 if(sscanf(name, "size%d:", &size)) {
842 // [HGM] font: font is meant for specific boardSize (likely from settings file);
843 // defer processing it until we know if it matches our board size
844 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
845 fontTable[number][size] = strdup(strchr(name, ':')+1);
846 fontValid[number][size] = True;
851 case 0: // CLOCK_FONT
852 appData.clockFont = strdup(name);
854 case 1: // MESSAGE_FONT
855 appData.font = strdup(name);
857 case 2: // COORD_FONT
858 appData.coordFont = strdup(name);
863 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
868 { // only 2 fonts currently
869 appData.clockFont = CLOCK_FONT_NAME;
870 appData.coordFont = COORD_FONT_NAME;
871 appData.font = DEFAULT_FONT_NAME;
876 { // no-op, until we identify the code for this already in XBoard and move it here
880 ParseColor (int n, char *name)
881 { // in XBoard, just copy the color-name string
882 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
886 ParseTextAttribs (ColorClass cc, char *s)
888 (&appData.colorShout)[cc] = strdup(s);
892 ParseBoardSize (void *addr, char *name)
894 appData.boardSize = strdup(name);
899 { // In XBoard the sound-playing program takes care of obtaining the actual sound
903 SetCommPortDefaults ()
904 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
907 // [HGM] args: these three cases taken out to stay in front-end
909 SaveFontArg (FILE *f, ArgDescriptor *ad)
912 int i, n = (int)(intptr_t)ad->argLoc;
914 case 0: // CLOCK_FONT
915 name = appData.clockFont;
917 case 1: // MESSAGE_FONT
920 case 2: // COORD_FONT
921 name = appData.coordFont;
926 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
927 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
928 fontTable[n][squareSize] = strdup(name);
929 fontValid[n][squareSize] = True;
932 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
933 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
938 { // nothing to do, as the sounds are at all times represented by their text-string names already
942 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
943 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
944 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
948 SaveColor (FILE *f, ArgDescriptor *ad)
949 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
950 if(colorVariable[(int)(intptr_t)ad->argLoc])
951 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
955 SaveBoardSize (FILE *f, char *name, void *addr)
956 { // wrapper to shield back-end from BoardSize & sizeInfo
957 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
961 ParseCommPortSettings (char *s)
962 { // no such option in XBoard (yet)
968 GetActualPlacement (Widget wg, WindowPlacement *wp)
973 XWindowAttributes winAt;
980 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
981 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
982 wp->x = rx - winAt.x;
983 wp->y = ry - winAt.y;
984 wp->height = winAt.height;
985 wp->width = winAt.width;
986 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
991 { // wrapper to shield use of window handles from back-end (make addressible by number?)
992 // In XBoard this will have to wait until awareness of window parameters is implemented
993 GetActualPlacement(shellWidget, &wpMain);
994 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
995 if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
996 if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
997 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
998 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
999 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
1003 PrintCommPortSettings (FILE *f, char *name)
1004 { // This option does not exist in XBoard
1008 EnsureOnScreen (int *x, int *y, int minX, int minY)
1015 { // [HGM] args: allows testing if main window is realized from back-end
1016 return xBoardWindow != 0;
1020 PopUpStartupDialog ()
1021 { // start menu not implemented in XBoard
1025 ConvertToLine (int argc, char **argv)
1027 static char line[128*1024], buf[1024];
1031 for(i=1; i<argc; i++)
1033 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1034 && argv[i][0] != '{' )
1035 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1037 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1038 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1041 line[strlen(line)-1] = NULLCHAR;
1045 //--------------------------------------------------------------------------------------------
1048 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1050 #define BoardSize int
1052 InitDrawingSizes (BoardSize boardSize, int flags)
1053 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1054 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1056 XtGeometryResult gres;
1058 static Dimension oldWidth, oldHeight;
1059 static VariantClass oldVariant;
1060 static int oldDual = -1, oldMono = -1;
1062 if(!formWidget) return;
1064 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1065 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1066 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1068 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1070 * Enable shell resizing.
1072 shellArgs[0].value = (XtArgVal) &w;
1073 shellArgs[1].value = (XtArgVal) &h;
1074 XtGetValues(shellWidget, shellArgs, 2);
1076 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1077 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1078 XtSetValues(shellWidget, &shellArgs[2], 4);
1080 XtSetArg(args[0], XtNdefaultDistance, &sep);
1081 XtGetValues(formWidget, args, 1);
1083 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1085 hOffset = boardWidth + 10;
1086 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1087 secondSegments[i] = gridSegments[i];
1088 secondSegments[i].x1 += hOffset;
1089 secondSegments[i].x2 += hOffset;
1092 XtSetArg(args[0], XtNwidth, boardWidth);
1093 XtSetArg(args[1], XtNheight, boardHeight);
1094 XtSetValues(boardWidget, args, 2);
1096 timerWidth = (boardWidth - sep) / 2;
1097 XtSetArg(args[0], XtNwidth, timerWidth);
1098 XtSetValues(whiteTimerWidget, args, 1);
1099 XtSetValues(blackTimerWidget, args, 1);
1101 XawFormDoLayout(formWidget, False);
1103 if (appData.titleInWindow) {
1105 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1106 XtSetArg(args[i], XtNheight, &h); i++;
1107 XtGetValues(titleWidget, args, i);
1109 w = boardWidth - 2*bor;
1111 XtSetArg(args[0], XtNwidth, &w);
1112 XtGetValues(menuBarWidget, args, 1);
1113 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1116 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1117 if (gres != XtGeometryYes && appData.debugMode) {
1119 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1120 programName, gres, w, h, wr, hr);
1124 XawFormDoLayout(formWidget, True);
1127 * Inhibit shell resizing.
1129 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1130 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1131 shellArgs[4].value = shellArgs[2].value = w;
1132 shellArgs[5].value = shellArgs[3].value = h;
1133 XtSetValues(shellWidget, &shellArgs[0], 6);
1135 XSync(xDisplay, False);
1139 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1142 if(gameInfo.variant != oldVariant) { // and only if variant changed
1145 for(i=0; i<4; i++) {
1147 for(p=0; p<=(int)WhiteKing; p++)
1148 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1149 if(gameInfo.variant == VariantShogi) {
1150 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1151 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1152 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1153 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1154 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1157 if(gameInfo.variant == VariantGothic) {
1158 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1161 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1162 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1163 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1166 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1167 for(p=0; p<=(int)WhiteKing; p++)
1168 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1169 if(gameInfo.variant == VariantShogi) {
1170 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1171 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1172 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1173 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1174 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1177 if(gameInfo.variant == VariantGothic) {
1178 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1181 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1182 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1183 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1188 for(i=0; i<2; i++) {
1190 for(p=0; p<=(int)WhiteKing; p++)
1191 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1192 if(gameInfo.variant == VariantShogi) {
1193 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1194 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1195 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1196 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1197 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1200 if(gameInfo.variant == VariantGothic) {
1201 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1204 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1205 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1206 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1210 oldMono = -10; // kludge to force recreation of animation masks
1211 oldVariant = gameInfo.variant;
1214 if(appData.monoMode != oldMono)
1217 oldMono = appData.monoMode;
1222 MakeOneColor (char *name, Pixel *color)
1224 XrmValue vFrom, vTo;
1225 if (!appData.monoMode) {
1226 vFrom.addr = (caddr_t) name;
1227 vFrom.size = strlen(name);
1228 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1229 if (vTo.addr == NULL) {
1230 appData.monoMode = True;
1233 *color = *(Pixel *) vTo.addr;
1241 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1242 int forceMono = False;
1244 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1245 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1246 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1247 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1248 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1249 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1250 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1251 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1258 { // [HGM] taken out of main
1260 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1261 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1262 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1264 if (appData.bitmapDirectory[0] != NULLCHAR) {
1268 CreateXPMBoard(appData.liteBackTextureFile, 1);
1269 CreateXPMBoard(appData.darkBackTextureFile, 0);
1273 /* Create regular pieces */
1274 if (!useImages) CreatePieces();
1279 InitDrawingParams ()
1281 MakeColors(); CreateGCs(True);
1286 main (int argc, char **argv)
1288 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1289 XSetWindowAttributes window_attributes;
1291 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1292 XrmValue vFrom, vTo;
1293 XtGeometryResult gres;
1296 int forceMono = False;
1298 srandom(time(0)); // [HGM] book: make random truly random
1300 setbuf(stdout, NULL);
1301 setbuf(stderr, NULL);
1304 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1305 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1309 programName = strrchr(argv[0], '/');
1310 if (programName == NULL)
1311 programName = argv[0];
1316 XtSetLanguageProc(NULL, NULL, NULL);
1317 bindtextdomain(PACKAGE, LOCALEDIR);
1318 textdomain(PACKAGE);
1322 XtAppInitialize(&appContext, "XBoard", shellOptions,
1323 XtNumber(shellOptions),
1324 &argc, argv, xboardResources, NULL, 0);
1325 appData.boardSize = "";
1326 InitAppData(ConvertToLine(argc, argv));
1328 if (p == NULL) p = "/tmp";
1329 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1330 gameCopyFilename = (char*) malloc(i);
1331 gamePasteFilename = (char*) malloc(i);
1332 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1333 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1335 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1336 clientResources, XtNumber(clientResources),
1339 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1340 static char buf[MSG_SIZ];
1341 EscapeExpand(buf, appData.firstInitString);
1342 appData.firstInitString = strdup(buf);
1343 EscapeExpand(buf, appData.secondInitString);
1344 appData.secondInitString = strdup(buf);
1345 EscapeExpand(buf, appData.firstComputerString);
1346 appData.firstComputerString = strdup(buf);
1347 EscapeExpand(buf, appData.secondComputerString);
1348 appData.secondComputerString = strdup(buf);
1351 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1354 if (chdir(chessDir) != 0) {
1355 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1361 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1362 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1363 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1364 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1367 setbuf(debugFP, NULL);
1371 if (appData.debugMode) {
1372 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1376 /* [HGM,HR] make sure board size is acceptable */
1377 if(appData.NrFiles > BOARD_FILES ||
1378 appData.NrRanks > BOARD_RANKS )
1379 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1382 /* This feature does not work; animation needs a rewrite */
1383 appData.highlightDragging = FALSE;
1387 xDisplay = XtDisplay(shellWidget);
1388 xScreen = DefaultScreen(xDisplay);
1389 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1391 gameInfo.variant = StringToVariant(appData.variant);
1392 InitPosition(FALSE);
1395 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1397 if (isdigit(appData.boardSize[0])) {
1398 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1399 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1400 &fontPxlSize, &smallLayout, &tinyLayout);
1402 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1403 programName, appData.boardSize);
1407 /* Find some defaults; use the nearest known size */
1408 SizeDefaults *szd, *nearest;
1409 int distance = 99999;
1410 nearest = szd = sizeDefaults;
1411 while (szd->name != NULL) {
1412 if (abs(szd->squareSize - squareSize) < distance) {
1414 distance = abs(szd->squareSize - squareSize);
1415 if (distance == 0) break;
1419 if (i < 2) lineGap = nearest->lineGap;
1420 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1421 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1422 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1423 if (i < 6) smallLayout = nearest->smallLayout;
1424 if (i < 7) tinyLayout = nearest->tinyLayout;
1427 SizeDefaults *szd = sizeDefaults;
1428 if (*appData.boardSize == NULLCHAR) {
1429 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1430 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1433 if (szd->name == NULL) szd--;
1434 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1436 while (szd->name != NULL &&
1437 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1438 if (szd->name == NULL) {
1439 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1440 programName, appData.boardSize);
1444 squareSize = szd->squareSize;
1445 lineGap = szd->lineGap;
1446 clockFontPxlSize = szd->clockFontPxlSize;
1447 coordFontPxlSize = szd->coordFontPxlSize;
1448 fontPxlSize = szd->fontPxlSize;
1449 smallLayout = szd->smallLayout;
1450 tinyLayout = szd->tinyLayout;
1451 // [HGM] font: use defaults from settings file if available and not overruled
1453 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1454 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1455 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1456 appData.font = fontTable[MESSAGE_FONT][squareSize];
1457 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1458 appData.coordFont = fontTable[COORD_FONT][squareSize];
1460 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1461 if (strlen(appData.pixmapDirectory) > 0) {
1462 p = ExpandPathName(appData.pixmapDirectory);
1464 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1465 appData.pixmapDirectory);
1468 if (appData.debugMode) {
1469 fprintf(stderr, _("\
1470 XBoard square size (hint): %d\n\
1471 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1473 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1474 if (appData.debugMode) {
1475 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1478 defaultLineGap = lineGap;
1479 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1481 /* [HR] height treated separately (hacked) */
1482 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1483 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1484 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1485 XtSetArg(boardArgs[2], XtNheight, boardHeight);
1488 * Determine what fonts to use.
1491 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1492 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1493 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1494 fontSet = CreateFontSet(appData.font);
1495 clockFontSet = CreateFontSet(appData.clockFont);
1497 /* For the coordFont, use the 0th font of the fontset. */
1498 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1499 XFontStruct **font_struct_list;
1500 XFontSetExtents *fontSize;
1501 char **font_name_list;
1502 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1503 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1504 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1505 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1506 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1509 appData.font = FindFont(appData.font, fontPxlSize);
1510 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1511 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1512 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1513 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1514 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1515 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1517 countFontID = coordFontID; // [HGM] holdings
1518 countFontStruct = coordFontStruct;
1520 xdb = XtDatabase(xDisplay);
1522 XrmPutLineResource(&xdb, "*international: True");
1523 vTo.size = sizeof(XFontSet);
1524 vTo.addr = (XtPointer) &fontSet;
1525 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1527 XrmPutStringResource(&xdb, "*font", appData.font);
1531 * Detect if there are not enough colors available and adapt.
1533 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1534 appData.monoMode = True;
1537 forceMono = MakeColors();
1540 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1542 appData.monoMode = True;
1545 if (appData.lowTimeWarning && !appData.monoMode) {
1546 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1547 vFrom.size = strlen(appData.lowTimeWarningColor);
1548 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1549 if (vTo.addr == NULL)
1550 appData.monoMode = True;
1552 lowTimeWarningColor = *(Pixel *) vTo.addr;
1555 if (appData.monoMode && appData.debugMode) {
1556 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1557 (unsigned long) XWhitePixel(xDisplay, xScreen),
1558 (unsigned long) XBlackPixel(xDisplay, xScreen));
1561 ParseIcsTextColors();
1563 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1569 layoutName = "tinyLayout";
1570 } else if (smallLayout) {
1571 layoutName = "smallLayout";
1573 layoutName = "normalLayout";
1575 /* Outer layoutWidget is there only to provide a name for use in
1576 resources that depend on the layout style */
1578 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
1579 layoutArgs, XtNumber(layoutArgs));
1581 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
1582 formArgs, XtNumber(formArgs));
1583 XtSetArg(args[0], XtNdefaultDistance, &sep);
1584 XtGetValues(formWidget, args, 1);
1587 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
1588 XtSetArg(args[0], XtNtop, XtChainTop);
1589 XtSetArg(args[1], XtNbottom, XtChainTop);
1590 XtSetArg(args[2], XtNright, XtChainLeft);
1591 XtSetValues(menuBarWidget, args, 3);
1593 widgetList[j++] = whiteTimerWidget =
1594 XtCreateWidget("whiteTime", labelWidgetClass,
1595 formWidget, timerArgs, XtNumber(timerArgs));
1597 XtSetArg(args[0], XtNfontSet, clockFontSet);
1599 XtSetArg(args[0], XtNfont, clockFontStruct);
1601 XtSetArg(args[1], XtNtop, XtChainTop);
1602 XtSetArg(args[2], XtNbottom, XtChainTop);
1603 XtSetValues(whiteTimerWidget, args, 3);
1605 widgetList[j++] = blackTimerWidget =
1606 XtCreateWidget("blackTime", labelWidgetClass,
1607 formWidget, timerArgs, XtNumber(timerArgs));
1609 XtSetArg(args[0], XtNfontSet, clockFontSet);
1611 XtSetArg(args[0], XtNfont, clockFontStruct);
1613 XtSetArg(args[1], XtNtop, XtChainTop);
1614 XtSetArg(args[2], XtNbottom, XtChainTop);
1615 XtSetValues(blackTimerWidget, args, 3);
1617 if (appData.titleInWindow) {
1618 widgetList[j++] = titleWidget =
1619 XtCreateWidget("title", labelWidgetClass, formWidget,
1620 titleArgs, XtNumber(titleArgs));
1621 XtSetArg(args[0], XtNtop, XtChainTop);
1622 XtSetArg(args[1], XtNbottom, XtChainTop);
1623 XtSetValues(titleWidget, args, 2);
1626 if (appData.showButtonBar) {
1627 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
1628 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
1629 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
1630 XtSetArg(args[2], XtNtop, XtChainTop);
1631 XtSetArg(args[3], XtNbottom, XtChainTop);
1632 XtSetValues(buttonBarWidget, args, 4);
1635 widgetList[j++] = messageWidget =
1636 XtCreateWidget("message", labelWidgetClass, formWidget,
1637 messageArgs, XtNumber(messageArgs));
1638 XtSetArg(args[0], XtNtop, XtChainTop);
1639 XtSetArg(args[1], XtNbottom, XtChainTop);
1640 XtSetValues(messageWidget, args, 2);
1642 widgetList[j++] = boardWidget =
1643 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
1644 XtNumber(boardArgs));
1646 XtManageChildren(widgetList, j);
1648 timerWidth = (boardWidth - sep) / 2;
1649 XtSetArg(args[0], XtNwidth, timerWidth);
1650 XtSetValues(whiteTimerWidget, args, 1);
1651 XtSetValues(blackTimerWidget, args, 1);
1653 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1654 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1655 XtGetValues(whiteTimerWidget, args, 2);
1657 if (appData.showButtonBar) {
1658 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1659 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1660 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
1664 * formWidget uses these constraints but they are stored
1668 XtSetArg(args[i], XtNfromHoriz, 0); i++;
1669 XtSetValues(menuBarWidget, args, i);
1670 if (appData.titleInWindow) {
1673 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1674 XtSetValues(whiteTimerWidget, args, i);
1676 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1677 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1678 XtSetValues(blackTimerWidget, args, i);
1680 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1681 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
1682 XtSetValues(titleWidget, args, i);
1684 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1685 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1686 XtSetValues(messageWidget, args, i);
1687 if (appData.showButtonBar) {
1689 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1690 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1691 XtSetValues(buttonBarWidget, args, i);
1695 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1696 XtSetValues(whiteTimerWidget, args, i);
1698 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1699 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1700 XtSetValues(blackTimerWidget, args, i);
1702 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
1703 XtSetValues(titleWidget, args, i);
1705 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1706 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1707 XtSetValues(messageWidget, args, i);
1708 if (appData.showButtonBar) {
1710 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1711 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1712 XtSetValues(buttonBarWidget, args, i);
1717 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1718 XtSetValues(whiteTimerWidget, args, i);
1720 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1721 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1722 XtSetValues(blackTimerWidget, args, i);
1724 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1725 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1726 XtSetValues(messageWidget, args, i);
1727 if (appData.showButtonBar) {
1729 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1730 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1731 XtSetValues(buttonBarWidget, args, i);
1735 XtSetArg(args[0], XtNfromVert, messageWidget);
1736 XtSetArg(args[1], XtNtop, XtChainTop);
1737 XtSetArg(args[2], XtNbottom, XtChainBottom);
1738 XtSetArg(args[3], XtNleft, XtChainLeft);
1739 XtSetArg(args[4], XtNright, XtChainRight);
1740 XtSetValues(boardWidget, args, 5);
1742 XtRealizeWidget(shellWidget);
1745 XtSetArg(args[0], XtNx, wpMain.x);
1746 XtSetArg(args[1], XtNy, wpMain.y);
1747 XtSetValues(shellWidget, args, 2);
1751 * Correct the width of the message and title widgets.
1752 * It is not known why some systems need the extra fudge term.
1753 * The value "2" is probably larger than needed.
1755 XawFormDoLayout(formWidget, False);
1757 #define WIDTH_FUDGE 2
1759 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1760 XtSetArg(args[i], XtNheight, &h); i++;
1761 XtGetValues(messageWidget, args, i);
1762 if (appData.showButtonBar) {
1764 XtSetArg(args[i], XtNwidth, &w); i++;
1765 XtGetValues(buttonBarWidget, args, i);
1766 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1768 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
1771 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1772 if (gres != XtGeometryYes && appData.debugMode) {
1773 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1774 programName, gres, w, h, wr, hr);
1777 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
1778 /* The size used for the child widget in layout lags one resize behind
1779 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
1781 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1782 if (gres != XtGeometryYes && appData.debugMode) {
1783 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1784 programName, gres, w, h, wr, hr);
1787 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
1788 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
1789 XtSetArg(args[1], XtNright, XtChainRight);
1790 XtSetValues(messageWidget, args, 2);
1792 if (appData.titleInWindow) {
1794 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1795 XtSetArg(args[i], XtNheight, &h); i++;
1796 XtGetValues(titleWidget, args, i);
1798 w = boardWidth - 2*bor;
1800 XtSetArg(args[0], XtNwidth, &w);
1801 XtGetValues(menuBarWidget, args, 1);
1802 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1805 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1806 if (gres != XtGeometryYes && appData.debugMode) {
1808 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1809 programName, gres, w, h, wr, hr);
1812 XawFormDoLayout(formWidget, True);
1814 xBoardWindow = XtWindow(boardWidget);
1816 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1817 // not need to go into InitDrawingSizes().
1821 * Create X checkmark bitmap and initialize option menu checks.
1823 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1824 checkmark_bits, checkmark_width, checkmark_height);
1830 ReadBitmap(&wIconPixmap, "icon_white.bm",
1831 icon_white_bits, icon_white_width, icon_white_height);
1832 ReadBitmap(&bIconPixmap, "icon_black.bm",
1833 icon_black_bits, icon_black_width, icon_black_height);
1834 iconPixmap = wIconPixmap;
1836 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1837 XtSetValues(shellWidget, args, i);
1840 * Create a cursor for the board widget.
1842 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1843 XChangeWindowAttributes(xDisplay, xBoardWindow,
1844 CWCursor, &window_attributes);
1847 * Inhibit shell resizing.
1849 shellArgs[0].value = (XtArgVal) &w;
1850 shellArgs[1].value = (XtArgVal) &h;
1851 XtGetValues(shellWidget, shellArgs, 2);
1852 shellArgs[4].value = shellArgs[2].value = w;
1853 shellArgs[5].value = shellArgs[3].value = h;
1854 XtSetValues(shellWidget, &shellArgs[2], 4);
1855 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1856 marginH = h - boardHeight;
1858 CatchDeleteWindow(shellWidget, "QuitProc");
1866 if (appData.animate || appData.animateDragging)
1869 XtAugmentTranslations(formWidget,
1870 XtParseTranslationTable(globalTranslations));
1871 XtAugmentTranslations(boardWidget,
1872 XtParseTranslationTable(boardTranslations));
1873 XtAugmentTranslations(whiteTimerWidget,
1874 XtParseTranslationTable(whiteTranslations));
1875 XtAugmentTranslations(blackTimerWidget,
1876 XtParseTranslationTable(blackTranslations));
1878 /* Why is the following needed on some versions of X instead
1879 * of a translation? */
1880 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
1881 (XtEventHandler) EventProc, NULL);
1883 XtAddEventHandler(formWidget, KeyPressMask, False,
1884 (XtEventHandler) MoveTypeInProc, NULL);
1885 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1886 (XtEventHandler) EventProc, NULL);
1888 /* [AS] Restore layout */
1889 if( wpMoveHistory.visible ) {
1893 if( wpEvalGraph.visible )
1898 if( wpEngineOutput.visible ) {
1899 EngineOutputPopUp();
1904 if (errorExitStatus == -1) {
1905 if (appData.icsActive) {
1906 /* We now wait until we see "login:" from the ICS before
1907 sending the logon script (problems with timestamp otherwise) */
1908 /*ICSInitScript();*/
1909 if (appData.icsInputBox) ICSInputBoxPopUp();
1913 signal(SIGWINCH, TermSizeSigHandler);
1915 signal(SIGINT, IntSigHandler);
1916 signal(SIGTERM, IntSigHandler);
1917 if (*appData.cmailGameName != NULLCHAR) {
1918 signal(SIGUSR1, CmailSigHandler);
1922 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1924 // XtSetKeyboardFocus(shellWidget, formWidget);
1925 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1927 XtAppMainLoop(appContext);
1928 if (appData.debugMode) fclose(debugFP); // [DM] debug
1933 TermSizeSigHandler (int sig)
1939 IntSigHandler (int sig)
1945 CmailSigHandler (int sig)
1950 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1952 /* Activate call-back function CmailSigHandlerCallBack() */
1953 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1955 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1959 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1962 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1964 /**** end signal code ****/
1967 #define Abs(n) ((n)<0 ? -(n) : (n))
1971 InsertPxlSize (char *pattern, int targetPxlSize)
1973 char *base_fnt_lst, strInt[12], *p, *q;
1974 int alternatives, i, len, strIntLen;
1977 * Replace the "*" (if present) in the pixel-size slot of each
1978 * alternative with the targetPxlSize.
1982 while ((p = strchr(p, ',')) != NULL) {
1986 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1987 strIntLen = strlen(strInt);
1988 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1992 while (alternatives--) {
1993 char *comma = strchr(p, ',');
1994 for (i=0; i<14; i++) {
1995 char *hyphen = strchr(p, '-');
1997 if (comma && hyphen > comma) break;
1998 len = hyphen + 1 - p;
1999 if (i == 7 && *p == '*' && len == 2) {
2001 memcpy(q, strInt, strIntLen);
2011 len = comma + 1 - p;
2018 return base_fnt_lst;
2022 CreateFontSet (char *base_fnt_lst)
2025 char **missing_list;
2029 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
2030 &missing_list, &missing_count, &def_string);
2031 if (appData.debugMode) {
2033 XFontStruct **font_struct_list;
2034 char **font_name_list;
2035 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
2037 fprintf(debugFP, " got list %s, locale %s\n",
2038 XBaseFontNameListOfFontSet(fntSet),
2039 XLocaleOfFontSet(fntSet));
2040 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
2041 for (i = 0; i < count; i++) {
2042 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
2045 for (i = 0; i < missing_count; i++) {
2046 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
2049 if (fntSet == NULL) {
2050 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
2055 #else // not ENABLE_NLS
2057 * Find a font that matches "pattern" that is as close as
2058 * possible to the targetPxlSize. Prefer fonts that are k
2059 * pixels smaller to fonts that are k pixels larger. The
2060 * pattern must be in the X Consortium standard format,
2061 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2062 * The return value should be freed with XtFree when no
2066 FindFont (char *pattern, int targetPxlSize)
2068 char **fonts, *p, *best, *scalable, *scalableTail;
2069 int i, j, nfonts, minerr, err, pxlSize;
2071 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2073 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2074 programName, pattern);
2081 for (i=0; i<nfonts; i++) {
2084 if (*p != '-') continue;
2086 if (*p == NULLCHAR) break;
2087 if (*p++ == '-') j++;
2089 if (j < 7) continue;
2092 scalable = fonts[i];
2095 err = pxlSize - targetPxlSize;
2096 if (Abs(err) < Abs(minerr) ||
2097 (minerr > 0 && err < 0 && -err == minerr)) {
2103 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2104 /* If the error is too big and there is a scalable font,
2105 use the scalable font. */
2106 int headlen = scalableTail - scalable;
2107 p = (char *) XtMalloc(strlen(scalable) + 10);
2108 while (isdigit(*scalableTail)) scalableTail++;
2109 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2111 p = (char *) XtMalloc(strlen(best) + 2);
2112 safeStrCpy(p, best, strlen(best)+1 );
2114 if (appData.debugMode) {
2115 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2116 pattern, targetPxlSize, p);
2118 XFreeFontNames(fonts);
2125 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
2126 // must be called before all non-first callse to CreateGCs()
2127 XtReleaseGC(shellWidget, highlineGC);
2128 XtReleaseGC(shellWidget, lightSquareGC);
2129 XtReleaseGC(shellWidget, darkSquareGC);
2130 XtReleaseGC(shellWidget, lineGC);
2131 if (appData.monoMode) {
2132 if (DefaultDepth(xDisplay, xScreen) == 1) {
2133 XtReleaseGC(shellWidget, wbPieceGC);
2135 XtReleaseGC(shellWidget, bwPieceGC);
2138 XtReleaseGC(shellWidget, prelineGC);
2139 XtReleaseGC(shellWidget, wdPieceGC);
2140 XtReleaseGC(shellWidget, wlPieceGC);
2141 XtReleaseGC(shellWidget, bdPieceGC);
2142 XtReleaseGC(shellWidget, blPieceGC);
2147 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
2149 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2150 | GCBackground | GCFunction | GCPlaneMask;
2151 gc_values->foreground = foreground;
2152 gc_values->background = background;
2153 return XtGetGC(shellWidget, value_mask, gc_values);
2157 CreateGCs (int redo)
2159 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2160 | GCBackground | GCFunction | GCPlaneMask;
2161 XGCValues gc_values;
2163 Pixel white = XWhitePixel(xDisplay, xScreen);
2164 Pixel black = XBlackPixel(xDisplay, xScreen);
2166 gc_values.plane_mask = AllPlanes;
2167 gc_values.line_width = lineGap;
2168 gc_values.line_style = LineSolid;
2169 gc_values.function = GXcopy;
2172 DeleteGCs(); // called a second time; clean up old GCs first
2173 } else { // [HGM] grid and font GCs created on first call only
2174 coordGC = CreateOneGC(&gc_values, black, white);
2175 XSetFont(xDisplay, coordGC, coordFontID);
2177 // [HGM] make font for holdings counts (white on black)
2178 countGC = CreateOneGC(&gc_values, white, black);
2179 XSetFont(xDisplay, countGC, countFontID);
2181 lineGC = CreateOneGC(&gc_values, black, black);
2183 if (appData.monoMode) {
2185 highlineGC = CreateOneGC(&gc_values, white, white);
2186 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
2187 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
2189 if (DefaultDepth(xDisplay, xScreen) == 1) {
2190 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2191 gc_values.function = GXcopyInverted;
2192 copyInvertedGC = CreateOneGC(&gc_values, black, white);
2193 gc_values.function = GXcopy;
2194 if (XBlackPixel(xDisplay, xScreen) == 1) {
2195 bwPieceGC = darkSquareGC;
2196 wbPieceGC = copyInvertedGC;
2198 bwPieceGC = copyInvertedGC;
2199 wbPieceGC = lightSquareGC;
2204 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
2205 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
2206 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
2207 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
2208 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
2209 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
2210 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
2211 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
2216 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
2224 fp = fopen(filename, "rb");
2226 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
2233 for (y=0; y<h; ++y) {
2234 for (x=0; x<h; ++x) {
2239 XPutPixel(xim, x, y, blackPieceColor);
2241 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2244 XPutPixel(xim, x, y, darkSquareColor);
2246 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2249 XPutPixel(xim, x, y, whitePieceColor);
2251 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2254 XPutPixel(xim, x, y, lightSquareColor);
2256 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2264 /* create Pixmap of piece */
2265 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2267 XPutImage(xDisplay, *dest, lightSquareGC, xim,
2270 /* create Pixmap of clipmask
2271 Note: We assume the white/black pieces have the same
2272 outline, so we make only 6 masks. This is okay
2273 since the XPM clipmask routines do the same. */
2275 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2277 XPutImage(xDisplay, temp, lightSquareGC, xmask,
2280 /* now create the 1-bit version */
2281 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2284 values.foreground = 1;
2285 values.background = 0;
2287 /* Don't use XtGetGC, not read only */
2288 maskGC = XCreateGC(xDisplay, *mask,
2289 GCForeground | GCBackground, &values);
2290 XCopyPlane(xDisplay, temp, *mask, maskGC,
2291 0, 0, squareSize, squareSize, 0, 0, 1);
2292 XFreePixmap(xDisplay, temp);
2297 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
2305 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
2310 /* The XSynchronize calls were copied from CreatePieces.
2311 Not sure if needed, but can't hurt */
2312 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2315 /* temp needed by loadXIM() */
2316 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2317 0, 0, ss, ss, AllPlanes, XYPixmap);
2319 if (strlen(appData.pixmapDirectory) == 0) {
2323 if (appData.monoMode) {
2324 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
2328 fprintf(stderr, _("\nLoading XIMs...\n"));
2330 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2331 fprintf(stderr, "%d", piece+1);
2332 for (kind=0; kind<4; kind++) {
2333 fprintf(stderr, ".");
2334 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2335 ExpandPathName(appData.pixmapDirectory),
2336 piece <= (int) WhiteKing ? "" : "w",
2337 pieceBitmapNames[piece],
2339 ximPieceBitmap[kind][piece] =
2340 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2341 0, 0, ss, ss, AllPlanes, XYPixmap);
2342 if (appData.debugMode)
2343 fprintf(stderr, _("(File:%s:) "), buf);
2344 loadXIM(ximPieceBitmap[kind][piece],
2346 &(xpmPieceBitmap2[kind][piece]),
2347 &(ximMaskPm2[piece]));
2348 if(piece <= (int)WhiteKing)
2349 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2351 fprintf(stderr," ");
2353 /* Load light and dark squares */
2354 /* If the LSQ and DSQ pieces don't exist, we will
2355 draw them with solid squares. */
2356 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2357 if (access(buf, 0) != 0) {
2361 fprintf(stderr, _("light square "));
2363 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2364 0, 0, ss, ss, AllPlanes, XYPixmap);
2365 if (appData.debugMode)
2366 fprintf(stderr, _("(File:%s:) "), buf);
2368 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2369 fprintf(stderr, _("dark square "));
2370 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2371 ExpandPathName(appData.pixmapDirectory), ss);
2372 if (appData.debugMode)
2373 fprintf(stderr, _("(File:%s:) "), buf);
2375 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2376 0, 0, ss, ss, AllPlanes, XYPixmap);
2377 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2378 xpmJailSquare = xpmLightSquare;
2380 fprintf(stderr, _("Done.\n"));
2382 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2385 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2389 CreateXPMBoard (char *s, int kind)
2393 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2394 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2395 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2401 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2402 // thisroutine has to be called t free the old piece pixmaps
2404 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2405 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2407 XFreePixmap(xDisplay, xpmLightSquare);
2408 XFreePixmap(xDisplay, xpmDarkSquare);
2417 u_int ss = squareSize;
2419 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2420 XpmColorSymbol symbols[4];
2421 static int redo = False;
2423 if(redo) FreeXPMPieces(); else redo = 1;
2425 /* The XSynchronize calls were copied from CreatePieces.
2426 Not sure if needed, but can't hurt */
2427 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2429 /* Setup translations so piece colors match square colors */
2430 symbols[0].name = "light_piece";
2431 symbols[0].value = appData.whitePieceColor;
2432 symbols[1].name = "dark_piece";
2433 symbols[1].value = appData.blackPieceColor;
2434 symbols[2].name = "light_square";
2435 symbols[2].value = appData.lightSquareColor;
2436 symbols[3].name = "dark_square";
2437 symbols[3].value = appData.darkSquareColor;
2439 attr.valuemask = XpmColorSymbols;
2440 attr.colorsymbols = symbols;
2441 attr.numsymbols = 4;
2443 if (appData.monoMode) {
2444 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2448 if (strlen(appData.pixmapDirectory) == 0) {
2449 XpmPieces* pieces = builtInXpms;
2452 while (pieces->size != squareSize && pieces->size) pieces++;
2453 if (!pieces->size) {
2454 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2457 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2458 for (kind=0; kind<4; kind++) {
2460 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2461 pieces->xpm[piece][kind],
2462 &(xpmPieceBitmap2[kind][piece]),
2463 NULL, &attr)) != 0) {
2464 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2468 if(piece <= (int) WhiteKing)
2469 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2473 xpmJailSquare = xpmLightSquare;
2477 fprintf(stderr, _("\nLoading XPMs...\n"));
2480 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2481 fprintf(stderr, "%d ", piece+1);
2482 for (kind=0; kind<4; kind++) {
2483 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2484 ExpandPathName(appData.pixmapDirectory),
2485 piece > (int) WhiteKing ? "w" : "",
2486 pieceBitmapNames[piece],
2488 if (appData.debugMode) {
2489 fprintf(stderr, _("(File:%s:) "), buf);
2491 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2492 &(xpmPieceBitmap2[kind][piece]),
2493 NULL, &attr)) != 0) {
2494 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2495 // [HGM] missing: read of unorthodox piece failed; substitute King.
2496 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2497 ExpandPathName(appData.pixmapDirectory),
2499 if (appData.debugMode) {
2500 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2502 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2503 &(xpmPieceBitmap2[kind][piece]),
2507 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2512 if(piece <= (int) WhiteKing)
2513 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2516 /* Load light and dark squares */
2517 /* If the LSQ and DSQ pieces don't exist, we will
2518 draw them with solid squares. */
2519 fprintf(stderr, _("light square "));
2520 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2521 if (access(buf, 0) != 0) {
2525 if (appData.debugMode)
2526 fprintf(stderr, _("(File:%s:) "), buf);
2528 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2529 &xpmLightSquare, NULL, &attr)) != 0) {
2530 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2533 fprintf(stderr, _("dark square "));
2534 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2535 ExpandPathName(appData.pixmapDirectory), ss);
2536 if (appData.debugMode) {
2537 fprintf(stderr, _("(File:%s:) "), buf);
2539 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2540 &xpmDarkSquare, NULL, &attr)) != 0) {
2541 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2545 xpmJailSquare = xpmLightSquare;
2546 fprintf(stderr, _("Done.\n"));
2548 oldVariant = -1; // kludge to force re-makig of animation masks
2549 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2552 #endif /* HAVE_LIBXPM */
2555 /* No built-in bitmaps */
2560 u_int ss = squareSize;
2562 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2565 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2566 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2567 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2568 pieceBitmapNames[piece],
2569 ss, kind == SOLID ? 's' : 'o');
2570 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2571 if(piece <= (int)WhiteKing)
2572 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2576 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2580 /* With built-in bitmaps */
2584 BuiltInBits* bib = builtInBits;
2587 u_int ss = squareSize;
2589 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2592 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2594 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2595 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2596 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2597 pieceBitmapNames[piece],
2598 ss, kind == SOLID ? 's' : 'o');
2599 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2600 bib->bits[kind][piece], ss, ss);
2601 if(piece <= (int)WhiteKing)
2602 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2606 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2612 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2617 char msg[MSG_SIZ], fullname[MSG_SIZ];
2619 if (*appData.bitmapDirectory != NULLCHAR) {
2620 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2621 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2622 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2623 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2624 &w, &h, pm, &x_hot, &y_hot);
2625 fprintf(stderr, "load %s\n", name);
2626 if (errcode != BitmapSuccess) {
2628 case BitmapOpenFailed:
2629 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2631 case BitmapFileInvalid:
2632 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2634 case BitmapNoMemory:
2635 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2639 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2643 fprintf(stderr, _("%s: %s...using built-in\n"),
2645 } else if (w != wreq || h != hreq) {
2647 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2648 programName, fullname, w, h, wreq, hreq);
2654 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2664 if (lineGap == 0) return;
2666 /* [HR] Split this into 2 loops for non-square boards. */
2668 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2669 gridSegments[i].x1 = 0;
2670 gridSegments[i].x2 =
2671 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2672 gridSegments[i].y1 = gridSegments[i].y2
2673 = lineGap / 2 + (i * (squareSize + lineGap));
2676 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2677 gridSegments[j + i].y1 = 0;
2678 gridSegments[j + i].y2 =
2679 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2680 gridSegments[j + i].x1 = gridSegments[j + i].x2
2681 = lineGap / 2 + (j * (squareSize + lineGap));
2685 int nrOfMenuItems = 7;
2686 Widget menuWidget[150];
2687 MenuListItem menuItemList[150] = {
2688 { "LoadNextGameProc", LoadNextGameProc },
2689 { "LoadPrevGameProc", LoadPrevGameProc },
2690 { "ReloadGameProc", ReloadGameProc },
2691 { "ReloadPositionProc", ReloadPositionProc },
2692 #ifndef OPTIONSDIALOG
2693 { "AlwaysQueenProc", AlwaysQueenProc },
2694 { "AnimateDraggingProc", AnimateDraggingProc },
2695 { "AnimateMovingProc", AnimateMovingProc },
2696 { "AutoflagProc", AutoflagProc },
2697 { "AutoflipProc", AutoflipProc },
2698 { "BlindfoldProc", BlindfoldProc },
2699 { "FlashMovesProc", FlashMovesProc },
2701 { "HighlightDraggingProc", HighlightDraggingProc },
2703 { "HighlightLastMoveProc", HighlightLastMoveProc },
2704 // { "IcsAlarmProc", IcsAlarmProc },
2705 { "MoveSoundProc", MoveSoundProc },
2706 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
2707 { "PopupExitMessageProc", PopupExitMessageProc },
2708 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
2709 // { "PremoveProc", PremoveProc },
2710 { "ShowCoordsProc", ShowCoordsProc },
2711 { "ShowThinkingProc", ShowThinkingProc },
2712 { "HideThinkingProc", HideThinkingProc },
2713 { "TestLegalityProc", TestLegalityProc },
2715 { "AboutGameProc", AboutGameEvent },
2716 { "DebugProc", DebugProc },
2717 { "NothingProc", NothingProc },
2722 MarkMenuItem (char *menuRef, int state)
2724 int nr = MenuToNumber(menuRef);
2727 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2728 XtSetValues(menuWidget[nr], args, 1);
2733 EnableMenuItem (char *menuRef, int state)
2735 int nr = MenuToNumber(menuRef);
2736 if(nr >= 0) XtSetSensitive(menuWidget[nr], state);
2740 EnableButtonBar (int state)
2742 XtSetSensitive(buttonBarWidget, state);
2747 SetMenuEnables (Enables *enab)
2749 while (enab->name != NULL) {
2750 EnableMenuItem(enab->name, enab->value);
2756 Equal(char *p, char *s)
2757 { // compare strings skipping spaces in second
2759 if(*s == ' ') { s++; continue; }
2760 if(*s++ != *p++) return 0;
2766 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2767 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2769 if(*nprms == 0) return;
2770 for(i=0; menuItemList[i].name; i++) {
2771 if(Equal(prms[0], menuItemList[i].name)) {
2772 (menuItemList[i].proc) ();
2779 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
2781 MenuProc *proc = (MenuProc *) addr;
2787 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2789 RecentEngineEvent((int) (intptr_t) addr);
2792 // some stuff that must remain in front-end
2793 static Widget mainBar, currentMenu;
2794 static int wtot, nr = 0, widths[10];
2797 AppendMenuItem (char *text, char *name, MenuProc *action)
2804 XtSetArg(args[j], XtNleftMargin, 20); j++;
2805 XtSetArg(args[j], XtNrightMargin, 20); j++;
2807 if (strcmp(text, "----") == 0) {
2808 entry = XtCreateManagedWidget(text, smeLineObjectClass,
2809 currentMenu, args, j);
2811 XtSetArg(args[j], XtNlabel, XtNewString(_(text)));
2812 entry = XtCreateManagedWidget(name, smeBSBObjectClass,
2813 currentMenu, args, j+1);
2814 XtAddCallback(entry, XtNcallback,
2815 (XtCallbackProc) (strcmp(name, "recent") ? MenuBarSelect : MenuEngineSelect),
2817 menuWidget[nrOfMenuItems] = entry;
2822 CreateMenuButton (char *name, Menu *mb)
2823 { // create menu button on main bar, and shell for pull-down list
2829 XtSetArg(args[j], XtNmenuName, XtNewString(name)); j++;
2830 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
2831 XtSetArg(args[j], XtNborderWidth, 0); j++;
2832 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
2834 currentMenu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2837 XtSetArg(args[j], XtNwidth, &w); j++;
2838 XtGetValues(mb->subMenu, args, j);
2839 wtot += mb->textWidth = widths[nr++] = w;
2843 CreateMenuBar (Menu *mb, int boardWidth)
2847 char menuName[MSG_SIZ];
2851 // create bar itself
2853 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2854 XtSetArg(args[j], XtNvSpace, 0); j++;
2855 XtSetArg(args[j], XtNborderWidth, 0); j++;
2856 mainBar = XtCreateWidget("menuBar", boxWidgetClass,
2857 formWidget, args, j);
2859 CreateMainMenus(mb); // put menus in bar according to description in back-end
2861 // size buttons to make menu bar fit, clipping menu names where necessary
2862 while(wtot > boardWidth - 40) {
2864 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
2868 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
2870 XtSetArg(args[j], XtNwidth, widths[i]); j++;
2871 XtSetValues(ma[i].subMenu, args, j);
2878 CreateButtonBar (MenuItem *mi)
2881 Widget button, buttonBar;
2885 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2887 XtSetArg(args[j], XtNhSpace, 0); j++;
2889 XtSetArg(args[j], XtNborderWidth, 0); j++;
2890 XtSetArg(args[j], XtNvSpace, 0); j++;
2891 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
2892 formWidget, args, j);
2894 while (mi->string != NULL) {
2897 XtSetArg(args[j], XtNinternalWidth, 2); j++;
2898 XtSetArg(args[j], XtNborderWidth, 0); j++;
2900 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
2901 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
2902 buttonBar, args, j);
2903 XtAddCallback(button, XtNcallback,
2904 (XtCallbackProc) MenuBarSelect,
2905 (caddr_t) mi->proc);
2912 CreatePieceMenu (char *name, int color)
2917 ChessSquare selection;
2919 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2920 boardWidget, args, 0);
2922 for (i = 0; i < PIECE_MENU_SIZE; i++) {
2923 String item = pieceMenuStrings[color][i];
2925 if (strcmp(item, "----") == 0) {
2926 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2929 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2930 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2932 selection = pieceMenuTranslation[color][i];
2933 XtAddCallback(entry, XtNcallback,
2934 (XtCallbackProc) PieceMenuSelect,
2935 (caddr_t) selection);
2936 if (selection == WhitePawn || selection == BlackPawn) {
2937 XtSetArg(args[0], XtNpopupOnEntry, entry);
2938 XtSetValues(menu, args, 1);
2951 ChessSquare selection;
2953 whitePieceMenu = CreatePieceMenu("menuW", 0);
2954 blackPieceMenu = CreatePieceMenu("menuB", 1);
2956 if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
2957 XtRegisterGrabAction(PieceMenuPopup, True,
2958 (unsigned)(ButtonPressMask|ButtonReleaseMask),
2959 GrabModeAsync, GrabModeAsync);
2961 XtSetArg(args[0], XtNlabel, _("Drop"));
2962 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
2963 boardWidget, args, 1);
2964 for (i = 0; i < DROP_MENU_SIZE; i++) {
2965 String item = dropMenuStrings[i];
2967 if (strcmp(item, "----") == 0) {
2968 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2971 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2972 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2974 selection = dropMenuTranslation[i];
2975 XtAddCallback(entry, XtNcallback,
2976 (XtCallbackProc) DropMenuSelect,
2977 (caddr_t) selection);
2991 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2992 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2993 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2994 dmEnables[i].piece);
2995 XtSetSensitive(entry, p != NULL || !appData.testLegality
2996 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2997 && !appData.icsActive));
2999 while (p && *p++ == dmEnables[i].piece) count++;
3000 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3002 XtSetArg(args[j], XtNlabel, label); j++;
3003 XtSetValues(entry, args, j);
3008 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
3010 String whichMenu; int menuNr = -2;
3011 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3012 if (event->type == ButtonRelease)
3013 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3014 else if (event->type == ButtonPress)
3015 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3017 case 0: whichMenu = params[0]; break;
3018 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3020 case -1: ErrorPopDown();
3023 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3027 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3029 if (pmFromX < 0 || pmFromY < 0) return;
3030 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3034 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3036 if (pmFromX < 0 || pmFromY < 0) return;
3037 DropMenuEvent(piece, pmFromX, pmFromY);
3041 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3043 shiftKey = prms[0][0] & 1;
3048 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3050 shiftKey = prms[0][0] & 1;
3056 do_flash_delay (unsigned long msec)
3062 DrawBorder (int x, int y, int type)
3066 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
3068 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3069 squareSize+lineGap, squareSize+lineGap);
3073 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
3075 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
3076 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
3078 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
3079 if(textureW[kind] < W*squareSize)
3080 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
3082 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
3083 if(textureH[kind] < H*squareSize)
3084 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
3086 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
3091 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
3092 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
3094 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
3095 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
3096 squareSize, squareSize, x*fac, y*fac);
3098 if (useImages && useImageSqs) {
3102 pm = xpmLightSquare;
3107 case 2: /* neutral */
3109 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
3112 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3113 squareSize, squareSize, x*fac, y*fac);
3123 case 2: /* neutral */
3128 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
3133 I split out the routines to draw a piece so that I could
3134 make a generic flash routine.
3137 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3139 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3140 switch (square_color) {
3142 case 2: /* neutral */
3144 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3145 ? *pieceToOutline(piece)
3146 : *pieceToSolid(piece),
3147 dest, bwPieceGC, 0, 0,
3148 squareSize, squareSize, x, y);
3151 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3152 ? *pieceToSolid(piece)
3153 : *pieceToOutline(piece),
3154 dest, wbPieceGC, 0, 0,
3155 squareSize, squareSize, x, y);
3161 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3163 switch (square_color) {
3165 case 2: /* neutral */
3167 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3168 ? *pieceToOutline(piece)
3169 : *pieceToSolid(piece),
3170 dest, bwPieceGC, 0, 0,
3171 squareSize, squareSize, x, y, 1);
3174 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3175 ? *pieceToSolid(piece)
3176 : *pieceToOutline(piece),
3177 dest, wbPieceGC, 0, 0,
3178 squareSize, squareSize, x, y, 1);
3184 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3186 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
3187 switch (square_color) {
3189 XCopyPlane(xDisplay, *pieceToSolid(piece),
3190 dest, (int) piece < (int) BlackPawn
3191 ? wlPieceGC : blPieceGC, 0, 0,
3192 squareSize, squareSize, x, y, 1);
3195 XCopyPlane(xDisplay, *pieceToSolid(piece),
3196 dest, (int) piece < (int) BlackPawn
3197 ? wdPieceGC : bdPieceGC, 0, 0,
3198 squareSize, squareSize, x, y, 1);
3200 case 2: /* neutral */
3202 break; // should never contain pieces
3207 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3209 int kind, p = piece;
3211 switch (square_color) {
3213 case 2: /* neutral */
3215 if ((int)piece < (int) BlackPawn) {
3223 if ((int)piece < (int) BlackPawn) {
3231 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
3232 if(useTexture & square_color+1) {
3233 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
3234 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
3235 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
3236 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
3237 XSetClipMask(xDisplay, wlPieceGC, None);
3238 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
3240 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3241 dest, wlPieceGC, 0, 0,
3242 squareSize, squareSize, x, y);
3245 typedef void (*DrawFunc)();
3250 if (appData.monoMode) {
3251 if (DefaultDepth(xDisplay, xScreen) == 1) {
3252 return monoDrawPiece_1bit;
3254 return monoDrawPiece;
3258 return colorDrawPieceImage;
3260 return colorDrawPiece;
3265 DrawDot (int marker, int x, int y, int r)
3267 if(appData.monoMode) {
3268 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
3269 x, y, r, r, 0, 64*360);
3270 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
3271 x, y, r, r, 0, 64*360);
3273 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
3274 x, y, r, r, 0, 64*360);
3278 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
3279 { // basic front-end board-draw function: takes care of everything that can be in square:
3280 // piece, background, coordinate/count, marker dot
3281 int direction, font_ascent, font_descent;
3282 XCharStruct overall;
3285 if (piece == EmptySquare) {
3286 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
3288 drawfunc = ChooseDrawFunc();
3289 drawfunc(piece, square_color, x, y, xBoardWindow);
3292 if(align) { // square carries inscription (coord or piece count)
3294 GC hGC = align < 3 ? coordGC : countGC;
3295 // first calculate where it goes
3296 XTextExtents(countFontStruct, string, 1, &direction,
3297 &font_ascent, &font_descent, &overall);
3299 xx += squareSize - overall.width - 2;
3300 yy += squareSize - font_descent - 1;
3301 } else if (align == 2) {
3302 xx += 2, yy += font_ascent + 1;
3303 } else if (align == 3) {
3304 xx += squareSize - overall.width - 2;
3305 yy += font_ascent + 1;
3306 } else if (align == 4) {
3307 xx += 2, yy += font_ascent + 1;
3310 if (appData.monoMode) {
3311 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3313 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3317 if(marker) { // print fat marker dot, if requested
3318 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
3323 FlashDelay (int flash_delay)
3325 XSync(xDisplay, False);
3326 if(flash_delay) do_flash_delay(flash_delay);
3330 Fraction (int x, int start, int stop)
3332 double f = ((double) x - start)/(stop - start);
3333 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
3337 static WindowPlacement wpNew;
3340 CoDrag (Widget sh, WindowPlacement *wp)
3343 int j=0, touch=0, fudge = 2;
3344 GetActualPlacement(sh, wp);
3345 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
3346 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
3347 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
3348 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
3349 if(!touch ) return; // only windows that touch co-move
3350 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
3351 int heightInc = wpNew.height - wpMain.height;
3352 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3353 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3354 wp->y += fracTop * heightInc;
3355 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
3356 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
3357 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
3358 int widthInc = wpNew.width - wpMain.width;
3359 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3360 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3361 wp->y += fracLeft * widthInc;
3362 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
3363 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
3365 wp->x += wpNew.x - wpMain.x;
3366 wp->y += wpNew.y - wpMain.y;
3367 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
3368 if(touch == 3) wp->y += wpNew.height - wpMain.height;
3369 XtSetArg(args[j], XtNx, wp->x); j++;
3370 XtSetArg(args[j], XtNy, wp->y); j++;
3371 XtSetValues(sh, args, j);
3374 static XtIntervalId delayedDragID = 0;
3379 GetActualPlacement(shellWidget, &wpNew);
3380 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
3381 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
3382 return; // false alarm
3383 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
3384 if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
3385 if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
3386 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
3388 DrawPosition(True, NULL);
3389 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
3396 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
3398 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
3401 /* Why is this needed on some versions of X? */
3403 EventProc (Widget widget, caddr_t unused, XEvent *event)
3405 if (!XtIsRealized(widget))
3407 switch (event->type) {
3408 case ConfigureNotify: // main window is being dragged: drag attached windows with it
3409 if(appData.useStickyWindows)
3410 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
3413 if (event->xexpose.count > 0) return; /* no clipping is done */
3414 DrawPosition(True, NULL);
3415 if(twoBoards) { // [HGM] dual: draw other board in other orientation
3416 flipView = !flipView; partnerUp = !partnerUp;
3417 DrawPosition(True, NULL);
3418 flipView = !flipView; partnerUp = !partnerUp;
3422 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
3429 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
3431 DrawSeekAxis (int x, int y, int xTo, int yTo)
3433 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
3437 DrawSeekBackground (int left, int top, int right, int bottom)
3439 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
3443 DrawSeekText (char *buf, int x, int y)
3445 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
3449 DrawSeekDot (int x, int y, int colorNr)
3451 int square = colorNr & 0x80;
3454 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
3456 XFillRectangle(xDisplay, xBoardWindow, color,
3457 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3459 XFillArc(xDisplay, xBoardWindow, color,
3460 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
3464 DrawGrid (int second)
3466 XDrawSegments(xDisplay, xBoardWindow, lineGC,
3467 second ? secondSegments : // [HGM] dual
3468 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
3473 * event handler for redrawing the board
3476 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3478 DrawPosition(True, NULL);
3483 * event handler for parsing user moves
3485 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
3486 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
3487 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
3488 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
3489 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
3490 // and at the end FinishMove() to perform the move after optional promotion popups.
3491 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
3493 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3495 if (w != boardWidget || errorExitStatus != -1) return;
3496 if(nprms) shiftKey = !strcmp(prms[0], "1");
3498 if (shellUp[PromoDlg]) { // [HGM] is this still needed?
3499 if (event->type == ButtonPress) {
3508 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
3509 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
3510 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
3514 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
3516 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
3517 DragPieceMove(event->xmotion.x, event->xmotion.y);
3521 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3522 { // [HGM] pv: walk PV
3523 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3526 static int savedIndex; /* gross that this is global */
3529 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3532 XawTextPosition index, dummy;
3535 XawTextGetSelectionPos(w, &index, &dummy);
3536 XtSetArg(arg, XtNstring, &val);
3537 XtGetValues(w, &arg, 1);
3538 ReplaceComment(savedIndex, val);
3539 if(savedIndex != currentMove) ToNrEvent(savedIndex);
3540 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3544 EditCommentPopUp (int index, char *title, char *text)
3547 if (text == NULL) text = "";
3548 NewCommentPopup(title, text, index);
3552 CommentPopUp (char *title, char *text)
3554 savedIndex = currentMove; // [HGM] vari
3555 NewCommentPopup(title, text, currentMove);
3561 PopDown(CommentDlg);
3564 static char *openName;
3570 (void) (*fileProc)(openFP, 0, openName);
3574 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
3576 fileProc = proc; /* I can't see a way not */
3577 fileOpenMode = openMode; /* to use globals here */
3578 { // [HGM] use file-selector dialog stolen from Ghostview
3579 int index; // this is not supported yet
3580 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
3581 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
3582 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
3583 ScheduleDelayedEvent(&DelayedLoad, 50);
3588 /* Disable all user input other than deleting the window */
3589 static int frozen = 0;
3595 /* Grab by a widget that doesn't accept input */
3596 XtAddGrab(messageWidget, TRUE, FALSE);
3600 /* Undo a FreezeUI */
3604 if (!frozen) return;
3605 XtRemoveGrab(messageWidget);
3613 static int oldPausing = FALSE;
3614 static GameMode oldmode = (GameMode) -1;
3617 if (!boardWidget || !XtIsRealized(boardWidget)) return;
3619 if (pausing != oldPausing) {
3620 oldPausing = pausing;
3621 MarkMenuItem("Pause", pausing);
3623 if (appData.showButtonBar) {
3624 /* Always toggle, don't set. Previous code messes up when
3625 invoked while the button is pressed, as releasing it
3626 toggles the state again. */
3629 XtSetArg(args[0], XtNbackground, &oldbg);
3630 XtSetArg(args[1], XtNforeground, &oldfg);
3631 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
3633 XtSetArg(args[0], XtNbackground, oldfg);
3634 XtSetArg(args[1], XtNforeground, oldbg);
3636 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
3640 wname = ModeToWidgetName(oldmode);
3641 if (wname != NULL) {
3642 MarkMenuItem(wname, False);
3644 wname = ModeToWidgetName(gameMode);
3645 if (wname != NULL) {
3646 MarkMenuItem(wname, True);
3649 MarkMenuItem("Machine Match", matchMode && matchGame < appData.matchGames);
3651 /* Maybe all the enables should be handled here, not just this one */
3652 EnableMenuItem("Training", gameMode == Training || gameMode == PlayFromGameFile);
3657 * Button/menu procedures
3660 LoadGamePopUp (FILE *f, int gameNumber, char *title)
3662 cmailMsgLoaded = FALSE;
3663 if (gameNumber == 0) {
3664 int error = GameListBuild(f);
3666 DisplayError(_("Cannot build game list"), error);
3667 } else if (!ListEmpty(&gameList) &&
3668 ((ListGame *) gameList.tailPred)->number > 1) {
3669 GameListPopUp(f, title);
3675 return LoadGame(f, gameNumber, title, FALSE);
3678 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3679 char *selected_fen_position=NULL;
3682 SendPositionSelection (Widget w, Atom *selection, Atom *target,
3683 Atom *type_return, XtPointer *value_return,
3684 unsigned long *length_return, int *format_return)
3686 char *selection_tmp;
3688 // if (!selected_fen_position) return False; /* should never happen */
3689 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3690 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
3691 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
3694 if (f == NULL) return False;
3698 selection_tmp = XtMalloc(len + 1);
3699 count = fread(selection_tmp, 1, len, f);
3702 XtFree(selection_tmp);
3705 selection_tmp[len] = NULLCHAR;
3707 /* note: since no XtSelectionDoneProc was registered, Xt will
3708 * automatically call XtFree on the value returned. So have to
3709 * make a copy of it allocated with XtMalloc */
3710 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3711 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
3714 *value_return=selection_tmp;
3715 *length_return=strlen(selection_tmp);
3716 *type_return=*target;
3717 *format_return = 8; /* bits per byte */
3719 } else if (*target == XA_TARGETS(xDisplay)) {
3720 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3721 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3722 targets_tmp[1] = XA_STRING;
3723 *value_return = targets_tmp;
3724 *type_return = XA_ATOM;
3727 // This code leads to a read of value_return out of bounds on 64-bit systems.
3728 // Other code which I have seen always sets *format_return to 32 independent of
3729 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
3730 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
3731 *format_return = 8 * sizeof(Atom);
3732 if (*format_return > 32) {
3733 *length_return *= *format_return / 32;
3734 *format_return = 32;
3737 *format_return = 32;
3745 /* note: when called from menu all parameters are NULL, so no clue what the
3746 * Widget which was clicked on was, or what the click event was
3749 CopySomething (char *src)
3751 selected_fen_position = src;
3753 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3754 * have a notion of a position that is selected but not copied.
3755 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3757 XtOwnSelection(menuBarWidget, XA_PRIMARY,
3759 SendPositionSelection,
3760 NULL/* lose_ownership_proc */ ,
3761 NULL/* transfer_done_proc */);
3762 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3764 SendPositionSelection,
3765 NULL/* lose_ownership_proc */ ,
3766 NULL/* transfer_done_proc */);
3769 /* function called when the data to Paste is ready */
3771 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3772 Atom *type, XtPointer value, unsigned long *len, int *format)
3775 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3776 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3777 EditPositionPasteFEN(fenstr);
3781 /* called when Paste Position button is pressed,
3782 * all parameters will be NULL */
3784 PastePositionProc ()
3786 XtGetSelectionValue(menuBarWidget,
3787 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3788 /* (XtSelectionCallbackProc) */ PastePositionCB,
3789 NULL, /* client_data passed to PastePositionCB */
3791 /* better to use the time field from the event that triggered the
3792 * call to this function, but that isn't trivial to get
3799 /* note: when called from menu all parameters are NULL, so no clue what the
3800 * Widget which was clicked on was, or what the click event was
3802 /* function called when the data to Paste is ready */
3804 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3805 Atom *type, XtPointer value, unsigned long *len, int *format)
3808 if (value == NULL || *len == 0) {
3809 return; /* nothing had been selected to copy */
3811 f = fopen(gamePasteFilename, "w");
3813 DisplayError(_("Can't open temp file"), errno);
3816 fwrite(value, 1, *len, f);
3819 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3822 /* called when Paste Game button is pressed,
3823 * all parameters will be NULL */
3827 XtGetSelectionValue(menuBarWidget,
3828 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3829 /* (XtSelectionCallbackProc) */ PasteGameCB,
3830 NULL, /* client_data passed to PasteGameCB */
3832 /* better to use the time field from the event that triggered the
3833 * call to this function, but that isn't trivial to get
3842 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3849 { // bassic primitive for determining if modifier keys are pressed
3850 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3853 XQueryKeymap(xDisplay,keys);
3854 for(i=0; i<6; i++) {
3856 j = XKeysymToKeycode(xDisplay, codes[i]);
3857 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3863 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3867 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3868 if ( n == 1 && *buf >= 32 // printable
3869 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3870 ) BoxAutoPopUp (buf);
3874 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3875 { // [HGM] input: let up-arrow recall previous line from history
3880 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3881 { // [HGM] input: let down-arrow recall next line from history
3886 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3892 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3894 if (!TempBackwardActive) {
3895 TempBackwardActive = True;
3901 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3903 /* Check to see if triggered by a key release event for a repeating key.
3904 * If so the next queued event will be a key press of the same key at the same time */
3905 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3907 XPeekEvent(xDisplay, &next);
3908 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3909 next.xkey.keycode == event->xkey.keycode)
3913 TempBackwardActive = False;
3917 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3918 { // called as key binding
3921 if (nprms && *nprms > 0)
3925 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3930 DisplayMessage (char *message, char *extMessage)
3932 /* display a message in the message widget */
3941 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
3946 message = extMessage;
3950 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
3952 /* need to test if messageWidget already exists, since this function
3953 can also be called during the startup, if for example a Xresource
3954 is not set up correctly */
3957 XtSetArg(arg, XtNlabel, message);
3958 XtSetValues(messageWidget, &arg, 1);
3965 SetWindowTitle (char *text, char *title, char *icon)
3969 if (appData.titleInWindow) {
3971 XtSetArg(args[i], XtNlabel, text); i++;
3972 XtSetValues(titleWidget, args, i);
3975 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3976 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3977 XtSetValues(shellWidget, args, i);
3978 XSync(xDisplay, False);
3983 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3989 DisplayIcsInteractionTitle (String message)
3991 if (oldICSInteractionTitle == NULL) {
3992 /* Magic to find the old window title, adapted from vim */
3993 char *wina = getenv("WINDOWID");
3995 Window win = (Window) atoi(wina);
3996 Window root, parent, *children;
3997 unsigned int nchildren;
3998 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4000 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4001 if (!XQueryTree(xDisplay, win, &root, &parent,
4002 &children, &nchildren)) break;
4003 if (children) XFree((void *)children);
4004 if (parent == root || parent == 0) break;
4007 XSetErrorHandler(oldHandler);
4009 if (oldICSInteractionTitle == NULL) {
4010 oldICSInteractionTitle = "xterm";
4013 printf("\033]0;%s\007", message);
4018 XtIntervalId delayedEventTimerXID = 0;
4019 DelayedEventCallback delayedEventCallback = 0;
4024 delayedEventTimerXID = 0;
4025 delayedEventCallback();
4029 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
4031 if(delayedEventTimerXID && delayedEventCallback == cb)
4032 // [HGM] alive: replace, rather than add or flush identical event
4033 XtRemoveTimeOut(delayedEventTimerXID);
4034 delayedEventCallback = cb;
4035 delayedEventTimerXID =
4036 XtAppAddTimeOut(appContext, millisec,
4037 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
4040 DelayedEventCallback
4043 if (delayedEventTimerXID) {
4044 return delayedEventCallback;
4051 CancelDelayedEvent ()
4053 if (delayedEventTimerXID) {
4054 XtRemoveTimeOut(delayedEventTimerXID);
4055 delayedEventTimerXID = 0;
4059 XtIntervalId loadGameTimerXID = 0;
4062 LoadGameTimerRunning ()
4064 return loadGameTimerXID != 0;
4068 StopLoadGameTimer ()
4070 if (loadGameTimerXID != 0) {
4071 XtRemoveTimeOut(loadGameTimerXID);
4072 loadGameTimerXID = 0;
4080 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
4082 loadGameTimerXID = 0;
4087 StartLoadGameTimer (long millisec)
4090 XtAppAddTimeOut(appContext, millisec,
4091 (XtTimerCallbackProc) LoadGameTimerCallback,
4095 XtIntervalId analysisClockXID = 0;
4098 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
4100 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
4101 || appData.icsEngineAnalyze) { // [DM]
4102 AnalysisPeriodicEvent(0);
4103 StartAnalysisClock();
4108 StartAnalysisClock ()
4111 XtAppAddTimeOut(appContext, 2000,
4112 (XtTimerCallbackProc) AnalysisClockCallback,
4116 XtIntervalId clockTimerXID = 0;
4119 ClockTimerRunning ()
4121 return clockTimerXID != 0;
4127 if (clockTimerXID != 0) {
4128 XtRemoveTimeOut(clockTimerXID);
4137 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
4144 StartClockTimer (long millisec)
4147 XtAppAddTimeOut(appContext, millisec,
4148 (XtTimerCallbackProc) ClockTimerCallback,
4153 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
4158 /* check for low time warning */
4159 Pixel foregroundOrWarningColor = timerForegroundPixel;
4162 appData.lowTimeWarning &&
4163 (timer / 1000) < appData.icsAlarmTime)
4164 foregroundOrWarningColor = lowTimeWarningColor;
4166 if (appData.clockMode) {
4167 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
4168 XtSetArg(args[0], XtNlabel, buf);
4170 snprintf(buf, MSG_SIZ, "%s ", color);
4171 XtSetArg(args[0], XtNlabel, buf);
4176 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
4177 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
4179 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
4180 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
4183 XtSetValues(w, args, 3);
4187 DisplayWhiteClock (long timeRemaining, int highlight)
4191 if(appData.noGUI) return;
4192 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
4193 if (highlight && iconPixmap == bIconPixmap) {
4194 iconPixmap = wIconPixmap;
4195 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
4196 XtSetValues(shellWidget, args, 1);
4201 DisplayBlackClock (long timeRemaining, int highlight)
4205 if(appData.noGUI) return;
4206 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
4207 if (highlight && iconPixmap == wIconPixmap) {
4208 iconPixmap = bIconPixmap;
4209 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
4210 XtSetValues(shellWidget, args, 1);
4215 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
4217 InputSource *is = (InputSource *) closure;
4222 if (is->lineByLine) {
4223 count = read(is->fd, is->unused,
4224 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
4226 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
4229 is->unused += count;
4231 while (p < is->unused) {
4232 q = memchr(p, '\n', is->unused - p);
4233 if (q == NULL) break;
4235 (is->func)(is, is->closure, p, q - p, 0);
4239 while (p < is->unused) {
4244 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
4249 (is->func)(is, is->closure, is->buf, count, error);
4254 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
4257 ChildProc *cp = (ChildProc *) pr;
4259 is = (InputSource *) calloc(1, sizeof(InputSource));
4260 is->lineByLine = lineByLine;
4264 is->fd = fileno(stdin);
4266 is->kind = cp->kind;
4267 is->fd = cp->fdFrom;
4270 is->unused = is->buf;
4273 is->xid = XtAppAddInput(appContext, is->fd,
4274 (XtPointer) (XtInputReadMask),
4275 (XtInputCallbackProc) DoInputCallback,
4277 is->closure = closure;
4278 return (InputSourceRef) is;
4282 RemoveInputSource (InputSourceRef isr)
4284 InputSource *is = (InputSource *) isr;
4286 if (is->xid == 0) return;
4287 XtRemoveInput(is->xid);
4291 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
4293 /* Masks for XPM pieces. Black and white pieces can have
4294 different shapes, but in the interest of retaining my
4295 sanity pieces must have the same outline on both light
4296 and dark squares, and all pieces must use the same
4297 background square colors/images. */
4299 static int xpmDone = 0;
4300 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
4301 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
4304 CreateAnimMasks (int pieceDepth)
4310 unsigned long plane;
4313 /* Need a bitmap just to get a GC with right depth */
4314 buf = XCreatePixmap(xDisplay, xBoardWindow,
4316 values.foreground = 1;
4317 values.background = 0;
4318 /* Don't use XtGetGC, not read only */
4319 maskGC = XCreateGC(xDisplay, buf,
4320 GCForeground | GCBackground, &values);
4321 XFreePixmap(xDisplay, buf);
4323 buf = XCreatePixmap(xDisplay, xBoardWindow,
4324 squareSize, squareSize, pieceDepth);
4325 values.foreground = XBlackPixel(xDisplay, xScreen);
4326 values.background = XWhitePixel(xDisplay, xScreen);
4327 bufGC = XCreateGC(xDisplay, buf,
4328 GCForeground | GCBackground, &values);
4330 for (piece = WhitePawn; piece <= BlackKing; piece++) {
4331 /* Begin with empty mask */
4332 if(!xpmDone) // [HGM] pieces: keep using existing
4333 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
4334 squareSize, squareSize, 1);
4335 XSetFunction(xDisplay, maskGC, GXclear);
4336 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
4337 0, 0, squareSize, squareSize);
4339 /* Take a copy of the piece */
4344 XSetFunction(xDisplay, bufGC, GXcopy);
4345 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
4347 0, 0, squareSize, squareSize, 0, 0);
4349 /* XOR the background (light) over the piece */
4350 XSetFunction(xDisplay, bufGC, GXxor);
4352 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
4353 0, 0, squareSize, squareSize, 0, 0);
4355 XSetForeground(xDisplay, bufGC, lightSquareColor);
4356 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
4359 /* We now have an inverted piece image with the background
4360 erased. Construct mask by just selecting all the non-zero
4361 pixels - no need to reconstruct the original image. */
4362 XSetFunction(xDisplay, maskGC, GXor);
4364 /* Might be quicker to download an XImage and create bitmap
4365 data from it rather than this N copies per piece, but it
4366 only takes a fraction of a second and there is a much
4367 longer delay for loading the pieces. */
4368 for (n = 0; n < pieceDepth; n ++) {
4369 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
4370 0, 0, squareSize, squareSize,
4376 XFreePixmap(xDisplay, buf);
4377 XFreeGC(xDisplay, bufGC);
4378 XFreeGC(xDisplay, maskGC);
4382 InitAnimState (AnimNr anr, XWindowAttributes *info)
4387 /* Each buffer is square size, same depth as window */
4388 animBufs[anr+4] = xBoardWindow;
4389 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
4390 squareSize, squareSize, info->depth);
4391 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
4392 squareSize, squareSize, info->depth);
4394 /* Create a plain GC for blitting */
4395 mask = GCForeground | GCBackground | GCFunction |
4396 GCPlaneMask | GCGraphicsExposures;
4397 values.foreground = XBlackPixel(xDisplay, xScreen);
4398 values.background = XWhitePixel(xDisplay, xScreen);
4399 values.function = GXcopy;
4400 values.plane_mask = AllPlanes;
4401 values.graphics_exposures = False;
4402 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
4404 /* Piece will be copied from an existing context at
4405 the start of each new animation/drag. */
4406 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
4408 /* Outline will be a read-only copy of an existing */
4409 animGCs[anr+4] = None;
4415 XWindowAttributes info;
4417 if (xpmDone && gameInfo.variant == oldVariant) return;
4418 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
4419 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
4421 InitAnimState(Game, &info);
4422 InitAnimState(Player, &info);
4424 /* For XPM pieces, we need bitmaps to use as masks. */
4426 CreateAnimMasks(info.depth), xpmDone = 1;
4431 static Boolean frameWaiting;
4434 FrameAlarm (int sig)
4436 frameWaiting = False;
4437 /* In case System-V style signals. Needed?? */
4438 signal(SIGALRM, FrameAlarm);
4442 FrameDelay (int time)
4444 struct itimerval delay;
4446 XSync(xDisplay, False);
4449 frameWaiting = True;
4450 signal(SIGALRM, FrameAlarm);
4451 delay.it_interval.tv_sec =
4452 delay.it_value.tv_sec = time / 1000;
4453 delay.it_interval.tv_usec =
4454 delay.it_value.tv_usec = (time % 1000) * 1000;
4455 setitimer(ITIMER_REAL, &delay, NULL);
4456 while (frameWaiting) pause();
4457 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
4458 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
4459 setitimer(ITIMER_REAL, &delay, NULL);
4466 FrameDelay (int time)
4468 XSync(xDisplay, False);
4470 usleep(time * 1000);
4476 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
4480 /* Bitmap for piece being moved. */
4481 if (appData.monoMode) {
4482 *mask = *pieceToSolid(piece);
4483 } else if (useImages) {
4485 *mask = xpmMask[piece];
4487 *mask = ximMaskPm[piece];
4490 *mask = *pieceToSolid(piece);
4493 /* GC for piece being moved. Square color doesn't matter, but
4494 since it gets modified we make a copy of the original. */
4496 if (appData.monoMode)
4501 if (appData.monoMode)
4506 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
4508 /* Outline only used in mono mode and is not modified */
4510 *outline = bwPieceGC;
4512 *outline = wbPieceGC;
4516 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
4521 /* Draw solid rectangle which will be clipped to shape of piece */
4522 XFillRectangle(xDisplay, dest, clip,
4523 0, 0, squareSize, squareSize);
4524 if (appData.monoMode)
4525 /* Also draw outline in contrasting color for black
4526 on black / white on white cases */
4527 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
4528 0, 0, squareSize, squareSize, 0, 0, 1);
4530 /* Copy the piece */
4535 if(appData.upsideDown && flipView) kind ^= 2;
4536 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4538 0, 0, squareSize, squareSize,
4544 InsertPiece (AnimNr anr, ChessSquare piece)
4546 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
4550 DrawBlank (AnimNr anr, int x, int y, int startColor)
4552 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
4555 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
4556 int srcX, int srcY, int width, int height, int destX, int destY)
4558 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
4559 srcX, srcY, width, height, destX, destY);
4563 SetDragPiece (AnimNr anr, ChessSquare piece)
4566 /* The piece will be drawn using its own bitmap as a matte */
4567 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
4568 XSetClipMask(xDisplay, animGCs[anr+2], mask);
4571 /* [AS] Arrow highlighting support */
4574 DrawPolygon (Pnt arrow[], int nr)
4578 for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
4579 XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
4580 if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
4584 UpdateLogos (int displ)
4586 return; // no logos in XBoard yet