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 PromotionPopDown P((void));
279 void PromotionCallback P((Widget w, XtPointer client_data,
280 XtPointer call_data));
281 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
282 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
283 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
284 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
285 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
286 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
287 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
288 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
289 Boolean TempBackwardActive = False;
290 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
291 void DisplayMove P((int moveNumber));
292 void ICSInitScript P((void));
293 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
294 void update_ics_width P(());
295 int get_term_width P(());
296 int CopyMemoProc P(());
299 * XBoard depends on Xt R4 or higher
301 int xtVersion = XtSpecificationRelease;
306 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
307 highlightSquareColor, premoveHighlightColor;
308 Pixel lowTimeWarningColor;
309 GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
310 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
312 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
313 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
314 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
315 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
316 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
317 ICSInputShell, fileNameShell;
318 Widget historyShell, evalGraphShell, gameListShell;
319 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
320 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
322 XFontSet fontSet, clockFontSet;
325 XFontStruct *clockFontStruct;
327 Font coordFontID, countFontID;
328 XFontStruct *coordFontStruct, *countFontStruct;
329 XtAppContext appContext;
334 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
336 Position commentX = -1, commentY = -1;
337 Dimension commentW, commentH;
338 typedef unsigned int BoardSize;
340 Boolean chessProgram;
342 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
343 int smallLayout = 0, tinyLayout = 0,
344 marginW, marginH, // [HGM] for run-time resizing
345 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
346 ICSInputBoxUp = False,
347 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
348 errorUp = False, errorExitStatus = -1, defaultLineGap;
349 Dimension textHeight;
350 Pixel timerForegroundPixel, timerBackgroundPixel;
351 Pixel buttonForegroundPixel, buttonBackgroundPixel;
352 char *chessDir, *programName, *programVersion;
353 Boolean alwaysOnTop = False;
354 char *icsTextMenuString;
356 char *firstChessProgramNames;
357 char *secondChessProgramNames;
359 WindowPlacement wpMain;
360 WindowPlacement wpConsole;
361 WindowPlacement wpComment;
362 WindowPlacement wpMoveHistory;
363 WindowPlacement wpEvalGraph;
364 WindowPlacement wpEngineOutput;
365 WindowPlacement wpGameList;
366 WindowPlacement wpTags;
371 Pixmap pieceBitmap[2][(int)BlackPawn];
372 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
373 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
374 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
375 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
376 Pixmap xpmBoardBitmap[2];
377 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
378 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
379 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
380 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
381 XImage *ximLightSquare, *ximDarkSquare;
384 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
385 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
387 #define White(piece) ((int)(piece) < (int)BlackPawn)
389 /* Bitmaps for use as masks when drawing XPM pieces.
390 Need one for each black and white piece. */
391 static Pixmap xpmMask[BlackKing + 1];
393 /* This magic number is the number of intermediate frames used
394 in each half of the animation. For short moves it's reduced
395 by 1. The total number of frames will be factor * 2 + 1. */
398 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
400 #define PAUSE_BUTTON "P"
401 MenuItem buttonBar[] = {
402 {"<<", "<<", ToStartEvent},
403 {"<", "<", BackwardEvent},
404 {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseEvent},
405 {">", ">", ForwardEvent},
406 {">>", ">>", ToEndEvent},
410 #define PIECE_MENU_SIZE 18
411 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
412 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
413 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
414 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
415 N_("Empty square"), N_("Clear board") },
416 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
417 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
418 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
419 N_("Empty square"), N_("Clear board") }
421 /* must be in same order as pieceMenuStrings! */
422 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
423 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
424 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
425 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
426 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
427 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
428 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
429 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
430 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
433 #define DROP_MENU_SIZE 6
434 String dropMenuStrings[DROP_MENU_SIZE] = {
435 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
437 /* must be in same order as dropMenuStrings! */
438 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
439 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
440 WhiteRook, WhiteQueen
448 DropMenuEnables dmEnables[] = {
466 { XtNborderWidth, 0 },
467 { XtNdefaultDistance, 0 },
471 { XtNborderWidth, 0 },
472 { XtNresizable, (XtArgVal) True },
476 { XtNborderWidth, 0 },
482 { XtNjustify, (XtArgVal) XtJustifyRight },
483 { XtNlabel, (XtArgVal) "..." },
484 { XtNresizable, (XtArgVal) True },
485 { XtNresize, (XtArgVal) False }
488 Arg messageArgs[] = {
489 { XtNjustify, (XtArgVal) XtJustifyLeft },
490 { XtNlabel, (XtArgVal) "..." },
491 { XtNresizable, (XtArgVal) True },
492 { XtNresize, (XtArgVal) False }
496 { XtNborderWidth, 0 },
497 { XtNjustify, (XtArgVal) XtJustifyLeft }
500 XtResource clientResources[] = {
501 { "flashCount", "flashCount", XtRInt, sizeof(int),
502 XtOffset(AppDataPtr, flashCount), XtRImmediate,
503 (XtPointer) FLASH_COUNT },
506 XrmOptionDescRec shellOptions[] = {
507 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
508 { "-flash", "flashCount", XrmoptionNoArg, "3" },
509 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
512 XtActionsRec boardActions[] = {
513 { "DrawPosition", DrawPositionProc },
514 { "HandleUserMove", HandleUserMove },
515 { "AnimateUserMove", AnimateUserMove },
516 { "HandlePV", HandlePV },
517 { "SelectPV", SelectPV },
518 { "StopPV", StopPV },
519 { "PieceMenuPopup", PieceMenuPopup },
520 { "WhiteClock", WhiteClock },
521 { "BlackClock", BlackClock },
522 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
523 { "QuitProc", QuitWrapper },
524 { "ManProc", ManInner },
525 { "TempBackwardProc", TempBackwardProc },
526 { "TempForwardProc", TempForwardProc },
527 { "CommentClick", (XtActionProc) CommentClick },
528 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
529 { "GameListPopDown", (XtActionProc) GameListPopDown },
530 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
531 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
532 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
533 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
534 { "GenericPopDown", (XtActionProc) GenericPopDown },
535 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
536 { "SelectMove", (XtActionProc) SelectMove },
537 { "LoadSelectedProc", LoadSelectedProc },
538 { "SetFilterProc", SetFilterProc },
539 { "TypeInProc", TypeInProc },
540 { "EnterKeyProc", EnterKeyProc },
541 { "UpKeyProc", UpKeyProc },
542 { "DownKeyProc", DownKeyProc },
543 { "WheelProc", WheelProc },
544 { "TabProc", TabProc },
547 char globalTranslations[] =
548 ":<Key>F9: MenuItem(ResignProc) \n \
549 :Ctrl<Key>n: MenuItem(NewGame) \n \
550 :Meta<Key>V: MenuItem(NewVariant) \n \
551 :Ctrl<Key>o: MenuItem(LoadGame) \n \
552 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
553 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
554 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
555 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
556 :Ctrl<Key>s: MenuItem(SaveGame) \n \
557 :Ctrl<Key>c: MenuItem(CopyGame) \n \
558 :Ctrl<Key>v: MenuItem(PasteGame) \n \
559 :Ctrl<Key>O: MenuItem(LoadPosition) \n \
560 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
561 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
562 :Ctrl<Key>S: MenuItem(SavePosition) \n \
563 :Ctrl<Key>C: MenuItem(CopyPosition) \n \
564 :Ctrl<Key>V: MenuItem(PastePosition) \n \
565 :Ctrl<Key>q: MenuItem(Exit) \n \
566 :Ctrl<Key>w: MenuItem(MachineWhite) \n \
567 :Ctrl<Key>b: MenuItem(MachineBlack) \n \
568 :Ctrl<Key>t: MenuItem(TwoMachines) \n \
569 :Ctrl<Key>a: MenuItem(AnalysisMode) \n \
570 :Ctrl<Key>g: MenuItem(AnalyzeFile) \n \
571 :Ctrl<Key>e: MenuItem(EditGame) \n \
572 :Ctrl<Key>E: MenuItem(EditPosition) \n \
573 :Meta<Key>O: MenuItem(ShowEngineOutput) \n \
574 :Meta<Key>E: MenuItem(ShowEvaluationGraph) \n \
575 :Meta<Key>G: MenuItem(ShowGameList) \n \
576 :Meta<Key>H: MenuItem(ShowMoveHistory) \n \
577 :<Key>Pause: MenuItem(Pause) \n \
578 :<Key>F3: MenuItem(Accept) \n \
579 :<Key>F4: MenuItem(Decline) \n \
580 :<Key>F12: MenuItem(Rematch) \n \
581 :<Key>F5: MenuItem(CallFlag) \n \
582 :<Key>F6: MenuItem(Draw) \n \
583 :<Key>F7: MenuItem(Adjourn) \n \
584 :<Key>F8: MenuItem(Abort) \n \
585 :<Key>F10: MenuItem(StopObserving) \n \
586 :<Key>F11: MenuItem(StopExamining) \n \
587 :Ctrl<Key>d: MenuItem(DebugProc) \n \
588 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
589 :Meta<Key>End: MenuItem(ToEnd) \n \
590 :Meta<Key>Right: MenuItem(Forward) \n \
591 :Meta<Key>Home: MenuItem(ToStart) \n \
592 :Meta<Key>Left: MenuItem(Backward) \n \
593 :<Key>Left: MenuItem(Backward) \n \
594 :<Key>Right: MenuItem(Forward) \n \
595 :<Key>Home: MenuItem(Revert) \n \
596 :<Key>End: MenuItem(TruncateGame) \n \
597 :Ctrl<Key>m: MenuItem(MoveNow) \n \
598 :Ctrl<Key>x: MenuItem(RetractMove) \n \
599 :Meta<Key>J: MenuItem(Adjudications) \n \
600 :Meta<Key>U: MenuItem(CommonEngine) \n \
601 :Meta<Key>T: MenuItem(TimeControl) \n \
602 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
603 #ifndef OPTIONSDIALOG
605 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
606 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
607 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
608 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
609 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
612 :<Key>F1: MenuItem(Manual) \n \
613 :<Key>F2: MenuItem(FlipView) \n \
614 :<KeyDown>Return: TempBackwardProc() \n \
615 :<KeyUp>Return: TempForwardProc() \n";
617 char boardTranslations[] =
618 "<Btn1Down>: HandleUserMove(0) \n \
619 Shift<Btn1Up>: HandleUserMove(1) \n \
620 <Btn1Up>: HandleUserMove(0) \n \
621 <Btn1Motion>: AnimateUserMove() \n \
622 <Btn3Motion>: HandlePV() \n \
623 <Btn2Motion>: HandlePV() \n \
624 <Btn3Up>: PieceMenuPopup(menuB) \n \
625 <Btn2Up>: PieceMenuPopup(menuB) \n \
626 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
627 PieceMenuPopup(menuB) \n \
628 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
629 PieceMenuPopup(menuW) \n \
630 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
631 PieceMenuPopup(menuW) \n \
632 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
633 PieceMenuPopup(menuB) \n";
635 char whiteTranslations[] =
636 "Shift<BtnDown>: WhiteClock(1)\n \
637 <BtnDown>: WhiteClock(0)\n";
638 char blackTranslations[] =
639 "Shift<BtnDown>: BlackClock(1)\n \
640 <BtnDown>: BlackClock(0)\n";
642 char ICSInputTranslations[] =
643 "<Key>Up: UpKeyProc() \n "
644 "<Key>Down: DownKeyProc() \n "
645 "<Key>Return: EnterKeyProc() \n";
647 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
648 // as the widget is destroyed before the up-click can call extend-end
649 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
651 String xboardResources[] = {
652 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
657 /* Max possible square size */
658 #define MAXSQSIZE 256
660 static int xpm_avail[MAXSQSIZE];
662 #ifdef HAVE_DIR_STRUCT
664 /* Extract piece size from filename */
666 xpm_getsize (char *name, int len, char *ext)
674 if ((p=strchr(name, '.')) == NULL ||
675 StrCaseCmp(p+1, ext) != 0)
681 while (*p && isdigit(*p))
688 /* Setup xpm_avail */
690 xpm_getavail (char *dirname, char *ext)
696 for (i=0; i<MAXSQSIZE; ++i)
699 if (appData.debugMode)
700 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
702 dir = opendir(dirname);
705 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
706 programName, dirname);
710 while ((ent=readdir(dir)) != NULL) {
711 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
712 if (i > 0 && i < MAXSQSIZE)
722 xpm_print_avail (FILE *fp, char *ext)
726 fprintf(fp, _("Available `%s' sizes:\n"), ext);
727 for (i=1; i<MAXSQSIZE; ++i) {
733 /* Return XPM piecesize closest to size */
735 xpm_closest_to (char *dirname, int size, char *ext)
738 int sm_diff = MAXSQSIZE;
742 xpm_getavail(dirname, ext);
744 if (appData.debugMode)
745 xpm_print_avail(stderr, ext);
747 for (i=1; i<MAXSQSIZE; ++i) {
750 diff = (diff<0) ? -diff : diff;
751 if (diff < sm_diff) {
759 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
765 #else /* !HAVE_DIR_STRUCT */
766 /* If we are on a system without a DIR struct, we can't
767 read the directory, so we can't collect a list of
768 filenames, etc., so we can't do any size-fitting. */
770 xpm_closest_to (char *dirname, int size, char *ext)
773 Warning: No DIR structure found on this system --\n\
774 Unable to autosize for XPM/XIM pieces.\n\
775 Please report this error to %s.\n\
776 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
779 #endif /* HAVE_DIR_STRUCT */
782 /* Arrange to catch delete-window events */
783 Atom wm_delete_window;
785 CatchDeleteWindow (Widget w, String procname)
788 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
789 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
790 XtAugmentTranslations(w, XtParseTranslationTable(buf));
797 XtSetArg(args[0], XtNiconic, False);
798 XtSetValues(shellWidget, args, 1);
800 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
803 //---------------------------------------------------------------------------------------------------------
804 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
807 #define CW_USEDEFAULT (1<<31)
808 #define ICS_TEXT_MENU_SIZE 90
809 #define DEBUG_FILE "xboard.debug"
810 #define SetCurrentDirectory chdir
811 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
815 // these two must some day move to frontend.h, when they are implemented
816 Boolean GameListIsUp();
818 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
821 // front-end part of option handling
823 // [HGM] This platform-dependent table provides the location for storing the color info
824 extern char *crWhite, * crBlack;
828 &appData.whitePieceColor,
829 &appData.blackPieceColor,
830 &appData.lightSquareColor,
831 &appData.darkSquareColor,
832 &appData.highlightSquareColor,
833 &appData.premoveHighlightColor,
834 &appData.lowTimeWarningColor,
845 // [HGM] font: keep a font for each square size, even non-stndard ones
848 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
849 char *fontTable[NUM_FONTS][MAX_SIZE];
852 ParseFont (char *name, int number)
853 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
855 if(sscanf(name, "size%d:", &size)) {
856 // [HGM] font: font is meant for specific boardSize (likely from settings file);
857 // defer processing it until we know if it matches our board size
858 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
859 fontTable[number][size] = strdup(strchr(name, ':')+1);
860 fontValid[number][size] = True;
865 case 0: // CLOCK_FONT
866 appData.clockFont = strdup(name);
868 case 1: // MESSAGE_FONT
869 appData.font = strdup(name);
871 case 2: // COORD_FONT
872 appData.coordFont = strdup(name);
877 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
882 { // only 2 fonts currently
883 appData.clockFont = CLOCK_FONT_NAME;
884 appData.coordFont = COORD_FONT_NAME;
885 appData.font = DEFAULT_FONT_NAME;
890 { // no-op, until we identify the code for this already in XBoard and move it here
894 ParseColor (int n, char *name)
895 { // in XBoard, just copy the color-name string
896 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
900 ParseTextAttribs (ColorClass cc, char *s)
902 (&appData.colorShout)[cc] = strdup(s);
906 ParseBoardSize (void *addr, char *name)
908 appData.boardSize = strdup(name);
913 { // In XBoard the sound-playing program takes care of obtaining the actual sound
917 SetCommPortDefaults ()
918 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
921 // [HGM] args: these three cases taken out to stay in front-end
923 SaveFontArg (FILE *f, ArgDescriptor *ad)
926 int i, n = (int)(intptr_t)ad->argLoc;
928 case 0: // CLOCK_FONT
929 name = appData.clockFont;
931 case 1: // MESSAGE_FONT
934 case 2: // COORD_FONT
935 name = appData.coordFont;
940 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
941 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
942 fontTable[n][squareSize] = strdup(name);
943 fontValid[n][squareSize] = True;
946 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
947 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
952 { // nothing to do, as the sounds are at all times represented by their text-string names already
956 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
957 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
958 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
962 SaveColor (FILE *f, ArgDescriptor *ad)
963 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
964 if(colorVariable[(int)(intptr_t)ad->argLoc])
965 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
969 SaveBoardSize (FILE *f, char *name, void *addr)
970 { // wrapper to shield back-end from BoardSize & sizeInfo
971 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
975 ParseCommPortSettings (char *s)
976 { // no such option in XBoard (yet)
979 extern Widget engineOutputShell;
983 GetActualPlacement (Widget wg, WindowPlacement *wp)
988 XWindowAttributes winAt;
995 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
996 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
997 wp->x = rx - winAt.x;
998 wp->y = ry - winAt.y;
999 wp->height = winAt.height;
1000 wp->width = winAt.width;
1001 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
1006 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1007 // In XBoard this will have to wait until awareness of window parameters is implemented
1008 GetActualPlacement(shellWidget, &wpMain);
1009 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1010 if(MoveHistoryIsUp()) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
1011 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1012 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1013 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
1014 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
1018 PrintCommPortSettings (FILE *f, char *name)
1019 { // This option does not exist in XBoard
1023 EnsureOnScreen (int *x, int *y, int minX, int minY)
1030 { // [HGM] args: allows testing if main window is realized from back-end
1031 return xBoardWindow != 0;
1035 PopUpStartupDialog ()
1036 { // start menu not implemented in XBoard
1040 ConvertToLine (int argc, char **argv)
1042 static char line[128*1024], buf[1024];
1046 for(i=1; i<argc; i++)
1048 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1049 && argv[i][0] != '{' )
1050 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1052 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1053 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1056 line[strlen(line)-1] = NULLCHAR;
1060 //--------------------------------------------------------------------------------------------
1063 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1065 #define BoardSize int
1067 InitDrawingSizes (BoardSize boardSize, int flags)
1068 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1069 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1071 XtGeometryResult gres;
1073 static Dimension oldWidth, oldHeight;
1074 static VariantClass oldVariant;
1075 static int oldDual = -1, oldMono = -1;
1077 if(!formWidget) return;
1079 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1080 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1081 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1083 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1085 * Enable shell resizing.
1087 shellArgs[0].value = (XtArgVal) &w;
1088 shellArgs[1].value = (XtArgVal) &h;
1089 XtGetValues(shellWidget, shellArgs, 2);
1091 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1092 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1093 XtSetValues(shellWidget, &shellArgs[2], 4);
1095 XtSetArg(args[0], XtNdefaultDistance, &sep);
1096 XtGetValues(formWidget, args, 1);
1098 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1100 hOffset = boardWidth + 10;
1101 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1102 secondSegments[i] = gridSegments[i];
1103 secondSegments[i].x1 += hOffset;
1104 secondSegments[i].x2 += hOffset;
1107 XtSetArg(args[0], XtNwidth, boardWidth);
1108 XtSetArg(args[1], XtNheight, boardHeight);
1109 XtSetValues(boardWidget, args, 2);
1111 timerWidth = (boardWidth - sep) / 2;
1112 XtSetArg(args[0], XtNwidth, timerWidth);
1113 XtSetValues(whiteTimerWidget, args, 1);
1114 XtSetValues(blackTimerWidget, args, 1);
1116 XawFormDoLayout(formWidget, False);
1118 if (appData.titleInWindow) {
1120 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1121 XtSetArg(args[i], XtNheight, &h); i++;
1122 XtGetValues(titleWidget, args, i);
1124 w = boardWidth - 2*bor;
1126 XtSetArg(args[0], XtNwidth, &w);
1127 XtGetValues(menuBarWidget, args, 1);
1128 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1131 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1132 if (gres != XtGeometryYes && appData.debugMode) {
1134 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1135 programName, gres, w, h, wr, hr);
1139 XawFormDoLayout(formWidget, True);
1142 * Inhibit shell resizing.
1144 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1145 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1146 shellArgs[4].value = shellArgs[2].value = w;
1147 shellArgs[5].value = shellArgs[3].value = h;
1148 XtSetValues(shellWidget, &shellArgs[0], 6);
1150 XSync(xDisplay, False);
1154 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1157 if(gameInfo.variant != oldVariant) { // and only if variant changed
1160 for(i=0; i<4; i++) {
1162 for(p=0; p<=(int)WhiteKing; p++)
1163 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1164 if(gameInfo.variant == VariantShogi) {
1165 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1166 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1167 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1168 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1169 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1172 if(gameInfo.variant == VariantGothic) {
1173 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1176 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1177 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1178 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1181 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1182 for(p=0; p<=(int)WhiteKing; p++)
1183 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1184 if(gameInfo.variant == VariantShogi) {
1185 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1186 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1187 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1188 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1189 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1192 if(gameInfo.variant == VariantGothic) {
1193 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1196 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1197 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1198 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1203 for(i=0; i<2; i++) {
1205 for(p=0; p<=(int)WhiteKing; p++)
1206 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1207 if(gameInfo.variant == VariantShogi) {
1208 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1209 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1210 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1211 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1212 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1215 if(gameInfo.variant == VariantGothic) {
1216 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1219 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1220 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1221 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1225 oldMono = -10; // kludge to force recreation of animation masks
1226 oldVariant = gameInfo.variant;
1229 if(appData.monoMode != oldMono)
1232 oldMono = appData.monoMode;
1237 MakeOneColor (char *name, Pixel *color)
1239 XrmValue vFrom, vTo;
1240 if (!appData.monoMode) {
1241 vFrom.addr = (caddr_t) name;
1242 vFrom.size = strlen(name);
1243 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1244 if (vTo.addr == NULL) {
1245 appData.monoMode = True;
1248 *color = *(Pixel *) vTo.addr;
1256 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1257 int forceMono = False;
1259 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1260 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1261 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1262 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1263 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1264 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1271 { // [HGM] taken out of main
1273 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1274 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1275 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1277 if (appData.bitmapDirectory[0] != NULLCHAR) {
1281 CreateXPMBoard(appData.liteBackTextureFile, 1);
1282 CreateXPMBoard(appData.darkBackTextureFile, 0);
1286 /* Create regular pieces */
1287 if (!useImages) CreatePieces();
1292 InitDrawingParams ()
1294 MakeColors(); CreateGCs(True);
1299 main (int argc, char **argv)
1301 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1302 XSetWindowAttributes window_attributes;
1304 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1305 XrmValue vFrom, vTo;
1306 XtGeometryResult gres;
1309 int forceMono = False;
1311 srandom(time(0)); // [HGM] book: make random truly random
1313 setbuf(stdout, NULL);
1314 setbuf(stderr, NULL);
1317 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1318 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1322 programName = strrchr(argv[0], '/');
1323 if (programName == NULL)
1324 programName = argv[0];
1329 XtSetLanguageProc(NULL, NULL, NULL);
1330 bindtextdomain(PACKAGE, LOCALEDIR);
1331 textdomain(PACKAGE);
1335 XtAppInitialize(&appContext, "XBoard", shellOptions,
1336 XtNumber(shellOptions),
1337 &argc, argv, xboardResources, NULL, 0);
1338 appData.boardSize = "";
1339 InitAppData(ConvertToLine(argc, argv));
1341 if (p == NULL) p = "/tmp";
1342 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1343 gameCopyFilename = (char*) malloc(i);
1344 gamePasteFilename = (char*) malloc(i);
1345 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1346 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1348 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1349 clientResources, XtNumber(clientResources),
1352 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1353 static char buf[MSG_SIZ];
1354 EscapeExpand(buf, appData.firstInitString);
1355 appData.firstInitString = strdup(buf);
1356 EscapeExpand(buf, appData.secondInitString);
1357 appData.secondInitString = strdup(buf);
1358 EscapeExpand(buf, appData.firstComputerString);
1359 appData.firstComputerString = strdup(buf);
1360 EscapeExpand(buf, appData.secondComputerString);
1361 appData.secondComputerString = strdup(buf);
1364 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1367 if (chdir(chessDir) != 0) {
1368 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1374 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1375 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1376 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1377 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1380 setbuf(debugFP, NULL);
1384 if (appData.debugMode) {
1385 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1389 /* [HGM,HR] make sure board size is acceptable */
1390 if(appData.NrFiles > BOARD_FILES ||
1391 appData.NrRanks > BOARD_RANKS )
1392 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1395 /* This feature does not work; animation needs a rewrite */
1396 appData.highlightDragging = FALSE;
1400 xDisplay = XtDisplay(shellWidget);
1401 xScreen = DefaultScreen(xDisplay);
1402 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1404 gameInfo.variant = StringToVariant(appData.variant);
1405 InitPosition(FALSE);
1408 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1410 if (isdigit(appData.boardSize[0])) {
1411 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1412 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1413 &fontPxlSize, &smallLayout, &tinyLayout);
1415 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1416 programName, appData.boardSize);
1420 /* Find some defaults; use the nearest known size */
1421 SizeDefaults *szd, *nearest;
1422 int distance = 99999;
1423 nearest = szd = sizeDefaults;
1424 while (szd->name != NULL) {
1425 if (abs(szd->squareSize - squareSize) < distance) {
1427 distance = abs(szd->squareSize - squareSize);
1428 if (distance == 0) break;
1432 if (i < 2) lineGap = nearest->lineGap;
1433 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1434 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1435 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1436 if (i < 6) smallLayout = nearest->smallLayout;
1437 if (i < 7) tinyLayout = nearest->tinyLayout;
1440 SizeDefaults *szd = sizeDefaults;
1441 if (*appData.boardSize == NULLCHAR) {
1442 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1443 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1446 if (szd->name == NULL) szd--;
1447 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1449 while (szd->name != NULL &&
1450 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1451 if (szd->name == NULL) {
1452 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1453 programName, appData.boardSize);
1457 squareSize = szd->squareSize;
1458 lineGap = szd->lineGap;
1459 clockFontPxlSize = szd->clockFontPxlSize;
1460 coordFontPxlSize = szd->coordFontPxlSize;
1461 fontPxlSize = szd->fontPxlSize;
1462 smallLayout = szd->smallLayout;
1463 tinyLayout = szd->tinyLayout;
1464 // [HGM] font: use defaults from settings file if available and not overruled
1466 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1467 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1468 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1469 appData.font = fontTable[MESSAGE_FONT][squareSize];
1470 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1471 appData.coordFont = fontTable[COORD_FONT][squareSize];
1473 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1474 if (strlen(appData.pixmapDirectory) > 0) {
1475 p = ExpandPathName(appData.pixmapDirectory);
1477 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1478 appData.pixmapDirectory);
1481 if (appData.debugMode) {
1482 fprintf(stderr, _("\
1483 XBoard square size (hint): %d\n\
1484 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1486 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1487 if (appData.debugMode) {
1488 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1491 defaultLineGap = lineGap;
1492 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1494 /* [HR] height treated separately (hacked) */
1495 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1496 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1497 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1498 XtSetArg(boardArgs[2], XtNheight, boardHeight);
1501 * Determine what fonts to use.
1504 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1505 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1506 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1507 fontSet = CreateFontSet(appData.font);
1508 clockFontSet = CreateFontSet(appData.clockFont);
1510 /* For the coordFont, use the 0th font of the fontset. */
1511 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1512 XFontStruct **font_struct_list;
1513 XFontSetExtents *fontSize;
1514 char **font_name_list;
1515 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1516 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1517 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1518 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1519 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1522 appData.font = FindFont(appData.font, fontPxlSize);
1523 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1524 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1525 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1526 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1527 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1528 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1530 countFontID = coordFontID; // [HGM] holdings
1531 countFontStruct = coordFontStruct;
1533 xdb = XtDatabase(xDisplay);
1535 XrmPutLineResource(&xdb, "*international: True");
1536 vTo.size = sizeof(XFontSet);
1537 vTo.addr = (XtPointer) &fontSet;
1538 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1540 XrmPutStringResource(&xdb, "*font", appData.font);
1544 * Detect if there are not enough colors available and adapt.
1546 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1547 appData.monoMode = True;
1550 forceMono = MakeColors();
1553 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1555 appData.monoMode = True;
1558 if (appData.lowTimeWarning && !appData.monoMode) {
1559 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1560 vFrom.size = strlen(appData.lowTimeWarningColor);
1561 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1562 if (vTo.addr == NULL)
1563 appData.monoMode = True;
1565 lowTimeWarningColor = *(Pixel *) vTo.addr;
1568 if (appData.monoMode && appData.debugMode) {
1569 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1570 (unsigned long) XWhitePixel(xDisplay, xScreen),
1571 (unsigned long) XBlackPixel(xDisplay, xScreen));
1574 ParseIcsTextColors();
1576 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1582 layoutName = "tinyLayout";
1583 } else if (smallLayout) {
1584 layoutName = "smallLayout";
1586 layoutName = "normalLayout";
1588 /* Outer layoutWidget is there only to provide a name for use in
1589 resources that depend on the layout style */
1591 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
1592 layoutArgs, XtNumber(layoutArgs));
1594 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
1595 formArgs, XtNumber(formArgs));
1596 XtSetArg(args[0], XtNdefaultDistance, &sep);
1597 XtGetValues(formWidget, args, 1);
1600 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
1601 XtSetArg(args[0], XtNtop, XtChainTop);
1602 XtSetArg(args[1], XtNbottom, XtChainTop);
1603 XtSetArg(args[2], XtNright, XtChainLeft);
1604 XtSetValues(menuBarWidget, args, 3);
1606 widgetList[j++] = whiteTimerWidget =
1607 XtCreateWidget("whiteTime", labelWidgetClass,
1608 formWidget, timerArgs, XtNumber(timerArgs));
1610 XtSetArg(args[0], XtNfontSet, clockFontSet);
1612 XtSetArg(args[0], XtNfont, clockFontStruct);
1614 XtSetArg(args[1], XtNtop, XtChainTop);
1615 XtSetArg(args[2], XtNbottom, XtChainTop);
1616 XtSetValues(whiteTimerWidget, args, 3);
1618 widgetList[j++] = blackTimerWidget =
1619 XtCreateWidget("blackTime", labelWidgetClass,
1620 formWidget, timerArgs, XtNumber(timerArgs));
1622 XtSetArg(args[0], XtNfontSet, clockFontSet);
1624 XtSetArg(args[0], XtNfont, clockFontStruct);
1626 XtSetArg(args[1], XtNtop, XtChainTop);
1627 XtSetArg(args[2], XtNbottom, XtChainTop);
1628 XtSetValues(blackTimerWidget, args, 3);
1630 if (appData.titleInWindow) {
1631 widgetList[j++] = titleWidget =
1632 XtCreateWidget("title", labelWidgetClass, formWidget,
1633 titleArgs, XtNumber(titleArgs));
1634 XtSetArg(args[0], XtNtop, XtChainTop);
1635 XtSetArg(args[1], XtNbottom, XtChainTop);
1636 XtSetValues(titleWidget, args, 2);
1639 if (appData.showButtonBar) {
1640 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
1641 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
1642 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
1643 XtSetArg(args[2], XtNtop, XtChainTop);
1644 XtSetArg(args[3], XtNbottom, XtChainTop);
1645 XtSetValues(buttonBarWidget, args, 4);
1648 widgetList[j++] = messageWidget =
1649 XtCreateWidget("message", labelWidgetClass, formWidget,
1650 messageArgs, XtNumber(messageArgs));
1651 XtSetArg(args[0], XtNtop, XtChainTop);
1652 XtSetArg(args[1], XtNbottom, XtChainTop);
1653 XtSetValues(messageWidget, args, 2);
1655 widgetList[j++] = boardWidget =
1656 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
1657 XtNumber(boardArgs));
1659 XtManageChildren(widgetList, j);
1661 timerWidth = (boardWidth - sep) / 2;
1662 XtSetArg(args[0], XtNwidth, timerWidth);
1663 XtSetValues(whiteTimerWidget, args, 1);
1664 XtSetValues(blackTimerWidget, args, 1);
1666 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1667 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1668 XtGetValues(whiteTimerWidget, args, 2);
1670 if (appData.showButtonBar) {
1671 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1672 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1673 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
1677 * formWidget uses these constraints but they are stored
1681 XtSetArg(args[i], XtNfromHoriz, 0); i++;
1682 XtSetValues(menuBarWidget, args, i);
1683 if (appData.titleInWindow) {
1686 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1687 XtSetValues(whiteTimerWidget, args, i);
1689 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1690 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1691 XtSetValues(blackTimerWidget, args, i);
1693 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1694 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
1695 XtSetValues(titleWidget, args, i);
1697 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1698 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1699 XtSetValues(messageWidget, args, i);
1700 if (appData.showButtonBar) {
1702 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1703 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1704 XtSetValues(buttonBarWidget, args, i);
1708 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1709 XtSetValues(whiteTimerWidget, args, i);
1711 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1712 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1713 XtSetValues(blackTimerWidget, args, i);
1715 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
1716 XtSetValues(titleWidget, args, i);
1718 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1719 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1720 XtSetValues(messageWidget, args, i);
1721 if (appData.showButtonBar) {
1723 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1724 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1725 XtSetValues(buttonBarWidget, args, i);
1730 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1731 XtSetValues(whiteTimerWidget, args, i);
1733 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1734 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1735 XtSetValues(blackTimerWidget, args, i);
1737 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1738 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1739 XtSetValues(messageWidget, args, i);
1740 if (appData.showButtonBar) {
1742 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1743 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1744 XtSetValues(buttonBarWidget, args, i);
1748 XtSetArg(args[0], XtNfromVert, messageWidget);
1749 XtSetArg(args[1], XtNtop, XtChainTop);
1750 XtSetArg(args[2], XtNbottom, XtChainBottom);
1751 XtSetArg(args[3], XtNleft, XtChainLeft);
1752 XtSetArg(args[4], XtNright, XtChainRight);
1753 XtSetValues(boardWidget, args, 5);
1755 XtRealizeWidget(shellWidget);
1758 XtSetArg(args[0], XtNx, wpMain.x);
1759 XtSetArg(args[1], XtNy, wpMain.y);
1760 XtSetValues(shellWidget, args, 2);
1764 * Correct the width of the message and title widgets.
1765 * It is not known why some systems need the extra fudge term.
1766 * The value "2" is probably larger than needed.
1768 XawFormDoLayout(formWidget, False);
1770 #define WIDTH_FUDGE 2
1772 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1773 XtSetArg(args[i], XtNheight, &h); i++;
1774 XtGetValues(messageWidget, args, i);
1775 if (appData.showButtonBar) {
1777 XtSetArg(args[i], XtNwidth, &w); i++;
1778 XtGetValues(buttonBarWidget, args, i);
1779 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1781 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
1784 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1785 if (gres != XtGeometryYes && appData.debugMode) {
1786 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1787 programName, gres, w, h, wr, hr);
1790 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
1791 /* The size used for the child widget in layout lags one resize behind
1792 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
1794 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1795 if (gres != XtGeometryYes && appData.debugMode) {
1796 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1797 programName, gres, w, h, wr, hr);
1800 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
1801 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
1802 XtSetArg(args[1], XtNright, XtChainRight);
1803 XtSetValues(messageWidget, args, 2);
1805 if (appData.titleInWindow) {
1807 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1808 XtSetArg(args[i], XtNheight, &h); i++;
1809 XtGetValues(titleWidget, args, i);
1811 w = boardWidth - 2*bor;
1813 XtSetArg(args[0], XtNwidth, &w);
1814 XtGetValues(menuBarWidget, args, 1);
1815 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1818 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1819 if (gres != XtGeometryYes && appData.debugMode) {
1821 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1822 programName, gres, w, h, wr, hr);
1825 XawFormDoLayout(formWidget, True);
1827 xBoardWindow = XtWindow(boardWidget);
1829 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1830 // not need to go into InitDrawingSizes().
1834 * Create X checkmark bitmap and initialize option menu checks.
1836 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1837 checkmark_bits, checkmark_width, checkmark_height);
1843 ReadBitmap(&wIconPixmap, "icon_white.bm",
1844 icon_white_bits, icon_white_width, icon_white_height);
1845 ReadBitmap(&bIconPixmap, "icon_black.bm",
1846 icon_black_bits, icon_black_width, icon_black_height);
1847 iconPixmap = wIconPixmap;
1849 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1850 XtSetValues(shellWidget, args, i);
1853 * Create a cursor for the board widget.
1855 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1856 XChangeWindowAttributes(xDisplay, xBoardWindow,
1857 CWCursor, &window_attributes);
1860 * Inhibit shell resizing.
1862 shellArgs[0].value = (XtArgVal) &w;
1863 shellArgs[1].value = (XtArgVal) &h;
1864 XtGetValues(shellWidget, shellArgs, 2);
1865 shellArgs[4].value = shellArgs[2].value = w;
1866 shellArgs[5].value = shellArgs[3].value = h;
1867 XtSetValues(shellWidget, &shellArgs[2], 4);
1868 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1869 marginH = h - boardHeight;
1871 CatchDeleteWindow(shellWidget, "QuitProc");
1879 if (appData.animate || appData.animateDragging)
1882 XtAugmentTranslations(formWidget,
1883 XtParseTranslationTable(globalTranslations));
1884 XtAugmentTranslations(boardWidget,
1885 XtParseTranslationTable(boardTranslations));
1886 XtAugmentTranslations(whiteTimerWidget,
1887 XtParseTranslationTable(whiteTranslations));
1888 XtAugmentTranslations(blackTimerWidget,
1889 XtParseTranslationTable(blackTranslations));
1891 /* Why is the following needed on some versions of X instead
1892 * of a translation? */
1893 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
1894 (XtEventHandler) EventProc, NULL);
1896 XtAddEventHandler(formWidget, KeyPressMask, False,
1897 (XtEventHandler) MoveTypeInProc, NULL);
1898 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1899 (XtEventHandler) EventProc, NULL);
1901 /* [AS] Restore layout */
1902 if( wpMoveHistory.visible ) {
1906 if( wpEvalGraph.visible )
1911 if( wpEngineOutput.visible ) {
1912 EngineOutputPopUp();
1917 if (errorExitStatus == -1) {
1918 if (appData.icsActive) {
1919 /* We now wait until we see "login:" from the ICS before
1920 sending the logon script (problems with timestamp otherwise) */
1921 /*ICSInitScript();*/
1922 if (appData.icsInputBox) ICSInputBoxPopUp();
1926 signal(SIGWINCH, TermSizeSigHandler);
1928 signal(SIGINT, IntSigHandler);
1929 signal(SIGTERM, IntSigHandler);
1930 if (*appData.cmailGameName != NULLCHAR) {
1931 signal(SIGUSR1, CmailSigHandler);
1935 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1937 // XtSetKeyboardFocus(shellWidget, formWidget);
1938 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1940 XtAppMainLoop(appContext);
1941 if (appData.debugMode) fclose(debugFP); // [DM] debug
1946 TermSizeSigHandler (int sig)
1952 IntSigHandler (int sig)
1958 CmailSigHandler (int sig)
1963 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1965 /* Activate call-back function CmailSigHandlerCallBack() */
1966 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1968 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1972 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1975 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1977 /**** end signal code ****/
1980 #define Abs(n) ((n)<0 ? -(n) : (n))
1984 InsertPxlSize (char *pattern, int targetPxlSize)
1986 char *base_fnt_lst, strInt[12], *p, *q;
1987 int alternatives, i, len, strIntLen;
1990 * Replace the "*" (if present) in the pixel-size slot of each
1991 * alternative with the targetPxlSize.
1995 while ((p = strchr(p, ',')) != NULL) {
1999 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
2000 strIntLen = strlen(strInt);
2001 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
2005 while (alternatives--) {
2006 char *comma = strchr(p, ',');
2007 for (i=0; i<14; i++) {
2008 char *hyphen = strchr(p, '-');
2010 if (comma && hyphen > comma) break;
2011 len = hyphen + 1 - p;
2012 if (i == 7 && *p == '*' && len == 2) {
2014 memcpy(q, strInt, strIntLen);
2024 len = comma + 1 - p;
2031 return base_fnt_lst;
2035 CreateFontSet (char *base_fnt_lst)
2038 char **missing_list;
2042 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
2043 &missing_list, &missing_count, &def_string);
2044 if (appData.debugMode) {
2046 XFontStruct **font_struct_list;
2047 char **font_name_list;
2048 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
2050 fprintf(debugFP, " got list %s, locale %s\n",
2051 XBaseFontNameListOfFontSet(fntSet),
2052 XLocaleOfFontSet(fntSet));
2053 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
2054 for (i = 0; i < count; i++) {
2055 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
2058 for (i = 0; i < missing_count; i++) {
2059 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
2062 if (fntSet == NULL) {
2063 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
2068 #else // not ENABLE_NLS
2070 * Find a font that matches "pattern" that is as close as
2071 * possible to the targetPxlSize. Prefer fonts that are k
2072 * pixels smaller to fonts that are k pixels larger. The
2073 * pattern must be in the X Consortium standard format,
2074 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2075 * The return value should be freed with XtFree when no
2079 FindFont (char *pattern, int targetPxlSize)
2081 char **fonts, *p, *best, *scalable, *scalableTail;
2082 int i, j, nfonts, minerr, err, pxlSize;
2084 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2086 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2087 programName, pattern);
2094 for (i=0; i<nfonts; i++) {
2097 if (*p != '-') continue;
2099 if (*p == NULLCHAR) break;
2100 if (*p++ == '-') j++;
2102 if (j < 7) continue;
2105 scalable = fonts[i];
2108 err = pxlSize - targetPxlSize;
2109 if (Abs(err) < Abs(minerr) ||
2110 (minerr > 0 && err < 0 && -err == minerr)) {
2116 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2117 /* If the error is too big and there is a scalable font,
2118 use the scalable font. */
2119 int headlen = scalableTail - scalable;
2120 p = (char *) XtMalloc(strlen(scalable) + 10);
2121 while (isdigit(*scalableTail)) scalableTail++;
2122 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2124 p = (char *) XtMalloc(strlen(best) + 2);
2125 safeStrCpy(p, best, strlen(best)+1 );
2127 if (appData.debugMode) {
2128 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2129 pattern, targetPxlSize, p);
2131 XFreeFontNames(fonts);
2138 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
2139 // must be called before all non-first callse to CreateGCs()
2140 XtReleaseGC(shellWidget, highlineGC);
2141 XtReleaseGC(shellWidget, lightSquareGC);
2142 XtReleaseGC(shellWidget, darkSquareGC);
2143 XtReleaseGC(shellWidget, lineGC);
2144 if (appData.monoMode) {
2145 if (DefaultDepth(xDisplay, xScreen) == 1) {
2146 XtReleaseGC(shellWidget, wbPieceGC);
2148 XtReleaseGC(shellWidget, bwPieceGC);
2151 XtReleaseGC(shellWidget, prelineGC);
2152 XtReleaseGC(shellWidget, wdPieceGC);
2153 XtReleaseGC(shellWidget, wlPieceGC);
2154 XtReleaseGC(shellWidget, bdPieceGC);
2155 XtReleaseGC(shellWidget, blPieceGC);
2160 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
2162 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2163 | GCBackground | GCFunction | GCPlaneMask;
2164 gc_values->foreground = foreground;
2165 gc_values->background = background;
2166 return XtGetGC(shellWidget, value_mask, gc_values);
2170 CreateGCs (int redo)
2172 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2173 | GCBackground | GCFunction | GCPlaneMask;
2174 XGCValues gc_values;
2176 Pixel white = XWhitePixel(xDisplay, xScreen);
2177 Pixel black = XBlackPixel(xDisplay, xScreen);
2179 gc_values.plane_mask = AllPlanes;
2180 gc_values.line_width = lineGap;
2181 gc_values.line_style = LineSolid;
2182 gc_values.function = GXcopy;
2185 DeleteGCs(); // called a second time; clean up old GCs first
2186 } else { // [HGM] grid and font GCs created on first call only
2187 coordGC = CreateOneGC(&gc_values, black, white);
2188 XSetFont(xDisplay, coordGC, coordFontID);
2190 // [HGM] make font for holdings counts (white on black)
2191 countGC = CreateOneGC(&gc_values, white, black);
2192 XSetFont(xDisplay, countGC, countFontID);
2194 lineGC = CreateOneGC(&gc_values, black, black);
2196 if (appData.monoMode) {
2198 highlineGC = CreateOneGC(&gc_values, white, white);
2199 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
2200 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
2202 if (DefaultDepth(xDisplay, xScreen) == 1) {
2203 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2204 gc_values.function = GXcopyInverted;
2205 copyInvertedGC = CreateOneGC(&gc_values, black, white);
2206 gc_values.function = GXcopy;
2207 if (XBlackPixel(xDisplay, xScreen) == 1) {
2208 bwPieceGC = darkSquareGC;
2209 wbPieceGC = copyInvertedGC;
2211 bwPieceGC = copyInvertedGC;
2212 wbPieceGC = lightSquareGC;
2217 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
2218 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
2219 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
2220 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
2221 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
2222 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
2223 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
2224 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
2229 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
2237 fp = fopen(filename, "rb");
2239 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
2246 for (y=0; y<h; ++y) {
2247 for (x=0; x<h; ++x) {
2252 XPutPixel(xim, x, y, blackPieceColor);
2254 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2257 XPutPixel(xim, x, y, darkSquareColor);
2259 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2262 XPutPixel(xim, x, y, whitePieceColor);
2264 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2267 XPutPixel(xim, x, y, lightSquareColor);
2269 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2277 /* create Pixmap of piece */
2278 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2280 XPutImage(xDisplay, *dest, lightSquareGC, xim,
2283 /* create Pixmap of clipmask
2284 Note: We assume the white/black pieces have the same
2285 outline, so we make only 6 masks. This is okay
2286 since the XPM clipmask routines do the same. */
2288 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2290 XPutImage(xDisplay, temp, lightSquareGC, xmask,
2293 /* now create the 1-bit version */
2294 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2297 values.foreground = 1;
2298 values.background = 0;
2300 /* Don't use XtGetGC, not read only */
2301 maskGC = XCreateGC(xDisplay, *mask,
2302 GCForeground | GCBackground, &values);
2303 XCopyPlane(xDisplay, temp, *mask, maskGC,
2304 0, 0, squareSize, squareSize, 0, 0, 1);
2305 XFreePixmap(xDisplay, temp);
2310 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
2318 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
2323 /* The XSynchronize calls were copied from CreatePieces.
2324 Not sure if needed, but can't hurt */
2325 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2328 /* temp needed by loadXIM() */
2329 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2330 0, 0, ss, ss, AllPlanes, XYPixmap);
2332 if (strlen(appData.pixmapDirectory) == 0) {
2336 if (appData.monoMode) {
2337 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
2341 fprintf(stderr, _("\nLoading XIMs...\n"));
2343 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2344 fprintf(stderr, "%d", piece+1);
2345 for (kind=0; kind<4; kind++) {
2346 fprintf(stderr, ".");
2347 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2348 ExpandPathName(appData.pixmapDirectory),
2349 piece <= (int) WhiteKing ? "" : "w",
2350 pieceBitmapNames[piece],
2352 ximPieceBitmap[kind][piece] =
2353 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2354 0, 0, ss, ss, AllPlanes, XYPixmap);
2355 if (appData.debugMode)
2356 fprintf(stderr, _("(File:%s:) "), buf);
2357 loadXIM(ximPieceBitmap[kind][piece],
2359 &(xpmPieceBitmap2[kind][piece]),
2360 &(ximMaskPm2[piece]));
2361 if(piece <= (int)WhiteKing)
2362 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2364 fprintf(stderr," ");
2366 /* Load light and dark squares */
2367 /* If the LSQ and DSQ pieces don't exist, we will
2368 draw them with solid squares. */
2369 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2370 if (access(buf, 0) != 0) {
2374 fprintf(stderr, _("light square "));
2376 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2377 0, 0, ss, ss, AllPlanes, XYPixmap);
2378 if (appData.debugMode)
2379 fprintf(stderr, _("(File:%s:) "), buf);
2381 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2382 fprintf(stderr, _("dark square "));
2383 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2384 ExpandPathName(appData.pixmapDirectory), ss);
2385 if (appData.debugMode)
2386 fprintf(stderr, _("(File:%s:) "), buf);
2388 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2389 0, 0, ss, ss, AllPlanes, XYPixmap);
2390 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2391 xpmJailSquare = xpmLightSquare;
2393 fprintf(stderr, _("Done.\n"));
2395 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2398 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2402 CreateXPMBoard (char *s, int kind)
2406 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2407 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2408 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2414 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2415 // thisroutine has to be called t free the old piece pixmaps
2417 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2418 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2420 XFreePixmap(xDisplay, xpmLightSquare);
2421 XFreePixmap(xDisplay, xpmDarkSquare);
2430 u_int ss = squareSize;
2432 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2433 XpmColorSymbol symbols[4];
2434 static int redo = False;
2436 if(redo) FreeXPMPieces(); else redo = 1;
2438 /* The XSynchronize calls were copied from CreatePieces.
2439 Not sure if needed, but can't hurt */
2440 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2442 /* Setup translations so piece colors match square colors */
2443 symbols[0].name = "light_piece";
2444 symbols[0].value = appData.whitePieceColor;
2445 symbols[1].name = "dark_piece";
2446 symbols[1].value = appData.blackPieceColor;
2447 symbols[2].name = "light_square";
2448 symbols[2].value = appData.lightSquareColor;
2449 symbols[3].name = "dark_square";
2450 symbols[3].value = appData.darkSquareColor;
2452 attr.valuemask = XpmColorSymbols;
2453 attr.colorsymbols = symbols;
2454 attr.numsymbols = 4;
2456 if (appData.monoMode) {
2457 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2461 if (strlen(appData.pixmapDirectory) == 0) {
2462 XpmPieces* pieces = builtInXpms;
2465 while (pieces->size != squareSize && pieces->size) pieces++;
2466 if (!pieces->size) {
2467 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2470 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2471 for (kind=0; kind<4; kind++) {
2473 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2474 pieces->xpm[piece][kind],
2475 &(xpmPieceBitmap2[kind][piece]),
2476 NULL, &attr)) != 0) {
2477 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2481 if(piece <= (int) WhiteKing)
2482 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2486 xpmJailSquare = xpmLightSquare;
2490 fprintf(stderr, _("\nLoading XPMs...\n"));
2493 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2494 fprintf(stderr, "%d ", piece+1);
2495 for (kind=0; kind<4; kind++) {
2496 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2497 ExpandPathName(appData.pixmapDirectory),
2498 piece > (int) WhiteKing ? "w" : "",
2499 pieceBitmapNames[piece],
2501 if (appData.debugMode) {
2502 fprintf(stderr, _("(File:%s:) "), buf);
2504 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2505 &(xpmPieceBitmap2[kind][piece]),
2506 NULL, &attr)) != 0) {
2507 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2508 // [HGM] missing: read of unorthodox piece failed; substitute King.
2509 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2510 ExpandPathName(appData.pixmapDirectory),
2512 if (appData.debugMode) {
2513 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2515 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2516 &(xpmPieceBitmap2[kind][piece]),
2520 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2525 if(piece <= (int) WhiteKing)
2526 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2529 /* Load light and dark squares */
2530 /* If the LSQ and DSQ pieces don't exist, we will
2531 draw them with solid squares. */
2532 fprintf(stderr, _("light square "));
2533 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2534 if (access(buf, 0) != 0) {
2538 if (appData.debugMode)
2539 fprintf(stderr, _("(File:%s:) "), buf);
2541 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2542 &xpmLightSquare, NULL, &attr)) != 0) {
2543 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2546 fprintf(stderr, _("dark square "));
2547 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2548 ExpandPathName(appData.pixmapDirectory), ss);
2549 if (appData.debugMode) {
2550 fprintf(stderr, _("(File:%s:) "), buf);
2552 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2553 &xpmDarkSquare, NULL, &attr)) != 0) {
2554 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2558 xpmJailSquare = xpmLightSquare;
2559 fprintf(stderr, _("Done.\n"));
2561 oldVariant = -1; // kludge to force re-makig of animation masks
2562 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2565 #endif /* HAVE_LIBXPM */
2568 /* No built-in bitmaps */
2573 u_int ss = squareSize;
2575 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2578 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2579 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2580 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2581 pieceBitmapNames[piece],
2582 ss, kind == SOLID ? 's' : 'o');
2583 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2584 if(piece <= (int)WhiteKing)
2585 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2589 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2593 /* With built-in bitmaps */
2597 BuiltInBits* bib = builtInBits;
2600 u_int ss = squareSize;
2602 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2605 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2607 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2608 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2609 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2610 pieceBitmapNames[piece],
2611 ss, kind == SOLID ? 's' : 'o');
2612 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2613 bib->bits[kind][piece], ss, ss);
2614 if(piece <= (int)WhiteKing)
2615 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2619 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2625 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2630 char msg[MSG_SIZ], fullname[MSG_SIZ];
2632 if (*appData.bitmapDirectory != NULLCHAR) {
2633 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2634 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2635 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2636 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2637 &w, &h, pm, &x_hot, &y_hot);
2638 fprintf(stderr, "load %s\n", name);
2639 if (errcode != BitmapSuccess) {
2641 case BitmapOpenFailed:
2642 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2644 case BitmapFileInvalid:
2645 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2647 case BitmapNoMemory:
2648 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2652 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2656 fprintf(stderr, _("%s: %s...using built-in\n"),
2658 } else if (w != wreq || h != hreq) {
2660 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2661 programName, fullname, w, h, wreq, hreq);
2667 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2677 if (lineGap == 0) return;
2679 /* [HR] Split this into 2 loops for non-square boards. */
2681 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2682 gridSegments[i].x1 = 0;
2683 gridSegments[i].x2 =
2684 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2685 gridSegments[i].y1 = gridSegments[i].y2
2686 = lineGap / 2 + (i * (squareSize + lineGap));
2689 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2690 gridSegments[j + i].y1 = 0;
2691 gridSegments[j + i].y2 =
2692 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2693 gridSegments[j + i].x1 = gridSegments[j + i].x2
2694 = lineGap / 2 + (j * (squareSize + lineGap));
2698 int nrOfMenuItems = 7;
2699 Widget menuWidget[150];
2700 MenuListItem menuItemList[150] = {
2701 { "LoadNextGameProc", LoadNextGameProc },
2702 { "LoadPrevGameProc", LoadPrevGameProc },
2703 { "ReloadGameProc", ReloadGameProc },
2704 { "ReloadPositionProc", ReloadPositionProc },
2705 #ifndef OPTIONSDIALOG
2706 { "AlwaysQueenProc", AlwaysQueenProc },
2707 { "AnimateDraggingProc", AnimateDraggingProc },
2708 { "AnimateMovingProc", AnimateMovingProc },
2709 { "AutoflagProc", AutoflagProc },
2710 { "AutoflipProc", AutoflipProc },
2711 { "BlindfoldProc", BlindfoldProc },
2712 { "FlashMovesProc", FlashMovesProc },
2714 { "HighlightDraggingProc", HighlightDraggingProc },
2716 { "HighlightLastMoveProc", HighlightLastMoveProc },
2717 // { "IcsAlarmProc", IcsAlarmProc },
2718 { "MoveSoundProc", MoveSoundProc },
2719 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
2720 { "PopupExitMessageProc", PopupExitMessageProc },
2721 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
2722 // { "PremoveProc", PremoveProc },
2723 { "ShowCoordsProc", ShowCoordsProc },
2724 { "ShowThinkingProc", ShowThinkingProc },
2725 { "HideThinkingProc", HideThinkingProc },
2726 { "TestLegalityProc", TestLegalityProc },
2728 { "AboutGameProc", AboutGameEvent },
2729 { "DebugProc", DebugProc },
2730 { "NothingProc", NothingProc },
2735 MarkMenuItem (char *menuRef, int state)
2737 int nr = MenuToNumber(menuRef);
2740 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2741 XtSetValues(menuWidget[nr], args, 1);
2746 EnableMenuItem (char *menuRef, int state)
2748 int nr = MenuToNumber(menuRef);
2749 if(nr >= 0) XtSetSensitive(menuWidget[nr], state);
2753 EnableButtonBar (int state)
2755 XtSetSensitive(buttonBarWidget, state);
2760 SetMenuEnables (Enables *enab)
2762 while (enab->name != NULL) {
2763 EnableMenuItem(enab->name, enab->value);
2769 Equal(char *p, char *s)
2770 { // compare strings skipping spaces in second
2772 if(*s == ' ') { s++; continue; }
2773 if(*s++ != *p++) return 0;
2779 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2780 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2782 if(*nprms == 0) return;
2783 for(i=0; menuItemList[i].name; i++) {
2784 if(Equal(prms[0], menuItemList[i].name)) {
2785 (menuItemList[i].proc) ();
2792 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
2794 MenuProc *proc = (MenuProc *) addr;
2800 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2802 RecentEngineEvent((int) (intptr_t) addr);
2805 // some stuff that must remain in front-end
2806 static Widget mainBar, currentMenu;
2807 static int wtot, nr = 0, widths[10];
2810 AppendMenuItem (char *text, char *name, MenuProc *action)
2817 XtSetArg(args[j], XtNleftMargin, 20); j++;
2818 XtSetArg(args[j], XtNrightMargin, 20); j++;
2820 if (strcmp(text, "----") == 0) {
2821 entry = XtCreateManagedWidget(text, smeLineObjectClass,
2822 currentMenu, args, j);
2824 XtSetArg(args[j], XtNlabel, XtNewString(_(text)));
2825 entry = XtCreateManagedWidget(name, smeBSBObjectClass,
2826 currentMenu, args, j+1);
2827 XtAddCallback(entry, XtNcallback,
2828 (XtCallbackProc) (strcmp(name, "recent") ? MenuBarSelect : MenuEngineSelect),
2830 menuWidget[nrOfMenuItems] = entry;
2835 CreateMenuButton (char *name, Menu *mb)
2836 { // create menu button on main bar, and shell for pull-down list
2842 XtSetArg(args[j], XtNmenuName, XtNewString(name)); j++;
2843 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
2844 XtSetArg(args[j], XtNborderWidth, 0); j++;
2845 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
2847 currentMenu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2850 XtSetArg(args[j], XtNwidth, &w); j++;
2851 XtGetValues(mb->subMenu, args, j);
2852 wtot += mb->textWidth = widths[nr++] = w;
2856 CreateMenuBar (Menu *mb, int boardWidth)
2860 char menuName[MSG_SIZ];
2864 // create bar itself
2866 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2867 XtSetArg(args[j], XtNvSpace, 0); j++;
2868 XtSetArg(args[j], XtNborderWidth, 0); j++;
2869 mainBar = XtCreateWidget("menuBar", boxWidgetClass,
2870 formWidget, args, j);
2872 CreateMainMenus(mb); // put menus in bar according to description in back-end
2874 // size buttons to make menu bar fit, clipping menu names where necessary
2875 while(wtot > boardWidth - 40) {
2877 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
2881 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
2883 XtSetArg(args[j], XtNwidth, widths[i]); j++;
2884 XtSetValues(ma[i].subMenu, args, j);
2891 CreateButtonBar (MenuItem *mi)
2894 Widget button, buttonBar;
2898 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2900 XtSetArg(args[j], XtNhSpace, 0); j++;
2902 XtSetArg(args[j], XtNborderWidth, 0); j++;
2903 XtSetArg(args[j], XtNvSpace, 0); j++;
2904 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
2905 formWidget, args, j);
2907 while (mi->string != NULL) {
2910 XtSetArg(args[j], XtNinternalWidth, 2); j++;
2911 XtSetArg(args[j], XtNborderWidth, 0); j++;
2913 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
2914 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
2915 buttonBar, args, j);
2916 XtAddCallback(button, XtNcallback,
2917 (XtCallbackProc) MenuBarSelect,
2918 (caddr_t) mi->proc);
2925 CreatePieceMenu (char *name, int color)
2930 ChessSquare selection;
2932 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2933 boardWidget, args, 0);
2935 for (i = 0; i < PIECE_MENU_SIZE; i++) {
2936 String item = pieceMenuStrings[color][i];
2938 if (strcmp(item, "----") == 0) {
2939 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2942 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2943 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2945 selection = pieceMenuTranslation[color][i];
2946 XtAddCallback(entry, XtNcallback,
2947 (XtCallbackProc) PieceMenuSelect,
2948 (caddr_t) selection);
2949 if (selection == WhitePawn || selection == BlackPawn) {
2950 XtSetArg(args[0], XtNpopupOnEntry, entry);
2951 XtSetValues(menu, args, 1);
2964 ChessSquare selection;
2966 whitePieceMenu = CreatePieceMenu("menuW", 0);
2967 blackPieceMenu = CreatePieceMenu("menuB", 1);
2969 if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
2970 XtRegisterGrabAction(PieceMenuPopup, True,
2971 (unsigned)(ButtonPressMask|ButtonReleaseMask),
2972 GrabModeAsync, GrabModeAsync);
2974 XtSetArg(args[0], XtNlabel, _("Drop"));
2975 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
2976 boardWidget, args, 1);
2977 for (i = 0; i < DROP_MENU_SIZE; i++) {
2978 String item = dropMenuStrings[i];
2980 if (strcmp(item, "----") == 0) {
2981 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2984 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2985 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2987 selection = dropMenuTranslation[i];
2988 XtAddCallback(entry, XtNcallback,
2989 (XtCallbackProc) DropMenuSelect,
2990 (caddr_t) selection);
3004 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3005 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3006 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3007 dmEnables[i].piece);
3008 XtSetSensitive(entry, p != NULL || !appData.testLegality
3009 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3010 && !appData.icsActive));
3012 while (p && *p++ == dmEnables[i].piece) count++;
3013 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3015 XtSetArg(args[j], XtNlabel, label); j++;
3016 XtSetValues(entry, args, j);
3021 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
3023 String whichMenu; int menuNr = -2;
3024 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3025 if (event->type == ButtonRelease)
3026 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3027 else if (event->type == ButtonPress)
3028 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3030 case 0: whichMenu = params[0]; break;
3031 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3033 case -1: if (errorUp) ErrorPopDown();
3036 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3040 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3042 if (pmFromX < 0 || pmFromY < 0) return;
3043 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3047 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3049 if (pmFromX < 0 || pmFromY < 0) return;
3050 DropMenuEvent(piece, pmFromX, pmFromY);
3054 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3056 shiftKey = prms[0][0] & 1;
3061 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3063 shiftKey = prms[0][0] & 1;
3069 do_flash_delay (unsigned long msec)
3075 DrawBorder (int x, int y, int type)
3079 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
3081 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3082 squareSize+lineGap, squareSize+lineGap);
3086 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
3088 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
3089 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
3091 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
3092 if(textureW[kind] < W*squareSize)
3093 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
3095 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
3096 if(textureH[kind] < H*squareSize)
3097 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
3099 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
3104 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
3105 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
3107 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
3108 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
3109 squareSize, squareSize, x*fac, y*fac);
3111 if (useImages && useImageSqs) {
3115 pm = xpmLightSquare;
3120 case 2: /* neutral */
3122 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
3125 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3126 squareSize, squareSize, x*fac, y*fac);
3136 case 2: /* neutral */
3141 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
3146 I split out the routines to draw a piece so that I could
3147 make a generic flash routine.
3150 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3152 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3153 switch (square_color) {
3155 case 2: /* neutral */
3157 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3158 ? *pieceToOutline(piece)
3159 : *pieceToSolid(piece),
3160 dest, bwPieceGC, 0, 0,
3161 squareSize, squareSize, x, y);
3164 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3165 ? *pieceToSolid(piece)
3166 : *pieceToOutline(piece),
3167 dest, wbPieceGC, 0, 0,
3168 squareSize, squareSize, x, y);
3174 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3176 switch (square_color) {
3178 case 2: /* neutral */
3180 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3181 ? *pieceToOutline(piece)
3182 : *pieceToSolid(piece),
3183 dest, bwPieceGC, 0, 0,
3184 squareSize, squareSize, x, y, 1);
3187 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3188 ? *pieceToSolid(piece)
3189 : *pieceToOutline(piece),
3190 dest, wbPieceGC, 0, 0,
3191 squareSize, squareSize, x, y, 1);
3197 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3199 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
3200 switch (square_color) {
3202 XCopyPlane(xDisplay, *pieceToSolid(piece),
3203 dest, (int) piece < (int) BlackPawn
3204 ? wlPieceGC : blPieceGC, 0, 0,
3205 squareSize, squareSize, x, y, 1);
3208 XCopyPlane(xDisplay, *pieceToSolid(piece),
3209 dest, (int) piece < (int) BlackPawn
3210 ? wdPieceGC : bdPieceGC, 0, 0,
3211 squareSize, squareSize, x, y, 1);
3213 case 2: /* neutral */
3215 break; // should never contain pieces
3220 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3222 int kind, p = piece;
3224 switch (square_color) {
3226 case 2: /* neutral */
3228 if ((int)piece < (int) BlackPawn) {
3236 if ((int)piece < (int) BlackPawn) {
3244 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
3245 if(useTexture & square_color+1) {
3246 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
3247 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
3248 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
3249 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
3250 XSetClipMask(xDisplay, wlPieceGC, None);
3251 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
3253 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3254 dest, wlPieceGC, 0, 0,
3255 squareSize, squareSize, x, y);
3258 typedef void (*DrawFunc)();
3263 if (appData.monoMode) {
3264 if (DefaultDepth(xDisplay, xScreen) == 1) {
3265 return monoDrawPiece_1bit;
3267 return monoDrawPiece;
3271 return colorDrawPieceImage;
3273 return colorDrawPiece;
3278 DrawDot (int marker, int x, int y, int r)
3280 if(appData.monoMode) {
3281 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
3282 x, y, r, r, 0, 64*360);
3283 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
3284 x, y, r, r, 0, 64*360);
3286 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
3287 x, y, r, r, 0, 64*360);
3291 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
3292 { // basic front-end board-draw function: takes care of everything that can be in square:
3293 // piece, background, coordinate/count, marker dot
3294 int direction, font_ascent, font_descent;
3295 XCharStruct overall;
3298 if (piece == EmptySquare) {
3299 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
3301 drawfunc = ChooseDrawFunc();
3302 drawfunc(piece, square_color, x, y, xBoardWindow);
3305 if(align) { // square carries inscription (coord or piece count)
3307 GC hGC = align < 3 ? coordGC : countGC;
3308 // first calculate where it goes
3309 XTextExtents(countFontStruct, string, 1, &direction,
3310 &font_ascent, &font_descent, &overall);
3312 xx += squareSize - overall.width - 2;
3313 yy += squareSize - font_descent - 1;
3314 } else if (align == 2) {
3315 xx += 2, yy += font_ascent + 1;
3316 } else if (align == 3) {
3317 xx += squareSize - overall.width - 2;
3318 yy += font_ascent + 1;
3319 } else if (align == 4) {
3320 xx += 2, yy += font_ascent + 1;
3323 if (appData.monoMode) {
3324 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3326 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3330 if(marker) { // print fat marker dot, if requested
3331 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
3336 FlashDelay (int flash_delay)
3338 XSync(xDisplay, False);
3339 if(flash_delay) do_flash_delay(flash_delay);
3343 Fraction (int x, int start, int stop)
3345 double f = ((double) x - start)/(stop - start);
3346 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
3350 static WindowPlacement wpNew;
3353 CoDrag (Widget sh, WindowPlacement *wp)
3356 int j=0, touch=0, fudge = 2;
3357 GetActualPlacement(sh, wp);
3358 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
3359 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
3360 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
3361 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
3362 if(!touch ) return; // only windows that touch co-move
3363 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
3364 int heightInc = wpNew.height - wpMain.height;
3365 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3366 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3367 wp->y += fracTop * heightInc;
3368 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
3369 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
3370 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
3371 int widthInc = wpNew.width - wpMain.width;
3372 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3373 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3374 wp->y += fracLeft * widthInc;
3375 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
3376 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
3378 wp->x += wpNew.x - wpMain.x;
3379 wp->y += wpNew.y - wpMain.y;
3380 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
3381 if(touch == 3) wp->y += wpNew.height - wpMain.height;
3382 XtSetArg(args[j], XtNx, wp->x); j++;
3383 XtSetArg(args[j], XtNy, wp->y); j++;
3384 XtSetValues(sh, args, j);
3387 static XtIntervalId delayedDragID = 0;
3392 GetActualPlacement(shellWidget, &wpNew);
3393 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
3394 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
3395 return; // false alarm
3396 if(EngineOutputIsUp()) CoDrag(engineOutputShell, &wpEngineOutput);
3397 if(MoveHistoryIsUp()) CoDrag(shells[HistoryDlg], &wpMoveHistory);
3398 if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
3399 if(GameListIsUp()) CoDrag(gameListShell, &wpGameList);
3401 DrawPosition(True, NULL);
3402 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
3409 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
3411 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
3414 /* Why is this needed on some versions of X? */
3416 EventProc (Widget widget, caddr_t unused, XEvent *event)
3418 if (!XtIsRealized(widget))
3420 switch (event->type) {
3421 case ConfigureNotify: // main window is being dragged: drag attached windows with it
3422 if(appData.useStickyWindows)
3423 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
3426 if (event->xexpose.count > 0) return; /* no clipping is done */
3427 DrawPosition(True, NULL);
3428 if(twoBoards) { // [HGM] dual: draw other board in other orientation
3429 flipView = !flipView; partnerUp = !partnerUp;
3430 DrawPosition(True, NULL);
3431 flipView = !flipView; partnerUp = !partnerUp;
3435 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
3442 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
3444 DrawSeekAxis (int x, int y, int xTo, int yTo)
3446 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
3450 DrawSeekBackground (int left, int top, int right, int bottom)
3452 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
3456 DrawSeekText (char *buf, int x, int y)
3458 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
3462 DrawSeekDot (int x, int y, int colorNr)
3464 int square = colorNr & 0x80;
3467 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
3469 XFillRectangle(xDisplay, xBoardWindow, color,
3470 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3472 XFillArc(xDisplay, xBoardWindow, color,
3473 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
3477 DrawGrid (int second)
3479 XDrawSegments(xDisplay, xBoardWindow, lineGC,
3480 second ? secondSegments : // [HGM] dual
3481 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
3486 * event handler for redrawing the board
3489 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3491 DrawPosition(True, NULL);
3496 * event handler for parsing user moves
3498 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
3499 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
3500 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
3501 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
3502 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
3503 // and at the end FinishMove() to perform the move after optional promotion popups.
3504 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
3506 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3508 if (w != boardWidget || errorExitStatus != -1) return;
3509 if(nprms) shiftKey = !strcmp(prms[0], "1");
3512 if (event->type == ButtonPress) {
3513 XtPopdown(promotionShell);
3514 XtDestroyWidget(promotionShell);
3515 promotionUp = False;
3523 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
3524 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
3525 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
3529 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
3531 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
3532 DragPieceMove(event->xmotion.x, event->xmotion.y);
3536 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3537 { // [HGM] pv: walk PV
3538 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3541 static int savedIndex; /* gross that this is global */
3544 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3547 XawTextPosition index, dummy;
3550 XawTextGetSelectionPos(w, &index, &dummy);
3551 XtSetArg(arg, XtNstring, &val);
3552 XtGetValues(w, &arg, 1);
3553 ReplaceComment(savedIndex, val);
3554 if(savedIndex != currentMove) ToNrEvent(savedIndex);
3555 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3559 EditCommentPopUp (int index, char *title, char *text)
3562 if (text == NULL) text = "";
3563 NewCommentPopup(title, text, index);
3567 CommentPopUp (char *title, char *text)
3569 savedIndex = currentMove; // [HGM] vari
3570 NewCommentPopup(title, text, currentMove);
3576 PopDown(CommentDlg);
3579 static char *openName;
3585 (void) (*fileProc)(openFP, 0, openName);
3589 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
3591 fileProc = proc; /* I can't see a way not */
3592 fileOpenMode = openMode; /* to use globals here */
3593 { // [HGM] use file-selector dialog stolen from Ghostview
3594 int index; // this is not supported yet
3595 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
3596 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
3597 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
3598 ScheduleDelayedEvent(&DelayedLoad, 50);
3606 Widget dialog, layout;
3608 Dimension bw_width, pw_width;
3610 char *PromoChars = "wglcqrbnkac+=\0";
3613 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3614 XtGetValues(boardWidget, args, j);
3617 XtSetArg(args[j], XtNresizable, True); j++;
3618 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
3620 XtCreatePopupShell("Promotion", transientShellWidgetClass,
3621 shellWidget, args, j);
3623 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
3624 layoutArgs, XtNumber(layoutArgs));
3627 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
3628 XtSetArg(args[j], XtNborderWidth, 0); j++;
3629 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
3632 if(gameInfo.variant != VariantShogi) {
3633 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
3634 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
3635 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
3636 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
3637 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
3639 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
3640 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
3641 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
3642 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
3644 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
3645 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
3646 gameInfo.variant == VariantGiveaway) {
3647 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
3649 if(gameInfo.variant == VariantCapablanca ||
3650 gameInfo.variant == VariantGothic ||
3651 gameInfo.variant == VariantCapaRandom) {
3652 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
3653 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
3655 } else // [HGM] shogi
3657 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
3658 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
3660 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
3662 XtRealizeWidget(promotionShell);
3663 CatchDeleteWindow(promotionShell, "PromotionPopDown");
3666 XtSetArg(args[j], XtNwidth, &pw_width); j++;
3667 XtGetValues(promotionShell, args, j);
3669 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3670 lineGap + squareSize/3 +
3671 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
3672 0 : 6*(squareSize + lineGap)), &x, &y);
3675 XtSetArg(args[j], XtNx, x); j++;
3676 XtSetArg(args[j], XtNy, y); j++;
3677 XtSetValues(promotionShell, args, j);
3679 XtPopup(promotionShell, XtGrabNone);
3687 if (!promotionUp) return;
3688 XtPopdown(promotionShell);
3689 XtDestroyWidget(promotionShell);
3690 promotionUp = False;
3694 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
3696 int promoChar = * (const char *) client_data;
3700 if (fromX == -1) return;
3707 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
3709 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
3710 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3716 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
3718 dialogError = errorUp = False;
3719 XtPopdown(w = XtParent(XtParent(XtParent(w))));
3721 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3728 if (!errorUp) return;
3729 dialogError = errorUp = False;
3730 XtPopdown(errorShell);
3731 XtDestroyWidget(errorShell);
3732 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3736 ErrorPopUp (char *title, char *label, int modal)
3739 Widget dialog, layout;
3743 Dimension bw_width, pw_width;
3744 Dimension pw_height;
3748 XtSetArg(args[i], XtNresizable, True); i++;
3749 XtSetArg(args[i], XtNtitle, title); i++;
3751 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
3752 shellUp[TransientDlg] ? (dialogError = modal = TRUE, shells[TransientDlg]) : shellWidget, args, i);
3754 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
3755 layoutArgs, XtNumber(layoutArgs));
3758 XtSetArg(args[i], XtNlabel, label); i++;
3759 XtSetArg(args[i], XtNborderWidth, 0); i++;
3760 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
3763 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
3765 XtRealizeWidget(errorShell);
3766 CatchDeleteWindow(errorShell, "ErrorPopDown");
3769 XtSetArg(args[i], XtNwidth, &bw_width); i++;
3770 XtGetValues(boardWidget, args, i);
3772 XtSetArg(args[i], XtNwidth, &pw_width); i++;
3773 XtSetArg(args[i], XtNheight, &pw_height); i++;
3774 XtGetValues(errorShell, args, i);
3777 /* This code seems to tickle an X bug if it is executed too soon
3778 after xboard starts up. The coordinates get transformed as if
3779 the main window was positioned at (0, 0).
3781 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3782 0 - pw_height + squareSize / 3, &x, &y);
3784 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
3785 RootWindowOfScreen(XtScreen(boardWidget)),
3786 (bw_width - pw_width) / 2,
3787 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
3791 if (y < 0) y = 0; /*avoid positioning top offscreen*/
3794 XtSetArg(args[i], XtNx, x); i++;
3795 XtSetArg(args[i], XtNy, y); i++;
3796 XtSetValues(errorShell, args, i);
3799 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
3802 /* Disable all user input other than deleting the window */
3803 static int frozen = 0;
3809 /* Grab by a widget that doesn't accept input */
3810 XtAddGrab(messageWidget, TRUE, FALSE);
3814 /* Undo a FreezeUI */
3818 if (!frozen) return;
3819 XtRemoveGrab(messageWidget);
3827 static int oldPausing = FALSE;
3828 static GameMode oldmode = (GameMode) -1;
3831 if (!boardWidget || !XtIsRealized(boardWidget)) return;
3833 if (pausing != oldPausing) {
3834 oldPausing = pausing;
3835 MarkMenuItem("Pause", pausing);
3837 if (appData.showButtonBar) {
3838 /* Always toggle, don't set. Previous code messes up when
3839 invoked while the button is pressed, as releasing it
3840 toggles the state again. */
3843 XtSetArg(args[0], XtNbackground, &oldbg);
3844 XtSetArg(args[1], XtNforeground, &oldfg);
3845 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
3847 XtSetArg(args[0], XtNbackground, oldfg);
3848 XtSetArg(args[1], XtNforeground, oldbg);
3850 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
3854 wname = ModeToWidgetName(oldmode);
3855 if (wname != NULL) {
3856 MarkMenuItem(wname, False);
3858 wname = ModeToWidgetName(gameMode);
3859 if (wname != NULL) {
3860 MarkMenuItem(wname, True);
3863 MarkMenuItem("Machine Match", matchMode && matchGame < appData.matchGames);
3865 /* Maybe all the enables should be handled here, not just this one */
3866 EnableMenuItem("Training", gameMode == Training || gameMode == PlayFromGameFile);
3871 * Button/menu procedures
3874 LoadGamePopUp (FILE *f, int gameNumber, char *title)
3876 cmailMsgLoaded = FALSE;
3877 if (gameNumber == 0) {
3878 int error = GameListBuild(f);
3880 DisplayError(_("Cannot build game list"), error);
3881 } else if (!ListEmpty(&gameList) &&
3882 ((ListGame *) gameList.tailPred)->number > 1) {
3883 GameListPopUp(f, title);
3889 return LoadGame(f, gameNumber, title, FALSE);
3892 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3893 char *selected_fen_position=NULL;
3896 SendPositionSelection (Widget w, Atom *selection, Atom *target,
3897 Atom *type_return, XtPointer *value_return,
3898 unsigned long *length_return, int *format_return)
3900 char *selection_tmp;
3902 // if (!selected_fen_position) return False; /* should never happen */
3903 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3904 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
3905 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
3908 if (f == NULL) return False;
3912 selection_tmp = XtMalloc(len + 1);
3913 count = fread(selection_tmp, 1, len, f);
3916 XtFree(selection_tmp);
3919 selection_tmp[len] = NULLCHAR;
3921 /* note: since no XtSelectionDoneProc was registered, Xt will
3922 * automatically call XtFree on the value returned. So have to
3923 * make a copy of it allocated with XtMalloc */
3924 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3925 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
3928 *value_return=selection_tmp;
3929 *length_return=strlen(selection_tmp);
3930 *type_return=*target;
3931 *format_return = 8; /* bits per byte */
3933 } else if (*target == XA_TARGETS(xDisplay)) {
3934 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3935 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3936 targets_tmp[1] = XA_STRING;
3937 *value_return = targets_tmp;
3938 *type_return = XA_ATOM;
3941 // This code leads to a read of value_return out of bounds on 64-bit systems.
3942 // Other code which I have seen always sets *format_return to 32 independent of
3943 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
3944 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
3945 *format_return = 8 * sizeof(Atom);
3946 if (*format_return > 32) {
3947 *length_return *= *format_return / 32;
3948 *format_return = 32;
3951 *format_return = 32;
3959 /* note: when called from menu all parameters are NULL, so no clue what the
3960 * Widget which was clicked on was, or what the click event was
3963 CopySomething (char *src)
3965 selected_fen_position = src;
3967 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3968 * have a notion of a position that is selected but not copied.
3969 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3971 XtOwnSelection(menuBarWidget, XA_PRIMARY,
3973 SendPositionSelection,
3974 NULL/* lose_ownership_proc */ ,
3975 NULL/* transfer_done_proc */);
3976 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3978 SendPositionSelection,
3979 NULL/* lose_ownership_proc */ ,
3980 NULL/* transfer_done_proc */);
3983 /* function called when the data to Paste is ready */
3985 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3986 Atom *type, XtPointer value, unsigned long *len, int *format)
3989 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3990 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3991 EditPositionPasteFEN(fenstr);
3995 /* called when Paste Position button is pressed,
3996 * all parameters will be NULL */
3998 PastePositionProc ()
4000 XtGetSelectionValue(menuBarWidget,
4001 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4002 /* (XtSelectionCallbackProc) */ PastePositionCB,
4003 NULL, /* client_data passed to PastePositionCB */
4005 /* better to use the time field from the event that triggered the
4006 * call to this function, but that isn't trivial to get
4013 /* note: when called from menu all parameters are NULL, so no clue what the
4014 * Widget which was clicked on was, or what the click event was
4016 /* function called when the data to Paste is ready */
4018 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
4019 Atom *type, XtPointer value, unsigned long *len, int *format)
4022 if (value == NULL || *len == 0) {
4023 return; /* nothing had been selected to copy */
4025 f = fopen(gamePasteFilename, "w");
4027 DisplayError(_("Can't open temp file"), errno);
4030 fwrite(value, 1, *len, f);
4033 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
4036 /* called when Paste Game button is pressed,
4037 * all parameters will be NULL */
4041 XtGetSelectionValue(menuBarWidget,
4042 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4043 /* (XtSelectionCallbackProc) */ PasteGameCB,
4044 NULL, /* client_data passed to PasteGameCB */
4046 /* better to use the time field from the event that triggered the
4047 * call to this function, but that isn't trivial to get
4056 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4063 { // bassic primitive for determining if modifier keys are pressed
4064 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
4067 XQueryKeymap(xDisplay,keys);
4068 for(i=0; i<6; i++) {
4070 j = XKeysymToKeycode(xDisplay, codes[i]);
4071 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
4077 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
4081 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
4082 if ( n == 1 && *buf >= 32 // printable
4083 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
4084 ) BoxAutoPopUp (buf);
4088 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4089 { // [HGM] input: let up-arrow recall previous line from history
4094 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4095 { // [HGM] input: let down-arrow recall next line from history
4100 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4106 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4108 if (!TempBackwardActive) {
4109 TempBackwardActive = True;
4115 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4117 /* Check to see if triggered by a key release event for a repeating key.
4118 * If so the next queued event will be a key press of the same key at the same time */
4119 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
4121 XPeekEvent(xDisplay, &next);
4122 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
4123 next.xkey.keycode == event->xkey.keycode)
4127 TempBackwardActive = False;
4131 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4132 { // called as key binding
4135 if (nprms && *nprms > 0)
4139 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
4144 DisplayMessage (char *message, char *extMessage)
4146 /* display a message in the message widget */
4155 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
4160 message = extMessage;
4164 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
4166 /* need to test if messageWidget already exists, since this function
4167 can also be called during the startup, if for example a Xresource
4168 is not set up correctly */
4171 XtSetArg(arg, XtNlabel, message);
4172 XtSetValues(messageWidget, &arg, 1);
4179 SetWindowTitle (char *text, char *title, char *icon)
4183 if (appData.titleInWindow) {
4185 XtSetArg(args[i], XtNlabel, text); i++;
4186 XtSetValues(titleWidget, args, i);
4189 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
4190 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
4191 XtSetValues(shellWidget, args, i);
4192 XSync(xDisplay, False);
4197 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
4203 DisplayIcsInteractionTitle (String message)
4205 if (oldICSInteractionTitle == NULL) {
4206 /* Magic to find the old window title, adapted from vim */
4207 char *wina = getenv("WINDOWID");
4209 Window win = (Window) atoi(wina);
4210 Window root, parent, *children;
4211 unsigned int nchildren;
4212 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4214 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4215 if (!XQueryTree(xDisplay, win, &root, &parent,
4216 &children, &nchildren)) break;
4217 if (children) XFree((void *)children);
4218 if (parent == root || parent == 0) break;
4221 XSetErrorHandler(oldHandler);
4223 if (oldICSInteractionTitle == NULL) {
4224 oldICSInteractionTitle = "xterm";
4227 printf("\033]0;%s\007", message);
4232 XtIntervalId delayedEventTimerXID = 0;
4233 DelayedEventCallback delayedEventCallback = 0;
4238 delayedEventTimerXID = 0;
4239 delayedEventCallback();
4243 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
4245 if(delayedEventTimerXID && delayedEventCallback == cb)
4246 // [HGM] alive: replace, rather than add or flush identical event
4247 XtRemoveTimeOut(delayedEventTimerXID);
4248 delayedEventCallback = cb;
4249 delayedEventTimerXID =
4250 XtAppAddTimeOut(appContext, millisec,
4251 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
4254 DelayedEventCallback
4257 if (delayedEventTimerXID) {
4258 return delayedEventCallback;
4265 CancelDelayedEvent ()
4267 if (delayedEventTimerXID) {
4268 XtRemoveTimeOut(delayedEventTimerXID);
4269 delayedEventTimerXID = 0;
4273 XtIntervalId loadGameTimerXID = 0;
4276 LoadGameTimerRunning ()
4278 return loadGameTimerXID != 0;
4282 StopLoadGameTimer ()
4284 if (loadGameTimerXID != 0) {
4285 XtRemoveTimeOut(loadGameTimerXID);
4286 loadGameTimerXID = 0;
4294 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
4296 loadGameTimerXID = 0;
4301 StartLoadGameTimer (long millisec)
4304 XtAppAddTimeOut(appContext, millisec,
4305 (XtTimerCallbackProc) LoadGameTimerCallback,
4309 XtIntervalId analysisClockXID = 0;
4312 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
4314 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
4315 || appData.icsEngineAnalyze) { // [DM]
4316 AnalysisPeriodicEvent(0);
4317 StartAnalysisClock();
4322 StartAnalysisClock ()
4325 XtAppAddTimeOut(appContext, 2000,
4326 (XtTimerCallbackProc) AnalysisClockCallback,
4330 XtIntervalId clockTimerXID = 0;
4333 ClockTimerRunning ()
4335 return clockTimerXID != 0;
4341 if (clockTimerXID != 0) {
4342 XtRemoveTimeOut(clockTimerXID);
4351 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
4358 StartClockTimer (long millisec)
4361 XtAppAddTimeOut(appContext, millisec,
4362 (XtTimerCallbackProc) ClockTimerCallback,
4367 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
4372 /* check for low time warning */
4373 Pixel foregroundOrWarningColor = timerForegroundPixel;
4376 appData.lowTimeWarning &&
4377 (timer / 1000) < appData.icsAlarmTime)
4378 foregroundOrWarningColor = lowTimeWarningColor;
4380 if (appData.clockMode) {
4381 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
4382 XtSetArg(args[0], XtNlabel, buf);
4384 snprintf(buf, MSG_SIZ, "%s ", color);
4385 XtSetArg(args[0], XtNlabel, buf);
4390 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
4391 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
4393 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
4394 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
4397 XtSetValues(w, args, 3);
4401 DisplayWhiteClock (long timeRemaining, int highlight)
4405 if(appData.noGUI) return;
4406 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
4407 if (highlight && iconPixmap == bIconPixmap) {
4408 iconPixmap = wIconPixmap;
4409 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
4410 XtSetValues(shellWidget, args, 1);
4415 DisplayBlackClock (long timeRemaining, int highlight)
4419 if(appData.noGUI) return;
4420 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
4421 if (highlight && iconPixmap == wIconPixmap) {
4422 iconPixmap = bIconPixmap;
4423 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
4424 XtSetValues(shellWidget, args, 1);
4429 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
4431 InputSource *is = (InputSource *) closure;
4436 if (is->lineByLine) {
4437 count = read(is->fd, is->unused,
4438 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
4440 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
4443 is->unused += count;
4445 while (p < is->unused) {
4446 q = memchr(p, '\n', is->unused - p);
4447 if (q == NULL) break;
4449 (is->func)(is, is->closure, p, q - p, 0);
4453 while (p < is->unused) {
4458 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
4463 (is->func)(is, is->closure, is->buf, count, error);
4468 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
4471 ChildProc *cp = (ChildProc *) pr;
4473 is = (InputSource *) calloc(1, sizeof(InputSource));
4474 is->lineByLine = lineByLine;
4478 is->fd = fileno(stdin);
4480 is->kind = cp->kind;
4481 is->fd = cp->fdFrom;
4484 is->unused = is->buf;
4487 is->xid = XtAppAddInput(appContext, is->fd,
4488 (XtPointer) (XtInputReadMask),
4489 (XtInputCallbackProc) DoInputCallback,
4491 is->closure = closure;
4492 return (InputSourceRef) is;
4496 RemoveInputSource (InputSourceRef isr)
4498 InputSource *is = (InputSource *) isr;
4500 if (is->xid == 0) return;
4501 XtRemoveInput(is->xid);
4505 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
4507 /* Masks for XPM pieces. Black and white pieces can have
4508 different shapes, but in the interest of retaining my
4509 sanity pieces must have the same outline on both light
4510 and dark squares, and all pieces must use the same
4511 background square colors/images. */
4513 static int xpmDone = 0;
4514 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
4515 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
4518 CreateAnimMasks (int pieceDepth)
4524 unsigned long plane;
4527 /* Need a bitmap just to get a GC with right depth */
4528 buf = XCreatePixmap(xDisplay, xBoardWindow,
4530 values.foreground = 1;
4531 values.background = 0;
4532 /* Don't use XtGetGC, not read only */
4533 maskGC = XCreateGC(xDisplay, buf,
4534 GCForeground | GCBackground, &values);
4535 XFreePixmap(xDisplay, buf);
4537 buf = XCreatePixmap(xDisplay, xBoardWindow,
4538 squareSize, squareSize, pieceDepth);
4539 values.foreground = XBlackPixel(xDisplay, xScreen);
4540 values.background = XWhitePixel(xDisplay, xScreen);
4541 bufGC = XCreateGC(xDisplay, buf,
4542 GCForeground | GCBackground, &values);
4544 for (piece = WhitePawn; piece <= BlackKing; piece++) {
4545 /* Begin with empty mask */
4546 if(!xpmDone) // [HGM] pieces: keep using existing
4547 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
4548 squareSize, squareSize, 1);
4549 XSetFunction(xDisplay, maskGC, GXclear);
4550 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
4551 0, 0, squareSize, squareSize);
4553 /* Take a copy of the piece */
4558 XSetFunction(xDisplay, bufGC, GXcopy);
4559 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
4561 0, 0, squareSize, squareSize, 0, 0);
4563 /* XOR the background (light) over the piece */
4564 XSetFunction(xDisplay, bufGC, GXxor);
4566 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
4567 0, 0, squareSize, squareSize, 0, 0);
4569 XSetForeground(xDisplay, bufGC, lightSquareColor);
4570 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
4573 /* We now have an inverted piece image with the background
4574 erased. Construct mask by just selecting all the non-zero
4575 pixels - no need to reconstruct the original image. */
4576 XSetFunction(xDisplay, maskGC, GXor);
4578 /* Might be quicker to download an XImage and create bitmap
4579 data from it rather than this N copies per piece, but it
4580 only takes a fraction of a second and there is a much
4581 longer delay for loading the pieces. */
4582 for (n = 0; n < pieceDepth; n ++) {
4583 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
4584 0, 0, squareSize, squareSize,
4590 XFreePixmap(xDisplay, buf);
4591 XFreeGC(xDisplay, bufGC);
4592 XFreeGC(xDisplay, maskGC);
4596 InitAnimState (AnimNr anr, XWindowAttributes *info)
4601 /* Each buffer is square size, same depth as window */
4602 animBufs[anr+4] = xBoardWindow;
4603 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
4604 squareSize, squareSize, info->depth);
4605 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
4606 squareSize, squareSize, info->depth);
4608 /* Create a plain GC for blitting */
4609 mask = GCForeground | GCBackground | GCFunction |
4610 GCPlaneMask | GCGraphicsExposures;
4611 values.foreground = XBlackPixel(xDisplay, xScreen);
4612 values.background = XWhitePixel(xDisplay, xScreen);
4613 values.function = GXcopy;
4614 values.plane_mask = AllPlanes;
4615 values.graphics_exposures = False;
4616 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
4618 /* Piece will be copied from an existing context at
4619 the start of each new animation/drag. */
4620 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
4622 /* Outline will be a read-only copy of an existing */
4623 animGCs[anr+4] = None;
4629 XWindowAttributes info;
4631 if (xpmDone && gameInfo.variant == oldVariant) return;
4632 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
4633 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
4635 InitAnimState(Game, &info);
4636 InitAnimState(Player, &info);
4638 /* For XPM pieces, we need bitmaps to use as masks. */
4640 CreateAnimMasks(info.depth), xpmDone = 1;
4645 static Boolean frameWaiting;
4648 FrameAlarm (int sig)
4650 frameWaiting = False;
4651 /* In case System-V style signals. Needed?? */
4652 signal(SIGALRM, FrameAlarm);
4656 FrameDelay (int time)
4658 struct itimerval delay;
4660 XSync(xDisplay, False);
4663 frameWaiting = True;
4664 signal(SIGALRM, FrameAlarm);
4665 delay.it_interval.tv_sec =
4666 delay.it_value.tv_sec = time / 1000;
4667 delay.it_interval.tv_usec =
4668 delay.it_value.tv_usec = (time % 1000) * 1000;
4669 setitimer(ITIMER_REAL, &delay, NULL);
4670 while (frameWaiting) pause();
4671 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
4672 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
4673 setitimer(ITIMER_REAL, &delay, NULL);
4680 FrameDelay (int time)
4682 XSync(xDisplay, False);
4684 usleep(time * 1000);
4690 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
4694 /* Bitmap for piece being moved. */
4695 if (appData.monoMode) {
4696 *mask = *pieceToSolid(piece);
4697 } else if (useImages) {
4699 *mask = xpmMask[piece];
4701 *mask = ximMaskPm[piece];
4704 *mask = *pieceToSolid(piece);
4707 /* GC for piece being moved. Square color doesn't matter, but
4708 since it gets modified we make a copy of the original. */
4710 if (appData.monoMode)
4715 if (appData.monoMode)
4720 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
4722 /* Outline only used in mono mode and is not modified */
4724 *outline = bwPieceGC;
4726 *outline = wbPieceGC;
4730 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
4735 /* Draw solid rectangle which will be clipped to shape of piece */
4736 XFillRectangle(xDisplay, dest, clip,
4737 0, 0, squareSize, squareSize);
4738 if (appData.monoMode)
4739 /* Also draw outline in contrasting color for black
4740 on black / white on white cases */
4741 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
4742 0, 0, squareSize, squareSize, 0, 0, 1);
4744 /* Copy the piece */
4749 if(appData.upsideDown && flipView) kind ^= 2;
4750 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4752 0, 0, squareSize, squareSize,
4758 InsertPiece (AnimNr anr, ChessSquare piece)
4760 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
4764 DrawBlank (AnimNr anr, int x, int y, int startColor)
4766 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
4769 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
4770 int srcX, int srcY, int width, int height, int destX, int destY)
4772 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
4773 srcX, srcY, width, height, destX, destY);
4777 SetDragPiece (AnimNr anr, ChessSquare piece)
4780 /* The piece will be drawn using its own bitmap as a matte */
4781 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
4782 XSetClipMask(xDisplay, animGCs[anr+2], mask);
4785 /* [AS] Arrow highlighting support */
4788 DrawPolygon (Pnt arrow[], int nr)
4792 for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
4793 XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
4794 if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
4798 UpdateLogos (int displ)
4800 return; // no logos in XBoard yet