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"
217 #define usleep(t) _sleep2(((t)+500)/1000)
221 # define _(s) gettext (s)
222 # define N_(s) gettext_noop (s)
228 int main P((int argc, char **argv));
229 RETSIGTYPE CmailSigHandler P((int sig));
230 RETSIGTYPE IntSigHandler P((int sig));
231 RETSIGTYPE TermSizeSigHandler P((int sig));
232 static void CreateGCs P((int redo));
233 static void CreateAnyPieces P((void));
234 void CreateXIMPieces P((void));
235 void CreateXPMPieces P((void));
236 void CreateXPMBoard P((char *s, int n));
237 void CreatePieces P((void));
238 void CreatePieceMenus P((void));
239 Widget CreateMenuBar P((Menu *mb, int boardWidth));
240 Widget CreateButtonBar P ((MenuItem *mi));
242 char *InsertPxlSize P((char *pattern, int targetPxlSize));
243 XFontSet CreateFontSet P((char *base_fnt_lst));
245 char *FindFont P((char *pattern, int targetPxlSize));
247 void PieceMenuPopup P((Widget w, XEvent *event,
248 String *params, Cardinal *num_params));
249 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
250 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
251 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
252 u_int wreq, u_int hreq));
253 void CreateGrid P((void));
254 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
255 void DelayedDrag P((void));
256 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
257 void HandleUserMove P((Widget w, XEvent *event,
258 String *prms, Cardinal *nprms));
259 void AnimateUserMove P((Widget w, XEvent * event,
260 String * params, Cardinal * nParams));
261 void HandlePV P((Widget w, XEvent * event,
262 String * params, Cardinal * nParams));
263 void SelectPV P((Widget w, XEvent * event,
264 String * params, Cardinal * nParams));
265 void StopPV P((Widget w, XEvent * event,
266 String * params, Cardinal * nParams));
267 void WhiteClock P((Widget w, XEvent *event,
268 String *prms, Cardinal *nprms));
269 void BlackClock P((Widget w, XEvent *event,
270 String *prms, Cardinal *nprms));
271 void DrawPositionProc P((Widget w, XEvent *event,
272 String *prms, Cardinal *nprms));
273 void CommentClick P((Widget w, XEvent * event,
274 String * params, Cardinal * nParams));
275 void ICSInputBoxPopUp P((void));
276 void FileNamePopUp P((char *label, char *def, char *filter,
277 FileProc proc, char *openMode));
278 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
279 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
280 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
281 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
282 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
283 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
284 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
285 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
286 Boolean TempBackwardActive = False;
287 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
288 void DisplayMove P((int moveNumber));
289 void ICSInitScript P((void));
290 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
291 void update_ics_width P(());
292 int get_term_width P(());
293 int CopyMemoProc P(());
296 * XBoard depends on Xt R4 or higher
298 int xtVersion = XtSpecificationRelease;
303 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
304 highlightSquareColor, premoveHighlightColor, dialogColor, buttonColor;
305 Pixel lowTimeWarningColor;
306 GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
307 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
309 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
310 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
311 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
312 commentShell, whitePieceMenu, blackPieceMenu, dropMenu,
313 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
314 ICSInputShell, fileNameShell;
315 Widget historyShell, evalGraphShell, gameListShell;
316 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
317 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
319 XFontSet fontSet, clockFontSet;
322 XFontStruct *clockFontStruct;
324 Font coordFontID, countFontID;
325 XFontStruct *coordFontStruct, *countFontStruct;
326 XtAppContext appContext;
331 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
333 Position commentX = -1, commentY = -1;
334 Dimension commentW, commentH;
335 typedef unsigned int BoardSize;
337 Boolean chessProgram;
339 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
340 int smallLayout = 0, tinyLayout = 0,
341 marginW, marginH, // [HGM] for run-time resizing
342 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
343 ICSInputBoxUp = False,
344 filenameUp = False, pmFromX = -1, pmFromY = -1,
345 errorExitStatus = -1, defaultLineGap;
346 Dimension textHeight;
347 Pixel timerForegroundPixel, timerBackgroundPixel;
348 Pixel buttonForegroundPixel, buttonBackgroundPixel;
349 char *chessDir, *programName, *programVersion;
350 Boolean alwaysOnTop = False;
351 char *icsTextMenuString;
353 char *firstChessProgramNames;
354 char *secondChessProgramNames;
356 WindowPlacement wpMain;
357 WindowPlacement wpConsole;
358 WindowPlacement wpComment;
359 WindowPlacement wpMoveHistory;
360 WindowPlacement wpEvalGraph;
361 WindowPlacement wpEngineOutput;
362 WindowPlacement wpGameList;
363 WindowPlacement wpTags;
368 Pixmap pieceBitmap[2][(int)BlackPawn];
369 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
370 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
371 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
372 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
373 Pixmap xpmBoardBitmap[2];
374 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
375 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
376 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
377 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
378 XImage *ximLightSquare, *ximDarkSquare;
381 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
382 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
384 #define White(piece) ((int)(piece) < (int)BlackPawn)
386 /* Bitmaps for use as masks when drawing XPM pieces.
387 Need one for each black and white piece. */
388 static Pixmap xpmMask[BlackKing + 1];
390 /* This magic number is the number of intermediate frames used
391 in each half of the animation. For short moves it's reduced
392 by 1. The total number of frames will be factor * 2 + 1. */
395 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
397 #define PAUSE_BUTTON "P"
398 MenuItem buttonBar[] = {
399 {"<<", "<<", ToStartEvent},
400 {"<", "<", BackwardEvent},
401 {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseEvent},
402 {">", ">", ForwardEvent},
403 {">>", ">>", ToEndEvent},
407 #define PIECE_MENU_SIZE 18
408 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
409 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
410 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
411 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
412 N_("Empty square"), N_("Clear board") },
413 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
414 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
415 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
416 N_("Empty square"), N_("Clear board") }
418 /* must be in same order as pieceMenuStrings! */
419 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
420 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
421 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
422 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
423 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
424 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
425 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
426 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
427 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
430 #define DROP_MENU_SIZE 6
431 String dropMenuStrings[DROP_MENU_SIZE] = {
432 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
434 /* must be in same order as dropMenuStrings! */
435 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
436 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
437 WhiteRook, WhiteQueen
445 DropMenuEnables dmEnables[] = {
463 { XtNborderWidth, 0 },
464 { XtNdefaultDistance, 0 },
468 { XtNborderWidth, 0 },
469 { XtNresizable, (XtArgVal) True },
473 { XtNborderWidth, 0 },
479 { XtNjustify, (XtArgVal) XtJustifyRight },
480 { XtNlabel, (XtArgVal) "..." },
481 { XtNresizable, (XtArgVal) True },
482 { XtNresize, (XtArgVal) False }
485 Arg messageArgs[] = {
486 { XtNjustify, (XtArgVal) XtJustifyLeft },
487 { XtNlabel, (XtArgVal) "..." },
488 { XtNresizable, (XtArgVal) True },
489 { XtNresize, (XtArgVal) False }
493 { XtNborderWidth, 0 },
494 { XtNjustify, (XtArgVal) XtJustifyLeft }
497 XtResource clientResources[] = {
498 { "flashCount", "flashCount", XtRInt, sizeof(int),
499 XtOffset(AppDataPtr, flashCount), XtRImmediate,
500 (XtPointer) FLASH_COUNT },
503 XrmOptionDescRec shellOptions[] = {
504 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
505 { "-flash", "flashCount", XrmoptionNoArg, "3" },
506 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
509 XtActionsRec boardActions[] = {
510 { "DrawPosition", DrawPositionProc },
511 { "HandleUserMove", HandleUserMove },
512 { "AnimateUserMove", AnimateUserMove },
513 { "HandlePV", HandlePV },
514 { "SelectPV", SelectPV },
515 { "StopPV", StopPV },
516 { "PieceMenuPopup", PieceMenuPopup },
517 { "WhiteClock", WhiteClock },
518 { "BlackClock", BlackClock },
519 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
520 { "QuitProc", QuitWrapper },
521 { "ManProc", ManInner },
522 { "TempBackwardProc", TempBackwardProc },
523 { "TempForwardProc", TempForwardProc },
524 { "CommentClick", (XtActionProc) CommentClick },
525 { "GameListPopDown", (XtActionProc) GameListPopDown },
526 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
527 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
528 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
529 { "GenericPopDown", (XtActionProc) GenericPopDown },
530 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
531 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
532 { "SelectMove", (XtActionProc) SelectMove },
533 { "LoadSelectedProc", LoadSelectedProc },
534 { "SetFilterProc", SetFilterProc },
535 { "TypeInProc", TypeInProc },
536 { "EnterKeyProc", EnterKeyProc },
537 { "UpKeyProc", UpKeyProc },
538 { "DownKeyProc", DownKeyProc },
539 { "WheelProc", WheelProc },
540 { "TabProc", TabProc },
543 char globalTranslations[] =
544 ":<Key>F9: MenuItem(ResignProc) \n \
545 :Ctrl<Key>n: MenuItem(NewGame) \n \
546 :Meta<Key>V: MenuItem(NewVariant) \n \
547 :Ctrl<Key>o: MenuItem(LoadGame) \n \
548 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
549 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
550 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
551 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
552 :Ctrl<Key>s: MenuItem(SaveGame) \n \
553 :Ctrl<Key>c: MenuItem(CopyGame) \n \
554 :Ctrl<Key>v: MenuItem(PasteGame) \n \
555 :Ctrl<Key>O: MenuItem(LoadPosition) \n \
556 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
557 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
558 :Ctrl<Key>S: MenuItem(SavePosition) \n \
559 :Ctrl<Key>C: MenuItem(CopyPosition) \n \
560 :Ctrl<Key>V: MenuItem(PastePosition) \n \
561 :Ctrl<Key>q: MenuItem(Exit) \n \
562 :Ctrl<Key>w: MenuItem(MachineWhite) \n \
563 :Ctrl<Key>b: MenuItem(MachineBlack) \n \
564 :Ctrl<Key>t: MenuItem(TwoMachines) \n \
565 :Ctrl<Key>a: MenuItem(AnalysisMode) \n \
566 :Ctrl<Key>g: MenuItem(AnalyzeFile) \n \
567 :Ctrl<Key>e: MenuItem(EditGame) \n \
568 :Ctrl<Key>E: MenuItem(EditPosition) \n \
569 :Meta<Key>O: MenuItem(ShowEngineOutput) \n \
570 :Meta<Key>E: MenuItem(ShowEvaluationGraph) \n \
571 :Meta<Key>G: MenuItem(ShowGameList) \n \
572 :Meta<Key>H: MenuItem(ShowMoveHistory) \n \
573 :<Key>Pause: MenuItem(Pause) \n \
574 :<Key>F3: MenuItem(Accept) \n \
575 :<Key>F4: MenuItem(Decline) \n \
576 :<Key>F12: MenuItem(Rematch) \n \
577 :<Key>F5: MenuItem(CallFlag) \n \
578 :<Key>F6: MenuItem(Draw) \n \
579 :<Key>F7: MenuItem(Adjourn) \n \
580 :<Key>F8: MenuItem(Abort) \n \
581 :<Key>F10: MenuItem(StopObserving) \n \
582 :<Key>F11: MenuItem(StopExamining) \n \
583 :Ctrl<Key>d: MenuItem(DebugProc) \n \
584 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
585 :Meta<Key>End: MenuItem(ToEnd) \n \
586 :Meta<Key>Right: MenuItem(Forward) \n \
587 :Meta<Key>Home: MenuItem(ToStart) \n \
588 :Meta<Key>Left: MenuItem(Backward) \n \
589 :<Key>Left: MenuItem(Backward) \n \
590 :<Key>Right: MenuItem(Forward) \n \
591 :<Key>Home: MenuItem(Revert) \n \
592 :<Key>End: MenuItem(TruncateGame) \n \
593 :Ctrl<Key>m: MenuItem(MoveNow) \n \
594 :Ctrl<Key>x: MenuItem(RetractMove) \n \
595 :Meta<Key>J: MenuItem(Adjudications) \n \
596 :Meta<Key>U: MenuItem(CommonEngine) \n \
597 :Meta<Key>T: MenuItem(TimeControl) \n \
598 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
599 #ifndef OPTIONSDIALOG
601 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
602 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
603 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
604 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
605 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
608 :<Key>F1: MenuItem(Manual) \n \
609 :<Key>F2: MenuItem(FlipView) \n \
610 :<KeyDown>Return: TempBackwardProc() \n \
611 :<KeyUp>Return: TempForwardProc() \n";
613 char boardTranslations[] =
614 "<Btn1Down>: HandleUserMove(0) \n \
615 Shift<Btn1Up>: HandleUserMove(1) \n \
616 <Btn1Up>: HandleUserMove(0) \n \
617 <Btn1Motion>: AnimateUserMove() \n \
618 <Btn3Motion>: HandlePV() \n \
619 <Btn2Motion>: HandlePV() \n \
620 <Btn3Up>: PieceMenuPopup(menuB) \n \
621 <Btn2Up>: PieceMenuPopup(menuB) \n \
622 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
623 PieceMenuPopup(menuB) \n \
624 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
625 PieceMenuPopup(menuW) \n \
626 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
627 PieceMenuPopup(menuW) \n \
628 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
629 PieceMenuPopup(menuB) \n";
631 char whiteTranslations[] =
632 "Shift<BtnDown>: WhiteClock(1)\n \
633 <BtnDown>: WhiteClock(0)\n";
634 char blackTranslations[] =
635 "Shift<BtnDown>: BlackClock(1)\n \
636 <BtnDown>: BlackClock(0)\n";
638 char ICSInputTranslations[] =
639 "<Key>Up: UpKeyProc() \n "
640 "<Key>Down: DownKeyProc() \n "
641 "<Key>Return: EnterKeyProc() \n";
643 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
644 // as the widget is destroyed before the up-click can call extend-end
645 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
647 String xboardResources[] = {
648 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
653 /* Max possible square size */
654 #define MAXSQSIZE 256
656 static int xpm_avail[MAXSQSIZE];
658 #ifdef HAVE_DIR_STRUCT
660 /* Extract piece size from filename */
662 xpm_getsize (char *name, int len, char *ext)
670 if ((p=strchr(name, '.')) == NULL ||
671 StrCaseCmp(p+1, ext) != 0)
677 while (*p && isdigit(*p))
684 /* Setup xpm_avail */
686 xpm_getavail (char *dirname, char *ext)
692 for (i=0; i<MAXSQSIZE; ++i)
695 if (appData.debugMode)
696 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
698 dir = opendir(dirname);
701 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
702 programName, dirname);
706 while ((ent=readdir(dir)) != NULL) {
707 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
708 if (i > 0 && i < MAXSQSIZE)
718 xpm_print_avail (FILE *fp, char *ext)
722 fprintf(fp, _("Available `%s' sizes:\n"), ext);
723 for (i=1; i<MAXSQSIZE; ++i) {
729 /* Return XPM piecesize closest to size */
731 xpm_closest_to (char *dirname, int size, char *ext)
734 int sm_diff = MAXSQSIZE;
738 xpm_getavail(dirname, ext);
740 if (appData.debugMode)
741 xpm_print_avail(stderr, ext);
743 for (i=1; i<MAXSQSIZE; ++i) {
746 diff = (diff<0) ? -diff : diff;
747 if (diff < sm_diff) {
755 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
761 #else /* !HAVE_DIR_STRUCT */
762 /* If we are on a system without a DIR struct, we can't
763 read the directory, so we can't collect a list of
764 filenames, etc., so we can't do any size-fitting. */
766 xpm_closest_to (char *dirname, int size, char *ext)
769 Warning: No DIR structure found on this system --\n\
770 Unable to autosize for XPM/XIM pieces.\n\
771 Please report this error to %s.\n\
772 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
775 #endif /* HAVE_DIR_STRUCT */
778 /* Arrange to catch delete-window events */
779 Atom wm_delete_window;
781 CatchDeleteWindow (Widget w, String procname)
784 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
785 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
786 XtAugmentTranslations(w, XtParseTranslationTable(buf));
793 XtSetArg(args[0], XtNiconic, False);
794 XtSetValues(shellWidget, args, 1);
796 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
799 //---------------------------------------------------------------------------------------------------------
800 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
803 #define CW_USEDEFAULT (1<<31)
804 #define ICS_TEXT_MENU_SIZE 90
805 #define DEBUG_FILE "xboard.debug"
806 #define SetCurrentDirectory chdir
807 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
811 // these two must some day move to frontend.h, when they are implemented
812 Boolean GameListIsUp();
814 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
817 // front-end part of option handling
819 // [HGM] This platform-dependent table provides the location for storing the color info
820 extern char *crWhite, * crBlack;
824 &appData.whitePieceColor,
825 &appData.blackPieceColor,
826 &appData.lightSquareColor,
827 &appData.darkSquareColor,
828 &appData.highlightSquareColor,
829 &appData.premoveHighlightColor,
830 &appData.lowTimeWarningColor,
841 // [HGM] font: keep a font for each square size, even non-stndard ones
844 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
845 char *fontTable[NUM_FONTS][MAX_SIZE];
848 ParseFont (char *name, int number)
849 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
851 if(sscanf(name, "size%d:", &size)) {
852 // [HGM] font: font is meant for specific boardSize (likely from settings file);
853 // defer processing it until we know if it matches our board size
854 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
855 fontTable[number][size] = strdup(strchr(name, ':')+1);
856 fontValid[number][size] = True;
861 case 0: // CLOCK_FONT
862 appData.clockFont = strdup(name);
864 case 1: // MESSAGE_FONT
865 appData.font = strdup(name);
867 case 2: // COORD_FONT
868 appData.coordFont = strdup(name);
873 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
878 { // only 2 fonts currently
879 appData.clockFont = CLOCK_FONT_NAME;
880 appData.coordFont = COORD_FONT_NAME;
881 appData.font = DEFAULT_FONT_NAME;
886 { // no-op, until we identify the code for this already in XBoard and move it here
890 ParseColor (int n, char *name)
891 { // in XBoard, just copy the color-name string
892 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
896 ParseTextAttribs (ColorClass cc, char *s)
898 (&appData.colorShout)[cc] = strdup(s);
902 ParseBoardSize (void *addr, char *name)
904 appData.boardSize = strdup(name);
909 { // In XBoard the sound-playing program takes care of obtaining the actual sound
913 SetCommPortDefaults ()
914 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
917 // [HGM] args: these three cases taken out to stay in front-end
919 SaveFontArg (FILE *f, ArgDescriptor *ad)
922 int i, n = (int)(intptr_t)ad->argLoc;
924 case 0: // CLOCK_FONT
925 name = appData.clockFont;
927 case 1: // MESSAGE_FONT
930 case 2: // COORD_FONT
931 name = appData.coordFont;
936 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
937 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
938 fontTable[n][squareSize] = strdup(name);
939 fontValid[n][squareSize] = True;
942 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
943 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
948 { // nothing to do, as the sounds are at all times represented by their text-string names already
952 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
953 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
954 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
958 SaveColor (FILE *f, ArgDescriptor *ad)
959 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
960 if(colorVariable[(int)(intptr_t)ad->argLoc])
961 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
965 SaveBoardSize (FILE *f, char *name, void *addr)
966 { // wrapper to shield back-end from BoardSize & sizeInfo
967 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
971 ParseCommPortSettings (char *s)
972 { // no such option in XBoard (yet)
975 extern Widget engineOutputShell;
979 GetActualPlacement (Widget wg, WindowPlacement *wp)
984 XWindowAttributes winAt;
991 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
992 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
993 wp->x = rx - winAt.x;
994 wp->y = ry - winAt.y;
995 wp->height = winAt.height;
996 wp->width = winAt.width;
997 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
1002 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1003 // In XBoard this will have to wait until awareness of window parameters is implemented
1004 GetActualPlacement(shellWidget, &wpMain);
1005 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1006 if(MoveHistoryIsUp()) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
1007 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1008 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1009 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
1010 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
1014 PrintCommPortSettings (FILE *f, char *name)
1015 { // This option does not exist in XBoard
1019 EnsureOnScreen (int *x, int *y, int minX, int minY)
1026 { // [HGM] args: allows testing if main window is realized from back-end
1027 return xBoardWindow != 0;
1031 PopUpStartupDialog ()
1032 { // start menu not implemented in XBoard
1036 ConvertToLine (int argc, char **argv)
1038 static char line[128*1024], buf[1024];
1042 for(i=1; i<argc; i++)
1044 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1045 && argv[i][0] != '{' )
1046 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1048 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1049 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1052 line[strlen(line)-1] = NULLCHAR;
1056 //--------------------------------------------------------------------------------------------
1059 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1061 #define BoardSize int
1063 InitDrawingSizes (BoardSize boardSize, int flags)
1064 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1065 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1067 XtGeometryResult gres;
1069 static Dimension oldWidth, oldHeight;
1070 static VariantClass oldVariant;
1071 static int oldDual = -1, oldMono = -1;
1073 if(!formWidget) return;
1075 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1076 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1077 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1079 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1081 * Enable shell resizing.
1083 shellArgs[0].value = (XtArgVal) &w;
1084 shellArgs[1].value = (XtArgVal) &h;
1085 XtGetValues(shellWidget, shellArgs, 2);
1087 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1088 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1089 XtSetValues(shellWidget, &shellArgs[2], 4);
1091 XtSetArg(args[0], XtNdefaultDistance, &sep);
1092 XtGetValues(formWidget, args, 1);
1094 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1096 hOffset = boardWidth + 10;
1097 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1098 secondSegments[i] = gridSegments[i];
1099 secondSegments[i].x1 += hOffset;
1100 secondSegments[i].x2 += hOffset;
1103 XtSetArg(args[0], XtNwidth, boardWidth);
1104 XtSetArg(args[1], XtNheight, boardHeight);
1105 XtSetValues(boardWidget, args, 2);
1107 timerWidth = (boardWidth - sep) / 2;
1108 XtSetArg(args[0], XtNwidth, timerWidth);
1109 XtSetValues(whiteTimerWidget, args, 1);
1110 XtSetValues(blackTimerWidget, args, 1);
1112 XawFormDoLayout(formWidget, False);
1114 if (appData.titleInWindow) {
1116 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1117 XtSetArg(args[i], XtNheight, &h); i++;
1118 XtGetValues(titleWidget, args, i);
1120 w = boardWidth - 2*bor;
1122 XtSetArg(args[0], XtNwidth, &w);
1123 XtGetValues(menuBarWidget, args, 1);
1124 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1127 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1128 if (gres != XtGeometryYes && appData.debugMode) {
1130 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1131 programName, gres, w, h, wr, hr);
1135 XawFormDoLayout(formWidget, True);
1138 * Inhibit shell resizing.
1140 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1141 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1142 shellArgs[4].value = shellArgs[2].value = w;
1143 shellArgs[5].value = shellArgs[3].value = h;
1144 XtSetValues(shellWidget, &shellArgs[0], 6);
1146 XSync(xDisplay, False);
1150 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1153 if(gameInfo.variant != oldVariant) { // and only if variant changed
1156 for(i=0; i<4; i++) {
1158 for(p=0; p<=(int)WhiteKing; p++)
1159 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1160 if(gameInfo.variant == VariantShogi) {
1161 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1162 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1163 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1164 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1165 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1168 if(gameInfo.variant == VariantGothic) {
1169 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1172 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1173 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1174 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1177 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1178 for(p=0; p<=(int)WhiteKing; p++)
1179 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1180 if(gameInfo.variant == VariantShogi) {
1181 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1182 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1183 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1184 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1185 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1188 if(gameInfo.variant == VariantGothic) {
1189 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1192 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1193 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1194 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1199 for(i=0; i<2; i++) {
1201 for(p=0; p<=(int)WhiteKing; p++)
1202 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1203 if(gameInfo.variant == VariantShogi) {
1204 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1205 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1206 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1207 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1208 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1211 if(gameInfo.variant == VariantGothic) {
1212 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1215 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1216 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1217 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1221 oldMono = -10; // kludge to force recreation of animation masks
1222 oldVariant = gameInfo.variant;
1225 if(appData.monoMode != oldMono)
1228 oldMono = appData.monoMode;
1233 MakeOneColor (char *name, Pixel *color)
1235 XrmValue vFrom, vTo;
1236 if (!appData.monoMode) {
1237 vFrom.addr = (caddr_t) name;
1238 vFrom.size = strlen(name);
1239 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1240 if (vTo.addr == NULL) {
1241 appData.monoMode = True;
1244 *color = *(Pixel *) vTo.addr;
1252 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1253 int forceMono = False;
1255 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1256 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1257 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1258 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1259 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1260 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1261 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1262 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1269 { // [HGM] taken out of main
1271 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1272 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1273 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1275 if (appData.bitmapDirectory[0] != NULLCHAR) {
1279 CreateXPMBoard(appData.liteBackTextureFile, 1);
1280 CreateXPMBoard(appData.darkBackTextureFile, 0);
1284 /* Create regular pieces */
1285 if (!useImages) CreatePieces();
1290 InitDrawingParams ()
1292 MakeColors(); CreateGCs(True);
1297 main (int argc, char **argv)
1299 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1300 XSetWindowAttributes window_attributes;
1302 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1303 XrmValue vFrom, vTo;
1304 XtGeometryResult gres;
1307 int forceMono = False;
1309 srandom(time(0)); // [HGM] book: make random truly random
1311 setbuf(stdout, NULL);
1312 setbuf(stderr, NULL);
1315 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1316 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1320 programName = strrchr(argv[0], '/');
1321 if (programName == NULL)
1322 programName = argv[0];
1327 XtSetLanguageProc(NULL, NULL, NULL);
1328 bindtextdomain(PACKAGE, LOCALEDIR);
1329 textdomain(PACKAGE);
1333 XtAppInitialize(&appContext, "XBoard", shellOptions,
1334 XtNumber(shellOptions),
1335 &argc, argv, xboardResources, NULL, 0);
1336 appData.boardSize = "";
1337 InitAppData(ConvertToLine(argc, argv));
1339 if (p == NULL) p = "/tmp";
1340 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1341 gameCopyFilename = (char*) malloc(i);
1342 gamePasteFilename = (char*) malloc(i);
1343 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1344 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1346 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1347 clientResources, XtNumber(clientResources),
1350 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1351 static char buf[MSG_SIZ];
1352 EscapeExpand(buf, appData.firstInitString);
1353 appData.firstInitString = strdup(buf);
1354 EscapeExpand(buf, appData.secondInitString);
1355 appData.secondInitString = strdup(buf);
1356 EscapeExpand(buf, appData.firstComputerString);
1357 appData.firstComputerString = strdup(buf);
1358 EscapeExpand(buf, appData.secondComputerString);
1359 appData.secondComputerString = strdup(buf);
1362 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1365 if (chdir(chessDir) != 0) {
1366 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1372 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1373 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1374 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1375 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1378 setbuf(debugFP, NULL);
1382 if (appData.debugMode) {
1383 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1387 /* [HGM,HR] make sure board size is acceptable */
1388 if(appData.NrFiles > BOARD_FILES ||
1389 appData.NrRanks > BOARD_RANKS )
1390 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1393 /* This feature does not work; animation needs a rewrite */
1394 appData.highlightDragging = FALSE;
1398 xDisplay = XtDisplay(shellWidget);
1399 xScreen = DefaultScreen(xDisplay);
1400 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1402 gameInfo.variant = StringToVariant(appData.variant);
1403 InitPosition(FALSE);
1406 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1408 if (isdigit(appData.boardSize[0])) {
1409 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1410 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1411 &fontPxlSize, &smallLayout, &tinyLayout);
1413 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1414 programName, appData.boardSize);
1418 /* Find some defaults; use the nearest known size */
1419 SizeDefaults *szd, *nearest;
1420 int distance = 99999;
1421 nearest = szd = sizeDefaults;
1422 while (szd->name != NULL) {
1423 if (abs(szd->squareSize - squareSize) < distance) {
1425 distance = abs(szd->squareSize - squareSize);
1426 if (distance == 0) break;
1430 if (i < 2) lineGap = nearest->lineGap;
1431 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1432 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1433 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1434 if (i < 6) smallLayout = nearest->smallLayout;
1435 if (i < 7) tinyLayout = nearest->tinyLayout;
1438 SizeDefaults *szd = sizeDefaults;
1439 if (*appData.boardSize == NULLCHAR) {
1440 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1441 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1444 if (szd->name == NULL) szd--;
1445 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1447 while (szd->name != NULL &&
1448 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1449 if (szd->name == NULL) {
1450 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1451 programName, appData.boardSize);
1455 squareSize = szd->squareSize;
1456 lineGap = szd->lineGap;
1457 clockFontPxlSize = szd->clockFontPxlSize;
1458 coordFontPxlSize = szd->coordFontPxlSize;
1459 fontPxlSize = szd->fontPxlSize;
1460 smallLayout = szd->smallLayout;
1461 tinyLayout = szd->tinyLayout;
1462 // [HGM] font: use defaults from settings file if available and not overruled
1464 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1465 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1466 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1467 appData.font = fontTable[MESSAGE_FONT][squareSize];
1468 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1469 appData.coordFont = fontTable[COORD_FONT][squareSize];
1471 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1472 if (strlen(appData.pixmapDirectory) > 0) {
1473 p = ExpandPathName(appData.pixmapDirectory);
1475 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1476 appData.pixmapDirectory);
1479 if (appData.debugMode) {
1480 fprintf(stderr, _("\
1481 XBoard square size (hint): %d\n\
1482 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1484 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1485 if (appData.debugMode) {
1486 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1489 defaultLineGap = lineGap;
1490 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1492 /* [HR] height treated separately (hacked) */
1493 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1494 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1495 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1496 XtSetArg(boardArgs[2], XtNheight, boardHeight);
1499 * Determine what fonts to use.
1502 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1503 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1504 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1505 fontSet = CreateFontSet(appData.font);
1506 clockFontSet = CreateFontSet(appData.clockFont);
1508 /* For the coordFont, use the 0th font of the fontset. */
1509 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1510 XFontStruct **font_struct_list;
1511 XFontSetExtents *fontSize;
1512 char **font_name_list;
1513 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1514 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1515 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1516 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1517 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1520 appData.font = FindFont(appData.font, fontPxlSize);
1521 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1522 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1523 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1524 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1525 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1526 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1528 countFontID = coordFontID; // [HGM] holdings
1529 countFontStruct = coordFontStruct;
1531 xdb = XtDatabase(xDisplay);
1533 XrmPutLineResource(&xdb, "*international: True");
1534 vTo.size = sizeof(XFontSet);
1535 vTo.addr = (XtPointer) &fontSet;
1536 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1538 XrmPutStringResource(&xdb, "*font", appData.font);
1542 * Detect if there are not enough colors available and adapt.
1544 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1545 appData.monoMode = True;
1548 forceMono = MakeColors();
1551 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1553 appData.monoMode = True;
1556 if (appData.lowTimeWarning && !appData.monoMode) {
1557 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1558 vFrom.size = strlen(appData.lowTimeWarningColor);
1559 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1560 if (vTo.addr == NULL)
1561 appData.monoMode = True;
1563 lowTimeWarningColor = *(Pixel *) vTo.addr;
1566 if (appData.monoMode && appData.debugMode) {
1567 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1568 (unsigned long) XWhitePixel(xDisplay, xScreen),
1569 (unsigned long) XBlackPixel(xDisplay, xScreen));
1572 ParseIcsTextColors();
1574 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1580 layoutName = "tinyLayout";
1581 } else if (smallLayout) {
1582 layoutName = "smallLayout";
1584 layoutName = "normalLayout";
1586 /* Outer layoutWidget is there only to provide a name for use in
1587 resources that depend on the layout style */
1589 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
1590 layoutArgs, XtNumber(layoutArgs));
1592 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
1593 formArgs, XtNumber(formArgs));
1594 XtSetArg(args[0], XtNdefaultDistance, &sep);
1595 XtGetValues(formWidget, args, 1);
1598 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
1599 XtSetArg(args[0], XtNtop, XtChainTop);
1600 XtSetArg(args[1], XtNbottom, XtChainTop);
1601 XtSetArg(args[2], XtNright, XtChainLeft);
1602 XtSetValues(menuBarWidget, args, 3);
1604 widgetList[j++] = whiteTimerWidget =
1605 XtCreateWidget("whiteTime", labelWidgetClass,
1606 formWidget, timerArgs, XtNumber(timerArgs));
1608 XtSetArg(args[0], XtNfontSet, clockFontSet);
1610 XtSetArg(args[0], XtNfont, clockFontStruct);
1612 XtSetArg(args[1], XtNtop, XtChainTop);
1613 XtSetArg(args[2], XtNbottom, XtChainTop);
1614 XtSetValues(whiteTimerWidget, args, 3);
1616 widgetList[j++] = blackTimerWidget =
1617 XtCreateWidget("blackTime", labelWidgetClass,
1618 formWidget, timerArgs, XtNumber(timerArgs));
1620 XtSetArg(args[0], XtNfontSet, clockFontSet);
1622 XtSetArg(args[0], XtNfont, clockFontStruct);
1624 XtSetArg(args[1], XtNtop, XtChainTop);
1625 XtSetArg(args[2], XtNbottom, XtChainTop);
1626 XtSetValues(blackTimerWidget, args, 3);
1628 if (appData.titleInWindow) {
1629 widgetList[j++] = titleWidget =
1630 XtCreateWidget("title", labelWidgetClass, formWidget,
1631 titleArgs, XtNumber(titleArgs));
1632 XtSetArg(args[0], XtNtop, XtChainTop);
1633 XtSetArg(args[1], XtNbottom, XtChainTop);
1634 XtSetValues(titleWidget, args, 2);
1637 if (appData.showButtonBar) {
1638 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
1639 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
1640 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
1641 XtSetArg(args[2], XtNtop, XtChainTop);
1642 XtSetArg(args[3], XtNbottom, XtChainTop);
1643 XtSetValues(buttonBarWidget, args, 4);
1646 widgetList[j++] = messageWidget =
1647 XtCreateWidget("message", labelWidgetClass, formWidget,
1648 messageArgs, XtNumber(messageArgs));
1649 XtSetArg(args[0], XtNtop, XtChainTop);
1650 XtSetArg(args[1], XtNbottom, XtChainTop);
1651 XtSetValues(messageWidget, args, 2);
1653 widgetList[j++] = boardWidget =
1654 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
1655 XtNumber(boardArgs));
1657 XtManageChildren(widgetList, j);
1659 timerWidth = (boardWidth - sep) / 2;
1660 XtSetArg(args[0], XtNwidth, timerWidth);
1661 XtSetValues(whiteTimerWidget, args, 1);
1662 XtSetValues(blackTimerWidget, args, 1);
1664 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1665 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1666 XtGetValues(whiteTimerWidget, args, 2);
1668 if (appData.showButtonBar) {
1669 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1670 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1671 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
1675 * formWidget uses these constraints but they are stored
1679 XtSetArg(args[i], XtNfromHoriz, 0); i++;
1680 XtSetValues(menuBarWidget, args, i);
1681 if (appData.titleInWindow) {
1684 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1685 XtSetValues(whiteTimerWidget, args, i);
1687 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1688 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1689 XtSetValues(blackTimerWidget, args, i);
1691 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1692 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
1693 XtSetValues(titleWidget, args, i);
1695 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1696 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1697 XtSetValues(messageWidget, args, i);
1698 if (appData.showButtonBar) {
1700 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1701 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1702 XtSetValues(buttonBarWidget, args, i);
1706 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1707 XtSetValues(whiteTimerWidget, args, i);
1709 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1710 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1711 XtSetValues(blackTimerWidget, args, i);
1713 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
1714 XtSetValues(titleWidget, args, i);
1716 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1717 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1718 XtSetValues(messageWidget, args, i);
1719 if (appData.showButtonBar) {
1721 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1722 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1723 XtSetValues(buttonBarWidget, args, i);
1728 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1729 XtSetValues(whiteTimerWidget, args, i);
1731 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1732 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1733 XtSetValues(blackTimerWidget, args, i);
1735 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1736 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1737 XtSetValues(messageWidget, args, i);
1738 if (appData.showButtonBar) {
1740 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1741 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1742 XtSetValues(buttonBarWidget, args, i);
1746 XtSetArg(args[0], XtNfromVert, messageWidget);
1747 XtSetArg(args[1], XtNtop, XtChainTop);
1748 XtSetArg(args[2], XtNbottom, XtChainBottom);
1749 XtSetArg(args[3], XtNleft, XtChainLeft);
1750 XtSetArg(args[4], XtNright, XtChainRight);
1751 XtSetValues(boardWidget, args, 5);
1753 XtRealizeWidget(shellWidget);
1756 XtSetArg(args[0], XtNx, wpMain.x);
1757 XtSetArg(args[1], XtNy, wpMain.y);
1758 XtSetValues(shellWidget, args, 2);
1762 * Correct the width of the message and title widgets.
1763 * It is not known why some systems need the extra fudge term.
1764 * The value "2" is probably larger than needed.
1766 XawFormDoLayout(formWidget, False);
1768 #define WIDTH_FUDGE 2
1770 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1771 XtSetArg(args[i], XtNheight, &h); i++;
1772 XtGetValues(messageWidget, args, i);
1773 if (appData.showButtonBar) {
1775 XtSetArg(args[i], XtNwidth, &w); i++;
1776 XtGetValues(buttonBarWidget, args, i);
1777 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1779 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
1782 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1783 if (gres != XtGeometryYes && appData.debugMode) {
1784 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1785 programName, gres, w, h, wr, hr);
1788 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
1789 /* The size used for the child widget in layout lags one resize behind
1790 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
1792 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1793 if (gres != XtGeometryYes && appData.debugMode) {
1794 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1795 programName, gres, w, h, wr, hr);
1798 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
1799 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
1800 XtSetArg(args[1], XtNright, XtChainRight);
1801 XtSetValues(messageWidget, args, 2);
1803 if (appData.titleInWindow) {
1805 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1806 XtSetArg(args[i], XtNheight, &h); i++;
1807 XtGetValues(titleWidget, args, i);
1809 w = boardWidth - 2*bor;
1811 XtSetArg(args[0], XtNwidth, &w);
1812 XtGetValues(menuBarWidget, args, 1);
1813 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1816 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1817 if (gres != XtGeometryYes && appData.debugMode) {
1819 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1820 programName, gres, w, h, wr, hr);
1823 XawFormDoLayout(formWidget, True);
1825 xBoardWindow = XtWindow(boardWidget);
1827 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1828 // not need to go into InitDrawingSizes().
1832 * Create X checkmark bitmap and initialize option menu checks.
1834 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1835 checkmark_bits, checkmark_width, checkmark_height);
1841 ReadBitmap(&wIconPixmap, "icon_white.bm",
1842 icon_white_bits, icon_white_width, icon_white_height);
1843 ReadBitmap(&bIconPixmap, "icon_black.bm",
1844 icon_black_bits, icon_black_width, icon_black_height);
1845 iconPixmap = wIconPixmap;
1847 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1848 XtSetValues(shellWidget, args, i);
1851 * Create a cursor for the board widget.
1853 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1854 XChangeWindowAttributes(xDisplay, xBoardWindow,
1855 CWCursor, &window_attributes);
1858 * Inhibit shell resizing.
1860 shellArgs[0].value = (XtArgVal) &w;
1861 shellArgs[1].value = (XtArgVal) &h;
1862 XtGetValues(shellWidget, shellArgs, 2);
1863 shellArgs[4].value = shellArgs[2].value = w;
1864 shellArgs[5].value = shellArgs[3].value = h;
1865 XtSetValues(shellWidget, &shellArgs[2], 4);
1866 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1867 marginH = h - boardHeight;
1869 CatchDeleteWindow(shellWidget, "QuitProc");
1877 if (appData.animate || appData.animateDragging)
1880 XtAugmentTranslations(formWidget,
1881 XtParseTranslationTable(globalTranslations));
1882 XtAugmentTranslations(boardWidget,
1883 XtParseTranslationTable(boardTranslations));
1884 XtAugmentTranslations(whiteTimerWidget,
1885 XtParseTranslationTable(whiteTranslations));
1886 XtAugmentTranslations(blackTimerWidget,
1887 XtParseTranslationTable(blackTranslations));
1889 /* Why is the following needed on some versions of X instead
1890 * of a translation? */
1891 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
1892 (XtEventHandler) EventProc, NULL);
1894 XtAddEventHandler(formWidget, KeyPressMask, False,
1895 (XtEventHandler) MoveTypeInProc, NULL);
1896 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1897 (XtEventHandler) EventProc, NULL);
1899 /* [AS] Restore layout */
1900 if( wpMoveHistory.visible ) {
1904 if( wpEvalGraph.visible )
1909 if( wpEngineOutput.visible ) {
1910 EngineOutputPopUp();
1915 if (errorExitStatus == -1) {
1916 if (appData.icsActive) {
1917 /* We now wait until we see "login:" from the ICS before
1918 sending the logon script (problems with timestamp otherwise) */
1919 /*ICSInitScript();*/
1920 if (appData.icsInputBox) ICSInputBoxPopUp();
1924 signal(SIGWINCH, TermSizeSigHandler);
1926 signal(SIGINT, IntSigHandler);
1927 signal(SIGTERM, IntSigHandler);
1928 if (*appData.cmailGameName != NULLCHAR) {
1929 signal(SIGUSR1, CmailSigHandler);
1933 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1935 // XtSetKeyboardFocus(shellWidget, formWidget);
1936 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1938 XtAppMainLoop(appContext);
1939 if (appData.debugMode) fclose(debugFP); // [DM] debug
1944 TermSizeSigHandler (int sig)
1950 IntSigHandler (int sig)
1956 CmailSigHandler (int sig)
1961 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1963 /* Activate call-back function CmailSigHandlerCallBack() */
1964 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1966 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1970 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1973 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1975 /**** end signal code ****/
1978 #define Abs(n) ((n)<0 ? -(n) : (n))
1982 InsertPxlSize (char *pattern, int targetPxlSize)
1984 char *base_fnt_lst, strInt[12], *p, *q;
1985 int alternatives, i, len, strIntLen;
1988 * Replace the "*" (if present) in the pixel-size slot of each
1989 * alternative with the targetPxlSize.
1993 while ((p = strchr(p, ',')) != NULL) {
1997 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1998 strIntLen = strlen(strInt);
1999 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
2003 while (alternatives--) {
2004 char *comma = strchr(p, ',');
2005 for (i=0; i<14; i++) {
2006 char *hyphen = strchr(p, '-');
2008 if (comma && hyphen > comma) break;
2009 len = hyphen + 1 - p;
2010 if (i == 7 && *p == '*' && len == 2) {
2012 memcpy(q, strInt, strIntLen);
2022 len = comma + 1 - p;
2029 return base_fnt_lst;
2033 CreateFontSet (char *base_fnt_lst)
2036 char **missing_list;
2040 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
2041 &missing_list, &missing_count, &def_string);
2042 if (appData.debugMode) {
2044 XFontStruct **font_struct_list;
2045 char **font_name_list;
2046 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
2048 fprintf(debugFP, " got list %s, locale %s\n",
2049 XBaseFontNameListOfFontSet(fntSet),
2050 XLocaleOfFontSet(fntSet));
2051 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
2052 for (i = 0; i < count; i++) {
2053 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
2056 for (i = 0; i < missing_count; i++) {
2057 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
2060 if (fntSet == NULL) {
2061 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
2066 #else // not ENABLE_NLS
2068 * Find a font that matches "pattern" that is as close as
2069 * possible to the targetPxlSize. Prefer fonts that are k
2070 * pixels smaller to fonts that are k pixels larger. The
2071 * pattern must be in the X Consortium standard format,
2072 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2073 * The return value should be freed with XtFree when no
2077 FindFont (char *pattern, int targetPxlSize)
2079 char **fonts, *p, *best, *scalable, *scalableTail;
2080 int i, j, nfonts, minerr, err, pxlSize;
2082 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2084 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2085 programName, pattern);
2092 for (i=0; i<nfonts; i++) {
2095 if (*p != '-') continue;
2097 if (*p == NULLCHAR) break;
2098 if (*p++ == '-') j++;
2100 if (j < 7) continue;
2103 scalable = fonts[i];
2106 err = pxlSize - targetPxlSize;
2107 if (Abs(err) < Abs(minerr) ||
2108 (minerr > 0 && err < 0 && -err == minerr)) {
2114 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2115 /* If the error is too big and there is a scalable font,
2116 use the scalable font. */
2117 int headlen = scalableTail - scalable;
2118 p = (char *) XtMalloc(strlen(scalable) + 10);
2119 while (isdigit(*scalableTail)) scalableTail++;
2120 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2122 p = (char *) XtMalloc(strlen(best) + 2);
2123 safeStrCpy(p, best, strlen(best)+1 );
2125 if (appData.debugMode) {
2126 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2127 pattern, targetPxlSize, p);
2129 XFreeFontNames(fonts);
2136 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
2137 // must be called before all non-first callse to CreateGCs()
2138 XtReleaseGC(shellWidget, highlineGC);
2139 XtReleaseGC(shellWidget, lightSquareGC);
2140 XtReleaseGC(shellWidget, darkSquareGC);
2141 XtReleaseGC(shellWidget, lineGC);
2142 if (appData.monoMode) {
2143 if (DefaultDepth(xDisplay, xScreen) == 1) {
2144 XtReleaseGC(shellWidget, wbPieceGC);
2146 XtReleaseGC(shellWidget, bwPieceGC);
2149 XtReleaseGC(shellWidget, prelineGC);
2150 XtReleaseGC(shellWidget, wdPieceGC);
2151 XtReleaseGC(shellWidget, wlPieceGC);
2152 XtReleaseGC(shellWidget, bdPieceGC);
2153 XtReleaseGC(shellWidget, blPieceGC);
2158 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
2160 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2161 | GCBackground | GCFunction | GCPlaneMask;
2162 gc_values->foreground = foreground;
2163 gc_values->background = background;
2164 return XtGetGC(shellWidget, value_mask, gc_values);
2168 CreateGCs (int redo)
2170 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2171 | GCBackground | GCFunction | GCPlaneMask;
2172 XGCValues gc_values;
2174 Pixel white = XWhitePixel(xDisplay, xScreen);
2175 Pixel black = XBlackPixel(xDisplay, xScreen);
2177 gc_values.plane_mask = AllPlanes;
2178 gc_values.line_width = lineGap;
2179 gc_values.line_style = LineSolid;
2180 gc_values.function = GXcopy;
2183 DeleteGCs(); // called a second time; clean up old GCs first
2184 } else { // [HGM] grid and font GCs created on first call only
2185 coordGC = CreateOneGC(&gc_values, black, white);
2186 XSetFont(xDisplay, coordGC, coordFontID);
2188 // [HGM] make font for holdings counts (white on black)
2189 countGC = CreateOneGC(&gc_values, white, black);
2190 XSetFont(xDisplay, countGC, countFontID);
2192 lineGC = CreateOneGC(&gc_values, black, black);
2194 if (appData.monoMode) {
2196 highlineGC = CreateOneGC(&gc_values, white, white);
2197 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
2198 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
2200 if (DefaultDepth(xDisplay, xScreen) == 1) {
2201 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2202 gc_values.function = GXcopyInverted;
2203 copyInvertedGC = CreateOneGC(&gc_values, black, white);
2204 gc_values.function = GXcopy;
2205 if (XBlackPixel(xDisplay, xScreen) == 1) {
2206 bwPieceGC = darkSquareGC;
2207 wbPieceGC = copyInvertedGC;
2209 bwPieceGC = copyInvertedGC;
2210 wbPieceGC = lightSquareGC;
2215 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
2216 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
2217 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
2218 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
2219 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
2220 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
2221 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
2222 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
2227 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
2235 fp = fopen(filename, "rb");
2237 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
2244 for (y=0; y<h; ++y) {
2245 for (x=0; x<h; ++x) {
2250 XPutPixel(xim, x, y, blackPieceColor);
2252 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2255 XPutPixel(xim, x, y, darkSquareColor);
2257 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2260 XPutPixel(xim, x, y, whitePieceColor);
2262 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2265 XPutPixel(xim, x, y, lightSquareColor);
2267 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2275 /* create Pixmap of piece */
2276 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2278 XPutImage(xDisplay, *dest, lightSquareGC, xim,
2281 /* create Pixmap of clipmask
2282 Note: We assume the white/black pieces have the same
2283 outline, so we make only 6 masks. This is okay
2284 since the XPM clipmask routines do the same. */
2286 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2288 XPutImage(xDisplay, temp, lightSquareGC, xmask,
2291 /* now create the 1-bit version */
2292 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2295 values.foreground = 1;
2296 values.background = 0;
2298 /* Don't use XtGetGC, not read only */
2299 maskGC = XCreateGC(xDisplay, *mask,
2300 GCForeground | GCBackground, &values);
2301 XCopyPlane(xDisplay, temp, *mask, maskGC,
2302 0, 0, squareSize, squareSize, 0, 0, 1);
2303 XFreePixmap(xDisplay, temp);
2308 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
2316 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
2321 /* The XSynchronize calls were copied from CreatePieces.
2322 Not sure if needed, but can't hurt */
2323 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2326 /* temp needed by loadXIM() */
2327 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2328 0, 0, ss, ss, AllPlanes, XYPixmap);
2330 if (strlen(appData.pixmapDirectory) == 0) {
2334 if (appData.monoMode) {
2335 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
2339 fprintf(stderr, _("\nLoading XIMs...\n"));
2341 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2342 fprintf(stderr, "%d", piece+1);
2343 for (kind=0; kind<4; kind++) {
2344 fprintf(stderr, ".");
2345 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2346 ExpandPathName(appData.pixmapDirectory),
2347 piece <= (int) WhiteKing ? "" : "w",
2348 pieceBitmapNames[piece],
2350 ximPieceBitmap[kind][piece] =
2351 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2352 0, 0, ss, ss, AllPlanes, XYPixmap);
2353 if (appData.debugMode)
2354 fprintf(stderr, _("(File:%s:) "), buf);
2355 loadXIM(ximPieceBitmap[kind][piece],
2357 &(xpmPieceBitmap2[kind][piece]),
2358 &(ximMaskPm2[piece]));
2359 if(piece <= (int)WhiteKing)
2360 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2362 fprintf(stderr," ");
2364 /* Load light and dark squares */
2365 /* If the LSQ and DSQ pieces don't exist, we will
2366 draw them with solid squares. */
2367 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2368 if (access(buf, 0) != 0) {
2372 fprintf(stderr, _("light square "));
2374 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2375 0, 0, ss, ss, AllPlanes, XYPixmap);
2376 if (appData.debugMode)
2377 fprintf(stderr, _("(File:%s:) "), buf);
2379 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2380 fprintf(stderr, _("dark square "));
2381 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2382 ExpandPathName(appData.pixmapDirectory), ss);
2383 if (appData.debugMode)
2384 fprintf(stderr, _("(File:%s:) "), buf);
2386 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2387 0, 0, ss, ss, AllPlanes, XYPixmap);
2388 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2389 xpmJailSquare = xpmLightSquare;
2391 fprintf(stderr, _("Done.\n"));
2393 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2396 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2400 CreateXPMBoard (char *s, int kind)
2404 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2405 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2406 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2412 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2413 // thisroutine has to be called t free the old piece pixmaps
2415 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2416 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2418 XFreePixmap(xDisplay, xpmLightSquare);
2419 XFreePixmap(xDisplay, xpmDarkSquare);
2428 u_int ss = squareSize;
2430 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2431 XpmColorSymbol symbols[4];
2432 static int redo = False;
2434 if(redo) FreeXPMPieces(); else redo = 1;
2436 /* The XSynchronize calls were copied from CreatePieces.
2437 Not sure if needed, but can't hurt */
2438 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2440 /* Setup translations so piece colors match square colors */
2441 symbols[0].name = "light_piece";
2442 symbols[0].value = appData.whitePieceColor;
2443 symbols[1].name = "dark_piece";
2444 symbols[1].value = appData.blackPieceColor;
2445 symbols[2].name = "light_square";
2446 symbols[2].value = appData.lightSquareColor;
2447 symbols[3].name = "dark_square";
2448 symbols[3].value = appData.darkSquareColor;
2450 attr.valuemask = XpmColorSymbols;
2451 attr.colorsymbols = symbols;
2452 attr.numsymbols = 4;
2454 if (appData.monoMode) {
2455 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2459 if (strlen(appData.pixmapDirectory) == 0) {
2460 XpmPieces* pieces = builtInXpms;
2463 while (pieces->size != squareSize && pieces->size) pieces++;
2464 if (!pieces->size) {
2465 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2468 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2469 for (kind=0; kind<4; kind++) {
2471 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2472 pieces->xpm[piece][kind],
2473 &(xpmPieceBitmap2[kind][piece]),
2474 NULL, &attr)) != 0) {
2475 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2479 if(piece <= (int) WhiteKing)
2480 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2484 xpmJailSquare = xpmLightSquare;
2488 fprintf(stderr, _("\nLoading XPMs...\n"));
2491 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2492 fprintf(stderr, "%d ", piece+1);
2493 for (kind=0; kind<4; kind++) {
2494 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2495 ExpandPathName(appData.pixmapDirectory),
2496 piece > (int) WhiteKing ? "w" : "",
2497 pieceBitmapNames[piece],
2499 if (appData.debugMode) {
2500 fprintf(stderr, _("(File:%s:) "), buf);
2502 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2503 &(xpmPieceBitmap2[kind][piece]),
2504 NULL, &attr)) != 0) {
2505 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2506 // [HGM] missing: read of unorthodox piece failed; substitute King.
2507 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2508 ExpandPathName(appData.pixmapDirectory),
2510 if (appData.debugMode) {
2511 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2513 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2514 &(xpmPieceBitmap2[kind][piece]),
2518 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2523 if(piece <= (int) WhiteKing)
2524 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2527 /* Load light and dark squares */
2528 /* If the LSQ and DSQ pieces don't exist, we will
2529 draw them with solid squares. */
2530 fprintf(stderr, _("light square "));
2531 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2532 if (access(buf, 0) != 0) {
2536 if (appData.debugMode)
2537 fprintf(stderr, _("(File:%s:) "), buf);
2539 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2540 &xpmLightSquare, NULL, &attr)) != 0) {
2541 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2544 fprintf(stderr, _("dark square "));
2545 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2546 ExpandPathName(appData.pixmapDirectory), ss);
2547 if (appData.debugMode) {
2548 fprintf(stderr, _("(File:%s:) "), buf);
2550 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2551 &xpmDarkSquare, NULL, &attr)) != 0) {
2552 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2556 xpmJailSquare = xpmLightSquare;
2557 fprintf(stderr, _("Done.\n"));
2559 oldVariant = -1; // kludge to force re-makig of animation masks
2560 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2563 #endif /* HAVE_LIBXPM */
2566 /* No built-in bitmaps */
2571 u_int ss = squareSize;
2573 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2576 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2577 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2578 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2579 pieceBitmapNames[piece],
2580 ss, kind == SOLID ? 's' : 'o');
2581 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2582 if(piece <= (int)WhiteKing)
2583 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2587 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2591 /* With built-in bitmaps */
2595 BuiltInBits* bib = builtInBits;
2598 u_int ss = squareSize;
2600 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2603 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2605 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2606 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2607 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2608 pieceBitmapNames[piece],
2609 ss, kind == SOLID ? 's' : 'o');
2610 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2611 bib->bits[kind][piece], ss, ss);
2612 if(piece <= (int)WhiteKing)
2613 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2617 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2623 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2628 char msg[MSG_SIZ], fullname[MSG_SIZ];
2630 if (*appData.bitmapDirectory != NULLCHAR) {
2631 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2632 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2633 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2634 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2635 &w, &h, pm, &x_hot, &y_hot);
2636 fprintf(stderr, "load %s\n", name);
2637 if (errcode != BitmapSuccess) {
2639 case BitmapOpenFailed:
2640 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2642 case BitmapFileInvalid:
2643 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2645 case BitmapNoMemory:
2646 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2650 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2654 fprintf(stderr, _("%s: %s...using built-in\n"),
2656 } else if (w != wreq || h != hreq) {
2658 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2659 programName, fullname, w, h, wreq, hreq);
2665 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2675 if (lineGap == 0) return;
2677 /* [HR] Split this into 2 loops for non-square boards. */
2679 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2680 gridSegments[i].x1 = 0;
2681 gridSegments[i].x2 =
2682 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2683 gridSegments[i].y1 = gridSegments[i].y2
2684 = lineGap / 2 + (i * (squareSize + lineGap));
2687 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2688 gridSegments[j + i].y1 = 0;
2689 gridSegments[j + i].y2 =
2690 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2691 gridSegments[j + i].x1 = gridSegments[j + i].x2
2692 = lineGap / 2 + (j * (squareSize + lineGap));
2696 int nrOfMenuItems = 7;
2697 Widget menuWidget[150];
2698 MenuListItem menuItemList[150] = {
2699 { "LoadNextGameProc", LoadNextGameProc },
2700 { "LoadPrevGameProc", LoadPrevGameProc },
2701 { "ReloadGameProc", ReloadGameProc },
2702 { "ReloadPositionProc", ReloadPositionProc },
2703 #ifndef OPTIONSDIALOG
2704 { "AlwaysQueenProc", AlwaysQueenProc },
2705 { "AnimateDraggingProc", AnimateDraggingProc },
2706 { "AnimateMovingProc", AnimateMovingProc },
2707 { "AutoflagProc", AutoflagProc },
2708 { "AutoflipProc", AutoflipProc },
2709 { "BlindfoldProc", BlindfoldProc },
2710 { "FlashMovesProc", FlashMovesProc },
2712 { "HighlightDraggingProc", HighlightDraggingProc },
2714 { "HighlightLastMoveProc", HighlightLastMoveProc },
2715 // { "IcsAlarmProc", IcsAlarmProc },
2716 { "MoveSoundProc", MoveSoundProc },
2717 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
2718 { "PopupExitMessageProc", PopupExitMessageProc },
2719 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
2720 // { "PremoveProc", PremoveProc },
2721 { "ShowCoordsProc", ShowCoordsProc },
2722 { "ShowThinkingProc", ShowThinkingProc },
2723 { "HideThinkingProc", HideThinkingProc },
2724 { "TestLegalityProc", TestLegalityProc },
2726 { "AboutGameProc", AboutGameEvent },
2727 { "DebugProc", DebugProc },
2728 { "NothingProc", NothingProc },
2733 MarkMenuItem (char *menuRef, int state)
2735 int nr = MenuToNumber(menuRef);
2738 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2739 XtSetValues(menuWidget[nr], args, 1);
2744 EnableMenuItem (char *menuRef, int state)
2746 int nr = MenuToNumber(menuRef);
2747 if(nr >= 0) XtSetSensitive(menuWidget[nr], state);
2751 EnableButtonBar (int state)
2753 XtSetSensitive(buttonBarWidget, state);
2758 SetMenuEnables (Enables *enab)
2760 while (enab->name != NULL) {
2761 EnableMenuItem(enab->name, enab->value);
2767 Equal(char *p, char *s)
2768 { // compare strings skipping spaces in second
2770 if(*s == ' ') { s++; continue; }
2771 if(*s++ != *p++) return 0;
2777 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2778 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2780 if(*nprms == 0) return;
2781 for(i=0; menuItemList[i].name; i++) {
2782 if(Equal(prms[0], menuItemList[i].name)) {
2783 (menuItemList[i].proc) ();
2790 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
2792 MenuProc *proc = (MenuProc *) addr;
2798 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2800 RecentEngineEvent((int) (intptr_t) addr);
2803 // some stuff that must remain in front-end
2804 static Widget mainBar, currentMenu;
2805 static int wtot, nr = 0, widths[10];
2808 AppendMenuItem (char *text, char *name, MenuProc *action)
2815 XtSetArg(args[j], XtNleftMargin, 20); j++;
2816 XtSetArg(args[j], XtNrightMargin, 20); j++;
2818 if (strcmp(text, "----") == 0) {
2819 entry = XtCreateManagedWidget(text, smeLineObjectClass,
2820 currentMenu, args, j);
2822 XtSetArg(args[j], XtNlabel, XtNewString(_(text)));
2823 entry = XtCreateManagedWidget(name, smeBSBObjectClass,
2824 currentMenu, args, j+1);
2825 XtAddCallback(entry, XtNcallback,
2826 (XtCallbackProc) (strcmp(name, "recent") ? MenuBarSelect : MenuEngineSelect),
2828 menuWidget[nrOfMenuItems] = entry;
2833 CreateMenuButton (char *name, Menu *mb)
2834 { // create menu button on main bar, and shell for pull-down list
2840 XtSetArg(args[j], XtNmenuName, XtNewString(name)); j++;
2841 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
2842 XtSetArg(args[j], XtNborderWidth, 0); j++;
2843 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
2845 currentMenu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2848 XtSetArg(args[j], XtNwidth, &w); j++;
2849 XtGetValues(mb->subMenu, args, j);
2850 wtot += mb->textWidth = widths[nr++] = w;
2854 CreateMenuBar (Menu *mb, int boardWidth)
2858 char menuName[MSG_SIZ];
2862 // create bar itself
2864 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2865 XtSetArg(args[j], XtNvSpace, 0); j++;
2866 XtSetArg(args[j], XtNborderWidth, 0); j++;
2867 mainBar = XtCreateWidget("menuBar", boxWidgetClass,
2868 formWidget, args, j);
2870 CreateMainMenus(mb); // put menus in bar according to description in back-end
2872 // size buttons to make menu bar fit, clipping menu names where necessary
2873 while(wtot > boardWidth - 40) {
2875 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
2879 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
2881 XtSetArg(args[j], XtNwidth, widths[i]); j++;
2882 XtSetValues(ma[i].subMenu, args, j);
2889 CreateButtonBar (MenuItem *mi)
2892 Widget button, buttonBar;
2896 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2898 XtSetArg(args[j], XtNhSpace, 0); j++;
2900 XtSetArg(args[j], XtNborderWidth, 0); j++;
2901 XtSetArg(args[j], XtNvSpace, 0); j++;
2902 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
2903 formWidget, args, j);
2905 while (mi->string != NULL) {
2908 XtSetArg(args[j], XtNinternalWidth, 2); j++;
2909 XtSetArg(args[j], XtNborderWidth, 0); j++;
2911 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
2912 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
2913 buttonBar, args, j);
2914 XtAddCallback(button, XtNcallback,
2915 (XtCallbackProc) MenuBarSelect,
2916 (caddr_t) mi->proc);
2923 CreatePieceMenu (char *name, int color)
2928 ChessSquare selection;
2930 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2931 boardWidget, args, 0);
2933 for (i = 0; i < PIECE_MENU_SIZE; i++) {
2934 String item = pieceMenuStrings[color][i];
2936 if (strcmp(item, "----") == 0) {
2937 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2940 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2941 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2943 selection = pieceMenuTranslation[color][i];
2944 XtAddCallback(entry, XtNcallback,
2945 (XtCallbackProc) PieceMenuSelect,
2946 (caddr_t) selection);
2947 if (selection == WhitePawn || selection == BlackPawn) {
2948 XtSetArg(args[0], XtNpopupOnEntry, entry);
2949 XtSetValues(menu, args, 1);
2962 ChessSquare selection;
2964 whitePieceMenu = CreatePieceMenu("menuW", 0);
2965 blackPieceMenu = CreatePieceMenu("menuB", 1);
2967 if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
2968 XtRegisterGrabAction(PieceMenuPopup, True,
2969 (unsigned)(ButtonPressMask|ButtonReleaseMask),
2970 GrabModeAsync, GrabModeAsync);
2972 XtSetArg(args[0], XtNlabel, _("Drop"));
2973 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
2974 boardWidget, args, 1);
2975 for (i = 0; i < DROP_MENU_SIZE; i++) {
2976 String item = dropMenuStrings[i];
2978 if (strcmp(item, "----") == 0) {
2979 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2982 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2983 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2985 selection = dropMenuTranslation[i];
2986 XtAddCallback(entry, XtNcallback,
2987 (XtCallbackProc) DropMenuSelect,
2988 (caddr_t) selection);
3002 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3003 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3004 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3005 dmEnables[i].piece);
3006 XtSetSensitive(entry, p != NULL || !appData.testLegality
3007 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3008 && !appData.icsActive));
3010 while (p && *p++ == dmEnables[i].piece) count++;
3011 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3013 XtSetArg(args[j], XtNlabel, label); j++;
3014 XtSetValues(entry, args, j);
3019 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
3021 String whichMenu; int menuNr = -2;
3022 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3023 if (event->type == ButtonRelease)
3024 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3025 else if (event->type == ButtonPress)
3026 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3028 case 0: whichMenu = params[0]; break;
3029 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3031 case -1: ErrorPopDown();
3034 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3038 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3040 if (pmFromX < 0 || pmFromY < 0) return;
3041 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3045 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3047 if (pmFromX < 0 || pmFromY < 0) return;
3048 DropMenuEvent(piece, pmFromX, pmFromY);
3052 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3054 shiftKey = prms[0][0] & 1;
3059 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3061 shiftKey = prms[0][0] & 1;
3067 do_flash_delay (unsigned long msec)
3073 DrawBorder (int x, int y, int type)
3077 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
3079 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3080 squareSize+lineGap, squareSize+lineGap);
3084 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
3086 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
3087 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
3089 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
3090 if(textureW[kind] < W*squareSize)
3091 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
3093 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
3094 if(textureH[kind] < H*squareSize)
3095 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
3097 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
3102 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
3103 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
3105 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
3106 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
3107 squareSize, squareSize, x*fac, y*fac);
3109 if (useImages && useImageSqs) {
3113 pm = xpmLightSquare;
3118 case 2: /* neutral */
3120 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
3123 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3124 squareSize, squareSize, x*fac, y*fac);
3134 case 2: /* neutral */
3139 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
3144 I split out the routines to draw a piece so that I could
3145 make a generic flash routine.
3148 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3150 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3151 switch (square_color) {
3153 case 2: /* neutral */
3155 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3156 ? *pieceToOutline(piece)
3157 : *pieceToSolid(piece),
3158 dest, bwPieceGC, 0, 0,
3159 squareSize, squareSize, x, y);
3162 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3163 ? *pieceToSolid(piece)
3164 : *pieceToOutline(piece),
3165 dest, wbPieceGC, 0, 0,
3166 squareSize, squareSize, x, y);
3172 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3174 switch (square_color) {
3176 case 2: /* neutral */
3178 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3179 ? *pieceToOutline(piece)
3180 : *pieceToSolid(piece),
3181 dest, bwPieceGC, 0, 0,
3182 squareSize, squareSize, x, y, 1);
3185 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3186 ? *pieceToSolid(piece)
3187 : *pieceToOutline(piece),
3188 dest, wbPieceGC, 0, 0,
3189 squareSize, squareSize, x, y, 1);
3195 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3197 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
3198 switch (square_color) {
3200 XCopyPlane(xDisplay, *pieceToSolid(piece),
3201 dest, (int) piece < (int) BlackPawn
3202 ? wlPieceGC : blPieceGC, 0, 0,
3203 squareSize, squareSize, x, y, 1);
3206 XCopyPlane(xDisplay, *pieceToSolid(piece),
3207 dest, (int) piece < (int) BlackPawn
3208 ? wdPieceGC : bdPieceGC, 0, 0,
3209 squareSize, squareSize, x, y, 1);
3211 case 2: /* neutral */
3213 break; // should never contain pieces
3218 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3220 int kind, p = piece;
3222 switch (square_color) {
3224 case 2: /* neutral */
3226 if ((int)piece < (int) BlackPawn) {
3234 if ((int)piece < (int) BlackPawn) {
3242 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
3243 if(useTexture & square_color+1) {
3244 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
3245 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
3246 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
3247 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
3248 XSetClipMask(xDisplay, wlPieceGC, None);
3249 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
3251 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3252 dest, wlPieceGC, 0, 0,
3253 squareSize, squareSize, x, y);
3256 typedef void (*DrawFunc)();
3261 if (appData.monoMode) {
3262 if (DefaultDepth(xDisplay, xScreen) == 1) {
3263 return monoDrawPiece_1bit;
3265 return monoDrawPiece;
3269 return colorDrawPieceImage;
3271 return colorDrawPiece;
3276 DrawDot (int marker, int x, int y, int r)
3278 if(appData.monoMode) {
3279 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
3280 x, y, r, r, 0, 64*360);
3281 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
3282 x, y, r, r, 0, 64*360);
3284 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
3285 x, y, r, r, 0, 64*360);
3289 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
3290 { // basic front-end board-draw function: takes care of everything that can be in square:
3291 // piece, background, coordinate/count, marker dot
3292 int direction, font_ascent, font_descent;
3293 XCharStruct overall;
3296 if (piece == EmptySquare) {
3297 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
3299 drawfunc = ChooseDrawFunc();
3300 drawfunc(piece, square_color, x, y, xBoardWindow);
3303 if(align) { // square carries inscription (coord or piece count)
3305 GC hGC = align < 3 ? coordGC : countGC;
3306 // first calculate where it goes
3307 XTextExtents(countFontStruct, string, 1, &direction,
3308 &font_ascent, &font_descent, &overall);
3310 xx += squareSize - overall.width - 2;
3311 yy += squareSize - font_descent - 1;
3312 } else if (align == 2) {
3313 xx += 2, yy += font_ascent + 1;
3314 } else if (align == 3) {
3315 xx += squareSize - overall.width - 2;
3316 yy += font_ascent + 1;
3317 } else if (align == 4) {
3318 xx += 2, yy += font_ascent + 1;
3321 if (appData.monoMode) {
3322 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3324 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3328 if(marker) { // print fat marker dot, if requested
3329 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
3334 FlashDelay (int flash_delay)
3336 XSync(xDisplay, False);
3337 if(flash_delay) do_flash_delay(flash_delay);
3341 Fraction (int x, int start, int stop)
3343 double f = ((double) x - start)/(stop - start);
3344 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
3348 static WindowPlacement wpNew;
3351 CoDrag (Widget sh, WindowPlacement *wp)
3354 int j=0, touch=0, fudge = 2;
3355 GetActualPlacement(sh, wp);
3356 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
3357 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
3358 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
3359 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
3360 if(!touch ) return; // only windows that touch co-move
3361 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
3362 int heightInc = wpNew.height - wpMain.height;
3363 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3364 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3365 wp->y += fracTop * heightInc;
3366 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
3367 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
3368 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
3369 int widthInc = wpNew.width - wpMain.width;
3370 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3371 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3372 wp->y += fracLeft * widthInc;
3373 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
3374 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
3376 wp->x += wpNew.x - wpMain.x;
3377 wp->y += wpNew.y - wpMain.y;
3378 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
3379 if(touch == 3) wp->y += wpNew.height - wpMain.height;
3380 XtSetArg(args[j], XtNx, wp->x); j++;
3381 XtSetArg(args[j], XtNy, wp->y); j++;
3382 XtSetValues(sh, args, j);
3385 static XtIntervalId delayedDragID = 0;
3390 GetActualPlacement(shellWidget, &wpNew);
3391 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
3392 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
3393 return; // false alarm
3394 if(EngineOutputIsUp()) CoDrag(engineOutputShell, &wpEngineOutput);
3395 if(MoveHistoryIsUp()) CoDrag(shells[HistoryDlg], &wpMoveHistory);
3396 if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
3397 if(GameListIsUp()) CoDrag(gameListShell, &wpGameList);
3399 DrawPosition(True, NULL);
3400 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
3407 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
3409 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
3412 /* Why is this needed on some versions of X? */
3414 EventProc (Widget widget, caddr_t unused, XEvent *event)
3416 if (!XtIsRealized(widget))
3418 switch (event->type) {
3419 case ConfigureNotify: // main window is being dragged: drag attached windows with it
3420 if(appData.useStickyWindows)
3421 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
3424 if (event->xexpose.count > 0) return; /* no clipping is done */
3425 DrawPosition(True, NULL);
3426 if(twoBoards) { // [HGM] dual: draw other board in other orientation
3427 flipView = !flipView; partnerUp = !partnerUp;
3428 DrawPosition(True, NULL);
3429 flipView = !flipView; partnerUp = !partnerUp;
3433 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
3440 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
3442 DrawSeekAxis (int x, int y, int xTo, int yTo)
3444 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
3448 DrawSeekBackground (int left, int top, int right, int bottom)
3450 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
3454 DrawSeekText (char *buf, int x, int y)
3456 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
3460 DrawSeekDot (int x, int y, int colorNr)
3462 int square = colorNr & 0x80;
3465 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
3467 XFillRectangle(xDisplay, xBoardWindow, color,
3468 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3470 XFillArc(xDisplay, xBoardWindow, color,
3471 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
3475 DrawGrid (int second)
3477 XDrawSegments(xDisplay, xBoardWindow, lineGC,
3478 second ? secondSegments : // [HGM] dual
3479 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
3484 * event handler for redrawing the board
3487 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3489 DrawPosition(True, NULL);
3494 * event handler for parsing user moves
3496 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
3497 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
3498 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
3499 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
3500 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
3501 // and at the end FinishMove() to perform the move after optional promotion popups.
3502 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
3504 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3506 if (w != boardWidget || errorExitStatus != -1) return;
3507 if(nprms) shiftKey = !strcmp(prms[0], "1");
3509 if (shellUp[PromoDlg]) { // [HGM] is this still needed?
3510 if (event->type == ButtonPress) {
3519 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
3520 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
3521 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
3525 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
3527 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
3528 DragPieceMove(event->xmotion.x, event->xmotion.y);
3532 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3533 { // [HGM] pv: walk PV
3534 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3537 static int savedIndex; /* gross that this is global */
3540 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3543 XawTextPosition index, dummy;
3546 XawTextGetSelectionPos(w, &index, &dummy);
3547 XtSetArg(arg, XtNstring, &val);
3548 XtGetValues(w, &arg, 1);
3549 ReplaceComment(savedIndex, val);
3550 if(savedIndex != currentMove) ToNrEvent(savedIndex);
3551 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3555 EditCommentPopUp (int index, char *title, char *text)
3558 if (text == NULL) text = "";
3559 NewCommentPopup(title, text, index);
3563 CommentPopUp (char *title, char *text)
3565 savedIndex = currentMove; // [HGM] vari
3566 NewCommentPopup(title, text, currentMove);
3572 PopDown(CommentDlg);
3575 static char *openName;
3581 (void) (*fileProc)(openFP, 0, openName);
3585 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
3587 fileProc = proc; /* I can't see a way not */
3588 fileOpenMode = openMode; /* to use globals here */
3589 { // [HGM] use file-selector dialog stolen from Ghostview
3590 int index; // this is not supported yet
3591 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
3592 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
3593 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
3594 ScheduleDelayedEvent(&DelayedLoad, 50);
3599 /* Disable all user input other than deleting the window */
3600 static int frozen = 0;
3606 /* Grab by a widget that doesn't accept input */
3607 XtAddGrab(messageWidget, TRUE, FALSE);
3611 /* Undo a FreezeUI */
3615 if (!frozen) return;
3616 XtRemoveGrab(messageWidget);
3624 static int oldPausing = FALSE;
3625 static GameMode oldmode = (GameMode) -1;
3628 if (!boardWidget || !XtIsRealized(boardWidget)) return;
3630 if (pausing != oldPausing) {
3631 oldPausing = pausing;
3632 MarkMenuItem("Pause", pausing);
3634 if (appData.showButtonBar) {
3635 /* Always toggle, don't set. Previous code messes up when
3636 invoked while the button is pressed, as releasing it
3637 toggles the state again. */
3640 XtSetArg(args[0], XtNbackground, &oldbg);
3641 XtSetArg(args[1], XtNforeground, &oldfg);
3642 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
3644 XtSetArg(args[0], XtNbackground, oldfg);
3645 XtSetArg(args[1], XtNforeground, oldbg);
3647 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
3651 wname = ModeToWidgetName(oldmode);
3652 if (wname != NULL) {
3653 MarkMenuItem(wname, False);
3655 wname = ModeToWidgetName(gameMode);
3656 if (wname != NULL) {
3657 MarkMenuItem(wname, True);
3660 MarkMenuItem("Machine Match", matchMode && matchGame < appData.matchGames);
3662 /* Maybe all the enables should be handled here, not just this one */
3663 EnableMenuItem("Training", gameMode == Training || gameMode == PlayFromGameFile);
3668 * Button/menu procedures
3671 LoadGamePopUp (FILE *f, int gameNumber, char *title)
3673 cmailMsgLoaded = FALSE;
3674 if (gameNumber == 0) {
3675 int error = GameListBuild(f);
3677 DisplayError(_("Cannot build game list"), error);
3678 } else if (!ListEmpty(&gameList) &&
3679 ((ListGame *) gameList.tailPred)->number > 1) {
3680 GameListPopUp(f, title);
3686 return LoadGame(f, gameNumber, title, FALSE);
3689 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3690 char *selected_fen_position=NULL;
3693 SendPositionSelection (Widget w, Atom *selection, Atom *target,
3694 Atom *type_return, XtPointer *value_return,
3695 unsigned long *length_return, int *format_return)
3697 char *selection_tmp;
3699 // if (!selected_fen_position) return False; /* should never happen */
3700 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3701 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
3702 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
3705 if (f == NULL) return False;
3709 selection_tmp = XtMalloc(len + 1);
3710 count = fread(selection_tmp, 1, len, f);
3713 XtFree(selection_tmp);
3716 selection_tmp[len] = NULLCHAR;
3718 /* note: since no XtSelectionDoneProc was registered, Xt will
3719 * automatically call XtFree on the value returned. So have to
3720 * make a copy of it allocated with XtMalloc */
3721 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3722 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
3725 *value_return=selection_tmp;
3726 *length_return=strlen(selection_tmp);
3727 *type_return=*target;
3728 *format_return = 8; /* bits per byte */
3730 } else if (*target == XA_TARGETS(xDisplay)) {
3731 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3732 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3733 targets_tmp[1] = XA_STRING;
3734 *value_return = targets_tmp;
3735 *type_return = XA_ATOM;
3738 // This code leads to a read of value_return out of bounds on 64-bit systems.
3739 // Other code which I have seen always sets *format_return to 32 independent of
3740 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
3741 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
3742 *format_return = 8 * sizeof(Atom);
3743 if (*format_return > 32) {
3744 *length_return *= *format_return / 32;
3745 *format_return = 32;
3748 *format_return = 32;
3756 /* note: when called from menu all parameters are NULL, so no clue what the
3757 * Widget which was clicked on was, or what the click event was
3760 CopySomething (char *src)
3762 selected_fen_position = src;
3764 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3765 * have a notion of a position that is selected but not copied.
3766 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3768 XtOwnSelection(menuBarWidget, XA_PRIMARY,
3770 SendPositionSelection,
3771 NULL/* lose_ownership_proc */ ,
3772 NULL/* transfer_done_proc */);
3773 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3775 SendPositionSelection,
3776 NULL/* lose_ownership_proc */ ,
3777 NULL/* transfer_done_proc */);
3780 /* function called when the data to Paste is ready */
3782 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3783 Atom *type, XtPointer value, unsigned long *len, int *format)
3786 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3787 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3788 EditPositionPasteFEN(fenstr);
3792 /* called when Paste Position button is pressed,
3793 * all parameters will be NULL */
3795 PastePositionProc ()
3797 XtGetSelectionValue(menuBarWidget,
3798 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3799 /* (XtSelectionCallbackProc) */ PastePositionCB,
3800 NULL, /* client_data passed to PastePositionCB */
3802 /* better to use the time field from the event that triggered the
3803 * call to this function, but that isn't trivial to get
3810 /* note: when called from menu all parameters are NULL, so no clue what the
3811 * Widget which was clicked on was, or what the click event was
3813 /* function called when the data to Paste is ready */
3815 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3816 Atom *type, XtPointer value, unsigned long *len, int *format)
3819 if (value == NULL || *len == 0) {
3820 return; /* nothing had been selected to copy */
3822 f = fopen(gamePasteFilename, "w");
3824 DisplayError(_("Can't open temp file"), errno);
3827 fwrite(value, 1, *len, f);
3830 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3833 /* called when Paste Game button is pressed,
3834 * all parameters will be NULL */
3838 XtGetSelectionValue(menuBarWidget,
3839 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3840 /* (XtSelectionCallbackProc) */ PasteGameCB,
3841 NULL, /* client_data passed to PasteGameCB */
3843 /* better to use the time field from the event that triggered the
3844 * call to this function, but that isn't trivial to get
3853 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3860 { // bassic primitive for determining if modifier keys are pressed
3861 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3864 XQueryKeymap(xDisplay,keys);
3865 for(i=0; i<6; i++) {
3867 j = XKeysymToKeycode(xDisplay, codes[i]);
3868 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3874 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3878 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3879 if ( n == 1 && *buf >= 32 // printable
3880 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3881 ) BoxAutoPopUp (buf);
3885 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3886 { // [HGM] input: let up-arrow recall previous line from history
3891 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3892 { // [HGM] input: let down-arrow recall next line from history
3897 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3903 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3905 if (!TempBackwardActive) {
3906 TempBackwardActive = True;
3912 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3914 /* Check to see if triggered by a key release event for a repeating key.
3915 * If so the next queued event will be a key press of the same key at the same time */
3916 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3918 XPeekEvent(xDisplay, &next);
3919 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3920 next.xkey.keycode == event->xkey.keycode)
3924 TempBackwardActive = False;
3928 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3929 { // called as key binding
3932 if (nprms && *nprms > 0)
3936 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3941 DisplayMessage (char *message, char *extMessage)
3943 /* display a message in the message widget */
3952 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
3957 message = extMessage;
3961 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
3963 /* need to test if messageWidget already exists, since this function
3964 can also be called during the startup, if for example a Xresource
3965 is not set up correctly */
3968 XtSetArg(arg, XtNlabel, message);
3969 XtSetValues(messageWidget, &arg, 1);
3976 SetWindowTitle (char *text, char *title, char *icon)
3980 if (appData.titleInWindow) {
3982 XtSetArg(args[i], XtNlabel, text); i++;
3983 XtSetValues(titleWidget, args, i);
3986 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3987 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3988 XtSetValues(shellWidget, args, i);
3989 XSync(xDisplay, False);
3994 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
4000 DisplayIcsInteractionTitle (String message)
4002 if (oldICSInteractionTitle == NULL) {
4003 /* Magic to find the old window title, adapted from vim */
4004 char *wina = getenv("WINDOWID");
4006 Window win = (Window) atoi(wina);
4007 Window root, parent, *children;
4008 unsigned int nchildren;
4009 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4011 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4012 if (!XQueryTree(xDisplay, win, &root, &parent,
4013 &children, &nchildren)) break;
4014 if (children) XFree((void *)children);
4015 if (parent == root || parent == 0) break;
4018 XSetErrorHandler(oldHandler);
4020 if (oldICSInteractionTitle == NULL) {
4021 oldICSInteractionTitle = "xterm";
4024 printf("\033]0;%s\007", message);
4029 XtIntervalId delayedEventTimerXID = 0;
4030 DelayedEventCallback delayedEventCallback = 0;
4035 delayedEventTimerXID = 0;
4036 delayedEventCallback();
4040 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
4042 if(delayedEventTimerXID && delayedEventCallback == cb)
4043 // [HGM] alive: replace, rather than add or flush identical event
4044 XtRemoveTimeOut(delayedEventTimerXID);
4045 delayedEventCallback = cb;
4046 delayedEventTimerXID =
4047 XtAppAddTimeOut(appContext, millisec,
4048 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
4051 DelayedEventCallback
4054 if (delayedEventTimerXID) {
4055 return delayedEventCallback;
4062 CancelDelayedEvent ()
4064 if (delayedEventTimerXID) {
4065 XtRemoveTimeOut(delayedEventTimerXID);
4066 delayedEventTimerXID = 0;
4070 XtIntervalId loadGameTimerXID = 0;
4073 LoadGameTimerRunning ()
4075 return loadGameTimerXID != 0;
4079 StopLoadGameTimer ()
4081 if (loadGameTimerXID != 0) {
4082 XtRemoveTimeOut(loadGameTimerXID);
4083 loadGameTimerXID = 0;
4091 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
4093 loadGameTimerXID = 0;
4098 StartLoadGameTimer (long millisec)
4101 XtAppAddTimeOut(appContext, millisec,
4102 (XtTimerCallbackProc) LoadGameTimerCallback,
4106 XtIntervalId analysisClockXID = 0;
4109 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
4111 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
4112 || appData.icsEngineAnalyze) { // [DM]
4113 AnalysisPeriodicEvent(0);
4114 StartAnalysisClock();
4119 StartAnalysisClock ()
4122 XtAppAddTimeOut(appContext, 2000,
4123 (XtTimerCallbackProc) AnalysisClockCallback,
4127 XtIntervalId clockTimerXID = 0;
4130 ClockTimerRunning ()
4132 return clockTimerXID != 0;
4138 if (clockTimerXID != 0) {
4139 XtRemoveTimeOut(clockTimerXID);
4148 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
4155 StartClockTimer (long millisec)
4158 XtAppAddTimeOut(appContext, millisec,
4159 (XtTimerCallbackProc) ClockTimerCallback,
4164 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
4169 /* check for low time warning */
4170 Pixel foregroundOrWarningColor = timerForegroundPixel;
4173 appData.lowTimeWarning &&
4174 (timer / 1000) < appData.icsAlarmTime)
4175 foregroundOrWarningColor = lowTimeWarningColor;
4177 if (appData.clockMode) {
4178 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
4179 XtSetArg(args[0], XtNlabel, buf);
4181 snprintf(buf, MSG_SIZ, "%s ", color);
4182 XtSetArg(args[0], XtNlabel, buf);
4187 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
4188 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
4190 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
4191 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
4194 XtSetValues(w, args, 3);
4198 DisplayWhiteClock (long timeRemaining, int highlight)
4202 if(appData.noGUI) return;
4203 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
4204 if (highlight && iconPixmap == bIconPixmap) {
4205 iconPixmap = wIconPixmap;
4206 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
4207 XtSetValues(shellWidget, args, 1);
4212 DisplayBlackClock (long timeRemaining, int highlight)
4216 if(appData.noGUI) return;
4217 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
4218 if (highlight && iconPixmap == wIconPixmap) {
4219 iconPixmap = bIconPixmap;
4220 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
4221 XtSetValues(shellWidget, args, 1);
4226 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
4228 InputSource *is = (InputSource *) closure;
4233 if (is->lineByLine) {
4234 count = read(is->fd, is->unused,
4235 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
4237 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
4240 is->unused += count;
4242 while (p < is->unused) {
4243 q = memchr(p, '\n', is->unused - p);
4244 if (q == NULL) break;
4246 (is->func)(is, is->closure, p, q - p, 0);
4250 while (p < is->unused) {
4255 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
4260 (is->func)(is, is->closure, is->buf, count, error);
4265 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
4268 ChildProc *cp = (ChildProc *) pr;
4270 is = (InputSource *) calloc(1, sizeof(InputSource));
4271 is->lineByLine = lineByLine;
4275 is->fd = fileno(stdin);
4277 is->kind = cp->kind;
4278 is->fd = cp->fdFrom;
4281 is->unused = is->buf;
4284 is->xid = XtAppAddInput(appContext, is->fd,
4285 (XtPointer) (XtInputReadMask),
4286 (XtInputCallbackProc) DoInputCallback,
4288 is->closure = closure;
4289 return (InputSourceRef) is;
4293 RemoveInputSource (InputSourceRef isr)
4295 InputSource *is = (InputSource *) isr;
4297 if (is->xid == 0) return;
4298 XtRemoveInput(is->xid);
4302 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
4304 /* Masks for XPM pieces. Black and white pieces can have
4305 different shapes, but in the interest of retaining my
4306 sanity pieces must have the same outline on both light
4307 and dark squares, and all pieces must use the same
4308 background square colors/images. */
4310 static int xpmDone = 0;
4311 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
4312 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
4315 CreateAnimMasks (int pieceDepth)
4321 unsigned long plane;
4324 /* Need a bitmap just to get a GC with right depth */
4325 buf = XCreatePixmap(xDisplay, xBoardWindow,
4327 values.foreground = 1;
4328 values.background = 0;
4329 /* Don't use XtGetGC, not read only */
4330 maskGC = XCreateGC(xDisplay, buf,
4331 GCForeground | GCBackground, &values);
4332 XFreePixmap(xDisplay, buf);
4334 buf = XCreatePixmap(xDisplay, xBoardWindow,
4335 squareSize, squareSize, pieceDepth);
4336 values.foreground = XBlackPixel(xDisplay, xScreen);
4337 values.background = XWhitePixel(xDisplay, xScreen);
4338 bufGC = XCreateGC(xDisplay, buf,
4339 GCForeground | GCBackground, &values);
4341 for (piece = WhitePawn; piece <= BlackKing; piece++) {
4342 /* Begin with empty mask */
4343 if(!xpmDone) // [HGM] pieces: keep using existing
4344 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
4345 squareSize, squareSize, 1);
4346 XSetFunction(xDisplay, maskGC, GXclear);
4347 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
4348 0, 0, squareSize, squareSize);
4350 /* Take a copy of the piece */
4355 XSetFunction(xDisplay, bufGC, GXcopy);
4356 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
4358 0, 0, squareSize, squareSize, 0, 0);
4360 /* XOR the background (light) over the piece */
4361 XSetFunction(xDisplay, bufGC, GXxor);
4363 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
4364 0, 0, squareSize, squareSize, 0, 0);
4366 XSetForeground(xDisplay, bufGC, lightSquareColor);
4367 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
4370 /* We now have an inverted piece image with the background
4371 erased. Construct mask by just selecting all the non-zero
4372 pixels - no need to reconstruct the original image. */
4373 XSetFunction(xDisplay, maskGC, GXor);
4375 /* Might be quicker to download an XImage and create bitmap
4376 data from it rather than this N copies per piece, but it
4377 only takes a fraction of a second and there is a much
4378 longer delay for loading the pieces. */
4379 for (n = 0; n < pieceDepth; n ++) {
4380 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
4381 0, 0, squareSize, squareSize,
4387 XFreePixmap(xDisplay, buf);
4388 XFreeGC(xDisplay, bufGC);
4389 XFreeGC(xDisplay, maskGC);
4393 InitAnimState (AnimNr anr, XWindowAttributes *info)
4398 /* Each buffer is square size, same depth as window */
4399 animBufs[anr+4] = xBoardWindow;
4400 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
4401 squareSize, squareSize, info->depth);
4402 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
4403 squareSize, squareSize, info->depth);
4405 /* Create a plain GC for blitting */
4406 mask = GCForeground | GCBackground | GCFunction |
4407 GCPlaneMask | GCGraphicsExposures;
4408 values.foreground = XBlackPixel(xDisplay, xScreen);
4409 values.background = XWhitePixel(xDisplay, xScreen);
4410 values.function = GXcopy;
4411 values.plane_mask = AllPlanes;
4412 values.graphics_exposures = False;
4413 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
4415 /* Piece will be copied from an existing context at
4416 the start of each new animation/drag. */
4417 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
4419 /* Outline will be a read-only copy of an existing */
4420 animGCs[anr+4] = None;
4426 XWindowAttributes info;
4428 if (xpmDone && gameInfo.variant == oldVariant) return;
4429 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
4430 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
4432 InitAnimState(Game, &info);
4433 InitAnimState(Player, &info);
4435 /* For XPM pieces, we need bitmaps to use as masks. */
4437 CreateAnimMasks(info.depth), xpmDone = 1;
4442 static Boolean frameWaiting;
4445 FrameAlarm (int sig)
4447 frameWaiting = False;
4448 /* In case System-V style signals. Needed?? */
4449 signal(SIGALRM, FrameAlarm);
4453 FrameDelay (int time)
4455 struct itimerval delay;
4457 XSync(xDisplay, False);
4460 frameWaiting = True;
4461 signal(SIGALRM, FrameAlarm);
4462 delay.it_interval.tv_sec =
4463 delay.it_value.tv_sec = time / 1000;
4464 delay.it_interval.tv_usec =
4465 delay.it_value.tv_usec = (time % 1000) * 1000;
4466 setitimer(ITIMER_REAL, &delay, NULL);
4467 while (frameWaiting) pause();
4468 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
4469 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
4470 setitimer(ITIMER_REAL, &delay, NULL);
4477 FrameDelay (int time)
4479 XSync(xDisplay, False);
4481 usleep(time * 1000);
4487 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
4491 /* Bitmap for piece being moved. */
4492 if (appData.monoMode) {
4493 *mask = *pieceToSolid(piece);
4494 } else if (useImages) {
4496 *mask = xpmMask[piece];
4498 *mask = ximMaskPm[piece];
4501 *mask = *pieceToSolid(piece);
4504 /* GC for piece being moved. Square color doesn't matter, but
4505 since it gets modified we make a copy of the original. */
4507 if (appData.monoMode)
4512 if (appData.monoMode)
4517 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
4519 /* Outline only used in mono mode and is not modified */
4521 *outline = bwPieceGC;
4523 *outline = wbPieceGC;
4527 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
4532 /* Draw solid rectangle which will be clipped to shape of piece */
4533 XFillRectangle(xDisplay, dest, clip,
4534 0, 0, squareSize, squareSize);
4535 if (appData.monoMode)
4536 /* Also draw outline in contrasting color for black
4537 on black / white on white cases */
4538 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
4539 0, 0, squareSize, squareSize, 0, 0, 1);
4541 /* Copy the piece */
4546 if(appData.upsideDown && flipView) kind ^= 2;
4547 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4549 0, 0, squareSize, squareSize,
4555 InsertPiece (AnimNr anr, ChessSquare piece)
4557 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
4561 DrawBlank (AnimNr anr, int x, int y, int startColor)
4563 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
4566 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
4567 int srcX, int srcY, int width, int height, int destX, int destY)
4569 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
4570 srcX, srcY, width, height, destX, destY);
4574 SetDragPiece (AnimNr anr, ChessSquare piece)
4577 /* The piece will be drawn using its own bitmap as a matte */
4578 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
4579 XSetClipMask(xDisplay, animGCs[anr+2], mask);
4582 /* [AS] Arrow highlighting support */
4585 DrawPolygon (Pnt arrow[], int nr)
4589 for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
4590 XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
4591 if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
4595 UpdateLogos (int displ)
4597 return; // no logos in XBoard yet