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;
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 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
526 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
527 { "GenericPopDown", (XtActionProc) GenericPopDown },
528 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
529 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
530 { "SelectMove", (XtActionProc) SelectMove },
531 { "LoadSelectedProc", LoadSelectedProc },
532 { "SetFilterProc", SetFilterProc },
533 { "TypeInProc", TypeInProc },
534 { "EnterKeyProc", EnterKeyProc },
535 { "UpKeyProc", UpKeyProc },
536 { "DownKeyProc", DownKeyProc },
537 { "WheelProc", WheelProc },
538 { "TabProc", TabProc },
541 char globalTranslations[] =
542 ":<Key>F9: MenuItem(ResignProc) \n \
543 :Ctrl<Key>n: MenuItem(NewGame) \n \
544 :Meta<Key>V: MenuItem(NewVariant) \n \
545 :Ctrl<Key>o: MenuItem(LoadGame) \n \
546 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
547 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
548 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
549 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
550 :Ctrl<Key>s: MenuItem(SaveGame) \n \
551 :Ctrl<Key>c: MenuItem(CopyGame) \n \
552 :Ctrl<Key>v: MenuItem(PasteGame) \n \
553 :Ctrl<Key>O: MenuItem(LoadPosition) \n \
554 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
555 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
556 :Ctrl<Key>S: MenuItem(SavePosition) \n \
557 :Ctrl<Key>C: MenuItem(CopyPosition) \n \
558 :Ctrl<Key>V: MenuItem(PastePosition) \n \
559 :Ctrl<Key>q: MenuItem(Exit) \n \
560 :Ctrl<Key>w: MenuItem(MachineWhite) \n \
561 :Ctrl<Key>b: MenuItem(MachineBlack) \n \
562 :Ctrl<Key>t: MenuItem(TwoMachines) \n \
563 :Ctrl<Key>a: MenuItem(AnalysisMode) \n \
564 :Ctrl<Key>g: MenuItem(AnalyzeFile) \n \
565 :Ctrl<Key>e: MenuItem(EditGame) \n \
566 :Ctrl<Key>E: MenuItem(EditPosition) \n \
567 :Meta<Key>O: MenuItem(ShowEngineOutput) \n \
568 :Meta<Key>E: MenuItem(ShowEvaluationGraph) \n \
569 :Meta<Key>G: MenuItem(ShowGameList) \n \
570 :Meta<Key>H: MenuItem(ShowMoveHistory) \n \
571 :<Key>Pause: MenuItem(Pause) \n \
572 :<Key>F3: MenuItem(Accept) \n \
573 :<Key>F4: MenuItem(Decline) \n \
574 :<Key>F12: MenuItem(Rematch) \n \
575 :<Key>F5: MenuItem(CallFlag) \n \
576 :<Key>F6: MenuItem(Draw) \n \
577 :<Key>F7: MenuItem(Adjourn) \n \
578 :<Key>F8: MenuItem(Abort) \n \
579 :<Key>F10: MenuItem(StopObserving) \n \
580 :<Key>F11: MenuItem(StopExamining) \n \
581 :Ctrl<Key>d: MenuItem(DebugProc) \n \
582 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
583 :Meta<Key>End: MenuItem(ToEnd) \n \
584 :Meta<Key>Right: MenuItem(Forward) \n \
585 :Meta<Key>Home: MenuItem(ToStart) \n \
586 :Meta<Key>Left: MenuItem(Backward) \n \
587 :<Key>Left: MenuItem(Backward) \n \
588 :<Key>Right: MenuItem(Forward) \n \
589 :<Key>Home: MenuItem(Revert) \n \
590 :<Key>End: MenuItem(TruncateGame) \n \
591 :Ctrl<Key>m: MenuItem(MoveNow) \n \
592 :Ctrl<Key>x: MenuItem(RetractMove) \n \
593 :Meta<Key>J: MenuItem(Adjudications) \n \
594 :Meta<Key>U: MenuItem(CommonEngine) \n \
595 :Meta<Key>T: MenuItem(TimeControl) \n \
596 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
597 #ifndef OPTIONSDIALOG
599 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
600 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
601 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
602 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
603 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
606 :<Key>F1: MenuItem(Manual) \n \
607 :<Key>F2: MenuItem(FlipView) \n \
608 :<KeyDown>Return: TempBackwardProc() \n \
609 :<KeyUp>Return: TempForwardProc() \n";
611 char boardTranslations[] =
612 "<Btn1Down>: HandleUserMove(0) \n \
613 Shift<Btn1Up>: HandleUserMove(1) \n \
614 <Btn1Up>: HandleUserMove(0) \n \
615 <Btn1Motion>: AnimateUserMove() \n \
616 <Btn3Motion>: HandlePV() \n \
617 <Btn2Motion>: HandlePV() \n \
618 <Btn3Up>: PieceMenuPopup(menuB) \n \
619 <Btn2Up>: PieceMenuPopup(menuB) \n \
620 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
621 PieceMenuPopup(menuB) \n \
622 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
623 PieceMenuPopup(menuW) \n \
624 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
625 PieceMenuPopup(menuW) \n \
626 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
627 PieceMenuPopup(menuB) \n";
629 char whiteTranslations[] =
630 "Shift<BtnDown>: WhiteClock(1)\n \
631 <BtnDown>: WhiteClock(0)\n";
632 char blackTranslations[] =
633 "Shift<BtnDown>: BlackClock(1)\n \
634 <BtnDown>: BlackClock(0)\n";
636 char ICSInputTranslations[] =
637 "<Key>Up: UpKeyProc() \n "
638 "<Key>Down: DownKeyProc() \n "
639 "<Key>Return: EnterKeyProc() \n";
641 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
642 // as the widget is destroyed before the up-click can call extend-end
643 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
645 String xboardResources[] = {
646 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
651 /* Max possible square size */
652 #define MAXSQSIZE 256
654 static int xpm_avail[MAXSQSIZE];
656 #ifdef HAVE_DIR_STRUCT
658 /* Extract piece size from filename */
660 xpm_getsize (char *name, int len, char *ext)
668 if ((p=strchr(name, '.')) == NULL ||
669 StrCaseCmp(p+1, ext) != 0)
675 while (*p && isdigit(*p))
682 /* Setup xpm_avail */
684 xpm_getavail (char *dirname, char *ext)
690 for (i=0; i<MAXSQSIZE; ++i)
693 if (appData.debugMode)
694 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
696 dir = opendir(dirname);
699 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
700 programName, dirname);
704 while ((ent=readdir(dir)) != NULL) {
705 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
706 if (i > 0 && i < MAXSQSIZE)
716 xpm_print_avail (FILE *fp, char *ext)
720 fprintf(fp, _("Available `%s' sizes:\n"), ext);
721 for (i=1; i<MAXSQSIZE; ++i) {
727 /* Return XPM piecesize closest to size */
729 xpm_closest_to (char *dirname, int size, char *ext)
732 int sm_diff = MAXSQSIZE;
736 xpm_getavail(dirname, ext);
738 if (appData.debugMode)
739 xpm_print_avail(stderr, ext);
741 for (i=1; i<MAXSQSIZE; ++i) {
744 diff = (diff<0) ? -diff : diff;
745 if (diff < sm_diff) {
753 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
759 #else /* !HAVE_DIR_STRUCT */
760 /* If we are on a system without a DIR struct, we can't
761 read the directory, so we can't collect a list of
762 filenames, etc., so we can't do any size-fitting. */
764 xpm_closest_to (char *dirname, int size, char *ext)
767 Warning: No DIR structure found on this system --\n\
768 Unable to autosize for XPM/XIM pieces.\n\
769 Please report this error to %s.\n\
770 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
773 #endif /* HAVE_DIR_STRUCT */
776 /* Arrange to catch delete-window events */
777 Atom wm_delete_window;
779 CatchDeleteWindow (Widget w, String procname)
782 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
783 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
784 XtAugmentTranslations(w, XtParseTranslationTable(buf));
791 XtSetArg(args[0], XtNiconic, False);
792 XtSetValues(shellWidget, args, 1);
794 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
797 //---------------------------------------------------------------------------------------------------------
798 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
801 #define CW_USEDEFAULT (1<<31)
802 #define ICS_TEXT_MENU_SIZE 90
803 #define DEBUG_FILE "xboard.debug"
804 #define SetCurrentDirectory chdir
805 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
809 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
812 // front-end part of option handling
814 // [HGM] This platform-dependent table provides the location for storing the color info
815 extern char *crWhite, * crBlack;
819 &appData.whitePieceColor,
820 &appData.blackPieceColor,
821 &appData.lightSquareColor,
822 &appData.darkSquareColor,
823 &appData.highlightSquareColor,
824 &appData.premoveHighlightColor,
825 &appData.lowTimeWarningColor,
836 // [HGM] font: keep a font for each square size, even non-stndard ones
839 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
840 char *fontTable[NUM_FONTS][MAX_SIZE];
843 ParseFont (char *name, int number)
844 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
846 if(sscanf(name, "size%d:", &size)) {
847 // [HGM] font: font is meant for specific boardSize (likely from settings file);
848 // defer processing it until we know if it matches our board size
849 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
850 fontTable[number][size] = strdup(strchr(name, ':')+1);
851 fontValid[number][size] = True;
856 case 0: // CLOCK_FONT
857 appData.clockFont = strdup(name);
859 case 1: // MESSAGE_FONT
860 appData.font = strdup(name);
862 case 2: // COORD_FONT
863 appData.coordFont = strdup(name);
868 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
873 { // only 2 fonts currently
874 appData.clockFont = CLOCK_FONT_NAME;
875 appData.coordFont = COORD_FONT_NAME;
876 appData.font = DEFAULT_FONT_NAME;
881 { // no-op, until we identify the code for this already in XBoard and move it here
885 ParseColor (int n, char *name)
886 { // in XBoard, just copy the color-name string
887 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
891 ParseTextAttribs (ColorClass cc, char *s)
893 (&appData.colorShout)[cc] = strdup(s);
897 ParseBoardSize (void *addr, char *name)
899 appData.boardSize = strdup(name);
904 { // In XBoard the sound-playing program takes care of obtaining the actual sound
908 SetCommPortDefaults ()
909 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
912 // [HGM] args: these three cases taken out to stay in front-end
914 SaveFontArg (FILE *f, ArgDescriptor *ad)
917 int i, n = (int)(intptr_t)ad->argLoc;
919 case 0: // CLOCK_FONT
920 name = appData.clockFont;
922 case 1: // MESSAGE_FONT
925 case 2: // COORD_FONT
926 name = appData.coordFont;
931 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
932 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
933 fontTable[n][squareSize] = strdup(name);
934 fontValid[n][squareSize] = True;
937 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
938 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
943 { // nothing to do, as the sounds are at all times represented by their text-string names already
947 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
948 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
949 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
953 SaveColor (FILE *f, ArgDescriptor *ad)
954 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
955 if(colorVariable[(int)(intptr_t)ad->argLoc])
956 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
960 SaveBoardSize (FILE *f, char *name, void *addr)
961 { // wrapper to shield back-end from BoardSize & sizeInfo
962 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
966 ParseCommPortSettings (char *s)
967 { // no such option in XBoard (yet)
970 extern Widget engineOutputShell;
974 GetActualPlacement (Widget wg, WindowPlacement *wp)
979 XWindowAttributes winAt;
986 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
987 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
988 wp->x = rx - winAt.x;
989 wp->y = ry - winAt.y;
990 wp->height = winAt.height;
991 wp->width = winAt.width;
992 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
997 { // wrapper to shield use of window handles from back-end (make addressible by number?)
998 // In XBoard this will have to wait until awareness of window parameters is implemented
999 GetActualPlacement(shellWidget, &wpMain);
1000 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1001 if(MoveHistoryIsUp()) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
1002 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1003 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
1004 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
1005 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
1009 PrintCommPortSettings (FILE *f, char *name)
1010 { // This option does not exist in XBoard
1014 EnsureOnScreen (int *x, int *y, int minX, int minY)
1021 { // [HGM] args: allows testing if main window is realized from back-end
1022 return xBoardWindow != 0;
1026 PopUpStartupDialog ()
1027 { // start menu not implemented in XBoard
1031 ConvertToLine (int argc, char **argv)
1033 static char line[128*1024], buf[1024];
1037 for(i=1; i<argc; i++)
1039 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1040 && argv[i][0] != '{' )
1041 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1043 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1044 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1047 line[strlen(line)-1] = NULLCHAR;
1051 //--------------------------------------------------------------------------------------------
1054 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1056 #define BoardSize int
1058 InitDrawingSizes (BoardSize boardSize, int flags)
1059 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1060 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1062 XtGeometryResult gres;
1064 static Dimension oldWidth, oldHeight;
1065 static VariantClass oldVariant;
1066 static int oldDual = -1, oldMono = -1;
1068 if(!formWidget) return;
1070 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1071 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1072 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1074 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1076 * Enable shell resizing.
1078 shellArgs[0].value = (XtArgVal) &w;
1079 shellArgs[1].value = (XtArgVal) &h;
1080 XtGetValues(shellWidget, shellArgs, 2);
1082 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1083 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1084 XtSetValues(shellWidget, &shellArgs[2], 4);
1086 XtSetArg(args[0], XtNdefaultDistance, &sep);
1087 XtGetValues(formWidget, args, 1);
1089 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1091 hOffset = boardWidth + 10;
1092 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1093 secondSegments[i] = gridSegments[i];
1094 secondSegments[i].x1 += hOffset;
1095 secondSegments[i].x2 += hOffset;
1098 XtSetArg(args[0], XtNwidth, boardWidth);
1099 XtSetArg(args[1], XtNheight, boardHeight);
1100 XtSetValues(boardWidget, args, 2);
1102 timerWidth = (boardWidth - sep) / 2;
1103 XtSetArg(args[0], XtNwidth, timerWidth);
1104 XtSetValues(whiteTimerWidget, args, 1);
1105 XtSetValues(blackTimerWidget, args, 1);
1107 XawFormDoLayout(formWidget, False);
1109 if (appData.titleInWindow) {
1111 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1112 XtSetArg(args[i], XtNheight, &h); i++;
1113 XtGetValues(titleWidget, args, i);
1115 w = boardWidth - 2*bor;
1117 XtSetArg(args[0], XtNwidth, &w);
1118 XtGetValues(menuBarWidget, args, 1);
1119 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1122 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1123 if (gres != XtGeometryYes && appData.debugMode) {
1125 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1126 programName, gres, w, h, wr, hr);
1130 XawFormDoLayout(formWidget, True);
1133 * Inhibit shell resizing.
1135 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1136 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1137 shellArgs[4].value = shellArgs[2].value = w;
1138 shellArgs[5].value = shellArgs[3].value = h;
1139 XtSetValues(shellWidget, &shellArgs[0], 6);
1141 XSync(xDisplay, False);
1145 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1148 if(gameInfo.variant != oldVariant) { // and only if variant changed
1151 for(i=0; i<4; i++) {
1153 for(p=0; p<=(int)WhiteKing; p++)
1154 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1155 if(gameInfo.variant == VariantShogi) {
1156 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1157 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1158 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1159 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1160 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1163 if(gameInfo.variant == VariantGothic) {
1164 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1167 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1168 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1169 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1172 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1173 for(p=0; p<=(int)WhiteKing; p++)
1174 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1175 if(gameInfo.variant == VariantShogi) {
1176 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1177 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1178 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1179 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1180 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1183 if(gameInfo.variant == VariantGothic) {
1184 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1187 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1188 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1189 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1194 for(i=0; i<2; i++) {
1196 for(p=0; p<=(int)WhiteKing; p++)
1197 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1198 if(gameInfo.variant == VariantShogi) {
1199 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1200 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1201 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1202 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1203 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1206 if(gameInfo.variant == VariantGothic) {
1207 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1210 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1211 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1212 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1216 oldMono = -10; // kludge to force recreation of animation masks
1217 oldVariant = gameInfo.variant;
1220 if(appData.monoMode != oldMono)
1223 oldMono = appData.monoMode;
1228 MakeOneColor (char *name, Pixel *color)
1230 XrmValue vFrom, vTo;
1231 if (!appData.monoMode) {
1232 vFrom.addr = (caddr_t) name;
1233 vFrom.size = strlen(name);
1234 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1235 if (vTo.addr == NULL) {
1236 appData.monoMode = True;
1239 *color = *(Pixel *) vTo.addr;
1247 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1248 int forceMono = False;
1250 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1251 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1252 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1253 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1254 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1255 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1256 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1257 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1264 { // [HGM] taken out of main
1266 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1267 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1268 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1270 if (appData.bitmapDirectory[0] != NULLCHAR) {
1274 CreateXPMBoard(appData.liteBackTextureFile, 1);
1275 CreateXPMBoard(appData.darkBackTextureFile, 0);
1279 /* Create regular pieces */
1280 if (!useImages) CreatePieces();
1285 InitDrawingParams ()
1287 MakeColors(); CreateGCs(True);
1292 main (int argc, char **argv)
1294 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1295 XSetWindowAttributes window_attributes;
1297 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1298 XrmValue vFrom, vTo;
1299 XtGeometryResult gres;
1302 int forceMono = False;
1304 srandom(time(0)); // [HGM] book: make random truly random
1306 setbuf(stdout, NULL);
1307 setbuf(stderr, NULL);
1310 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1311 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1315 programName = strrchr(argv[0], '/');
1316 if (programName == NULL)
1317 programName = argv[0];
1322 XtSetLanguageProc(NULL, NULL, NULL);
1323 bindtextdomain(PACKAGE, LOCALEDIR);
1324 textdomain(PACKAGE);
1328 XtAppInitialize(&appContext, "XBoard", shellOptions,
1329 XtNumber(shellOptions),
1330 &argc, argv, xboardResources, NULL, 0);
1331 appData.boardSize = "";
1332 InitAppData(ConvertToLine(argc, argv));
1334 if (p == NULL) p = "/tmp";
1335 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1336 gameCopyFilename = (char*) malloc(i);
1337 gamePasteFilename = (char*) malloc(i);
1338 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1339 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1341 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1342 clientResources, XtNumber(clientResources),
1345 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1346 static char buf[MSG_SIZ];
1347 EscapeExpand(buf, appData.firstInitString);
1348 appData.firstInitString = strdup(buf);
1349 EscapeExpand(buf, appData.secondInitString);
1350 appData.secondInitString = strdup(buf);
1351 EscapeExpand(buf, appData.firstComputerString);
1352 appData.firstComputerString = strdup(buf);
1353 EscapeExpand(buf, appData.secondComputerString);
1354 appData.secondComputerString = strdup(buf);
1357 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1360 if (chdir(chessDir) != 0) {
1361 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1367 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1368 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1369 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1370 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1373 setbuf(debugFP, NULL);
1377 if (appData.debugMode) {
1378 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1382 /* [HGM,HR] make sure board size is acceptable */
1383 if(appData.NrFiles > BOARD_FILES ||
1384 appData.NrRanks > BOARD_RANKS )
1385 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1388 /* This feature does not work; animation needs a rewrite */
1389 appData.highlightDragging = FALSE;
1393 xDisplay = XtDisplay(shellWidget);
1394 xScreen = DefaultScreen(xDisplay);
1395 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1397 gameInfo.variant = StringToVariant(appData.variant);
1398 InitPosition(FALSE);
1401 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1403 if (isdigit(appData.boardSize[0])) {
1404 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1405 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1406 &fontPxlSize, &smallLayout, &tinyLayout);
1408 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1409 programName, appData.boardSize);
1413 /* Find some defaults; use the nearest known size */
1414 SizeDefaults *szd, *nearest;
1415 int distance = 99999;
1416 nearest = szd = sizeDefaults;
1417 while (szd->name != NULL) {
1418 if (abs(szd->squareSize - squareSize) < distance) {
1420 distance = abs(szd->squareSize - squareSize);
1421 if (distance == 0) break;
1425 if (i < 2) lineGap = nearest->lineGap;
1426 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1427 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1428 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1429 if (i < 6) smallLayout = nearest->smallLayout;
1430 if (i < 7) tinyLayout = nearest->tinyLayout;
1433 SizeDefaults *szd = sizeDefaults;
1434 if (*appData.boardSize == NULLCHAR) {
1435 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1436 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1439 if (szd->name == NULL) szd--;
1440 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1442 while (szd->name != NULL &&
1443 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1444 if (szd->name == NULL) {
1445 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1446 programName, appData.boardSize);
1450 squareSize = szd->squareSize;
1451 lineGap = szd->lineGap;
1452 clockFontPxlSize = szd->clockFontPxlSize;
1453 coordFontPxlSize = szd->coordFontPxlSize;
1454 fontPxlSize = szd->fontPxlSize;
1455 smallLayout = szd->smallLayout;
1456 tinyLayout = szd->tinyLayout;
1457 // [HGM] font: use defaults from settings file if available and not overruled
1459 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1460 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1461 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1462 appData.font = fontTable[MESSAGE_FONT][squareSize];
1463 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1464 appData.coordFont = fontTable[COORD_FONT][squareSize];
1466 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1467 if (strlen(appData.pixmapDirectory) > 0) {
1468 p = ExpandPathName(appData.pixmapDirectory);
1470 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1471 appData.pixmapDirectory);
1474 if (appData.debugMode) {
1475 fprintf(stderr, _("\
1476 XBoard square size (hint): %d\n\
1477 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1479 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1480 if (appData.debugMode) {
1481 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1484 defaultLineGap = lineGap;
1485 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1487 /* [HR] height treated separately (hacked) */
1488 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1489 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1490 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1491 XtSetArg(boardArgs[2], XtNheight, boardHeight);
1494 * Determine what fonts to use.
1497 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1498 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1499 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1500 fontSet = CreateFontSet(appData.font);
1501 clockFontSet = CreateFontSet(appData.clockFont);
1503 /* For the coordFont, use the 0th font of the fontset. */
1504 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1505 XFontStruct **font_struct_list;
1506 XFontSetExtents *fontSize;
1507 char **font_name_list;
1508 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1509 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1510 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1511 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1512 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1515 appData.font = FindFont(appData.font, fontPxlSize);
1516 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1517 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1518 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1519 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1520 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1521 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1523 countFontID = coordFontID; // [HGM] holdings
1524 countFontStruct = coordFontStruct;
1526 xdb = XtDatabase(xDisplay);
1528 XrmPutLineResource(&xdb, "*international: True");
1529 vTo.size = sizeof(XFontSet);
1530 vTo.addr = (XtPointer) &fontSet;
1531 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1533 XrmPutStringResource(&xdb, "*font", appData.font);
1537 * Detect if there are not enough colors available and adapt.
1539 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1540 appData.monoMode = True;
1543 forceMono = MakeColors();
1546 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1548 appData.monoMode = True;
1551 if (appData.lowTimeWarning && !appData.monoMode) {
1552 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1553 vFrom.size = strlen(appData.lowTimeWarningColor);
1554 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1555 if (vTo.addr == NULL)
1556 appData.monoMode = True;
1558 lowTimeWarningColor = *(Pixel *) vTo.addr;
1561 if (appData.monoMode && appData.debugMode) {
1562 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1563 (unsigned long) XWhitePixel(xDisplay, xScreen),
1564 (unsigned long) XBlackPixel(xDisplay, xScreen));
1567 ParseIcsTextColors();
1569 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1575 layoutName = "tinyLayout";
1576 } else if (smallLayout) {
1577 layoutName = "smallLayout";
1579 layoutName = "normalLayout";
1581 /* Outer layoutWidget is there only to provide a name for use in
1582 resources that depend on the layout style */
1584 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
1585 layoutArgs, XtNumber(layoutArgs));
1587 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
1588 formArgs, XtNumber(formArgs));
1589 XtSetArg(args[0], XtNdefaultDistance, &sep);
1590 XtGetValues(formWidget, args, 1);
1593 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
1594 XtSetArg(args[0], XtNtop, XtChainTop);
1595 XtSetArg(args[1], XtNbottom, XtChainTop);
1596 XtSetArg(args[2], XtNright, XtChainLeft);
1597 XtSetValues(menuBarWidget, args, 3);
1599 widgetList[j++] = whiteTimerWidget =
1600 XtCreateWidget("whiteTime", labelWidgetClass,
1601 formWidget, timerArgs, XtNumber(timerArgs));
1603 XtSetArg(args[0], XtNfontSet, clockFontSet);
1605 XtSetArg(args[0], XtNfont, clockFontStruct);
1607 XtSetArg(args[1], XtNtop, XtChainTop);
1608 XtSetArg(args[2], XtNbottom, XtChainTop);
1609 XtSetValues(whiteTimerWidget, args, 3);
1611 widgetList[j++] = blackTimerWidget =
1612 XtCreateWidget("blackTime", labelWidgetClass,
1613 formWidget, timerArgs, XtNumber(timerArgs));
1615 XtSetArg(args[0], XtNfontSet, clockFontSet);
1617 XtSetArg(args[0], XtNfont, clockFontStruct);
1619 XtSetArg(args[1], XtNtop, XtChainTop);
1620 XtSetArg(args[2], XtNbottom, XtChainTop);
1621 XtSetValues(blackTimerWidget, args, 3);
1623 if (appData.titleInWindow) {
1624 widgetList[j++] = titleWidget =
1625 XtCreateWidget("title", labelWidgetClass, formWidget,
1626 titleArgs, XtNumber(titleArgs));
1627 XtSetArg(args[0], XtNtop, XtChainTop);
1628 XtSetArg(args[1], XtNbottom, XtChainTop);
1629 XtSetValues(titleWidget, args, 2);
1632 if (appData.showButtonBar) {
1633 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
1634 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
1635 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
1636 XtSetArg(args[2], XtNtop, XtChainTop);
1637 XtSetArg(args[3], XtNbottom, XtChainTop);
1638 XtSetValues(buttonBarWidget, args, 4);
1641 widgetList[j++] = messageWidget =
1642 XtCreateWidget("message", labelWidgetClass, formWidget,
1643 messageArgs, XtNumber(messageArgs));
1644 XtSetArg(args[0], XtNtop, XtChainTop);
1645 XtSetArg(args[1], XtNbottom, XtChainTop);
1646 XtSetValues(messageWidget, args, 2);
1648 widgetList[j++] = boardWidget =
1649 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
1650 XtNumber(boardArgs));
1652 XtManageChildren(widgetList, j);
1654 timerWidth = (boardWidth - sep) / 2;
1655 XtSetArg(args[0], XtNwidth, timerWidth);
1656 XtSetValues(whiteTimerWidget, args, 1);
1657 XtSetValues(blackTimerWidget, args, 1);
1659 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1660 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1661 XtGetValues(whiteTimerWidget, args, 2);
1663 if (appData.showButtonBar) {
1664 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1665 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1666 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
1670 * formWidget uses these constraints but they are stored
1674 XtSetArg(args[i], XtNfromHoriz, 0); i++;
1675 XtSetValues(menuBarWidget, args, i);
1676 if (appData.titleInWindow) {
1679 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1680 XtSetValues(whiteTimerWidget, args, i);
1682 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1683 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1684 XtSetValues(blackTimerWidget, args, i);
1686 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1687 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
1688 XtSetValues(titleWidget, args, i);
1690 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1691 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1692 XtSetValues(messageWidget, args, i);
1693 if (appData.showButtonBar) {
1695 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1696 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1697 XtSetValues(buttonBarWidget, args, i);
1701 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1702 XtSetValues(whiteTimerWidget, args, i);
1704 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1705 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1706 XtSetValues(blackTimerWidget, args, i);
1708 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
1709 XtSetValues(titleWidget, args, i);
1711 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1712 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1713 XtSetValues(messageWidget, args, i);
1714 if (appData.showButtonBar) {
1716 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1717 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1718 XtSetValues(buttonBarWidget, args, i);
1723 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1724 XtSetValues(whiteTimerWidget, args, i);
1726 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1727 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1728 XtSetValues(blackTimerWidget, args, i);
1730 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1731 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1732 XtSetValues(messageWidget, args, i);
1733 if (appData.showButtonBar) {
1735 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1736 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1737 XtSetValues(buttonBarWidget, args, i);
1741 XtSetArg(args[0], XtNfromVert, messageWidget);
1742 XtSetArg(args[1], XtNtop, XtChainTop);
1743 XtSetArg(args[2], XtNbottom, XtChainBottom);
1744 XtSetArg(args[3], XtNleft, XtChainLeft);
1745 XtSetArg(args[4], XtNright, XtChainRight);
1746 XtSetValues(boardWidget, args, 5);
1748 XtRealizeWidget(shellWidget);
1751 XtSetArg(args[0], XtNx, wpMain.x);
1752 XtSetArg(args[1], XtNy, wpMain.y);
1753 XtSetValues(shellWidget, args, 2);
1757 * Correct the width of the message and title widgets.
1758 * It is not known why some systems need the extra fudge term.
1759 * The value "2" is probably larger than needed.
1761 XawFormDoLayout(formWidget, False);
1763 #define WIDTH_FUDGE 2
1765 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1766 XtSetArg(args[i], XtNheight, &h); i++;
1767 XtGetValues(messageWidget, args, i);
1768 if (appData.showButtonBar) {
1770 XtSetArg(args[i], XtNwidth, &w); i++;
1771 XtGetValues(buttonBarWidget, args, i);
1772 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1774 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
1777 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1778 if (gres != XtGeometryYes && appData.debugMode) {
1779 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1780 programName, gres, w, h, wr, hr);
1783 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
1784 /* The size used for the child widget in layout lags one resize behind
1785 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
1787 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1788 if (gres != XtGeometryYes && appData.debugMode) {
1789 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1790 programName, gres, w, h, wr, hr);
1793 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
1794 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
1795 XtSetArg(args[1], XtNright, XtChainRight);
1796 XtSetValues(messageWidget, args, 2);
1798 if (appData.titleInWindow) {
1800 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1801 XtSetArg(args[i], XtNheight, &h); i++;
1802 XtGetValues(titleWidget, args, i);
1804 w = boardWidth - 2*bor;
1806 XtSetArg(args[0], XtNwidth, &w);
1807 XtGetValues(menuBarWidget, args, 1);
1808 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1811 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1812 if (gres != XtGeometryYes && appData.debugMode) {
1814 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1815 programName, gres, w, h, wr, hr);
1818 XawFormDoLayout(formWidget, True);
1820 xBoardWindow = XtWindow(boardWidget);
1822 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1823 // not need to go into InitDrawingSizes().
1827 * Create X checkmark bitmap and initialize option menu checks.
1829 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1830 checkmark_bits, checkmark_width, checkmark_height);
1836 ReadBitmap(&wIconPixmap, "icon_white.bm",
1837 icon_white_bits, icon_white_width, icon_white_height);
1838 ReadBitmap(&bIconPixmap, "icon_black.bm",
1839 icon_black_bits, icon_black_width, icon_black_height);
1840 iconPixmap = wIconPixmap;
1842 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1843 XtSetValues(shellWidget, args, i);
1846 * Create a cursor for the board widget.
1848 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1849 XChangeWindowAttributes(xDisplay, xBoardWindow,
1850 CWCursor, &window_attributes);
1853 * Inhibit shell resizing.
1855 shellArgs[0].value = (XtArgVal) &w;
1856 shellArgs[1].value = (XtArgVal) &h;
1857 XtGetValues(shellWidget, shellArgs, 2);
1858 shellArgs[4].value = shellArgs[2].value = w;
1859 shellArgs[5].value = shellArgs[3].value = h;
1860 XtSetValues(shellWidget, &shellArgs[2], 4);
1861 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1862 marginH = h - boardHeight;
1864 CatchDeleteWindow(shellWidget, "QuitProc");
1872 if (appData.animate || appData.animateDragging)
1875 XtAugmentTranslations(formWidget,
1876 XtParseTranslationTable(globalTranslations));
1877 XtAugmentTranslations(boardWidget,
1878 XtParseTranslationTable(boardTranslations));
1879 XtAugmentTranslations(whiteTimerWidget,
1880 XtParseTranslationTable(whiteTranslations));
1881 XtAugmentTranslations(blackTimerWidget,
1882 XtParseTranslationTable(blackTranslations));
1884 /* Why is the following needed on some versions of X instead
1885 * of a translation? */
1886 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
1887 (XtEventHandler) EventProc, NULL);
1889 XtAddEventHandler(formWidget, KeyPressMask, False,
1890 (XtEventHandler) MoveTypeInProc, NULL);
1891 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1892 (XtEventHandler) EventProc, NULL);
1894 /* [AS] Restore layout */
1895 if( wpMoveHistory.visible ) {
1899 if( wpEvalGraph.visible )
1904 if( wpEngineOutput.visible ) {
1905 EngineOutputPopUp();
1910 if (errorExitStatus == -1) {
1911 if (appData.icsActive) {
1912 /* We now wait until we see "login:" from the ICS before
1913 sending the logon script (problems with timestamp otherwise) */
1914 /*ICSInitScript();*/
1915 if (appData.icsInputBox) ICSInputBoxPopUp();
1919 signal(SIGWINCH, TermSizeSigHandler);
1921 signal(SIGINT, IntSigHandler);
1922 signal(SIGTERM, IntSigHandler);
1923 if (*appData.cmailGameName != NULLCHAR) {
1924 signal(SIGUSR1, CmailSigHandler);
1928 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1930 // XtSetKeyboardFocus(shellWidget, formWidget);
1931 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1933 XtAppMainLoop(appContext);
1934 if (appData.debugMode) fclose(debugFP); // [DM] debug
1939 TermSizeSigHandler (int sig)
1945 IntSigHandler (int sig)
1951 CmailSigHandler (int sig)
1956 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1958 /* Activate call-back function CmailSigHandlerCallBack() */
1959 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1961 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1965 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1968 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1970 /**** end signal code ****/
1973 #define Abs(n) ((n)<0 ? -(n) : (n))
1977 InsertPxlSize (char *pattern, int targetPxlSize)
1979 char *base_fnt_lst, strInt[12], *p, *q;
1980 int alternatives, i, len, strIntLen;
1983 * Replace the "*" (if present) in the pixel-size slot of each
1984 * alternative with the targetPxlSize.
1988 while ((p = strchr(p, ',')) != NULL) {
1992 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1993 strIntLen = strlen(strInt);
1994 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1998 while (alternatives--) {
1999 char *comma = strchr(p, ',');
2000 for (i=0; i<14; i++) {
2001 char *hyphen = strchr(p, '-');
2003 if (comma && hyphen > comma) break;
2004 len = hyphen + 1 - p;
2005 if (i == 7 && *p == '*' && len == 2) {
2007 memcpy(q, strInt, strIntLen);
2017 len = comma + 1 - p;
2024 return base_fnt_lst;
2028 CreateFontSet (char *base_fnt_lst)
2031 char **missing_list;
2035 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
2036 &missing_list, &missing_count, &def_string);
2037 if (appData.debugMode) {
2039 XFontStruct **font_struct_list;
2040 char **font_name_list;
2041 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
2043 fprintf(debugFP, " got list %s, locale %s\n",
2044 XBaseFontNameListOfFontSet(fntSet),
2045 XLocaleOfFontSet(fntSet));
2046 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
2047 for (i = 0; i < count; i++) {
2048 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
2051 for (i = 0; i < missing_count; i++) {
2052 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
2055 if (fntSet == NULL) {
2056 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
2061 #else // not ENABLE_NLS
2063 * Find a font that matches "pattern" that is as close as
2064 * possible to the targetPxlSize. Prefer fonts that are k
2065 * pixels smaller to fonts that are k pixels larger. The
2066 * pattern must be in the X Consortium standard format,
2067 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2068 * The return value should be freed with XtFree when no
2072 FindFont (char *pattern, int targetPxlSize)
2074 char **fonts, *p, *best, *scalable, *scalableTail;
2075 int i, j, nfonts, minerr, err, pxlSize;
2077 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2079 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2080 programName, pattern);
2087 for (i=0; i<nfonts; i++) {
2090 if (*p != '-') continue;
2092 if (*p == NULLCHAR) break;
2093 if (*p++ == '-') j++;
2095 if (j < 7) continue;
2098 scalable = fonts[i];
2101 err = pxlSize - targetPxlSize;
2102 if (Abs(err) < Abs(minerr) ||
2103 (minerr > 0 && err < 0 && -err == minerr)) {
2109 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2110 /* If the error is too big and there is a scalable font,
2111 use the scalable font. */
2112 int headlen = scalableTail - scalable;
2113 p = (char *) XtMalloc(strlen(scalable) + 10);
2114 while (isdigit(*scalableTail)) scalableTail++;
2115 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2117 p = (char *) XtMalloc(strlen(best) + 2);
2118 safeStrCpy(p, best, strlen(best)+1 );
2120 if (appData.debugMode) {
2121 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2122 pattern, targetPxlSize, p);
2124 XFreeFontNames(fonts);
2131 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
2132 // must be called before all non-first callse to CreateGCs()
2133 XtReleaseGC(shellWidget, highlineGC);
2134 XtReleaseGC(shellWidget, lightSquareGC);
2135 XtReleaseGC(shellWidget, darkSquareGC);
2136 XtReleaseGC(shellWidget, lineGC);
2137 if (appData.monoMode) {
2138 if (DefaultDepth(xDisplay, xScreen) == 1) {
2139 XtReleaseGC(shellWidget, wbPieceGC);
2141 XtReleaseGC(shellWidget, bwPieceGC);
2144 XtReleaseGC(shellWidget, prelineGC);
2145 XtReleaseGC(shellWidget, wdPieceGC);
2146 XtReleaseGC(shellWidget, wlPieceGC);
2147 XtReleaseGC(shellWidget, bdPieceGC);
2148 XtReleaseGC(shellWidget, blPieceGC);
2153 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
2155 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2156 | GCBackground | GCFunction | GCPlaneMask;
2157 gc_values->foreground = foreground;
2158 gc_values->background = background;
2159 return XtGetGC(shellWidget, value_mask, gc_values);
2163 CreateGCs (int redo)
2165 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2166 | GCBackground | GCFunction | GCPlaneMask;
2167 XGCValues gc_values;
2169 Pixel white = XWhitePixel(xDisplay, xScreen);
2170 Pixel black = XBlackPixel(xDisplay, xScreen);
2172 gc_values.plane_mask = AllPlanes;
2173 gc_values.line_width = lineGap;
2174 gc_values.line_style = LineSolid;
2175 gc_values.function = GXcopy;
2178 DeleteGCs(); // called a second time; clean up old GCs first
2179 } else { // [HGM] grid and font GCs created on first call only
2180 coordGC = CreateOneGC(&gc_values, black, white);
2181 XSetFont(xDisplay, coordGC, coordFontID);
2183 // [HGM] make font for holdings counts (white on black)
2184 countGC = CreateOneGC(&gc_values, white, black);
2185 XSetFont(xDisplay, countGC, countFontID);
2187 lineGC = CreateOneGC(&gc_values, black, black);
2189 if (appData.monoMode) {
2191 highlineGC = CreateOneGC(&gc_values, white, white);
2192 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
2193 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
2195 if (DefaultDepth(xDisplay, xScreen) == 1) {
2196 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2197 gc_values.function = GXcopyInverted;
2198 copyInvertedGC = CreateOneGC(&gc_values, black, white);
2199 gc_values.function = GXcopy;
2200 if (XBlackPixel(xDisplay, xScreen) == 1) {
2201 bwPieceGC = darkSquareGC;
2202 wbPieceGC = copyInvertedGC;
2204 bwPieceGC = copyInvertedGC;
2205 wbPieceGC = lightSquareGC;
2210 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
2211 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
2212 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
2213 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
2214 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
2215 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
2216 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
2217 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
2222 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
2230 fp = fopen(filename, "rb");
2232 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
2239 for (y=0; y<h; ++y) {
2240 for (x=0; x<h; ++x) {
2245 XPutPixel(xim, x, y, blackPieceColor);
2247 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2250 XPutPixel(xim, x, y, darkSquareColor);
2252 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2255 XPutPixel(xim, x, y, whitePieceColor);
2257 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2260 XPutPixel(xim, x, y, lightSquareColor);
2262 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2270 /* create Pixmap of piece */
2271 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2273 XPutImage(xDisplay, *dest, lightSquareGC, xim,
2276 /* create Pixmap of clipmask
2277 Note: We assume the white/black pieces have the same
2278 outline, so we make only 6 masks. This is okay
2279 since the XPM clipmask routines do the same. */
2281 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2283 XPutImage(xDisplay, temp, lightSquareGC, xmask,
2286 /* now create the 1-bit version */
2287 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2290 values.foreground = 1;
2291 values.background = 0;
2293 /* Don't use XtGetGC, not read only */
2294 maskGC = XCreateGC(xDisplay, *mask,
2295 GCForeground | GCBackground, &values);
2296 XCopyPlane(xDisplay, temp, *mask, maskGC,
2297 0, 0, squareSize, squareSize, 0, 0, 1);
2298 XFreePixmap(xDisplay, temp);
2303 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
2311 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
2316 /* The XSynchronize calls were copied from CreatePieces.
2317 Not sure if needed, but can't hurt */
2318 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2321 /* temp needed by loadXIM() */
2322 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2323 0, 0, ss, ss, AllPlanes, XYPixmap);
2325 if (strlen(appData.pixmapDirectory) == 0) {
2329 if (appData.monoMode) {
2330 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
2334 fprintf(stderr, _("\nLoading XIMs...\n"));
2336 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2337 fprintf(stderr, "%d", piece+1);
2338 for (kind=0; kind<4; kind++) {
2339 fprintf(stderr, ".");
2340 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2341 ExpandPathName(appData.pixmapDirectory),
2342 piece <= (int) WhiteKing ? "" : "w",
2343 pieceBitmapNames[piece],
2345 ximPieceBitmap[kind][piece] =
2346 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2347 0, 0, ss, ss, AllPlanes, XYPixmap);
2348 if (appData.debugMode)
2349 fprintf(stderr, _("(File:%s:) "), buf);
2350 loadXIM(ximPieceBitmap[kind][piece],
2352 &(xpmPieceBitmap2[kind][piece]),
2353 &(ximMaskPm2[piece]));
2354 if(piece <= (int)WhiteKing)
2355 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2357 fprintf(stderr," ");
2359 /* Load light and dark squares */
2360 /* If the LSQ and DSQ pieces don't exist, we will
2361 draw them with solid squares. */
2362 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2363 if (access(buf, 0) != 0) {
2367 fprintf(stderr, _("light square "));
2369 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2370 0, 0, ss, ss, AllPlanes, XYPixmap);
2371 if (appData.debugMode)
2372 fprintf(stderr, _("(File:%s:) "), buf);
2374 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2375 fprintf(stderr, _("dark square "));
2376 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2377 ExpandPathName(appData.pixmapDirectory), ss);
2378 if (appData.debugMode)
2379 fprintf(stderr, _("(File:%s:) "), buf);
2381 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2382 0, 0, ss, ss, AllPlanes, XYPixmap);
2383 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2384 xpmJailSquare = xpmLightSquare;
2386 fprintf(stderr, _("Done.\n"));
2388 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2391 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2395 CreateXPMBoard (char *s, int kind)
2399 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2400 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2401 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2407 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2408 // thisroutine has to be called t free the old piece pixmaps
2410 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2411 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2413 XFreePixmap(xDisplay, xpmLightSquare);
2414 XFreePixmap(xDisplay, xpmDarkSquare);
2423 u_int ss = squareSize;
2425 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2426 XpmColorSymbol symbols[4];
2427 static int redo = False;
2429 if(redo) FreeXPMPieces(); else redo = 1;
2431 /* The XSynchronize calls were copied from CreatePieces.
2432 Not sure if needed, but can't hurt */
2433 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2435 /* Setup translations so piece colors match square colors */
2436 symbols[0].name = "light_piece";
2437 symbols[0].value = appData.whitePieceColor;
2438 symbols[1].name = "dark_piece";
2439 symbols[1].value = appData.blackPieceColor;
2440 symbols[2].name = "light_square";
2441 symbols[2].value = appData.lightSquareColor;
2442 symbols[3].name = "dark_square";
2443 symbols[3].value = appData.darkSquareColor;
2445 attr.valuemask = XpmColorSymbols;
2446 attr.colorsymbols = symbols;
2447 attr.numsymbols = 4;
2449 if (appData.monoMode) {
2450 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2454 if (strlen(appData.pixmapDirectory) == 0) {
2455 XpmPieces* pieces = builtInXpms;
2458 while (pieces->size != squareSize && pieces->size) pieces++;
2459 if (!pieces->size) {
2460 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2463 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2464 for (kind=0; kind<4; kind++) {
2466 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2467 pieces->xpm[piece][kind],
2468 &(xpmPieceBitmap2[kind][piece]),
2469 NULL, &attr)) != 0) {
2470 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2474 if(piece <= (int) WhiteKing)
2475 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2479 xpmJailSquare = xpmLightSquare;
2483 fprintf(stderr, _("\nLoading XPMs...\n"));
2486 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2487 fprintf(stderr, "%d ", piece+1);
2488 for (kind=0; kind<4; kind++) {
2489 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2490 ExpandPathName(appData.pixmapDirectory),
2491 piece > (int) WhiteKing ? "w" : "",
2492 pieceBitmapNames[piece],
2494 if (appData.debugMode) {
2495 fprintf(stderr, _("(File:%s:) "), buf);
2497 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2498 &(xpmPieceBitmap2[kind][piece]),
2499 NULL, &attr)) != 0) {
2500 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2501 // [HGM] missing: read of unorthodox piece failed; substitute King.
2502 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2503 ExpandPathName(appData.pixmapDirectory),
2505 if (appData.debugMode) {
2506 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2508 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2509 &(xpmPieceBitmap2[kind][piece]),
2513 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2518 if(piece <= (int) WhiteKing)
2519 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2522 /* Load light and dark squares */
2523 /* If the LSQ and DSQ pieces don't exist, we will
2524 draw them with solid squares. */
2525 fprintf(stderr, _("light square "));
2526 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2527 if (access(buf, 0) != 0) {
2531 if (appData.debugMode)
2532 fprintf(stderr, _("(File:%s:) "), buf);
2534 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2535 &xpmLightSquare, NULL, &attr)) != 0) {
2536 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2539 fprintf(stderr, _("dark square "));
2540 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2541 ExpandPathName(appData.pixmapDirectory), ss);
2542 if (appData.debugMode) {
2543 fprintf(stderr, _("(File:%s:) "), buf);
2545 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2546 &xpmDarkSquare, NULL, &attr)) != 0) {
2547 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2551 xpmJailSquare = xpmLightSquare;
2552 fprintf(stderr, _("Done.\n"));
2554 oldVariant = -1; // kludge to force re-makig of animation masks
2555 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2558 #endif /* HAVE_LIBXPM */
2561 /* No built-in bitmaps */
2566 u_int ss = squareSize;
2568 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2571 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2572 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2573 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2574 pieceBitmapNames[piece],
2575 ss, kind == SOLID ? 's' : 'o');
2576 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2577 if(piece <= (int)WhiteKing)
2578 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2582 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2586 /* With built-in bitmaps */
2590 BuiltInBits* bib = builtInBits;
2593 u_int ss = squareSize;
2595 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2598 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2600 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2601 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2602 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2603 pieceBitmapNames[piece],
2604 ss, kind == SOLID ? 's' : 'o');
2605 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2606 bib->bits[kind][piece], ss, ss);
2607 if(piece <= (int)WhiteKing)
2608 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2612 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2618 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2623 char msg[MSG_SIZ], fullname[MSG_SIZ];
2625 if (*appData.bitmapDirectory != NULLCHAR) {
2626 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2627 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2628 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2629 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2630 &w, &h, pm, &x_hot, &y_hot);
2631 fprintf(stderr, "load %s\n", name);
2632 if (errcode != BitmapSuccess) {
2634 case BitmapOpenFailed:
2635 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2637 case BitmapFileInvalid:
2638 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2640 case BitmapNoMemory:
2641 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2645 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2649 fprintf(stderr, _("%s: %s...using built-in\n"),
2651 } else if (w != wreq || h != hreq) {
2653 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2654 programName, fullname, w, h, wreq, hreq);
2660 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2670 if (lineGap == 0) return;
2672 /* [HR] Split this into 2 loops for non-square boards. */
2674 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2675 gridSegments[i].x1 = 0;
2676 gridSegments[i].x2 =
2677 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2678 gridSegments[i].y1 = gridSegments[i].y2
2679 = lineGap / 2 + (i * (squareSize + lineGap));
2682 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2683 gridSegments[j + i].y1 = 0;
2684 gridSegments[j + i].y2 =
2685 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2686 gridSegments[j + i].x1 = gridSegments[j + i].x2
2687 = lineGap / 2 + (j * (squareSize + lineGap));
2691 int nrOfMenuItems = 7;
2692 Widget menuWidget[150];
2693 MenuListItem menuItemList[150] = {
2694 { "LoadNextGameProc", LoadNextGameProc },
2695 { "LoadPrevGameProc", LoadPrevGameProc },
2696 { "ReloadGameProc", ReloadGameProc },
2697 { "ReloadPositionProc", ReloadPositionProc },
2698 #ifndef OPTIONSDIALOG
2699 { "AlwaysQueenProc", AlwaysQueenProc },
2700 { "AnimateDraggingProc", AnimateDraggingProc },
2701 { "AnimateMovingProc", AnimateMovingProc },
2702 { "AutoflagProc", AutoflagProc },
2703 { "AutoflipProc", AutoflipProc },
2704 { "BlindfoldProc", BlindfoldProc },
2705 { "FlashMovesProc", FlashMovesProc },
2707 { "HighlightDraggingProc", HighlightDraggingProc },
2709 { "HighlightLastMoveProc", HighlightLastMoveProc },
2710 // { "IcsAlarmProc", IcsAlarmProc },
2711 { "MoveSoundProc", MoveSoundProc },
2712 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
2713 { "PopupExitMessageProc", PopupExitMessageProc },
2714 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
2715 // { "PremoveProc", PremoveProc },
2716 { "ShowCoordsProc", ShowCoordsProc },
2717 { "ShowThinkingProc", ShowThinkingProc },
2718 { "HideThinkingProc", HideThinkingProc },
2719 { "TestLegalityProc", TestLegalityProc },
2721 { "AboutGameProc", AboutGameEvent },
2722 { "DebugProc", DebugProc },
2723 { "NothingProc", NothingProc },
2728 MarkMenuItem (char *menuRef, int state)
2730 int nr = MenuToNumber(menuRef);
2733 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2734 XtSetValues(menuWidget[nr], args, 1);
2739 EnableMenuItem (char *menuRef, int state)
2741 int nr = MenuToNumber(menuRef);
2742 if(nr >= 0) XtSetSensitive(menuWidget[nr], state);
2746 EnableButtonBar (int state)
2748 XtSetSensitive(buttonBarWidget, state);
2753 SetMenuEnables (Enables *enab)
2755 while (enab->name != NULL) {
2756 EnableMenuItem(enab->name, enab->value);
2762 Equal(char *p, char *s)
2763 { // compare strings skipping spaces in second
2765 if(*s == ' ') { s++; continue; }
2766 if(*s++ != *p++) return 0;
2772 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2773 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2775 if(*nprms == 0) return;
2776 for(i=0; menuItemList[i].name; i++) {
2777 if(Equal(prms[0], menuItemList[i].name)) {
2778 (menuItemList[i].proc) ();
2785 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
2787 MenuProc *proc = (MenuProc *) addr;
2793 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2795 RecentEngineEvent((int) (intptr_t) addr);
2798 // some stuff that must remain in front-end
2799 static Widget mainBar, currentMenu;
2800 static int wtot, nr = 0, widths[10];
2803 AppendMenuItem (char *text, char *name, MenuProc *action)
2810 XtSetArg(args[j], XtNleftMargin, 20); j++;
2811 XtSetArg(args[j], XtNrightMargin, 20); j++;
2813 if (strcmp(text, "----") == 0) {
2814 entry = XtCreateManagedWidget(text, smeLineObjectClass,
2815 currentMenu, args, j);
2817 XtSetArg(args[j], XtNlabel, XtNewString(_(text)));
2818 entry = XtCreateManagedWidget(name, smeBSBObjectClass,
2819 currentMenu, args, j+1);
2820 XtAddCallback(entry, XtNcallback,
2821 (XtCallbackProc) (strcmp(name, "recent") ? MenuBarSelect : MenuEngineSelect),
2823 menuWidget[nrOfMenuItems] = entry;
2828 CreateMenuButton (char *name, Menu *mb)
2829 { // create menu button on main bar, and shell for pull-down list
2835 XtSetArg(args[j], XtNmenuName, XtNewString(name)); j++;
2836 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
2837 XtSetArg(args[j], XtNborderWidth, 0); j++;
2838 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
2840 currentMenu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2843 XtSetArg(args[j], XtNwidth, &w); j++;
2844 XtGetValues(mb->subMenu, args, j);
2845 wtot += mb->textWidth = widths[nr++] = w;
2849 CreateMenuBar (Menu *mb, int boardWidth)
2853 char menuName[MSG_SIZ];
2857 // create bar itself
2859 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2860 XtSetArg(args[j], XtNvSpace, 0); j++;
2861 XtSetArg(args[j], XtNborderWidth, 0); j++;
2862 mainBar = XtCreateWidget("menuBar", boxWidgetClass,
2863 formWidget, args, j);
2865 CreateMainMenus(mb); // put menus in bar according to description in back-end
2867 // size buttons to make menu bar fit, clipping menu names where necessary
2868 while(wtot > boardWidth - 40) {
2870 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
2874 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
2876 XtSetArg(args[j], XtNwidth, widths[i]); j++;
2877 XtSetValues(ma[i].subMenu, args, j);
2884 CreateButtonBar (MenuItem *mi)
2887 Widget button, buttonBar;
2891 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2893 XtSetArg(args[j], XtNhSpace, 0); j++;
2895 XtSetArg(args[j], XtNborderWidth, 0); j++;
2896 XtSetArg(args[j], XtNvSpace, 0); j++;
2897 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
2898 formWidget, args, j);
2900 while (mi->string != NULL) {
2903 XtSetArg(args[j], XtNinternalWidth, 2); j++;
2904 XtSetArg(args[j], XtNborderWidth, 0); j++;
2906 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
2907 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
2908 buttonBar, args, j);
2909 XtAddCallback(button, XtNcallback,
2910 (XtCallbackProc) MenuBarSelect,
2911 (caddr_t) mi->proc);
2918 CreatePieceMenu (char *name, int color)
2923 ChessSquare selection;
2925 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2926 boardWidget, args, 0);
2928 for (i = 0; i < PIECE_MENU_SIZE; i++) {
2929 String item = pieceMenuStrings[color][i];
2931 if (strcmp(item, "----") == 0) {
2932 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2935 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2936 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2938 selection = pieceMenuTranslation[color][i];
2939 XtAddCallback(entry, XtNcallback,
2940 (XtCallbackProc) PieceMenuSelect,
2941 (caddr_t) selection);
2942 if (selection == WhitePawn || selection == BlackPawn) {
2943 XtSetArg(args[0], XtNpopupOnEntry, entry);
2944 XtSetValues(menu, args, 1);
2957 ChessSquare selection;
2959 whitePieceMenu = CreatePieceMenu("menuW", 0);
2960 blackPieceMenu = CreatePieceMenu("menuB", 1);
2962 if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
2963 XtRegisterGrabAction(PieceMenuPopup, True,
2964 (unsigned)(ButtonPressMask|ButtonReleaseMask),
2965 GrabModeAsync, GrabModeAsync);
2967 XtSetArg(args[0], XtNlabel, _("Drop"));
2968 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
2969 boardWidget, args, 1);
2970 for (i = 0; i < DROP_MENU_SIZE; i++) {
2971 String item = dropMenuStrings[i];
2973 if (strcmp(item, "----") == 0) {
2974 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2977 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2978 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2980 selection = dropMenuTranslation[i];
2981 XtAddCallback(entry, XtNcallback,
2982 (XtCallbackProc) DropMenuSelect,
2983 (caddr_t) selection);
2997 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2998 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2999 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3000 dmEnables[i].piece);
3001 XtSetSensitive(entry, p != NULL || !appData.testLegality
3002 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3003 && !appData.icsActive));
3005 while (p && *p++ == dmEnables[i].piece) count++;
3006 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3008 XtSetArg(args[j], XtNlabel, label); j++;
3009 XtSetValues(entry, args, j);
3014 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
3016 String whichMenu; int menuNr = -2;
3017 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3018 if (event->type == ButtonRelease)
3019 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3020 else if (event->type == ButtonPress)
3021 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3023 case 0: whichMenu = params[0]; break;
3024 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3026 case -1: ErrorPopDown();
3029 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3033 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3035 if (pmFromX < 0 || pmFromY < 0) return;
3036 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3040 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3042 if (pmFromX < 0 || pmFromY < 0) return;
3043 DropMenuEvent(piece, pmFromX, pmFromY);
3047 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3049 shiftKey = prms[0][0] & 1;
3054 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3056 shiftKey = prms[0][0] & 1;
3062 do_flash_delay (unsigned long msec)
3068 DrawBorder (int x, int y, int type)
3072 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
3074 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3075 squareSize+lineGap, squareSize+lineGap);
3079 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
3081 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
3082 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
3084 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
3085 if(textureW[kind] < W*squareSize)
3086 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
3088 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
3089 if(textureH[kind] < H*squareSize)
3090 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
3092 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
3097 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
3098 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
3100 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
3101 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
3102 squareSize, squareSize, x*fac, y*fac);
3104 if (useImages && useImageSqs) {
3108 pm = xpmLightSquare;
3113 case 2: /* neutral */
3115 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
3118 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3119 squareSize, squareSize, x*fac, y*fac);
3129 case 2: /* neutral */
3134 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
3139 I split out the routines to draw a piece so that I could
3140 make a generic flash routine.
3143 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3145 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3146 switch (square_color) {
3148 case 2: /* neutral */
3150 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3151 ? *pieceToOutline(piece)
3152 : *pieceToSolid(piece),
3153 dest, bwPieceGC, 0, 0,
3154 squareSize, squareSize, x, y);
3157 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3158 ? *pieceToSolid(piece)
3159 : *pieceToOutline(piece),
3160 dest, wbPieceGC, 0, 0,
3161 squareSize, squareSize, x, y);
3167 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3169 switch (square_color) {
3171 case 2: /* neutral */
3173 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3174 ? *pieceToOutline(piece)
3175 : *pieceToSolid(piece),
3176 dest, bwPieceGC, 0, 0,
3177 squareSize, squareSize, x, y, 1);
3180 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3181 ? *pieceToSolid(piece)
3182 : *pieceToOutline(piece),
3183 dest, wbPieceGC, 0, 0,
3184 squareSize, squareSize, x, y, 1);
3190 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3192 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
3193 switch (square_color) {
3195 XCopyPlane(xDisplay, *pieceToSolid(piece),
3196 dest, (int) piece < (int) BlackPawn
3197 ? wlPieceGC : blPieceGC, 0, 0,
3198 squareSize, squareSize, x, y, 1);
3201 XCopyPlane(xDisplay, *pieceToSolid(piece),
3202 dest, (int) piece < (int) BlackPawn
3203 ? wdPieceGC : bdPieceGC, 0, 0,
3204 squareSize, squareSize, x, y, 1);
3206 case 2: /* neutral */
3208 break; // should never contain pieces
3213 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3215 int kind, p = piece;
3217 switch (square_color) {
3219 case 2: /* neutral */
3221 if ((int)piece < (int) BlackPawn) {
3229 if ((int)piece < (int) BlackPawn) {
3237 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
3238 if(useTexture & square_color+1) {
3239 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
3240 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
3241 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
3242 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
3243 XSetClipMask(xDisplay, wlPieceGC, None);
3244 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
3246 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3247 dest, wlPieceGC, 0, 0,
3248 squareSize, squareSize, x, y);
3251 typedef void (*DrawFunc)();
3256 if (appData.monoMode) {
3257 if (DefaultDepth(xDisplay, xScreen) == 1) {
3258 return monoDrawPiece_1bit;
3260 return monoDrawPiece;
3264 return colorDrawPieceImage;
3266 return colorDrawPiece;
3271 DrawDot (int marker, int x, int y, int r)
3273 if(appData.monoMode) {
3274 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
3275 x, y, r, r, 0, 64*360);
3276 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
3277 x, y, r, r, 0, 64*360);
3279 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
3280 x, y, r, r, 0, 64*360);
3284 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
3285 { // basic front-end board-draw function: takes care of everything that can be in square:
3286 // piece, background, coordinate/count, marker dot
3287 int direction, font_ascent, font_descent;
3288 XCharStruct overall;
3291 if (piece == EmptySquare) {
3292 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
3294 drawfunc = ChooseDrawFunc();
3295 drawfunc(piece, square_color, x, y, xBoardWindow);
3298 if(align) { // square carries inscription (coord or piece count)
3300 GC hGC = align < 3 ? coordGC : countGC;
3301 // first calculate where it goes
3302 XTextExtents(countFontStruct, string, 1, &direction,
3303 &font_ascent, &font_descent, &overall);
3305 xx += squareSize - overall.width - 2;
3306 yy += squareSize - font_descent - 1;
3307 } else if (align == 2) {
3308 xx += 2, yy += font_ascent + 1;
3309 } else if (align == 3) {
3310 xx += squareSize - overall.width - 2;
3311 yy += font_ascent + 1;
3312 } else if (align == 4) {
3313 xx += 2, yy += font_ascent + 1;
3316 if (appData.monoMode) {
3317 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3319 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3323 if(marker) { // print fat marker dot, if requested
3324 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
3329 FlashDelay (int flash_delay)
3331 XSync(xDisplay, False);
3332 if(flash_delay) do_flash_delay(flash_delay);
3336 Fraction (int x, int start, int stop)
3338 double f = ((double) x - start)/(stop - start);
3339 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
3343 static WindowPlacement wpNew;
3346 CoDrag (Widget sh, WindowPlacement *wp)
3349 int j=0, touch=0, fudge = 2;
3350 GetActualPlacement(sh, wp);
3351 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
3352 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
3353 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
3354 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
3355 if(!touch ) return; // only windows that touch co-move
3356 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
3357 int heightInc = wpNew.height - wpMain.height;
3358 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3359 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3360 wp->y += fracTop * heightInc;
3361 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
3362 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
3363 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
3364 int widthInc = wpNew.width - wpMain.width;
3365 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3366 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3367 wp->y += fracLeft * widthInc;
3368 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
3369 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
3371 wp->x += wpNew.x - wpMain.x;
3372 wp->y += wpNew.y - wpMain.y;
3373 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
3374 if(touch == 3) wp->y += wpNew.height - wpMain.height;
3375 XtSetArg(args[j], XtNx, wp->x); j++;
3376 XtSetArg(args[j], XtNy, wp->y); j++;
3377 XtSetValues(sh, args, j);
3380 static XtIntervalId delayedDragID = 0;
3385 GetActualPlacement(shellWidget, &wpNew);
3386 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
3387 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
3388 return; // false alarm
3389 if(EngineOutputIsUp()) CoDrag(engineOutputShell, &wpEngineOutput);
3390 if(MoveHistoryIsUp()) CoDrag(shells[HistoryDlg], &wpMoveHistory);
3391 if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
3392 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
3394 DrawPosition(True, NULL);
3395 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
3402 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
3404 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
3407 /* Why is this needed on some versions of X? */
3409 EventProc (Widget widget, caddr_t unused, XEvent *event)
3411 if (!XtIsRealized(widget))
3413 switch (event->type) {
3414 case ConfigureNotify: // main window is being dragged: drag attached windows with it
3415 if(appData.useStickyWindows)
3416 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
3419 if (event->xexpose.count > 0) return; /* no clipping is done */
3420 DrawPosition(True, NULL);
3421 if(twoBoards) { // [HGM] dual: draw other board in other orientation
3422 flipView = !flipView; partnerUp = !partnerUp;
3423 DrawPosition(True, NULL);
3424 flipView = !flipView; partnerUp = !partnerUp;
3428 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
3435 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
3437 DrawSeekAxis (int x, int y, int xTo, int yTo)
3439 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
3443 DrawSeekBackground (int left, int top, int right, int bottom)
3445 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
3449 DrawSeekText (char *buf, int x, int y)
3451 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
3455 DrawSeekDot (int x, int y, int colorNr)
3457 int square = colorNr & 0x80;
3460 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
3462 XFillRectangle(xDisplay, xBoardWindow, color,
3463 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3465 XFillArc(xDisplay, xBoardWindow, color,
3466 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
3470 DrawGrid (int second)
3472 XDrawSegments(xDisplay, xBoardWindow, lineGC,
3473 second ? secondSegments : // [HGM] dual
3474 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
3479 * event handler for redrawing the board
3482 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3484 DrawPosition(True, NULL);
3489 * event handler for parsing user moves
3491 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
3492 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
3493 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
3494 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
3495 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
3496 // and at the end FinishMove() to perform the move after optional promotion popups.
3497 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
3499 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3501 if (w != boardWidget || errorExitStatus != -1) return;
3502 if(nprms) shiftKey = !strcmp(prms[0], "1");
3504 if (shellUp[PromoDlg]) { // [HGM] is this still needed?
3505 if (event->type == ButtonPress) {
3514 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
3515 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
3516 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
3520 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
3522 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
3523 DragPieceMove(event->xmotion.x, event->xmotion.y);
3527 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3528 { // [HGM] pv: walk PV
3529 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3532 static int savedIndex; /* gross that this is global */
3535 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3538 XawTextPosition index, dummy;
3541 XawTextGetSelectionPos(w, &index, &dummy);
3542 XtSetArg(arg, XtNstring, &val);
3543 XtGetValues(w, &arg, 1);
3544 ReplaceComment(savedIndex, val);
3545 if(savedIndex != currentMove) ToNrEvent(savedIndex);
3546 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3550 EditCommentPopUp (int index, char *title, char *text)
3553 if (text == NULL) text = "";
3554 NewCommentPopup(title, text, index);
3558 CommentPopUp (char *title, char *text)
3560 savedIndex = currentMove; // [HGM] vari
3561 NewCommentPopup(title, text, currentMove);
3567 PopDown(CommentDlg);
3570 static char *openName;
3576 (void) (*fileProc)(openFP, 0, openName);
3580 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
3582 fileProc = proc; /* I can't see a way not */
3583 fileOpenMode = openMode; /* to use globals here */
3584 { // [HGM] use file-selector dialog stolen from Ghostview
3585 int index; // this is not supported yet
3586 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
3587 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
3588 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
3589 ScheduleDelayedEvent(&DelayedLoad, 50);
3594 /* Disable all user input other than deleting the window */
3595 static int frozen = 0;
3601 /* Grab by a widget that doesn't accept input */
3602 XtAddGrab(messageWidget, TRUE, FALSE);
3606 /* Undo a FreezeUI */
3610 if (!frozen) return;
3611 XtRemoveGrab(messageWidget);
3619 static int oldPausing = FALSE;
3620 static GameMode oldmode = (GameMode) -1;
3623 if (!boardWidget || !XtIsRealized(boardWidget)) return;
3625 if (pausing != oldPausing) {
3626 oldPausing = pausing;
3627 MarkMenuItem("Pause", pausing);
3629 if (appData.showButtonBar) {
3630 /* Always toggle, don't set. Previous code messes up when
3631 invoked while the button is pressed, as releasing it
3632 toggles the state again. */
3635 XtSetArg(args[0], XtNbackground, &oldbg);
3636 XtSetArg(args[1], XtNforeground, &oldfg);
3637 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
3639 XtSetArg(args[0], XtNbackground, oldfg);
3640 XtSetArg(args[1], XtNforeground, oldbg);
3642 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
3646 wname = ModeToWidgetName(oldmode);
3647 if (wname != NULL) {
3648 MarkMenuItem(wname, False);
3650 wname = ModeToWidgetName(gameMode);
3651 if (wname != NULL) {
3652 MarkMenuItem(wname, True);
3655 MarkMenuItem("Machine Match", matchMode && matchGame < appData.matchGames);
3657 /* Maybe all the enables should be handled here, not just this one */
3658 EnableMenuItem("Training", gameMode == Training || gameMode == PlayFromGameFile);
3663 * Button/menu procedures
3666 LoadGamePopUp (FILE *f, int gameNumber, char *title)
3668 cmailMsgLoaded = FALSE;
3669 if (gameNumber == 0) {
3670 int error = GameListBuild(f);
3672 DisplayError(_("Cannot build game list"), error);
3673 } else if (!ListEmpty(&gameList) &&
3674 ((ListGame *) gameList.tailPred)->number > 1) {
3675 GameListPopUp(f, title);
3681 return LoadGame(f, gameNumber, title, FALSE);
3684 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3685 char *selected_fen_position=NULL;
3688 SendPositionSelection (Widget w, Atom *selection, Atom *target,
3689 Atom *type_return, XtPointer *value_return,
3690 unsigned long *length_return, int *format_return)
3692 char *selection_tmp;
3694 // if (!selected_fen_position) return False; /* should never happen */
3695 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3696 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
3697 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
3700 if (f == NULL) return False;
3704 selection_tmp = XtMalloc(len + 1);
3705 count = fread(selection_tmp, 1, len, f);
3708 XtFree(selection_tmp);
3711 selection_tmp[len] = NULLCHAR;
3713 /* note: since no XtSelectionDoneProc was registered, Xt will
3714 * automatically call XtFree on the value returned. So have to
3715 * make a copy of it allocated with XtMalloc */
3716 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3717 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
3720 *value_return=selection_tmp;
3721 *length_return=strlen(selection_tmp);
3722 *type_return=*target;
3723 *format_return = 8; /* bits per byte */
3725 } else if (*target == XA_TARGETS(xDisplay)) {
3726 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3727 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3728 targets_tmp[1] = XA_STRING;
3729 *value_return = targets_tmp;
3730 *type_return = XA_ATOM;
3733 // This code leads to a read of value_return out of bounds on 64-bit systems.
3734 // Other code which I have seen always sets *format_return to 32 independent of
3735 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
3736 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
3737 *format_return = 8 * sizeof(Atom);
3738 if (*format_return > 32) {
3739 *length_return *= *format_return / 32;
3740 *format_return = 32;
3743 *format_return = 32;
3751 /* note: when called from menu all parameters are NULL, so no clue what the
3752 * Widget which was clicked on was, or what the click event was
3755 CopySomething (char *src)
3757 selected_fen_position = src;
3759 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3760 * have a notion of a position that is selected but not copied.
3761 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3763 XtOwnSelection(menuBarWidget, XA_PRIMARY,
3765 SendPositionSelection,
3766 NULL/* lose_ownership_proc */ ,
3767 NULL/* transfer_done_proc */);
3768 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3770 SendPositionSelection,
3771 NULL/* lose_ownership_proc */ ,
3772 NULL/* transfer_done_proc */);
3775 /* function called when the data to Paste is ready */
3777 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3778 Atom *type, XtPointer value, unsigned long *len, int *format)
3781 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3782 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3783 EditPositionPasteFEN(fenstr);
3787 /* called when Paste Position button is pressed,
3788 * all parameters will be NULL */
3790 PastePositionProc ()
3792 XtGetSelectionValue(menuBarWidget,
3793 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3794 /* (XtSelectionCallbackProc) */ PastePositionCB,
3795 NULL, /* client_data passed to PastePositionCB */
3797 /* better to use the time field from the event that triggered the
3798 * call to this function, but that isn't trivial to get
3805 /* note: when called from menu all parameters are NULL, so no clue what the
3806 * Widget which was clicked on was, or what the click event was
3808 /* function called when the data to Paste is ready */
3810 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
3811 Atom *type, XtPointer value, unsigned long *len, int *format)
3814 if (value == NULL || *len == 0) {
3815 return; /* nothing had been selected to copy */
3817 f = fopen(gamePasteFilename, "w");
3819 DisplayError(_("Can't open temp file"), errno);
3822 fwrite(value, 1, *len, f);
3825 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
3828 /* called when Paste Game button is pressed,
3829 * all parameters will be NULL */
3833 XtGetSelectionValue(menuBarWidget,
3834 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
3835 /* (XtSelectionCallbackProc) */ PasteGameCB,
3836 NULL, /* client_data passed to PasteGameCB */
3838 /* better to use the time field from the event that triggered the
3839 * call to this function, but that isn't trivial to get
3848 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3855 { // bassic primitive for determining if modifier keys are pressed
3856 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
3859 XQueryKeymap(xDisplay,keys);
3860 for(i=0; i<6; i++) {
3862 j = XKeysymToKeycode(xDisplay, codes[i]);
3863 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
3869 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
3873 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
3874 if ( n == 1 && *buf >= 32 // printable
3875 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
3876 ) BoxAutoPopUp (buf);
3880 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3881 { // [HGM] input: let up-arrow recall previous line from history
3886 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3887 { // [HGM] input: let down-arrow recall next line from history
3892 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3898 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3900 if (!TempBackwardActive) {
3901 TempBackwardActive = True;
3907 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3909 /* Check to see if triggered by a key release event for a repeating key.
3910 * If so the next queued event will be a key press of the same key at the same time */
3911 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
3913 XPeekEvent(xDisplay, &next);
3914 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
3915 next.xkey.keycode == event->xkey.keycode)
3919 TempBackwardActive = False;
3923 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3924 { // called as key binding
3927 if (nprms && *nprms > 0)
3931 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
3936 DisplayMessage (char *message, char *extMessage)
3938 /* display a message in the message widget */
3947 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
3952 message = extMessage;
3956 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
3958 /* need to test if messageWidget already exists, since this function
3959 can also be called during the startup, if for example a Xresource
3960 is not set up correctly */
3963 XtSetArg(arg, XtNlabel, message);
3964 XtSetValues(messageWidget, &arg, 1);
3971 SetWindowTitle (char *text, char *title, char *icon)
3975 if (appData.titleInWindow) {
3977 XtSetArg(args[i], XtNlabel, text); i++;
3978 XtSetValues(titleWidget, args, i);
3981 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
3982 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
3983 XtSetValues(shellWidget, args, i);
3984 XSync(xDisplay, False);
3989 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
3995 DisplayIcsInteractionTitle (String message)
3997 if (oldICSInteractionTitle == NULL) {
3998 /* Magic to find the old window title, adapted from vim */
3999 char *wina = getenv("WINDOWID");
4001 Window win = (Window) atoi(wina);
4002 Window root, parent, *children;
4003 unsigned int nchildren;
4004 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4006 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4007 if (!XQueryTree(xDisplay, win, &root, &parent,
4008 &children, &nchildren)) break;
4009 if (children) XFree((void *)children);
4010 if (parent == root || parent == 0) break;
4013 XSetErrorHandler(oldHandler);
4015 if (oldICSInteractionTitle == NULL) {
4016 oldICSInteractionTitle = "xterm";
4019 printf("\033]0;%s\007", message);
4024 XtIntervalId delayedEventTimerXID = 0;
4025 DelayedEventCallback delayedEventCallback = 0;
4030 delayedEventTimerXID = 0;
4031 delayedEventCallback();
4035 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
4037 if(delayedEventTimerXID && delayedEventCallback == cb)
4038 // [HGM] alive: replace, rather than add or flush identical event
4039 XtRemoveTimeOut(delayedEventTimerXID);
4040 delayedEventCallback = cb;
4041 delayedEventTimerXID =
4042 XtAppAddTimeOut(appContext, millisec,
4043 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
4046 DelayedEventCallback
4049 if (delayedEventTimerXID) {
4050 return delayedEventCallback;
4057 CancelDelayedEvent ()
4059 if (delayedEventTimerXID) {
4060 XtRemoveTimeOut(delayedEventTimerXID);
4061 delayedEventTimerXID = 0;
4065 XtIntervalId loadGameTimerXID = 0;
4068 LoadGameTimerRunning ()
4070 return loadGameTimerXID != 0;
4074 StopLoadGameTimer ()
4076 if (loadGameTimerXID != 0) {
4077 XtRemoveTimeOut(loadGameTimerXID);
4078 loadGameTimerXID = 0;
4086 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
4088 loadGameTimerXID = 0;
4093 StartLoadGameTimer (long millisec)
4096 XtAppAddTimeOut(appContext, millisec,
4097 (XtTimerCallbackProc) LoadGameTimerCallback,
4101 XtIntervalId analysisClockXID = 0;
4104 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
4106 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
4107 || appData.icsEngineAnalyze) { // [DM]
4108 AnalysisPeriodicEvent(0);
4109 StartAnalysisClock();
4114 StartAnalysisClock ()
4117 XtAppAddTimeOut(appContext, 2000,
4118 (XtTimerCallbackProc) AnalysisClockCallback,
4122 XtIntervalId clockTimerXID = 0;
4125 ClockTimerRunning ()
4127 return clockTimerXID != 0;
4133 if (clockTimerXID != 0) {
4134 XtRemoveTimeOut(clockTimerXID);
4143 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
4150 StartClockTimer (long millisec)
4153 XtAppAddTimeOut(appContext, millisec,
4154 (XtTimerCallbackProc) ClockTimerCallback,
4159 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
4164 /* check for low time warning */
4165 Pixel foregroundOrWarningColor = timerForegroundPixel;
4168 appData.lowTimeWarning &&
4169 (timer / 1000) < appData.icsAlarmTime)
4170 foregroundOrWarningColor = lowTimeWarningColor;
4172 if (appData.clockMode) {
4173 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
4174 XtSetArg(args[0], XtNlabel, buf);
4176 snprintf(buf, MSG_SIZ, "%s ", color);
4177 XtSetArg(args[0], XtNlabel, buf);
4182 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
4183 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
4185 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
4186 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
4189 XtSetValues(w, args, 3);
4193 DisplayWhiteClock (long timeRemaining, int highlight)
4197 if(appData.noGUI) return;
4198 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
4199 if (highlight && iconPixmap == bIconPixmap) {
4200 iconPixmap = wIconPixmap;
4201 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
4202 XtSetValues(shellWidget, args, 1);
4207 DisplayBlackClock (long timeRemaining, int highlight)
4211 if(appData.noGUI) return;
4212 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
4213 if (highlight && iconPixmap == wIconPixmap) {
4214 iconPixmap = bIconPixmap;
4215 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
4216 XtSetValues(shellWidget, args, 1);
4221 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
4223 InputSource *is = (InputSource *) closure;
4228 if (is->lineByLine) {
4229 count = read(is->fd, is->unused,
4230 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
4232 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
4235 is->unused += count;
4237 while (p < is->unused) {
4238 q = memchr(p, '\n', is->unused - p);
4239 if (q == NULL) break;
4241 (is->func)(is, is->closure, p, q - p, 0);
4245 while (p < is->unused) {
4250 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
4255 (is->func)(is, is->closure, is->buf, count, error);
4260 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
4263 ChildProc *cp = (ChildProc *) pr;
4265 is = (InputSource *) calloc(1, sizeof(InputSource));
4266 is->lineByLine = lineByLine;
4270 is->fd = fileno(stdin);
4272 is->kind = cp->kind;
4273 is->fd = cp->fdFrom;
4276 is->unused = is->buf;
4279 is->xid = XtAppAddInput(appContext, is->fd,
4280 (XtPointer) (XtInputReadMask),
4281 (XtInputCallbackProc) DoInputCallback,
4283 is->closure = closure;
4284 return (InputSourceRef) is;
4288 RemoveInputSource (InputSourceRef isr)
4290 InputSource *is = (InputSource *) isr;
4292 if (is->xid == 0) return;
4293 XtRemoveInput(is->xid);
4297 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
4299 /* Masks for XPM pieces. Black and white pieces can have
4300 different shapes, but in the interest of retaining my
4301 sanity pieces must have the same outline on both light
4302 and dark squares, and all pieces must use the same
4303 background square colors/images. */
4305 static int xpmDone = 0;
4306 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
4307 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
4310 CreateAnimMasks (int pieceDepth)
4316 unsigned long plane;
4319 /* Need a bitmap just to get a GC with right depth */
4320 buf = XCreatePixmap(xDisplay, xBoardWindow,
4322 values.foreground = 1;
4323 values.background = 0;
4324 /* Don't use XtGetGC, not read only */
4325 maskGC = XCreateGC(xDisplay, buf,
4326 GCForeground | GCBackground, &values);
4327 XFreePixmap(xDisplay, buf);
4329 buf = XCreatePixmap(xDisplay, xBoardWindow,
4330 squareSize, squareSize, pieceDepth);
4331 values.foreground = XBlackPixel(xDisplay, xScreen);
4332 values.background = XWhitePixel(xDisplay, xScreen);
4333 bufGC = XCreateGC(xDisplay, buf,
4334 GCForeground | GCBackground, &values);
4336 for (piece = WhitePawn; piece <= BlackKing; piece++) {
4337 /* Begin with empty mask */
4338 if(!xpmDone) // [HGM] pieces: keep using existing
4339 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
4340 squareSize, squareSize, 1);
4341 XSetFunction(xDisplay, maskGC, GXclear);
4342 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
4343 0, 0, squareSize, squareSize);
4345 /* Take a copy of the piece */
4350 XSetFunction(xDisplay, bufGC, GXcopy);
4351 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
4353 0, 0, squareSize, squareSize, 0, 0);
4355 /* XOR the background (light) over the piece */
4356 XSetFunction(xDisplay, bufGC, GXxor);
4358 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
4359 0, 0, squareSize, squareSize, 0, 0);
4361 XSetForeground(xDisplay, bufGC, lightSquareColor);
4362 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
4365 /* We now have an inverted piece image with the background
4366 erased. Construct mask by just selecting all the non-zero
4367 pixels - no need to reconstruct the original image. */
4368 XSetFunction(xDisplay, maskGC, GXor);
4370 /* Might be quicker to download an XImage and create bitmap
4371 data from it rather than this N copies per piece, but it
4372 only takes a fraction of a second and there is a much
4373 longer delay for loading the pieces. */
4374 for (n = 0; n < pieceDepth; n ++) {
4375 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
4376 0, 0, squareSize, squareSize,
4382 XFreePixmap(xDisplay, buf);
4383 XFreeGC(xDisplay, bufGC);
4384 XFreeGC(xDisplay, maskGC);
4388 InitAnimState (AnimNr anr, XWindowAttributes *info)
4393 /* Each buffer is square size, same depth as window */
4394 animBufs[anr+4] = xBoardWindow;
4395 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
4396 squareSize, squareSize, info->depth);
4397 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
4398 squareSize, squareSize, info->depth);
4400 /* Create a plain GC for blitting */
4401 mask = GCForeground | GCBackground | GCFunction |
4402 GCPlaneMask | GCGraphicsExposures;
4403 values.foreground = XBlackPixel(xDisplay, xScreen);
4404 values.background = XWhitePixel(xDisplay, xScreen);
4405 values.function = GXcopy;
4406 values.plane_mask = AllPlanes;
4407 values.graphics_exposures = False;
4408 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
4410 /* Piece will be copied from an existing context at
4411 the start of each new animation/drag. */
4412 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
4414 /* Outline will be a read-only copy of an existing */
4415 animGCs[anr+4] = None;
4421 XWindowAttributes info;
4423 if (xpmDone && gameInfo.variant == oldVariant) return;
4424 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
4425 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
4427 InitAnimState(Game, &info);
4428 InitAnimState(Player, &info);
4430 /* For XPM pieces, we need bitmaps to use as masks. */
4432 CreateAnimMasks(info.depth), xpmDone = 1;
4437 static Boolean frameWaiting;
4440 FrameAlarm (int sig)
4442 frameWaiting = False;
4443 /* In case System-V style signals. Needed?? */
4444 signal(SIGALRM, FrameAlarm);
4448 FrameDelay (int time)
4450 struct itimerval delay;
4452 XSync(xDisplay, False);
4455 frameWaiting = True;
4456 signal(SIGALRM, FrameAlarm);
4457 delay.it_interval.tv_sec =
4458 delay.it_value.tv_sec = time / 1000;
4459 delay.it_interval.tv_usec =
4460 delay.it_value.tv_usec = (time % 1000) * 1000;
4461 setitimer(ITIMER_REAL, &delay, NULL);
4462 while (frameWaiting) pause();
4463 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
4464 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
4465 setitimer(ITIMER_REAL, &delay, NULL);
4472 FrameDelay (int time)
4474 XSync(xDisplay, False);
4476 usleep(time * 1000);
4482 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
4486 /* Bitmap for piece being moved. */
4487 if (appData.monoMode) {
4488 *mask = *pieceToSolid(piece);
4489 } else if (useImages) {
4491 *mask = xpmMask[piece];
4493 *mask = ximMaskPm[piece];
4496 *mask = *pieceToSolid(piece);
4499 /* GC for piece being moved. Square color doesn't matter, but
4500 since it gets modified we make a copy of the original. */
4502 if (appData.monoMode)
4507 if (appData.monoMode)
4512 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
4514 /* Outline only used in mono mode and is not modified */
4516 *outline = bwPieceGC;
4518 *outline = wbPieceGC;
4522 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
4527 /* Draw solid rectangle which will be clipped to shape of piece */
4528 XFillRectangle(xDisplay, dest, clip,
4529 0, 0, squareSize, squareSize);
4530 if (appData.monoMode)
4531 /* Also draw outline in contrasting color for black
4532 on black / white on white cases */
4533 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
4534 0, 0, squareSize, squareSize, 0, 0, 1);
4536 /* Copy the piece */
4541 if(appData.upsideDown && flipView) kind ^= 2;
4542 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4544 0, 0, squareSize, squareSize,
4550 InsertPiece (AnimNr anr, ChessSquare piece)
4552 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
4556 DrawBlank (AnimNr anr, int x, int y, int startColor)
4558 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
4561 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
4562 int srcX, int srcY, int width, int height, int destX, int destY)
4564 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
4565 srcX, srcY, width, height, destX, destY);
4569 SetDragPiece (AnimNr anr, ChessSquare piece)
4572 /* The piece will be drawn using its own bitmap as a matte */
4573 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
4574 XSetClipMask(xDisplay, animGCs[anr+2], mask);
4577 /* [AS] Arrow highlighting support */
4580 DrawPolygon (Pnt arrow[], int nr)
4584 for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
4585 XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
4586 if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
4590 UpdateLogos (int displ)
4592 return; // no logos in XBoard yet