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 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
527 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
528 { "GenericPopDown", (XtActionProc) GenericPopDown },
529 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
530 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
531 { "SelectMove", (XtActionProc) SelectMove },
532 { "LoadSelectedProc", LoadSelectedProc },
533 { "SetFilterProc", SetFilterProc },
534 { "TypeInProc", TypeInProc },
535 { "EnterKeyProc", EnterKeyProc },
536 { "UpKeyProc", UpKeyProc },
537 { "DownKeyProc", DownKeyProc },
538 { "WheelProc", WheelProc },
539 { "TabProc", TabProc },
542 char globalTranslations[] =
543 ":<Key>F9: MenuItem(ResignProc) \n \
544 :Ctrl<Key>n: MenuItem(NewGame) \n \
545 :Meta<Key>V: MenuItem(NewVariant) \n \
546 :Ctrl<Key>o: MenuItem(LoadGame) \n \
547 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
548 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
549 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
550 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
551 :Ctrl<Key>s: MenuItem(SaveGame) \n \
552 :Ctrl<Key>c: MenuItem(CopyGame) \n \
553 :Ctrl<Key>v: MenuItem(PasteGame) \n \
554 :Ctrl<Key>O: MenuItem(LoadPosition) \n \
555 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
556 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
557 :Ctrl<Key>S: MenuItem(SavePosition) \n \
558 :Ctrl<Key>C: MenuItem(CopyPosition) \n \
559 :Ctrl<Key>V: MenuItem(PastePosition) \n \
560 :Ctrl<Key>q: MenuItem(Exit) \n \
561 :Ctrl<Key>w: MenuItem(MachineWhite) \n \
562 :Ctrl<Key>b: MenuItem(MachineBlack) \n \
563 :Ctrl<Key>t: MenuItem(TwoMachines) \n \
564 :Ctrl<Key>a: MenuItem(AnalysisMode) \n \
565 :Ctrl<Key>g: MenuItem(AnalyzeFile) \n \
566 :Ctrl<Key>e: MenuItem(EditGame) \n \
567 :Ctrl<Key>E: MenuItem(EditPosition) \n \
568 :Meta<Key>O: MenuItem(ShowEngineOutput) \n \
569 :Meta<Key>E: MenuItem(ShowEvaluationGraph) \n \
570 :Meta<Key>G: MenuItem(ShowGameList) \n \
571 :Meta<Key>H: MenuItem(ShowMoveHistory) \n \
572 :<Key>Pause: MenuItem(Pause) \n \
573 :<Key>F3: MenuItem(Accept) \n \
574 :<Key>F4: MenuItem(Decline) \n \
575 :<Key>F12: MenuItem(Rematch) \n \
576 :<Key>F5: MenuItem(CallFlag) \n \
577 :<Key>F6: MenuItem(Draw) \n \
578 :<Key>F7: MenuItem(Adjourn) \n \
579 :<Key>F8: MenuItem(Abort) \n \
580 :<Key>F10: MenuItem(StopObserving) \n \
581 :<Key>F11: MenuItem(StopExamining) \n \
582 :Ctrl<Key>d: MenuItem(DebugProc) \n \
583 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
584 :Meta<Key>End: MenuItem(ToEnd) \n \
585 :Meta<Key>Right: MenuItem(Forward) \n \
586 :Meta<Key>Home: MenuItem(ToStart) \n \
587 :Meta<Key>Left: MenuItem(Backward) \n \
588 :<Key>Left: MenuItem(Backward) \n \
589 :<Key>Right: MenuItem(Forward) \n \
590 :<Key>Home: MenuItem(Revert) \n \
591 :<Key>End: MenuItem(TruncateGame) \n \
592 :Ctrl<Key>m: MenuItem(MoveNow) \n \
593 :Ctrl<Key>x: MenuItem(RetractMove) \n \
594 :Meta<Key>J: MenuItem(Adjudications) \n \
595 :Meta<Key>U: MenuItem(CommonEngine) \n \
596 :Meta<Key>T: MenuItem(TimeControl) \n \
597 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
598 #ifndef OPTIONSDIALOG
600 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
601 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
602 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
603 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
604 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
607 :<Key>F1: MenuItem(Manual) \n \
608 :<Key>F2: MenuItem(FlipView) \n \
609 :<KeyDown>Return: TempBackwardProc() \n \
610 :<KeyUp>Return: TempForwardProc() \n";
612 char boardTranslations[] =
613 "<Btn1Down>: HandleUserMove(0) \n \
614 Shift<Btn1Up>: HandleUserMove(1) \n \
615 <Btn1Up>: HandleUserMove(0) \n \
616 <Btn1Motion>: AnimateUserMove() \n \
617 <Btn3Motion>: HandlePV() \n \
618 <Btn2Motion>: HandlePV() \n \
619 <Btn3Up>: PieceMenuPopup(menuB) \n \
620 <Btn2Up>: PieceMenuPopup(menuB) \n \
621 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
622 PieceMenuPopup(menuB) \n \
623 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
624 PieceMenuPopup(menuW) \n \
625 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
626 PieceMenuPopup(menuW) \n \
627 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
628 PieceMenuPopup(menuB) \n";
630 char whiteTranslations[] =
631 "Shift<BtnDown>: WhiteClock(1)\n \
632 <BtnDown>: WhiteClock(0)\n";
633 char blackTranslations[] =
634 "Shift<BtnDown>: BlackClock(1)\n \
635 <BtnDown>: BlackClock(0)\n";
637 char ICSInputTranslations[] =
638 "<Key>Up: UpKeyProc() \n "
639 "<Key>Down: DownKeyProc() \n "
640 "<Key>Return: EnterKeyProc() \n";
642 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
643 // as the widget is destroyed before the up-click can call extend-end
644 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
646 String xboardResources[] = {
647 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
652 /* Max possible square size */
653 #define MAXSQSIZE 256
655 static int xpm_avail[MAXSQSIZE];
657 #ifdef HAVE_DIR_STRUCT
659 /* Extract piece size from filename */
661 xpm_getsize (char *name, int len, char *ext)
669 if ((p=strchr(name, '.')) == NULL ||
670 StrCaseCmp(p+1, ext) != 0)
676 while (*p && isdigit(*p))
683 /* Setup xpm_avail */
685 xpm_getavail (char *dirname, char *ext)
691 for (i=0; i<MAXSQSIZE; ++i)
694 if (appData.debugMode)
695 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
697 dir = opendir(dirname);
700 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
701 programName, dirname);
705 while ((ent=readdir(dir)) != NULL) {
706 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
707 if (i > 0 && i < MAXSQSIZE)
717 xpm_print_avail (FILE *fp, char *ext)
721 fprintf(fp, _("Available `%s' sizes:\n"), ext);
722 for (i=1; i<MAXSQSIZE; ++i) {
728 /* Return XPM piecesize closest to size */
730 xpm_closest_to (char *dirname, int size, char *ext)
733 int sm_diff = MAXSQSIZE;
737 xpm_getavail(dirname, ext);
739 if (appData.debugMode)
740 xpm_print_avail(stderr, ext);
742 for (i=1; i<MAXSQSIZE; ++i) {
745 diff = (diff<0) ? -diff : diff;
746 if (diff < sm_diff) {
754 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
760 #else /* !HAVE_DIR_STRUCT */
761 /* If we are on a system without a DIR struct, we can't
762 read the directory, so we can't collect a list of
763 filenames, etc., so we can't do any size-fitting. */
765 xpm_closest_to (char *dirname, int size, char *ext)
768 Warning: No DIR structure found on this system --\n\
769 Unable to autosize for XPM/XIM pieces.\n\
770 Please report this error to %s.\n\
771 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
774 #endif /* HAVE_DIR_STRUCT */
777 /* Arrange to catch delete-window events */
778 Atom wm_delete_window;
780 CatchDeleteWindow (Widget w, String procname)
783 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
784 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
785 XtAugmentTranslations(w, XtParseTranslationTable(buf));
792 XtSetArg(args[0], XtNiconic, False);
793 XtSetValues(shellWidget, args, 1);
795 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
798 //---------------------------------------------------------------------------------------------------------
799 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
802 #define CW_USEDEFAULT (1<<31)
803 #define ICS_TEXT_MENU_SIZE 90
804 #define DEBUG_FILE "xboard.debug"
805 #define SetCurrentDirectory chdir
806 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
810 // these two must some day move to frontend.h, when they are implemented
811 Boolean GameListIsUp();
813 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
816 // front-end part of option handling
818 // [HGM] This platform-dependent table provides the location for storing the color info
819 extern char *crWhite, * crBlack;
823 &appData.whitePieceColor,
824 &appData.blackPieceColor,
825 &appData.lightSquareColor,
826 &appData.darkSquareColor,
827 &appData.highlightSquareColor,
828 &appData.premoveHighlightColor,
829 &appData.lowTimeWarningColor,
840 // [HGM] font: keep a font for each square size, even non-stndard ones
843 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
844 char *fontTable[NUM_FONTS][MAX_SIZE];
847 ParseFont (char *name, int number)
848 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
850 if(sscanf(name, "size%d:", &size)) {
851 // [HGM] font: font is meant for specific boardSize (likely from settings file);
852 // defer processing it until we know if it matches our board size
853 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
854 fontTable[number][size] = strdup(strchr(name, ':')+1);
855 fontValid[number][size] = True;
860 case 0: // CLOCK_FONT
861 appData.clockFont = strdup(name);
863 case 1: // MESSAGE_FONT
864 appData.font = strdup(name);
866 case 2: // COORD_FONT
867 appData.coordFont = strdup(name);
872 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
877 { // only 2 fonts currently
878 appData.clockFont = CLOCK_FONT_NAME;
879 appData.coordFont = COORD_FONT_NAME;
880 appData.font = DEFAULT_FONT_NAME;
885 { // no-op, until we identify the code for this already in XBoard and move it here
889 ParseColor (int n, char *name)
890 { // in XBoard, just copy the color-name string
891 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
895 ParseTextAttribs (ColorClass cc, char *s)
897 (&appData.colorShout)[cc] = strdup(s);
901 ParseBoardSize (void *addr, char *name)
903 appData.boardSize = strdup(name);
908 { // In XBoard the sound-playing program takes care of obtaining the actual sound
912 SetCommPortDefaults ()
913 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
916 // [HGM] args: these three cases taken out to stay in front-end
918 SaveFontArg (FILE *f, ArgDescriptor *ad)
921 int i, n = (int)(intptr_t)ad->argLoc;
923 case 0: // CLOCK_FONT
924 name = appData.clockFont;
926 case 1: // MESSAGE_FONT
929 case 2: // COORD_FONT
930 name = appData.coordFont;
935 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
936 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
937 fontTable[n][squareSize] = strdup(name);
938 fontValid[n][squareSize] = True;
941 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
942 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
947 { // nothing to do, as the sounds are at all times represented by their text-string names already
951 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
952 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
953 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
957 SaveColor (FILE *f, ArgDescriptor *ad)
958 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
959 if(colorVariable[(int)(intptr_t)ad->argLoc])
960 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
964 SaveBoardSize (FILE *f, char *name, void *addr)
965 { // wrapper to shield back-end from BoardSize & sizeInfo
966 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
970 ParseCommPortSettings (char *s)
971 { // no such option in XBoard (yet)
974 extern Widget engineOutputShell;
978 GetActualPlacement (Widget wg, WindowPlacement *wp)
983 XWindowAttributes winAt;
990 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
991 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
992 wp->x = rx - winAt.x;
993 wp->y = ry - winAt.y;
994 wp->height = winAt.height;
995 wp->width = winAt.width;
996 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
1001 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1002 // In XBoard this will have to wait until awareness of window parameters is implemented
1003 GetActualPlacement(shellWidget, &wpMain);
1004 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1005 if(MoveHistoryIsUp()) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
1006 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1007 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1008 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
1009 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
1013 PrintCommPortSettings (FILE *f, char *name)
1014 { // This option does not exist in XBoard
1018 EnsureOnScreen (int *x, int *y, int minX, int minY)
1025 { // [HGM] args: allows testing if main window is realized from back-end
1026 return xBoardWindow != 0;
1030 PopUpStartupDialog ()
1031 { // start menu not implemented in XBoard
1035 ConvertToLine (int argc, char **argv)
1037 static char line[128*1024], buf[1024];
1041 for(i=1; i<argc; i++)
1043 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1044 && argv[i][0] != '{' )
1045 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1047 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1048 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1051 line[strlen(line)-1] = NULLCHAR;
1055 //--------------------------------------------------------------------------------------------
1058 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1060 #define BoardSize int
1062 InitDrawingSizes (BoardSize boardSize, int flags)
1063 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1064 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1066 XtGeometryResult gres;
1068 static Dimension oldWidth, oldHeight;
1069 static VariantClass oldVariant;
1070 static int oldDual = -1, oldMono = -1;
1072 if(!formWidget) return;
1074 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1075 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1076 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1078 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1080 * Enable shell resizing.
1082 shellArgs[0].value = (XtArgVal) &w;
1083 shellArgs[1].value = (XtArgVal) &h;
1084 XtGetValues(shellWidget, shellArgs, 2);
1086 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1087 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1088 XtSetValues(shellWidget, &shellArgs[2], 4);
1090 XtSetArg(args[0], XtNdefaultDistance, &sep);
1091 XtGetValues(formWidget, args, 1);
1093 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1095 hOffset = boardWidth + 10;
1096 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1097 secondSegments[i] = gridSegments[i];
1098 secondSegments[i].x1 += hOffset;
1099 secondSegments[i].x2 += hOffset;
1102 XtSetArg(args[0], XtNwidth, boardWidth);
1103 XtSetArg(args[1], XtNheight, boardHeight);
1104 XtSetValues(boardWidget, args, 2);
1106 timerWidth = (boardWidth - sep) / 2;
1107 XtSetArg(args[0], XtNwidth, timerWidth);
1108 XtSetValues(whiteTimerWidget, args, 1);
1109 XtSetValues(blackTimerWidget, args, 1);
1111 XawFormDoLayout(formWidget, False);
1113 if (appData.titleInWindow) {
1115 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1116 XtSetArg(args[i], XtNheight, &h); i++;
1117 XtGetValues(titleWidget, args, i);
1119 w = boardWidth - 2*bor;
1121 XtSetArg(args[0], XtNwidth, &w);
1122 XtGetValues(menuBarWidget, args, 1);
1123 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1126 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1127 if (gres != XtGeometryYes && appData.debugMode) {
1129 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1130 programName, gres, w, h, wr, hr);
1134 XawFormDoLayout(formWidget, True);
1137 * Inhibit shell resizing.
1139 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1140 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1141 shellArgs[4].value = shellArgs[2].value = w;
1142 shellArgs[5].value = shellArgs[3].value = h;
1143 XtSetValues(shellWidget, &shellArgs[0], 6);
1145 XSync(xDisplay, False);
1149 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1152 if(gameInfo.variant != oldVariant) { // and only if variant changed
1155 for(i=0; i<4; i++) {
1157 for(p=0; p<=(int)WhiteKing; p++)
1158 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1159 if(gameInfo.variant == VariantShogi) {
1160 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1161 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1162 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1163 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1164 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1167 if(gameInfo.variant == VariantGothic) {
1168 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1171 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1172 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1173 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1176 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1177 for(p=0; p<=(int)WhiteKing; p++)
1178 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1179 if(gameInfo.variant == VariantShogi) {
1180 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1181 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1182 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1183 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1184 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1187 if(gameInfo.variant == VariantGothic) {
1188 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1191 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1192 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1193 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1198 for(i=0; i<2; i++) {
1200 for(p=0; p<=(int)WhiteKing; p++)
1201 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1202 if(gameInfo.variant == VariantShogi) {
1203 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1204 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1205 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1206 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1207 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1210 if(gameInfo.variant == VariantGothic) {
1211 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1214 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1215 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1216 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1220 oldMono = -10; // kludge to force recreation of animation masks
1221 oldVariant = gameInfo.variant;
1224 if(appData.monoMode != oldMono)
1227 oldMono = appData.monoMode;
1232 MakeOneColor (char *name, Pixel *color)
1234 XrmValue vFrom, vTo;
1235 if (!appData.monoMode) {
1236 vFrom.addr = (caddr_t) name;
1237 vFrom.size = strlen(name);
1238 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1239 if (vTo.addr == NULL) {
1240 appData.monoMode = True;
1243 *color = *(Pixel *) vTo.addr;
1251 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1252 int forceMono = False;
1254 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1255 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1256 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1257 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1258 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1259 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1260 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1261 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1268 { // [HGM] taken out of main
1270 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1271 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1272 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1274 if (appData.bitmapDirectory[0] != NULLCHAR) {
1278 CreateXPMBoard(appData.liteBackTextureFile, 1);
1279 CreateXPMBoard(appData.darkBackTextureFile, 0);
1283 /* Create regular pieces */
1284 if (!useImages) CreatePieces();
1289 InitDrawingParams ()
1291 MakeColors(); CreateGCs(True);
1296 main (int argc, char **argv)
1298 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1299 XSetWindowAttributes window_attributes;
1301 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1302 XrmValue vFrom, vTo;
1303 XtGeometryResult gres;
1306 int forceMono = False;
1308 srandom(time(0)); // [HGM] book: make random truly random
1310 setbuf(stdout, NULL);
1311 setbuf(stderr, NULL);
1314 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1315 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1319 programName = strrchr(argv[0], '/');
1320 if (programName == NULL)
1321 programName = argv[0];
1326 XtSetLanguageProc(NULL, NULL, NULL);
1327 bindtextdomain(PACKAGE, LOCALEDIR);
1328 textdomain(PACKAGE);
1332 XtAppInitialize(&appContext, "XBoard", shellOptions,
1333 XtNumber(shellOptions),
1334 &argc, argv, xboardResources, NULL, 0);
1335 appData.boardSize = "";
1336 InitAppData(ConvertToLine(argc, argv));
1338 if (p == NULL) p = "/tmp";
1339 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1340 gameCopyFilename = (char*) malloc(i);
1341 gamePasteFilename = (char*) malloc(i);
1342 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1343 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1345 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1346 clientResources, XtNumber(clientResources),
1349 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1350 static char buf[MSG_SIZ];
1351 EscapeExpand(buf, appData.firstInitString);
1352 appData.firstInitString = strdup(buf);
1353 EscapeExpand(buf, appData.secondInitString);
1354 appData.secondInitString = strdup(buf);
1355 EscapeExpand(buf, appData.firstComputerString);
1356 appData.firstComputerString = strdup(buf);
1357 EscapeExpand(buf, appData.secondComputerString);
1358 appData.secondComputerString = strdup(buf);
1361 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1364 if (chdir(chessDir) != 0) {
1365 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1371 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1372 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1373 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1374 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1377 setbuf(debugFP, NULL);
1381 if (appData.debugMode) {
1382 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1386 /* [HGM,HR] make sure board size is acceptable */
1387 if(appData.NrFiles > BOARD_FILES ||
1388 appData.NrRanks > BOARD_RANKS )
1389 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1392 /* This feature does not work; animation needs a rewrite */
1393 appData.highlightDragging = FALSE;
1397 xDisplay = XtDisplay(shellWidget);
1398 xScreen = DefaultScreen(xDisplay);
1399 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1401 gameInfo.variant = StringToVariant(appData.variant);
1402 InitPosition(FALSE);
1405 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1407 if (isdigit(appData.boardSize[0])) {
1408 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1409 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1410 &fontPxlSize, &smallLayout, &tinyLayout);
1412 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1413 programName, appData.boardSize);
1417 /* Find some defaults; use the nearest known size */
1418 SizeDefaults *szd, *nearest;
1419 int distance = 99999;
1420 nearest = szd = sizeDefaults;
1421 while (szd->name != NULL) {
1422 if (abs(szd->squareSize - squareSize) < distance) {
1424 distance = abs(szd->squareSize - squareSize);
1425 if (distance == 0) break;
1429 if (i < 2) lineGap = nearest->lineGap;
1430 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1431 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1432 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1433 if (i < 6) smallLayout = nearest->smallLayout;
1434 if (i < 7) tinyLayout = nearest->tinyLayout;
1437 SizeDefaults *szd = sizeDefaults;
1438 if (*appData.boardSize == NULLCHAR) {
1439 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1440 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1443 if (szd->name == NULL) szd--;
1444 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1446 while (szd->name != NULL &&
1447 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1448 if (szd->name == NULL) {
1449 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1450 programName, appData.boardSize);
1454 squareSize = szd->squareSize;
1455 lineGap = szd->lineGap;
1456 clockFontPxlSize = szd->clockFontPxlSize;
1457 coordFontPxlSize = szd->coordFontPxlSize;
1458 fontPxlSize = szd->fontPxlSize;
1459 smallLayout = szd->smallLayout;
1460 tinyLayout = szd->tinyLayout;
1461 // [HGM] font: use defaults from settings file if available and not overruled
1463 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1464 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1465 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1466 appData.font = fontTable[MESSAGE_FONT][squareSize];
1467 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1468 appData.coordFont = fontTable[COORD_FONT][squareSize];
1470 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1471 if (strlen(appData.pixmapDirectory) > 0) {
1472 p = ExpandPathName(appData.pixmapDirectory);
1474 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1475 appData.pixmapDirectory);
1478 if (appData.debugMode) {
1479 fprintf(stderr, _("\
1480 XBoard square size (hint): %d\n\
1481 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1483 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1484 if (appData.debugMode) {
1485 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1488 defaultLineGap = lineGap;
1489 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1491 /* [HR] height treated separately (hacked) */
1492 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1493 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1494 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1495 XtSetArg(boardArgs[2], XtNheight, boardHeight);
1498 * Determine what fonts to use.
1501 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1502 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1503 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1504 fontSet = CreateFontSet(appData.font);
1505 clockFontSet = CreateFontSet(appData.clockFont);
1507 /* For the coordFont, use the 0th font of the fontset. */
1508 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1509 XFontStruct **font_struct_list;
1510 XFontSetExtents *fontSize;
1511 char **font_name_list;
1512 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1513 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1514 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1515 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1516 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1519 appData.font = FindFont(appData.font, fontPxlSize);
1520 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1521 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1522 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1523 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1524 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1525 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1527 countFontID = coordFontID; // [HGM] holdings
1528 countFontStruct = coordFontStruct;
1530 xdb = XtDatabase(xDisplay);
1532 XrmPutLineResource(&xdb, "*international: True");
1533 vTo.size = sizeof(XFontSet);
1534 vTo.addr = (XtPointer) &fontSet;
1535 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1537 XrmPutStringResource(&xdb, "*font", appData.font);
1541 * Detect if there are not enough colors available and adapt.
1543 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1544 appData.monoMode = True;
1547 forceMono = MakeColors();
1550 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1552 appData.monoMode = True;
1555 if (appData.lowTimeWarning && !appData.monoMode) {
1556 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1557 vFrom.size = strlen(appData.lowTimeWarningColor);
1558 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1559 if (vTo.addr == NULL)
1560 appData.monoMode = True;
1562 lowTimeWarningColor = *(Pixel *) vTo.addr;
1565 if (appData.monoMode && appData.debugMode) {
1566 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1567 (unsigned long) XWhitePixel(xDisplay, xScreen),
1568 (unsigned long) XBlackPixel(xDisplay, xScreen));
1571 ParseIcsTextColors();
1573 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1579 layoutName = "tinyLayout";
1580 } else if (smallLayout) {
1581 layoutName = "smallLayout";
1583 layoutName = "normalLayout";
1585 /* Outer layoutWidget is there only to provide a name for use in
1586 resources that depend on the layout style */
1588 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
1589 layoutArgs, XtNumber(layoutArgs));
1591 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
1592 formArgs, XtNumber(formArgs));
1593 XtSetArg(args[0], XtNdefaultDistance, &sep);
1594 XtGetValues(formWidget, args, 1);
1597 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
1598 XtSetArg(args[0], XtNtop, XtChainTop);
1599 XtSetArg(args[1], XtNbottom, XtChainTop);
1600 XtSetArg(args[2], XtNright, XtChainLeft);
1601 XtSetValues(menuBarWidget, args, 3);
1603 widgetList[j++] = whiteTimerWidget =
1604 XtCreateWidget("whiteTime", labelWidgetClass,
1605 formWidget, timerArgs, XtNumber(timerArgs));
1607 XtSetArg(args[0], XtNfontSet, clockFontSet);
1609 XtSetArg(args[0], XtNfont, clockFontStruct);
1611 XtSetArg(args[1], XtNtop, XtChainTop);
1612 XtSetArg(args[2], XtNbottom, XtChainTop);
1613 XtSetValues(whiteTimerWidget, args, 3);
1615 widgetList[j++] = blackTimerWidget =
1616 XtCreateWidget("blackTime", labelWidgetClass,
1617 formWidget, timerArgs, XtNumber(timerArgs));
1619 XtSetArg(args[0], XtNfontSet, clockFontSet);
1621 XtSetArg(args[0], XtNfont, clockFontStruct);
1623 XtSetArg(args[1], XtNtop, XtChainTop);
1624 XtSetArg(args[2], XtNbottom, XtChainTop);
1625 XtSetValues(blackTimerWidget, args, 3);
1627 if (appData.titleInWindow) {
1628 widgetList[j++] = titleWidget =
1629 XtCreateWidget("title", labelWidgetClass, formWidget,
1630 titleArgs, XtNumber(titleArgs));
1631 XtSetArg(args[0], XtNtop, XtChainTop);
1632 XtSetArg(args[1], XtNbottom, XtChainTop);
1633 XtSetValues(titleWidget, args, 2);
1636 if (appData.showButtonBar) {
1637 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
1638 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
1639 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
1640 XtSetArg(args[2], XtNtop, XtChainTop);
1641 XtSetArg(args[3], XtNbottom, XtChainTop);
1642 XtSetValues(buttonBarWidget, args, 4);
1645 widgetList[j++] = messageWidget =
1646 XtCreateWidget("message", labelWidgetClass, formWidget,
1647 messageArgs, XtNumber(messageArgs));
1648 XtSetArg(args[0], XtNtop, XtChainTop);
1649 XtSetArg(args[1], XtNbottom, XtChainTop);
1650 XtSetValues(messageWidget, args, 2);
1652 widgetList[j++] = boardWidget =
1653 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
1654 XtNumber(boardArgs));
1656 XtManageChildren(widgetList, j);
1658 timerWidth = (boardWidth - sep) / 2;
1659 XtSetArg(args[0], XtNwidth, timerWidth);
1660 XtSetValues(whiteTimerWidget, args, 1);
1661 XtSetValues(blackTimerWidget, args, 1);
1663 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1664 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1665 XtGetValues(whiteTimerWidget, args, 2);
1667 if (appData.showButtonBar) {
1668 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1669 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1670 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
1674 * formWidget uses these constraints but they are stored
1678 XtSetArg(args[i], XtNfromHoriz, 0); i++;
1679 XtSetValues(menuBarWidget, args, i);
1680 if (appData.titleInWindow) {
1683 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1684 XtSetValues(whiteTimerWidget, args, i);
1686 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1687 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1688 XtSetValues(blackTimerWidget, args, i);
1690 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1691 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
1692 XtSetValues(titleWidget, args, i);
1694 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1695 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1696 XtSetValues(messageWidget, args, i);
1697 if (appData.showButtonBar) {
1699 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1700 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1701 XtSetValues(buttonBarWidget, args, i);
1705 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1706 XtSetValues(whiteTimerWidget, args, i);
1708 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1709 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1710 XtSetValues(blackTimerWidget, args, i);
1712 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
1713 XtSetValues(titleWidget, args, i);
1715 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1716 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1717 XtSetValues(messageWidget, args, i);
1718 if (appData.showButtonBar) {
1720 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1721 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1722 XtSetValues(buttonBarWidget, args, i);
1727 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1728 XtSetValues(whiteTimerWidget, args, i);
1730 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1731 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1732 XtSetValues(blackTimerWidget, args, i);
1734 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1735 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1736 XtSetValues(messageWidget, args, i);
1737 if (appData.showButtonBar) {
1739 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1740 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1741 XtSetValues(buttonBarWidget, args, i);
1745 XtSetArg(args[0], XtNfromVert, messageWidget);
1746 XtSetArg(args[1], XtNtop, XtChainTop);
1747 XtSetArg(args[2], XtNbottom, XtChainBottom);
1748 XtSetArg(args[3], XtNleft, XtChainLeft);
1749 XtSetArg(args[4], XtNright, XtChainRight);
1750 XtSetValues(boardWidget, args, 5);
1752 XtRealizeWidget(shellWidget);
1755 XtSetArg(args[0], XtNx, wpMain.x);
1756 XtSetArg(args[1], XtNy, wpMain.y);
1757 XtSetValues(shellWidget, args, 2);
1761 * Correct the width of the message and title widgets.
1762 * It is not known why some systems need the extra fudge term.
1763 * The value "2" is probably larger than needed.
1765 XawFormDoLayout(formWidget, False);
1767 #define WIDTH_FUDGE 2
1769 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1770 XtSetArg(args[i], XtNheight, &h); i++;
1771 XtGetValues(messageWidget, args, i);
1772 if (appData.showButtonBar) {
1774 XtSetArg(args[i], XtNwidth, &w); i++;
1775 XtGetValues(buttonBarWidget, args, i);
1776 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1778 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
1781 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1782 if (gres != XtGeometryYes && appData.debugMode) {
1783 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1784 programName, gres, w, h, wr, hr);
1787 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
1788 /* The size used for the child widget in layout lags one resize behind
1789 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
1791 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1792 if (gres != XtGeometryYes && appData.debugMode) {
1793 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1794 programName, gres, w, h, wr, hr);
1797 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
1798 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
1799 XtSetArg(args[1], XtNright, XtChainRight);
1800 XtSetValues(messageWidget, args, 2);
1802 if (appData.titleInWindow) {
1804 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1805 XtSetArg(args[i], XtNheight, &h); i++;
1806 XtGetValues(titleWidget, args, i);
1808 w = boardWidth - 2*bor;
1810 XtSetArg(args[0], XtNwidth, &w);
1811 XtGetValues(menuBarWidget, args, 1);
1812 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1815 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1816 if (gres != XtGeometryYes && appData.debugMode) {
1818 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1819 programName, gres, w, h, wr, hr);
1822 XawFormDoLayout(formWidget, True);
1824 xBoardWindow = XtWindow(boardWidget);
1826 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1827 // not need to go into InitDrawingSizes().
1831 * Create X checkmark bitmap and initialize option menu checks.
1833 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1834 checkmark_bits, checkmark_width, checkmark_height);
1840 ReadBitmap(&wIconPixmap, "icon_white.bm",
1841 icon_white_bits, icon_white_width, icon_white_height);
1842 ReadBitmap(&bIconPixmap, "icon_black.bm",
1843 icon_black_bits, icon_black_width, icon_black_height);
1844 iconPixmap = wIconPixmap;
1846 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1847 XtSetValues(shellWidget, args, i);
1850 * Create a cursor for the board widget.
1852 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1853 XChangeWindowAttributes(xDisplay, xBoardWindow,
1854 CWCursor, &window_attributes);
1857 * Inhibit shell resizing.
1859 shellArgs[0].value = (XtArgVal) &w;
1860 shellArgs[1].value = (XtArgVal) &h;
1861 XtGetValues(shellWidget, shellArgs, 2);
1862 shellArgs[4].value = shellArgs[2].value = w;
1863 shellArgs[5].value = shellArgs[3].value = h;
1864 XtSetValues(shellWidget, &shellArgs[2], 4);
1865 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1866 marginH = h - boardHeight;
1868 CatchDeleteWindow(shellWidget, "QuitProc");
1876 if (appData.animate || appData.animateDragging)
1879 XtAugmentTranslations(formWidget,
1880 XtParseTranslationTable(globalTranslations));
1881 XtAugmentTranslations(boardWidget,
1882 XtParseTranslationTable(boardTranslations));
1883 XtAugmentTranslations(whiteTimerWidget,
1884 XtParseTranslationTable(whiteTranslations));
1885 XtAugmentTranslations(blackTimerWidget,
1886 XtParseTranslationTable(blackTranslations));
1888 /* Why is the following needed on some versions of X instead
1889 * of a translation? */
1890 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
1891 (XtEventHandler) EventProc, NULL);
1893 XtAddEventHandler(formWidget, KeyPressMask, False,
1894 (XtEventHandler) MoveTypeInProc, NULL);
1895 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1896 (XtEventHandler) EventProc, NULL);
1898 /* [AS] Restore layout */
1899 if( wpMoveHistory.visible ) {
1903 if( wpEvalGraph.visible )
1908 if( wpEngineOutput.visible ) {
1909 EngineOutputPopUp();
1914 if (errorExitStatus == -1) {
1915 if (appData.icsActive) {
1916 /* We now wait until we see "login:" from the ICS before
1917 sending the logon script (problems with timestamp otherwise) */
1918 /*ICSInitScript();*/
1919 if (appData.icsInputBox) ICSInputBoxPopUp();
1923 signal(SIGWINCH, TermSizeSigHandler);
1925 signal(SIGINT, IntSigHandler);
1926 signal(SIGTERM, IntSigHandler);
1927 if (*appData.cmailGameName != NULLCHAR) {
1928 signal(SIGUSR1, CmailSigHandler);
1932 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1934 // XtSetKeyboardFocus(shellWidget, formWidget);
1935 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1937 XtAppMainLoop(appContext);
1938 if (appData.debugMode) fclose(debugFP); // [DM] debug
1943 TermSizeSigHandler (int sig)
1949 IntSigHandler (int sig)
1955 CmailSigHandler (int sig)
1960 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1962 /* Activate call-back function CmailSigHandlerCallBack() */
1963 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1965 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1969 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1972 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1974 /**** end signal code ****/
1977 #define Abs(n) ((n)<0 ? -(n) : (n))
1981 InsertPxlSize (char *pattern, int targetPxlSize)
1983 char *base_fnt_lst, strInt[12], *p, *q;
1984 int alternatives, i, len, strIntLen;
1987 * Replace the "*" (if present) in the pixel-size slot of each
1988 * alternative with the targetPxlSize.
1992 while ((p = strchr(p, ',')) != NULL) {
1996 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1997 strIntLen = strlen(strInt);
1998 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
2002 while (alternatives--) {
2003 char *comma = strchr(p, ',');
2004 for (i=0; i<14; i++) {
2005 char *hyphen = strchr(p, '-');
2007 if (comma && hyphen > comma) break;
2008 len = hyphen + 1 - p;
2009 if (i == 7 && *p == '*' && len == 2) {
2011 memcpy(q, strInt, strIntLen);
2021 len = comma + 1 - p;
2028 return base_fnt_lst;
2032 CreateFontSet (char *base_fnt_lst)
2035 char **missing_list;
2039 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
2040 &missing_list, &missing_count, &def_string);
2041 if (appData.debugMode) {
2043 XFontStruct **font_struct_list;
2044 char **font_name_list;
2045 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
2047 fprintf(debugFP, " got list %s, locale %s\n",
2048 XBaseFontNameListOfFontSet(fntSet),
2049 XLocaleOfFontSet(fntSet));
2050 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
2051 for (i = 0; i < count; i++) {
2052 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
2055 for (i = 0; i < missing_count; i++) {
2056 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
2059 if (fntSet == NULL) {
2060 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
2065 #else // not ENABLE_NLS
2067 * Find a font that matches "pattern" that is as close as
2068 * possible to the targetPxlSize. Prefer fonts that are k
2069 * pixels smaller to fonts that are k pixels larger. The
2070 * pattern must be in the X Consortium standard format,
2071 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2072 * The return value should be freed with XtFree when no
2076 FindFont (char *pattern, int targetPxlSize)
2078 char **fonts, *p, *best, *scalable, *scalableTail;
2079 int i, j, nfonts, minerr, err, pxlSize;
2081 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2083 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2084 programName, pattern);
2091 for (i=0; i<nfonts; i++) {
2094 if (*p != '-') continue;
2096 if (*p == NULLCHAR) break;
2097 if (*p++ == '-') j++;
2099 if (j < 7) continue;
2102 scalable = fonts[i];
2105 err = pxlSize - targetPxlSize;
2106 if (Abs(err) < Abs(minerr) ||
2107 (minerr > 0 && err < 0 && -err == minerr)) {
2113 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2114 /* If the error is too big and there is a scalable font,
2115 use the scalable font. */
2116 int headlen = scalableTail - scalable;
2117 p = (char *) XtMalloc(strlen(scalable) + 10);
2118 while (isdigit(*scalableTail)) scalableTail++;
2119 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2121 p = (char *) XtMalloc(strlen(best) + 2);
2122 safeStrCpy(p, best, strlen(best)+1 );
2124 if (appData.debugMode) {
2125 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2126 pattern, targetPxlSize, p);
2128 XFreeFontNames(fonts);
2135 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
2136 // must be called before all non-first callse to CreateGCs()
2137 XtReleaseGC(shellWidget, highlineGC);
2138 XtReleaseGC(shellWidget, lightSquareGC);
2139 XtReleaseGC(shellWidget, darkSquareGC);
2140 XtReleaseGC(shellWidget, lineGC);
2141 if (appData.monoMode) {
2142 if (DefaultDepth(xDisplay, xScreen) == 1) {
2143 XtReleaseGC(shellWidget, wbPieceGC);
2145 XtReleaseGC(shellWidget, bwPieceGC);
2148 XtReleaseGC(shellWidget, prelineGC);
2149 XtReleaseGC(shellWidget, wdPieceGC);
2150 XtReleaseGC(shellWidget, wlPieceGC);
2151 XtReleaseGC(shellWidget, bdPieceGC);
2152 XtReleaseGC(shellWidget, blPieceGC);
2157 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
2159 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2160 | GCBackground | GCFunction | GCPlaneMask;
2161 gc_values->foreground = foreground;
2162 gc_values->background = background;
2163 return XtGetGC(shellWidget, value_mask, gc_values);
2167 CreateGCs (int redo)
2169 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2170 | GCBackground | GCFunction | GCPlaneMask;
2171 XGCValues gc_values;
2173 Pixel white = XWhitePixel(xDisplay, xScreen);
2174 Pixel black = XBlackPixel(xDisplay, xScreen);
2176 gc_values.plane_mask = AllPlanes;
2177 gc_values.line_width = lineGap;
2178 gc_values.line_style = LineSolid;
2179 gc_values.function = GXcopy;
2182 DeleteGCs(); // called a second time; clean up old GCs first
2183 } else { // [HGM] grid and font GCs created on first call only
2184 coordGC = CreateOneGC(&gc_values, black, white);
2185 XSetFont(xDisplay, coordGC, coordFontID);
2187 // [HGM] make font for holdings counts (white on black)
2188 countGC = CreateOneGC(&gc_values, white, black);
2189 XSetFont(xDisplay, countGC, countFontID);
2191 lineGC = CreateOneGC(&gc_values, black, black);
2193 if (appData.monoMode) {
2195 highlineGC = CreateOneGC(&gc_values, white, white);
2196 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
2197 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
2199 if (DefaultDepth(xDisplay, xScreen) == 1) {
2200 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2201 gc_values.function = GXcopyInverted;
2202 copyInvertedGC = CreateOneGC(&gc_values, black, white);
2203 gc_values.function = GXcopy;
2204 if (XBlackPixel(xDisplay, xScreen) == 1) {
2205 bwPieceGC = darkSquareGC;
2206 wbPieceGC = copyInvertedGC;
2208 bwPieceGC = copyInvertedGC;
2209 wbPieceGC = lightSquareGC;
2214 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
2215 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
2216 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
2217 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
2218 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
2219 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
2220 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
2221 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
2226 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
2234 fp = fopen(filename, "rb");
2236 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
2243 for (y=0; y<h; ++y) {
2244 for (x=0; x<h; ++x) {
2249 XPutPixel(xim, x, y, blackPieceColor);
2251 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2254 XPutPixel(xim, x, y, darkSquareColor);
2256 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2259 XPutPixel(xim, x, y, whitePieceColor);
2261 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2264 XPutPixel(xim, x, y, lightSquareColor);
2266 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2274 /* create Pixmap of piece */
2275 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2277 XPutImage(xDisplay, *dest, lightSquareGC, xim,
2280 /* create Pixmap of clipmask
2281 Note: We assume the white/black pieces have the same
2282 outline, so we make only 6 masks. This is okay
2283 since the XPM clipmask routines do the same. */
2285 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2287 XPutImage(xDisplay, temp, lightSquareGC, xmask,
2290 /* now create the 1-bit version */
2291 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2294 values.foreground = 1;
2295 values.background = 0;
2297 /* Don't use XtGetGC, not read only */
2298 maskGC = XCreateGC(xDisplay, *mask,
2299 GCForeground | GCBackground, &values);
2300 XCopyPlane(xDisplay, temp, *mask, maskGC,
2301 0, 0, squareSize, squareSize, 0, 0, 1);
2302 XFreePixmap(xDisplay, temp);
2307 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
2315 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
2320 /* The XSynchronize calls were copied from CreatePieces.
2321 Not sure if needed, but can't hurt */
2322 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2325 /* temp needed by loadXIM() */
2326 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2327 0, 0, ss, ss, AllPlanes, XYPixmap);
2329 if (strlen(appData.pixmapDirectory) == 0) {
2333 if (appData.monoMode) {
2334 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
2338 fprintf(stderr, _("\nLoading XIMs...\n"));
2340 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2341 fprintf(stderr, "%d", piece+1);
2342 for (kind=0; kind<4; kind++) {
2343 fprintf(stderr, ".");
2344 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2345 ExpandPathName(appData.pixmapDirectory),
2346 piece <= (int) WhiteKing ? "" : "w",
2347 pieceBitmapNames[piece],
2349 ximPieceBitmap[kind][piece] =
2350 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2351 0, 0, ss, ss, AllPlanes, XYPixmap);
2352 if (appData.debugMode)
2353 fprintf(stderr, _("(File:%s:) "), buf);
2354 loadXIM(ximPieceBitmap[kind][piece],
2356 &(xpmPieceBitmap2[kind][piece]),
2357 &(ximMaskPm2[piece]));
2358 if(piece <= (int)WhiteKing)
2359 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2361 fprintf(stderr," ");
2363 /* Load light and dark squares */
2364 /* If the LSQ and DSQ pieces don't exist, we will
2365 draw them with solid squares. */
2366 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2367 if (access(buf, 0) != 0) {
2371 fprintf(stderr, _("light square "));
2373 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2374 0, 0, ss, ss, AllPlanes, XYPixmap);
2375 if (appData.debugMode)
2376 fprintf(stderr, _("(File:%s:) "), buf);
2378 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2379 fprintf(stderr, _("dark square "));
2380 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2381 ExpandPathName(appData.pixmapDirectory), ss);
2382 if (appData.debugMode)
2383 fprintf(stderr, _("(File:%s:) "), buf);
2385 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2386 0, 0, ss, ss, AllPlanes, XYPixmap);
2387 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2388 xpmJailSquare = xpmLightSquare;
2390 fprintf(stderr, _("Done.\n"));
2392 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2395 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2399 CreateXPMBoard (char *s, int kind)
2403 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2404 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2405 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2411 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2412 // thisroutine has to be called t free the old piece pixmaps
2414 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2415 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2417 XFreePixmap(xDisplay, xpmLightSquare);
2418 XFreePixmap(xDisplay, xpmDarkSquare);
2427 u_int ss = squareSize;
2429 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2430 XpmColorSymbol symbols[4];
2431 static int redo = False;
2433 if(redo) FreeXPMPieces(); else redo = 1;
2435 /* The XSynchronize calls were copied from CreatePieces.
2436 Not sure if needed, but can't hurt */
2437 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2439 /* Setup translations so piece colors match square colors */
2440 symbols[0].name = "light_piece";
2441 symbols[0].value = appData.whitePieceColor;
2442 symbols[1].name = "dark_piece";
2443 symbols[1].value = appData.blackPieceColor;
2444 symbols[2].name = "light_square";
2445 symbols[2].value = appData.lightSquareColor;
2446 symbols[3].name = "dark_square";
2447 symbols[3].value = appData.darkSquareColor;
2449 attr.valuemask = XpmColorSymbols;
2450 attr.colorsymbols = symbols;
2451 attr.numsymbols = 4;
2453 if (appData.monoMode) {
2454 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2458 if (strlen(appData.pixmapDirectory) == 0) {
2459 XpmPieces* pieces = builtInXpms;
2462 while (pieces->size != squareSize && pieces->size) pieces++;
2463 if (!pieces->size) {
2464 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2467 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2468 for (kind=0; kind<4; kind++) {
2470 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2471 pieces->xpm[piece][kind],
2472 &(xpmPieceBitmap2[kind][piece]),
2473 NULL, &attr)) != 0) {
2474 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2478 if(piece <= (int) WhiteKing)
2479 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2483 xpmJailSquare = xpmLightSquare;
2487 fprintf(stderr, _("\nLoading XPMs...\n"));
2490 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2491 fprintf(stderr, "%d ", piece+1);
2492 for (kind=0; kind<4; kind++) {
2493 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2494 ExpandPathName(appData.pixmapDirectory),
2495 piece > (int) WhiteKing ? "w" : "",
2496 pieceBitmapNames[piece],
2498 if (appData.debugMode) {
2499 fprintf(stderr, _("(File:%s:) "), buf);
2501 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2502 &(xpmPieceBitmap2[kind][piece]),
2503 NULL, &attr)) != 0) {
2504 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2505 // [HGM] missing: read of unorthodox piece failed; substitute King.
2506 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2507 ExpandPathName(appData.pixmapDirectory),
2509 if (appData.debugMode) {
2510 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2512 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2513 &(xpmPieceBitmap2[kind][piece]),
2517 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2522 if(piece <= (int) WhiteKing)
2523 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2526 /* Load light and dark squares */
2527 /* If the LSQ and DSQ pieces don't exist, we will
2528 draw them with solid squares. */
2529 fprintf(stderr, _("light square "));
2530 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2531 if (access(buf, 0) != 0) {
2535 if (appData.debugMode)
2536 fprintf(stderr, _("(File:%s:) "), buf);
2538 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2539 &xpmLightSquare, NULL, &attr)) != 0) {
2540 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2543 fprintf(stderr, _("dark square "));
2544 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2545 ExpandPathName(appData.pixmapDirectory), ss);
2546 if (appData.debugMode) {
2547 fprintf(stderr, _("(File:%s:) "), buf);
2549 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2550 &xpmDarkSquare, NULL, &attr)) != 0) {
2551 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2555 xpmJailSquare = xpmLightSquare;
2556 fprintf(stderr, _("Done.\n"));
2558 oldVariant = -1; // kludge to force re-makig of animation masks
2559 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2562 #endif /* HAVE_LIBXPM */
2565 /* No built-in bitmaps */
2570 u_int ss = squareSize;
2572 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2575 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2576 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2577 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2578 pieceBitmapNames[piece],
2579 ss, kind == SOLID ? 's' : 'o');
2580 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2581 if(piece <= (int)WhiteKing)
2582 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2586 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2590 /* With built-in bitmaps */
2594 BuiltInBits* bib = builtInBits;
2597 u_int ss = squareSize;
2599 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2602 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2604 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2605 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2606 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2607 pieceBitmapNames[piece],
2608 ss, kind == SOLID ? 's' : 'o');
2609 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2610 bib->bits[kind][piece], ss, ss);
2611 if(piece <= (int)WhiteKing)
2612 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2616 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2622 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2627 char msg[MSG_SIZ], fullname[MSG_SIZ];
2629 if (*appData.bitmapDirectory != NULLCHAR) {
2630 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2631 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2632 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2633 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2634 &w, &h, pm, &x_hot, &y_hot);
2635 fprintf(stderr, "load %s\n", name);
2636 if (errcode != BitmapSuccess) {
2638 case BitmapOpenFailed:
2639 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2641 case BitmapFileInvalid:
2642 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2644 case BitmapNoMemory:
2645 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2649 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2653 fprintf(stderr, _("%s: %s...using built-in\n"),
2655 } else if (w != wreq || h != hreq) {
2657 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2658 programName, fullname, w, h, wreq, hreq);
2664 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2674 if (lineGap == 0) return;
2676 /* [HR] Split this into 2 loops for non-square boards. */
2678 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2679 gridSegments[i].x1 = 0;
2680 gridSegments[i].x2 =
2681 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2682 gridSegments[i].y1 = gridSegments[i].y2
2683 = lineGap / 2 + (i * (squareSize + lineGap));
2686 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2687 gridSegments[j + i].y1 = 0;
2688 gridSegments[j + i].y2 =
2689 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2690 gridSegments[j + i].x1 = gridSegments[j + i].x2
2691 = lineGap / 2 + (j * (squareSize + lineGap));
2695 int nrOfMenuItems = 7;
2696 Widget menuWidget[150];
2697 MenuListItem menuItemList[150] = {
2698 { "LoadNextGameProc", LoadNextGameProc },
2699 { "LoadPrevGameProc", LoadPrevGameProc },
2700 { "ReloadGameProc", ReloadGameProc },
2701 { "ReloadPositionProc", ReloadPositionProc },
2702 #ifndef OPTIONSDIALOG
2703 { "AlwaysQueenProc", AlwaysQueenProc },
2704 { "AnimateDraggingProc", AnimateDraggingProc },
2705 { "AnimateMovingProc", AnimateMovingProc },
2706 { "AutoflagProc", AutoflagProc },
2707 { "AutoflipProc", AutoflipProc },
2708 { "BlindfoldProc", BlindfoldProc },
2709 { "FlashMovesProc", FlashMovesProc },
2711 { "HighlightDraggingProc", HighlightDraggingProc },
2713 { "HighlightLastMoveProc", HighlightLastMoveProc },
2714 // { "IcsAlarmProc", IcsAlarmProc },
2715 { "MoveSoundProc", MoveSoundProc },
2716 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
2717 { "PopupExitMessageProc", PopupExitMessageProc },
2718 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
2719 // { "PremoveProc", PremoveProc },
2720 { "ShowCoordsProc", ShowCoordsProc },
2721 { "ShowThinkingProc", ShowThinkingProc },
2722 { "HideThinkingProc", HideThinkingProc },
2723 { "TestLegalityProc", TestLegalityProc },
2725 { "AboutGameProc", AboutGameEvent },
2726 { "DebugProc", DebugProc },
2727 { "NothingProc", NothingProc },
2732 MarkMenuItem (char *menuRef, int state)
2734 int nr = MenuToNumber(menuRef);
2737 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2738 XtSetValues(menuWidget[nr], args, 1);
2743 EnableMenuItem (char *menuRef, int state)
2745 int nr = MenuToNumber(menuRef);
2746 if(nr >= 0) XtSetSensitive(menuWidget[nr], state);
2750 EnableButtonBar (int state)
2752 XtSetSensitive(buttonBarWidget, state);
2757 SetMenuEnables (Enables *enab)
2759 while (enab->name != NULL) {
2760 EnableMenuItem(enab->name, enab->value);
2766 Equal(char *p, char *s)
2767 { // compare strings skipping spaces in second
2769 if(*s == ' ') { s++; continue; }
2770 if(*s++ != *p++) return 0;
2776 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2777 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2779 if(*nprms == 0) return;
2780 for(i=0; menuItemList[i].name; i++) {
2781 if(Equal(prms[0], menuItemList[i].name)) {
2782 (menuItemList[i].proc) ();
2789 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
2791 MenuProc *proc = (MenuProc *) addr;
2797 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2799 RecentEngineEvent((int) (intptr_t) addr);
2802 // some stuff that must remain in front-end
2803 static Widget mainBar, currentMenu;
2804 static int wtot, nr = 0, widths[10];
2807 AppendMenuItem (char *text, char *name, MenuProc *action)
2814 XtSetArg(args[j], XtNleftMargin, 20); j++;
2815 XtSetArg(args[j], XtNrightMargin, 20); j++;
2817 if (strcmp(text, "----") == 0) {
2818 entry = XtCreateManagedWidget(text, smeLineObjectClass,
2819 currentMenu, args, j);
2821 XtSetArg(args[j], XtNlabel, XtNewString(_(text)));
2822 entry = XtCreateManagedWidget(name, smeBSBObjectClass,
2823 currentMenu, args, j+1);
2824 XtAddCallback(entry, XtNcallback,
2825 (XtCallbackProc) (strcmp(name, "recent") ? MenuBarSelect : MenuEngineSelect),
2827 menuWidget[nrOfMenuItems] = entry;
2832 CreateMenuButton (char *name, Menu *mb)
2833 { // create menu button on main bar, and shell for pull-down list
2839 XtSetArg(args[j], XtNmenuName, XtNewString(name)); j++;
2840 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
2841 XtSetArg(args[j], XtNborderWidth, 0); j++;
2842 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
2844 currentMenu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2847 XtSetArg(args[j], XtNwidth, &w); j++;
2848 XtGetValues(mb->subMenu, args, j);
2849 wtot += mb->textWidth = widths[nr++] = w;
2853 CreateMenuBar (Menu *mb, int boardWidth)
2857 char menuName[MSG_SIZ];
2861 // create bar itself
2863 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2864 XtSetArg(args[j], XtNvSpace, 0); j++;
2865 XtSetArg(args[j], XtNborderWidth, 0); j++;
2866 mainBar = XtCreateWidget("menuBar", boxWidgetClass,
2867 formWidget, args, j);
2869 CreateMainMenus(mb); // put menus in bar according to description in back-end
2871 // size buttons to make menu bar fit, clipping menu names where necessary
2872 while(wtot > boardWidth - 40) {
2874 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
2878 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
2880 XtSetArg(args[j], XtNwidth, widths[i]); j++;
2881 XtSetValues(ma[i].subMenu, args, j);
2888 CreateButtonBar (MenuItem *mi)
2891 Widget button, buttonBar;
2895 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2897 XtSetArg(args[j], XtNhSpace, 0); j++;
2899 XtSetArg(args[j], XtNborderWidth, 0); j++;
2900 XtSetArg(args[j], XtNvSpace, 0); j++;
2901 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
2902 formWidget, args, j);
2904 while (mi->string != NULL) {
2907 XtSetArg(args[j], XtNinternalWidth, 2); j++;
2908 XtSetArg(args[j], XtNborderWidth, 0); j++;
2910 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
2911 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
2912 buttonBar, args, j);
2913 XtAddCallback(button, XtNcallback,
2914 (XtCallbackProc) MenuBarSelect,
2915 (caddr_t) mi->proc);
2922 CreatePieceMenu (char *name, int color)
2927 ChessSquare selection;
2929 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2930 boardWidget, args, 0);
2932 for (i = 0; i < PIECE_MENU_SIZE; i++) {
2933 String item = pieceMenuStrings[color][i];
2935 if (strcmp(item, "----") == 0) {
2936 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2939 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2940 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2942 selection = pieceMenuTranslation[color][i];
2943 XtAddCallback(entry, XtNcallback,
2944 (XtCallbackProc) PieceMenuSelect,
2945 (caddr_t) selection);
2946 if (selection == WhitePawn || selection == BlackPawn) {
2947 XtSetArg(args[0], XtNpopupOnEntry, entry);
2948 XtSetValues(menu, args, 1);
2961 ChessSquare selection;
2963 whitePieceMenu = CreatePieceMenu("menuW", 0);
2964 blackPieceMenu = CreatePieceMenu("menuB", 1);
2966 if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
2967 XtRegisterGrabAction(PieceMenuPopup, True,
2968 (unsigned)(ButtonPressMask|ButtonReleaseMask),
2969 GrabModeAsync, GrabModeAsync);
2971 XtSetArg(args[0], XtNlabel, _("Drop"));
2972 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
2973 boardWidget, args, 1);
2974 for (i = 0; i < DROP_MENU_SIZE; i++) {
2975 String item = dropMenuStrings[i];
2977 if (strcmp(item, "----") == 0) {
2978 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2981 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2982 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2984 selection = dropMenuTranslation[i];
2985 XtAddCallback(entry, XtNcallback,
2986 (XtCallbackProc) DropMenuSelect,
2987 (caddr_t) selection);
3001 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3002 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3003 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3004 dmEnables[i].piece);
3005 XtSetSensitive(entry, p != NULL || !appData.testLegality
3006 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3007 && !appData.icsActive));
3009 while (p && *p++ == dmEnables[i].piece) count++;
3010 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3012 XtSetArg(args[j], XtNlabel, label); j++;
3013 XtSetValues(entry, args, j);
3018 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
3020 String whichMenu; int menuNr = -2;
3021 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3022 if (event->type == ButtonRelease)
3023 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3024 else if (event->type == ButtonPress)
3025 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3027 case 0: whichMenu = params[0]; break;
3028 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3030 case -1: ErrorPopDown();
3033 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3037 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3039 if (pmFromX < 0 || pmFromY < 0) return;
3040 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3044 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3046 if (pmFromX < 0 || pmFromY < 0) return;
3047 DropMenuEvent(piece, pmFromX, pmFromY);
3051 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3053 shiftKey = prms[0][0] & 1;
3058 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3060 shiftKey = prms[0][0] & 1;
3066 do_flash_delay (unsigned long msec)
3072 DrawBorder (int x, int y, int type)
3076 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
3078 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3079 squareSize+lineGap, squareSize+lineGap);
3083 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
3085 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
3086 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
3088 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
3089 if(textureW[kind] < W*squareSize)
3090 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
3092 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
3093 if(textureH[kind] < H*squareSize)
3094 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
3096 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
3101 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
3102 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
3104 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
3105 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
3106 squareSize, squareSize, x*fac, y*fac);
3108 if (useImages && useImageSqs) {
3112 pm = xpmLightSquare;
3117 case 2: /* neutral */
3119 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
3122 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3123 squareSize, squareSize, x*fac, y*fac);
3133 case 2: /* neutral */
3138 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
3143 I split out the routines to draw a piece so that I could
3144 make a generic flash routine.
3147 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3149 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3150 switch (square_color) {
3152 case 2: /* neutral */
3154 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3155 ? *pieceToOutline(piece)
3156 : *pieceToSolid(piece),
3157 dest, bwPieceGC, 0, 0,
3158 squareSize, squareSize, x, y);
3161 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3162 ? *pieceToSolid(piece)
3163 : *pieceToOutline(piece),
3164 dest, wbPieceGC, 0, 0,
3165 squareSize, squareSize, x, y);
3171 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3173 switch (square_color) {
3175 case 2: /* neutral */
3177 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3178 ? *pieceToOutline(piece)
3179 : *pieceToSolid(piece),
3180 dest, bwPieceGC, 0, 0,
3181 squareSize, squareSize, x, y, 1);
3184 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3185 ? *pieceToSolid(piece)
3186 : *pieceToOutline(piece),
3187 dest, wbPieceGC, 0, 0,
3188 squareSize, squareSize, x, y, 1);
3194 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3196 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
3197 switch (square_color) {
3199 XCopyPlane(xDisplay, *pieceToSolid(piece),
3200 dest, (int) piece < (int) BlackPawn
3201 ? wlPieceGC : blPieceGC, 0, 0,
3202 squareSize, squareSize, x, y, 1);
3205 XCopyPlane(xDisplay, *pieceToSolid(piece),
3206 dest, (int) piece < (int) BlackPawn
3207 ? wdPieceGC : bdPieceGC, 0, 0,
3208 squareSize, squareSize, x, y, 1);
3210 case 2: /* neutral */
3212 break; // should never contain pieces
3217 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3219 int kind, p = piece;
3221 switch (square_color) {
3223 case 2: /* neutral */
3225 if ((int)piece < (int) BlackPawn) {
3233 if ((int)piece < (int) BlackPawn) {
3241 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
3242 if(useTexture & square_color+1) {
3243 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
3244 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
3245 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
3246 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
3247 XSetClipMask(xDisplay, wlPieceGC, None);
3248 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
3250 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3251 dest, wlPieceGC, 0, 0,
3252 squareSize, squareSize, x, y);
3255 typedef void (*DrawFunc)();
3260 if (appData.monoMode) {
3261 if (DefaultDepth(xDisplay, xScreen) == 1) {
3262 return monoDrawPiece_1bit;
3264 return monoDrawPiece;
3268 return colorDrawPieceImage;
3270 return colorDrawPiece;
3275 DrawDot (int marker, int x, int y, int r)
3277 if(appData.monoMode) {
3278 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
3279 x, y, r, r, 0, 64*360);
3280 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
3281 x, y, r, r, 0, 64*360);
3283 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
3284 x, y, r, r, 0, 64*360);
3288 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
3289 { // basic front-end board-draw function: takes care of everything that can be in square:
3290 // piece, background, coordinate/count, marker dot
3291 int direction, font_ascent, font_descent;
3292 XCharStruct overall;
3295 if (piece == EmptySquare) {
3296 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
3298 drawfunc = ChooseDrawFunc();
3299 drawfunc(piece, square_color, x, y, xBoardWindow);
3302 if(align) { // square carries inscription (coord or piece count)
3304 GC hGC = align < 3 ? coordGC : countGC;
3305 // first calculate where it goes
3306 XTextExtents(countFontStruct, string, 1, &direction,
3307 &font_ascent, &font_descent, &overall);
3309 xx += squareSize - overall.width - 2;
3310 yy += squareSize - font_descent - 1;
3311 } else if (align == 2) {
3312 xx += 2, yy += font_ascent + 1;
3313 } else if (align == 3) {
3314 xx += squareSize - overall.width - 2;
3315 yy += font_ascent + 1;
3316 } else if (align == 4) {
3317 xx += 2, yy += font_ascent + 1;
3320 if (appData.monoMode) {
3321 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3323 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3327 if(marker) { // print fat marker dot, if requested
3328 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
3333 FlashDelay (int flash_delay)
3335 XSync(xDisplay, False);
3336 if(flash_delay) do_flash_delay(flash_delay);
3340 Fraction (int x, int start, int stop)
3342 double f = ((double) x - start)/(stop - start);
3343 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
3347 static WindowPlacement wpNew;
3350 CoDrag (Widget sh, WindowPlacement *wp)
3353 int j=0, touch=0, fudge = 2;
3354 GetActualPlacement(sh, wp);
3355 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
3356 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
3357 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
3358 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
3359 if(!touch ) return; // only windows that touch co-move
3360 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
3361 int heightInc = wpNew.height - wpMain.height;
3362 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3363 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3364 wp->y += fracTop * heightInc;
3365 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
3366 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
3367 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
3368 int widthInc = wpNew.width - wpMain.width;
3369 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3370 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3371 wp->y += fracLeft * widthInc;
3372 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
3373 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
3375 wp->x += wpNew.x - wpMain.x;
3376 wp->y += wpNew.y - wpMain.y;
3377 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
3378 if(touch == 3) wp->y += wpNew.height - wpMain.height;
3379 XtSetArg(args[j], XtNx, wp->x); j++;
3380 XtSetArg(args[j], XtNy, wp->y); j++;
3381 XtSetValues(sh, args, j);
3384 static XtIntervalId delayedDragID = 0;
3389 GetActualPlacement(shellWidget, &wpNew);
3390 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
3391 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
3392 return; // false alarm
3393 if(EngineOutputIsUp()) CoDrag(engineOutputShell, &wpEngineOutput);
3394 if(MoveHistoryIsUp()) CoDrag(shells[HistoryDlg], &wpMoveHistory);
3395 if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
3396 if(GameListIsUp()) CoDrag(gameListShell, &wpGameList);
3398 DrawPosition(True, NULL);
3399 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
3406 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
3408 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
3411 /* Why is this needed on some versions of X? */
3413 EventProc (Widget widget, caddr_t unused, XEvent *event)
3415 if (!XtIsRealized(widget))
3417 switch (event->type) {
3418 case ConfigureNotify: // main window is being dragged: drag attached windows with it
3419 if(appData.useStickyWindows)
3420 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
3423 if (event->xexpose.count > 0) return; /* no clipping is done */
3424 DrawPosition(True, NULL);
3425 if(twoBoards) { // [HGM] dual: draw other board in other orientation
3426 flipView = !flipView; partnerUp = !partnerUp;
3427 DrawPosition(True, NULL);
3428 flipView = !flipView; partnerUp = !partnerUp;
3432 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
3439 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
3441 DrawSeekAxis (int x, int y, int xTo, int yTo)
3443 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
3447 DrawSeekBackground (int left, int top, int right, int bottom)
3449 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
3453 DrawSeekText (char *buf, int x, int y)
3455 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
3459 DrawSeekDot (int x, int y, int colorNr)
3461 int square = colorNr & 0x80;
3464 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
3466 XFillRectangle(xDisplay, xBoardWindow, color,
3467 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3469 XFillArc(xDisplay, xBoardWindow, color,
3470 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
3474 DrawGrid (int second)
3476 XDrawSegments(xDisplay, xBoardWindow, lineGC,
3477 second ? secondSegments : // [HGM] dual
3478 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
3483 * event handler for redrawing the board
3486 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3488 DrawPosition(True, NULL);
3493 * event handler for parsing user moves
3495 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
3496 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
3497 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
3498 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
3499 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
3500 // and at the end FinishMove() to perform the move after optional promotion popups.
3501 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
3503 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3505 if (w != boardWidget || errorExitStatus != -1) return;
3506 if(nprms) shiftKey = !strcmp(prms[0], "1");
3508 if (shellUp[PromoDlg]) { // [HGM] is this still needed?
3509 if (event->type == ButtonPress) {
3518 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
3519 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
3520 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
3524 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
3526 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
3527 DragPieceMove(event->xmotion.x, event->xmotion.y);
3531 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3532 { // [HGM] pv: walk PV
3533 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3536 static int savedIndex; /* gross that this is global */
3539 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3542 XawTextPosition index, dummy;
3545 XawTextGetSelectionPos(w, &index, &dummy);
3546 XtSetArg(arg, XtNstring, &val);
3547 XtGetValues(w, &arg, 1);
3548 ReplaceComment(savedIndex, val);
3549 if(savedIndex != currentMove) ToNrEvent(savedIndex);
3550 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3554 EditCommentPopUp (int index, char *title, char *text)
3557 if (text == NULL) text = "";
3558 NewCommentPopup(title, text, index);
3562 CommentPopUp (char *title, char *text)
3564 savedIndex = currentMove; // [HGM] vari
3565 NewCommentPopup(title, text, currentMove);
3571 PopDown(CommentDlg);
3574 static char *openName;
3580 (void) (*fileProc)(openFP, 0, openName);
3584 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
3586 fileProc = proc; /* I can't see a way not */
3587 fileOpenMode = openMode; /* to use globals here */
3588 { // [HGM] use file-selector dialog stolen from Ghostview
3589 int index; // this is not supported yet
3590 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
3591 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
3592 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
3593 ScheduleDelayedEvent(&DelayedLoad, 50);
3598 /* Disable all user input other than deleting the window */
3599 static int frozen = 0;
3605 /* Grab by a widget that doesn't accept input */
3606 XtAddGrab(messageWidget, TRUE, FALSE);
3610 /* Undo a FreezeUI */
3614 if (!frozen) return;
3615 XtRemoveGrab(messageWidget);
3623 static int oldPausing = FALSE;
3624 static GameMode oldmode = (GameMode) -1;
3627 if (!boardWidget || !XtIsRealized(boardWidget)) return;
3629 if (pausing != oldPausing) {
3630 oldPausing = pausing;
3631 MarkMenuItem("Pause", pausing);
3633 if (appData.showButtonBar) {
3634 /* Always toggle, don't set. Previous code messes up when
3635 invoked while the button is pressed, as releasing it
3636 toggles the state again. */
3639 XtSetArg(args[0], XtNbackground, &oldbg);
3640 XtSetArg(args[1], XtNforeground, &oldfg);
3641 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
3643 XtSetArg(args[0], XtNbackground, oldfg);
3644 XtSetArg(args[1], XtNforeground, oldbg);
3646 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
3650 wname = ModeToWidgetName(oldmode);
3651 if (wname != NULL) {
3652 MarkMenuItem(wname, False);
3654 wname = ModeToWidgetName(gameMode);
3655 if (wname != NULL) {
3656 MarkMenuItem(wname, True);
3659 MarkMenuItem("Machine Match", matchMode && matchGame < appData.matchGames);
3661 /* Maybe all the enables should be handled here, not just this one */
3662 EnableMenuItem("Training", gameMode == Training || gameMode == PlayFromGameFile);
3667 * Button/menu procedures
3670 LoadGamePopUp (FILE *f, int gameNumber, char *title)
3672 cmailMsgLoaded = FALSE;
3673 if (gameNumber == 0) {
3674 int error = GameListBuild(f);
3676 DisplayError(_("Cannot build game list"), error);
3677 } else if (!ListEmpty(&gameList) &&
3678 ((ListGame *) gameList.tailPred)->number > 1) {
3679 GameListPopUp(f, title);
3685 return LoadGame(f, gameNumber, title, FALSE);
3688 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3689 char *selected_fen_position=NULL;
3692 SendPositionSelection (Widget w, Atom *selection, Atom *target,
3693 Atom *type_return, XtPointer *value_return,
3694 unsigned long *length_return, int *format_return)
3696 char *selection_tmp;
3698 // if (!selected_fen_position) return False; /* should never happen */
3699 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3700 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
3701 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
3704 if (f == NULL) return False;
3708 selection_tmp = XtMalloc(len + 1);
3709 count = fread(selection_tmp, 1, len, f);
3712 XtFree(selection_tmp);
3715 selection_tmp[len] = NULLCHAR;
3717 /* note: since no XtSelectionDoneProc was registered, Xt will
3718 * automatically call XtFree on the value returned. So have to
3719 * make a copy of it allocated with XtMalloc */
3720 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3721 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
3724 *value_return=selection_tmp;
3725 *length_return=strlen(selection_tmp);
3726 *type_return=*target;
3727 *format_return = 8; /* bits per byte */
3729 } else if (*target == XA_TARGETS(xDisplay)) {
3730 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3731 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3732 targets_tmp[1] = XA_STRING;
3733 *value_return = targets_tmp;
3734 *type_return = XA_ATOM;
3737 // This code leads to a read of value_return out of bounds on 64-bit systems.
3738 // Other code which I have seen always sets *format_return to 32 independent of
3739 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
3740 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
3741 *format_return = 8 * sizeof(Atom);
3742 if (*format_return > 32) {
3743 *length_return *= *format_return / 32;
3744 *format_return = 32;
3747 *format_return = 32;
3755 /* note: when called from menu all parameters are NULL, so no clue what the
3756 * Widget which was clicked on was, or what the click event was
3759 CopySomething (char *src)
3761 selected_fen_position = src;
3763 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3764 * have a notion of a position that is selected but not copied.
3765 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3767 XtOwnSelection(menuBarWidget, XA_PRIMARY,
3769 SendPositionSelection,
3770 NULL/* lose_ownership_proc */ ,
3771 NULL/* transfer_done_proc */);
3772 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3774 SendPositionSelection,
3775 NULL/* lose_ownership_proc */ ,
3776 NULL/* transfer_done_proc */);
3779 /* function called when the data to Paste is ready */
3781 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3782 Atom *type, XtPointer value, unsigned long *len, int *format)
3785 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3786 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3787 EditPositionPasteFEN(fenstr);
3791 /* called when Paste Position button is pressed,
3792 * all parameters will be NULL */
3794 PastePositionProc ()
3796 XtGetSelectionValue(menuBarWidget,
3797 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3798 /* (XtSelectionCallbackProc) */ PastePositionCB,
3799 NULL, /* client_data passed to PastePositionCB */
3801 /* better to use the time field from the event that triggered the
3802 * call to this function, but that isn't trivial to get
3809 /* note: when called from menu all parameters are NULL, so no clue what the
3810 * Widget which was clicked on was, or what the click event was
3812 /* function called when the data to Paste is ready */
3814 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3815 Atom *type, XtPointer value, unsigned long *len, int *format)
3818 if (value == NULL || *len == 0) {
3819 return; /* nothing had been selected to copy */
3821 f = fopen(gamePasteFilename, "w");
3823 DisplayError(_("Can't open temp file"), errno);
3826 fwrite(value, 1, *len, f);
3829 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3832 /* called when Paste Game button is pressed,
3833 * all parameters will be NULL */
3837 XtGetSelectionValue(menuBarWidget,
3838 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3839 /* (XtSelectionCallbackProc) */ PasteGameCB,
3840 NULL, /* client_data passed to PasteGameCB */
3842 /* better to use the time field from the event that triggered the
3843 * call to this function, but that isn't trivial to get
3852 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3859 { // bassic primitive for determining if modifier keys are pressed
3860 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3863 XQueryKeymap(xDisplay,keys);
3864 for(i=0; i<6; i++) {
3866 j = XKeysymToKeycode(xDisplay, codes[i]);
3867 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3873 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3877 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3878 if ( n == 1 && *buf >= 32 // printable
3879 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3880 ) BoxAutoPopUp (buf);
3884 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3885 { // [HGM] input: let up-arrow recall previous line from history
3890 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3891 { // [HGM] input: let down-arrow recall next line from history
3896 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3902 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3904 if (!TempBackwardActive) {
3905 TempBackwardActive = True;
3911 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3913 /* Check to see if triggered by a key release event for a repeating key.
3914 * If so the next queued event will be a key press of the same key at the same time */
3915 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3917 XPeekEvent(xDisplay, &next);
3918 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3919 next.xkey.keycode == event->xkey.keycode)
3923 TempBackwardActive = False;
3927 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3928 { // called as key binding
3931 if (nprms && *nprms > 0)
3935 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3940 DisplayMessage (char *message, char *extMessage)
3942 /* display a message in the message widget */
3951 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
3956 message = extMessage;
3960 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
3962 /* need to test if messageWidget already exists, since this function
3963 can also be called during the startup, if for example a Xresource
3964 is not set up correctly */
3967 XtSetArg(arg, XtNlabel, message);
3968 XtSetValues(messageWidget, &arg, 1);
3975 SetWindowTitle (char *text, char *title, char *icon)
3979 if (appData.titleInWindow) {
3981 XtSetArg(args[i], XtNlabel, text); i++;
3982 XtSetValues(titleWidget, args, i);
3985 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3986 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3987 XtSetValues(shellWidget, args, i);
3988 XSync(xDisplay, False);
3993 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3999 DisplayIcsInteractionTitle (String message)
4001 if (oldICSInteractionTitle == NULL) {
4002 /* Magic to find the old window title, adapted from vim */
4003 char *wina = getenv("WINDOWID");
4005 Window win = (Window) atoi(wina);
4006 Window root, parent, *children;
4007 unsigned int nchildren;
4008 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4010 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4011 if (!XQueryTree(xDisplay, win, &root, &parent,
4012 &children, &nchildren)) break;
4013 if (children) XFree((void *)children);
4014 if (parent == root || parent == 0) break;
4017 XSetErrorHandler(oldHandler);
4019 if (oldICSInteractionTitle == NULL) {
4020 oldICSInteractionTitle = "xterm";
4023 printf("\033]0;%s\007", message);
4028 XtIntervalId delayedEventTimerXID = 0;
4029 DelayedEventCallback delayedEventCallback = 0;
4034 delayedEventTimerXID = 0;
4035 delayedEventCallback();
4039 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
4041 if(delayedEventTimerXID && delayedEventCallback == cb)
4042 // [HGM] alive: replace, rather than add or flush identical event
4043 XtRemoveTimeOut(delayedEventTimerXID);
4044 delayedEventCallback = cb;
4045 delayedEventTimerXID =
4046 XtAppAddTimeOut(appContext, millisec,
4047 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
4050 DelayedEventCallback
4053 if (delayedEventTimerXID) {
4054 return delayedEventCallback;
4061 CancelDelayedEvent ()
4063 if (delayedEventTimerXID) {
4064 XtRemoveTimeOut(delayedEventTimerXID);
4065 delayedEventTimerXID = 0;
4069 XtIntervalId loadGameTimerXID = 0;
4072 LoadGameTimerRunning ()
4074 return loadGameTimerXID != 0;
4078 StopLoadGameTimer ()
4080 if (loadGameTimerXID != 0) {
4081 XtRemoveTimeOut(loadGameTimerXID);
4082 loadGameTimerXID = 0;
4090 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
4092 loadGameTimerXID = 0;
4097 StartLoadGameTimer (long millisec)
4100 XtAppAddTimeOut(appContext, millisec,
4101 (XtTimerCallbackProc) LoadGameTimerCallback,
4105 XtIntervalId analysisClockXID = 0;
4108 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
4110 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
4111 || appData.icsEngineAnalyze) { // [DM]
4112 AnalysisPeriodicEvent(0);
4113 StartAnalysisClock();
4118 StartAnalysisClock ()
4121 XtAppAddTimeOut(appContext, 2000,
4122 (XtTimerCallbackProc) AnalysisClockCallback,
4126 XtIntervalId clockTimerXID = 0;
4129 ClockTimerRunning ()
4131 return clockTimerXID != 0;
4137 if (clockTimerXID != 0) {
4138 XtRemoveTimeOut(clockTimerXID);
4147 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
4154 StartClockTimer (long millisec)
4157 XtAppAddTimeOut(appContext, millisec,
4158 (XtTimerCallbackProc) ClockTimerCallback,
4163 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
4168 /* check for low time warning */
4169 Pixel foregroundOrWarningColor = timerForegroundPixel;
4172 appData.lowTimeWarning &&
4173 (timer / 1000) < appData.icsAlarmTime)
4174 foregroundOrWarningColor = lowTimeWarningColor;
4176 if (appData.clockMode) {
4177 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
4178 XtSetArg(args[0], XtNlabel, buf);
4180 snprintf(buf, MSG_SIZ, "%s ", color);
4181 XtSetArg(args[0], XtNlabel, buf);
4186 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
4187 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
4189 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
4190 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
4193 XtSetValues(w, args, 3);
4197 DisplayWhiteClock (long timeRemaining, int highlight)
4201 if(appData.noGUI) return;
4202 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
4203 if (highlight && iconPixmap == bIconPixmap) {
4204 iconPixmap = wIconPixmap;
4205 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
4206 XtSetValues(shellWidget, args, 1);
4211 DisplayBlackClock (long timeRemaining, int highlight)
4215 if(appData.noGUI) return;
4216 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
4217 if (highlight && iconPixmap == wIconPixmap) {
4218 iconPixmap = bIconPixmap;
4219 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
4220 XtSetValues(shellWidget, args, 1);
4225 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
4227 InputSource *is = (InputSource *) closure;
4232 if (is->lineByLine) {
4233 count = read(is->fd, is->unused,
4234 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
4236 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
4239 is->unused += count;
4241 while (p < is->unused) {
4242 q = memchr(p, '\n', is->unused - p);
4243 if (q == NULL) break;
4245 (is->func)(is, is->closure, p, q - p, 0);
4249 while (p < is->unused) {
4254 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
4259 (is->func)(is, is->closure, is->buf, count, error);
4264 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
4267 ChildProc *cp = (ChildProc *) pr;
4269 is = (InputSource *) calloc(1, sizeof(InputSource));
4270 is->lineByLine = lineByLine;
4274 is->fd = fileno(stdin);
4276 is->kind = cp->kind;
4277 is->fd = cp->fdFrom;
4280 is->unused = is->buf;
4283 is->xid = XtAppAddInput(appContext, is->fd,
4284 (XtPointer) (XtInputReadMask),
4285 (XtInputCallbackProc) DoInputCallback,
4287 is->closure = closure;
4288 return (InputSourceRef) is;
4292 RemoveInputSource (InputSourceRef isr)
4294 InputSource *is = (InputSource *) isr;
4296 if (is->xid == 0) return;
4297 XtRemoveInput(is->xid);
4301 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
4303 /* Masks for XPM pieces. Black and white pieces can have
4304 different shapes, but in the interest of retaining my
4305 sanity pieces must have the same outline on both light
4306 and dark squares, and all pieces must use the same
4307 background square colors/images. */
4309 static int xpmDone = 0;
4310 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
4311 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
4314 CreateAnimMasks (int pieceDepth)
4320 unsigned long plane;
4323 /* Need a bitmap just to get a GC with right depth */
4324 buf = XCreatePixmap(xDisplay, xBoardWindow,
4326 values.foreground = 1;
4327 values.background = 0;
4328 /* Don't use XtGetGC, not read only */
4329 maskGC = XCreateGC(xDisplay, buf,
4330 GCForeground | GCBackground, &values);
4331 XFreePixmap(xDisplay, buf);
4333 buf = XCreatePixmap(xDisplay, xBoardWindow,
4334 squareSize, squareSize, pieceDepth);
4335 values.foreground = XBlackPixel(xDisplay, xScreen);
4336 values.background = XWhitePixel(xDisplay, xScreen);
4337 bufGC = XCreateGC(xDisplay, buf,
4338 GCForeground | GCBackground, &values);
4340 for (piece = WhitePawn; piece <= BlackKing; piece++) {
4341 /* Begin with empty mask */
4342 if(!xpmDone) // [HGM] pieces: keep using existing
4343 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
4344 squareSize, squareSize, 1);
4345 XSetFunction(xDisplay, maskGC, GXclear);
4346 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
4347 0, 0, squareSize, squareSize);
4349 /* Take a copy of the piece */
4354 XSetFunction(xDisplay, bufGC, GXcopy);
4355 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
4357 0, 0, squareSize, squareSize, 0, 0);
4359 /* XOR the background (light) over the piece */
4360 XSetFunction(xDisplay, bufGC, GXxor);
4362 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
4363 0, 0, squareSize, squareSize, 0, 0);
4365 XSetForeground(xDisplay, bufGC, lightSquareColor);
4366 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
4369 /* We now have an inverted piece image with the background
4370 erased. Construct mask by just selecting all the non-zero
4371 pixels - no need to reconstruct the original image. */
4372 XSetFunction(xDisplay, maskGC, GXor);
4374 /* Might be quicker to download an XImage and create bitmap
4375 data from it rather than this N copies per piece, but it
4376 only takes a fraction of a second and there is a much
4377 longer delay for loading the pieces. */
4378 for (n = 0; n < pieceDepth; n ++) {
4379 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
4380 0, 0, squareSize, squareSize,
4386 XFreePixmap(xDisplay, buf);
4387 XFreeGC(xDisplay, bufGC);
4388 XFreeGC(xDisplay, maskGC);
4392 InitAnimState (AnimNr anr, XWindowAttributes *info)
4397 /* Each buffer is square size, same depth as window */
4398 animBufs[anr+4] = xBoardWindow;
4399 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
4400 squareSize, squareSize, info->depth);
4401 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
4402 squareSize, squareSize, info->depth);
4404 /* Create a plain GC for blitting */
4405 mask = GCForeground | GCBackground | GCFunction |
4406 GCPlaneMask | GCGraphicsExposures;
4407 values.foreground = XBlackPixel(xDisplay, xScreen);
4408 values.background = XWhitePixel(xDisplay, xScreen);
4409 values.function = GXcopy;
4410 values.plane_mask = AllPlanes;
4411 values.graphics_exposures = False;
4412 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
4414 /* Piece will be copied from an existing context at
4415 the start of each new animation/drag. */
4416 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
4418 /* Outline will be a read-only copy of an existing */
4419 animGCs[anr+4] = None;
4425 XWindowAttributes info;
4427 if (xpmDone && gameInfo.variant == oldVariant) return;
4428 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
4429 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
4431 InitAnimState(Game, &info);
4432 InitAnimState(Player, &info);
4434 /* For XPM pieces, we need bitmaps to use as masks. */
4436 CreateAnimMasks(info.depth), xpmDone = 1;
4441 static Boolean frameWaiting;
4444 FrameAlarm (int sig)
4446 frameWaiting = False;
4447 /* In case System-V style signals. Needed?? */
4448 signal(SIGALRM, FrameAlarm);
4452 FrameDelay (int time)
4454 struct itimerval delay;
4456 XSync(xDisplay, False);
4459 frameWaiting = True;
4460 signal(SIGALRM, FrameAlarm);
4461 delay.it_interval.tv_sec =
4462 delay.it_value.tv_sec = time / 1000;
4463 delay.it_interval.tv_usec =
4464 delay.it_value.tv_usec = (time % 1000) * 1000;
4465 setitimer(ITIMER_REAL, &delay, NULL);
4466 while (frameWaiting) pause();
4467 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
4468 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
4469 setitimer(ITIMER_REAL, &delay, NULL);
4476 FrameDelay (int time)
4478 XSync(xDisplay, False);
4480 usleep(time * 1000);
4486 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
4490 /* Bitmap for piece being moved. */
4491 if (appData.monoMode) {
4492 *mask = *pieceToSolid(piece);
4493 } else if (useImages) {
4495 *mask = xpmMask[piece];
4497 *mask = ximMaskPm[piece];
4500 *mask = *pieceToSolid(piece);
4503 /* GC for piece being moved. Square color doesn't matter, but
4504 since it gets modified we make a copy of the original. */
4506 if (appData.monoMode)
4511 if (appData.monoMode)
4516 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
4518 /* Outline only used in mono mode and is not modified */
4520 *outline = bwPieceGC;
4522 *outline = wbPieceGC;
4526 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
4531 /* Draw solid rectangle which will be clipped to shape of piece */
4532 XFillRectangle(xDisplay, dest, clip,
4533 0, 0, squareSize, squareSize);
4534 if (appData.monoMode)
4535 /* Also draw outline in contrasting color for black
4536 on black / white on white cases */
4537 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
4538 0, 0, squareSize, squareSize, 0, 0, 1);
4540 /* Copy the piece */
4545 if(appData.upsideDown && flipView) kind ^= 2;
4546 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4548 0, 0, squareSize, squareSize,
4554 InsertPiece (AnimNr anr, ChessSquare piece)
4556 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
4560 DrawBlank (AnimNr anr, int x, int y, int startColor)
4562 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
4565 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
4566 int srcX, int srcY, int width, int height, int destX, int destY)
4568 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
4569 srcX, srcY, width, height, destX, destY);
4573 SetDragPiece (AnimNr anr, ChessSquare piece)
4576 /* The piece will be drawn using its own bitmap as a matte */
4577 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
4578 XSetClipMask(xDisplay, animGCs[anr+2], mask);
4581 /* [AS] Arrow highlighting support */
4584 DrawPolygon (Pnt arrow[], int nr)
4588 for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
4589 XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
4590 if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
4594 UpdateLogos (int displ)
4596 return; // no logos in XBoard yet