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"
214 #define usleep(t) _sleep2(((t)+500)/1000)
218 # define _(s) gettext (s)
219 # define N_(s) gettext_noop (s)
225 int main P((int argc, char **argv));
226 FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
227 char *init_path, char *filter, char *mode, int (*show_entry)(), char **name_return));
228 RETSIGTYPE CmailSigHandler P((int sig));
229 RETSIGTYPE IntSigHandler P((int sig));
230 RETSIGTYPE TermSizeSigHandler P((int sig));
231 void CreateGCs P((int redo));
232 void CreateAnyPieces P((void));
233 void CreateXIMPieces P((void));
234 void CreateXPMPieces P((void));
235 void CreateXPMBoard P((char *s, int n));
236 void CreatePieces P((void));
237 void CreatePieceMenus P((void));
238 Widget CreateMenuBar P((Menu *mb, int boardWidth));
239 Widget CreateButtonBar P ((MenuItem *mi));
241 char *InsertPxlSize P((char *pattern, int targetPxlSize));
242 XFontSet CreateFontSet P((char *base_fnt_lst));
244 char *FindFont P((char *pattern, int targetPxlSize));
246 void PieceMenuPopup P((Widget w, XEvent *event,
247 String *params, Cardinal *num_params));
248 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
249 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
250 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
251 u_int wreq, u_int hreq));
252 void CreateGrid P((void));
253 int EventToSquare P((int x, int limit));
254 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
255 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
256 void DelayedDrag P((void));
257 void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
258 void HandleUserMove P((Widget w, XEvent *event,
259 String *prms, Cardinal *nprms));
260 void AnimateUserMove P((Widget w, XEvent * event,
261 String * params, Cardinal * nParams));
262 void HandlePV P((Widget w, XEvent * event,
263 String * params, Cardinal * nParams));
264 void SelectPV P((Widget w, XEvent * event,
265 String * params, Cardinal * nParams));
266 void StopPV P((Widget w, XEvent * event,
267 String * params, Cardinal * nParams));
268 void WhiteClock P((Widget w, XEvent *event,
269 String *prms, Cardinal *nprms));
270 void BlackClock P((Widget w, XEvent *event,
271 String *prms, Cardinal *nprms));
272 void DrawPositionProc P((Widget w, XEvent *event,
273 String *prms, Cardinal *nprms));
274 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
276 void CommentClick P((Widget w, XEvent * event,
277 String * params, Cardinal * nParams));
278 void CommentPopUp P((char *title, char *label));
279 void CommentPopDown P((void));
280 void ICSInputBoxPopUp P((void));
281 void ICSInputBoxPopDown P((void));
282 void FileNamePopUp P((char *label, char *def, char *filter,
283 FileProc proc, char *openMode));
284 void FileNamePopDown P((void));
285 void FileNameCallback P((Widget w, XtPointer client_data,
286 XtPointer call_data));
287 void FileNameAction P((Widget w, XEvent *event,
288 String *prms, Cardinal *nprms));
289 void AskQuestionReplyAction P((Widget w, XEvent *event,
290 String *prms, Cardinal *nprms));
291 void AskQuestionProc P((Widget w, XEvent *event,
292 String *prms, Cardinal *nprms));
293 void AskQuestionPopDown P((void));
294 void PromotionPopDown P((void));
295 void PromotionCallback P((Widget w, XtPointer client_data,
296 XtPointer call_data));
297 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
298 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
299 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
300 void TypeInProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
301 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
302 void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
303 void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
304 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
305 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
306 Boolean TempBackwardActive = False;
307 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
308 void DisplayMove P((int moveNumber));
309 void DisplayTitle P((char *title));
310 void ICSInitScript P((void));
311 void ErrorPopUp P((char *title, char *text, int modal));
312 void ErrorPopDown P((void));
313 static char *ExpandPathName P((char *path));
314 static void DragPieceMove P((int x, int y));
315 static void DrawDragPiece P((void));
316 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
317 void GameListOptionsPopDown P(());
318 void GenericPopDown P(());
319 void update_ics_width P(());
320 int get_term_width P(());
321 int CopyMemoProc P(());
322 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
323 Boolean IsDrawArrowEnabled P(());
326 * XBoard depends on Xt R4 or higher
328 int xtVersion = XtSpecificationRelease;
333 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
334 jailSquareColor, highlightSquareColor, premoveHighlightColor;
335 Pixel lowTimeWarningColor;
336 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
337 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
338 wjPieceGC, bjPieceGC, prelineGC, countGC;
339 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
340 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
341 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
342 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
343 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
344 ICSInputShell, fileNameShell, askQuestionShell;
345 Widget historyShell, evalGraphShell, gameListShell;
346 int hOffset; // [HGM] dual
347 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
348 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
349 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
351 XFontSet fontSet, clockFontSet;
354 XFontStruct *clockFontStruct;
356 Font coordFontID, countFontID;
357 XFontStruct *coordFontStruct, *countFontStruct;
358 XtAppContext appContext;
360 char *oldICSInteractionTitle;
364 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
366 Position commentX = -1, commentY = -1;
367 Dimension commentW, commentH;
368 typedef unsigned int BoardSize;
370 Boolean chessProgram;
372 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
373 int squareSize, smallLayout = 0, tinyLayout = 0,
374 marginW, marginH, // [HGM] for run-time resizing
375 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
376 ICSInputBoxUp = False, askQuestionUp = False,
377 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
378 errorUp = False, errorExitStatus = -1, lineGap, defaultLineGap;
379 Dimension textHeight;
380 Pixel timerForegroundPixel, timerBackgroundPixel;
381 Pixel buttonForegroundPixel, buttonBackgroundPixel;
382 char *chessDir, *programName, *programVersion;
383 Boolean alwaysOnTop = False;
384 char *icsTextMenuString;
386 char *firstChessProgramNames;
387 char *secondChessProgramNames;
389 WindowPlacement wpMain;
390 WindowPlacement wpConsole;
391 WindowPlacement wpComment;
392 WindowPlacement wpMoveHistory;
393 WindowPlacement wpEvalGraph;
394 WindowPlacement wpEngineOutput;
395 WindowPlacement wpGameList;
396 WindowPlacement wpTags;
398 extern Widget shells[];
399 extern Boolean shellUp[];
403 Pixmap pieceBitmap[2][(int)BlackPawn];
404 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
405 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
406 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
407 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
408 Pixmap xpmBoardBitmap[2];
409 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
410 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
411 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
412 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
413 XImage *ximLightSquare, *ximDarkSquare;
416 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
417 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
419 #define White(piece) ((int)(piece) < (int)BlackPawn)
421 /* Variables for doing smooth animation. This whole thing
422 would be much easier if the board was double-buffered,
423 but that would require a fairly major rewrite. */
428 GC blitGC, pieceGC, outlineGC;
429 XPoint startSquare, prevFrame, mouseDelta;
433 int startBoardX, startBoardY;
436 /* There can be two pieces being animated at once: a player
437 can begin dragging a piece before the remote opponent has moved. */
439 static AnimState game, player;
441 /* Bitmaps for use as masks when drawing XPM pieces.
442 Need one for each black and white piece. */
443 static Pixmap xpmMask[BlackKing + 1];
445 /* This magic number is the number of intermediate frames used
446 in each half of the animation. For short moves it's reduced
447 by 1. The total number of frames will be factor * 2 + 1. */
450 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
452 #define PAUSE_BUTTON "P"
453 MenuItem buttonBar[] = {
454 {"<<", "<<", ToStartEvent},
455 {"<", "<", BackwardEvent},
456 {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseEvent},
457 {">", ">", ForwardEvent},
458 {">>", ">>", ToEndEvent},
462 #define PIECE_MENU_SIZE 18
463 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
464 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
465 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
466 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
467 N_("Empty square"), N_("Clear board") },
468 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
469 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
470 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
471 N_("Empty square"), N_("Clear board") }
473 /* must be in same order as pieceMenuStrings! */
474 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
475 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
476 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
477 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
478 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
479 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
480 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
481 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
482 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
485 #define DROP_MENU_SIZE 6
486 String dropMenuStrings[DROP_MENU_SIZE] = {
487 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
489 /* must be in same order as dropMenuStrings! */
490 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
491 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
492 WhiteRook, WhiteQueen
500 DropMenuEnables dmEnables[] = {
518 { XtNborderWidth, 0 },
519 { XtNdefaultDistance, 0 },
523 { XtNborderWidth, 0 },
524 { XtNresizable, (XtArgVal) True },
528 { XtNborderWidth, 0 },
534 { XtNjustify, (XtArgVal) XtJustifyRight },
535 { XtNlabel, (XtArgVal) "..." },
536 { XtNresizable, (XtArgVal) True },
537 { XtNresize, (XtArgVal) False }
540 Arg messageArgs[] = {
541 { XtNjustify, (XtArgVal) XtJustifyLeft },
542 { XtNlabel, (XtArgVal) "..." },
543 { XtNresizable, (XtArgVal) True },
544 { XtNresize, (XtArgVal) False }
548 { XtNborderWidth, 0 },
549 { XtNjustify, (XtArgVal) XtJustifyLeft }
552 XtResource clientResources[] = {
553 { "flashCount", "flashCount", XtRInt, sizeof(int),
554 XtOffset(AppDataPtr, flashCount), XtRImmediate,
555 (XtPointer) FLASH_COUNT },
558 XrmOptionDescRec shellOptions[] = {
559 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
560 { "-flash", "flashCount", XrmoptionNoArg, "3" },
561 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
564 XtActionsRec boardActions[] = {
565 { "DrawPosition", DrawPositionProc },
566 { "HandleUserMove", HandleUserMove },
567 { "AnimateUserMove", AnimateUserMove },
568 { "HandlePV", HandlePV },
569 { "SelectPV", SelectPV },
570 { "StopPV", StopPV },
571 { "FileNameAction", FileNameAction },
572 { "AskQuestionProc", AskQuestionProc },
573 { "AskQuestionReplyAction", AskQuestionReplyAction },
574 { "PieceMenuPopup", PieceMenuPopup },
575 { "WhiteClock", WhiteClock },
576 { "BlackClock", BlackClock },
577 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
578 { "QuitProc", QuitWrapper },
579 { "ManProc", ManInner },
580 { "TempBackwardProc", TempBackwardProc },
581 { "TempForwardProc", TempForwardProc },
582 { "CommentClick", (XtActionProc) CommentClick },
583 { "CommentPopDown", (XtActionProc) CommentPopDown },
584 { "TagsPopDown", (XtActionProc) TagsPopDown },
585 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
586 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
587 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
588 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
589 { "GameListPopDown", (XtActionProc) GameListPopDown },
590 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
591 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
592 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
593 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
594 { "GenericPopDown", (XtActionProc) GenericPopDown },
595 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
596 { "SelectMove", (XtActionProc) SelectMove },
597 { "LoadSelectedProc", LoadSelectedProc },
598 { "SetFilterProc", SetFilterProc },
599 { "TypeInProc", TypeInProc },
600 { "EnterKeyProc", EnterKeyProc },
601 { "UpKeyProc", UpKeyProc },
602 { "DownKeyProc", DownKeyProc },
605 char globalTranslations[] =
606 ":<Key>F9: MenuItem(ResignProc) \n \
607 :Ctrl<Key>n: MenuItem(NewGame) \n \
608 :Meta<Key>V: MenuItem(NewVariant) \n \
609 :Ctrl<Key>o: MenuItem(LoadGame) \n \
610 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
611 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
612 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
613 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
614 :Ctrl<Key>s: MenuItem(SaveGame) \n \
615 :Ctrl<Key>c: MenuItem(CopyGame) \n \
616 :Ctrl<Key>v: MenuItem(PasteGame) \n \
617 :Ctrl<Key>O: MenuItem(LoadPosition) \n \
618 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
619 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
620 :Ctrl<Key>S: MenuItem(SavePosition) \n \
621 :Ctrl<Key>C: MenuItem(CopyPosition) \n \
622 :Ctrl<Key>V: MenuItem(PastePosition) \n \
623 :Ctrl<Key>q: MenuItem(Exit) \n \
624 :Ctrl<Key>w: MenuItem(MachineWhite) \n \
625 :Ctrl<Key>b: MenuItem(MachineBlack) \n \
626 :Ctrl<Key>t: MenuItem(TwoMachines) \n \
627 :Ctrl<Key>a: MenuItem(AnalysisMode) \n \
628 :Ctrl<Key>g: MenuItem(AnalyzeFile) \n \
629 :Ctrl<Key>e: MenuItem(EditGame) \n \
630 :Ctrl<Key>E: MenuItem(EditPosition) \n \
631 :Meta<Key>O: MenuItem(ShowEngineOutput) \n \
632 :Meta<Key>E: MenuItem(ShowEvaluationGraph) \n \
633 :Meta<Key>G: MenuItem(ShowGameList) \n \
634 :Meta<Key>H: MenuItem(ShowMoveHistory) \n \
635 :<Key>Pause: MenuItem(Pause) \n \
636 :<Key>F3: MenuItem(Accept) \n \
637 :<Key>F4: MenuItem(Decline) \n \
638 :<Key>F12: MenuItem(Rematch) \n \
639 :<Key>F5: MenuItem(CallFlag) \n \
640 :<Key>F6: MenuItem(Draw) \n \
641 :<Key>F7: MenuItem(Adjourn) \n \
642 :<Key>F8: MenuItem(Abort) \n \
643 :<Key>F10: MenuItem(StopObserving) \n \
644 :<Key>F11: MenuItem(StopExamining) \n \
645 :Ctrl<Key>d: MenuItem(DebugProc) \n \
646 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
647 :Meta<Key>End: MenuItem(ToEnd) \n \
648 :Meta<Key>Right: MenuItem(Forward) \n \
649 :Meta<Key>Home: MenuItem(ToStart) \n \
650 :Meta<Key>Left: MenuItem(Backward) \n \
651 :<Key>Left: MenuItem(Backward) \n \
652 :<Key>Right: MenuItem(Forward) \n \
653 :<Key>Home: MenuItem(Revert) \n \
654 :<Key>End: MenuItem(TruncateGame) \n \
655 :Ctrl<Key>m: MenuItem(MoveNow) \n \
656 :Ctrl<Key>x: MenuItem(RetractMove) \n \
657 :Meta<Key>J: MenuItem(Adjudications) \n \
658 :Meta<Key>U: MenuItem(CommonEngine) \n \
659 :Meta<Key>T: MenuItem(TimeControl) \n \
660 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
661 #ifndef OPTIONSDIALOG
663 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
664 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
665 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
666 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
667 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
670 :<Key>F1: MenuItem(Manual) \n \
671 :<Key>F2: MenuItem(FlipView) \n \
672 :<KeyDown>Return: TempBackwardProc() \n \
673 :<KeyUp>Return: TempForwardProc() \n";
675 char boardTranslations[] =
676 "<Btn1Down>: HandleUserMove(0) \n \
677 Shift<Btn1Up>: HandleUserMove(1) \n \
678 <Btn1Up>: HandleUserMove(0) \n \
679 <Btn1Motion>: AnimateUserMove() \n \
680 <Btn3Motion>: HandlePV() \n \
681 <Btn2Motion>: HandlePV() \n \
682 <Btn3Up>: PieceMenuPopup(menuB) \n \
683 <Btn2Up>: PieceMenuPopup(menuB) \n \
684 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
685 PieceMenuPopup(menuB) \n \
686 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
687 PieceMenuPopup(menuW) \n \
688 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
689 PieceMenuPopup(menuW) \n \
690 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
691 PieceMenuPopup(menuB) \n";
693 char whiteTranslations[] =
694 "Shift<BtnDown>: WhiteClock(1)\n \
695 <BtnDown>: WhiteClock(0)\n";
696 char blackTranslations[] =
697 "Shift<BtnDown>: BlackClock(1)\n \
698 <BtnDown>: BlackClock(0)\n";
700 char ICSInputTranslations[] =
701 "<Key>Up: UpKeyProc() \n "
702 "<Key>Down: DownKeyProc() \n "
703 "<Key>Return: EnterKeyProc() \n";
705 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
706 // as the widget is destroyed before the up-click can call extend-end
707 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
709 String xboardResources[] = {
710 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
711 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
712 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
717 /* Max possible square size */
718 #define MAXSQSIZE 256
720 static int xpm_avail[MAXSQSIZE];
722 #ifdef HAVE_DIR_STRUCT
724 /* Extract piece size from filename */
726 xpm_getsize (char *name, int len, char *ext)
734 if ((p=strchr(name, '.')) == NULL ||
735 StrCaseCmp(p+1, ext) != 0)
741 while (*p && isdigit(*p))
748 /* Setup xpm_avail */
750 xpm_getavail (char *dirname, char *ext)
756 for (i=0; i<MAXSQSIZE; ++i)
759 if (appData.debugMode)
760 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
762 dir = opendir(dirname);
765 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
766 programName, dirname);
770 while ((ent=readdir(dir)) != NULL) {
771 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
772 if (i > 0 && i < MAXSQSIZE)
782 xpm_print_avail (FILE *fp, char *ext)
786 fprintf(fp, _("Available `%s' sizes:\n"), ext);
787 for (i=1; i<MAXSQSIZE; ++i) {
793 /* Return XPM piecesize closest to size */
795 xpm_closest_to (char *dirname, int size, char *ext)
798 int sm_diff = MAXSQSIZE;
802 xpm_getavail(dirname, ext);
804 if (appData.debugMode)
805 xpm_print_avail(stderr, ext);
807 for (i=1; i<MAXSQSIZE; ++i) {
810 diff = (diff<0) ? -diff : diff;
811 if (diff < sm_diff) {
819 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
825 #else /* !HAVE_DIR_STRUCT */
826 /* If we are on a system without a DIR struct, we can't
827 read the directory, so we can't collect a list of
828 filenames, etc., so we can't do any size-fitting. */
830 xpm_closest_to (char *dirname, int size, char *ext)
833 Warning: No DIR structure found on this system --\n\
834 Unable to autosize for XPM/XIM pieces.\n\
835 Please report this error to %s.\n\
836 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
839 #endif /* HAVE_DIR_STRUCT */
841 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
842 "magenta", "cyan", "white" };
846 TextColors textColors[(int)NColorClasses];
848 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
850 parse_color (char *str, int which)
852 char *p, buf[100], *d;
855 if (strlen(str) > 99) /* watch bounds on buf */
860 for (i=0; i<which; ++i) {
867 /* Could be looking at something like:
869 .. in which case we want to stop on a comma also */
870 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
874 return -1; /* Use default for empty field */
877 if (which == 2 || isdigit(*p))
880 while (*p && isalpha(*p))
885 for (i=0; i<8; ++i) {
886 if (!StrCaseCmp(buf, cnames[i]))
887 return which? (i+40) : (i+30);
889 if (!StrCaseCmp(buf, "default")) return -1;
891 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
896 parse_cpair (ColorClass cc, char *str)
898 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
899 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
904 /* bg and attr are optional */
905 textColors[(int)cc].bg = parse_color(str, 1);
906 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
907 textColors[(int)cc].attr = 0;
913 /* Arrange to catch delete-window events */
914 Atom wm_delete_window;
916 CatchDeleteWindow (Widget w, String procname)
919 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
920 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
921 XtAugmentTranslations(w, XtParseTranslationTable(buf));
928 XtSetArg(args[0], XtNiconic, False);
929 XtSetValues(shellWidget, args, 1);
931 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
934 //---------------------------------------------------------------------------------------------------------
935 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
938 #define CW_USEDEFAULT (1<<31)
939 #define ICS_TEXT_MENU_SIZE 90
940 #define DEBUG_FILE "xboard.debug"
941 #define SetCurrentDirectory chdir
942 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
946 // these two must some day move to frontend.h, when they are implemented
947 Boolean GameListIsUp();
949 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
952 // front-end part of option handling
954 // [HGM] This platform-dependent table provides the location for storing the color info
955 extern char *crWhite, * crBlack;
959 &appData.whitePieceColor,
960 &appData.blackPieceColor,
961 &appData.lightSquareColor,
962 &appData.darkSquareColor,
963 &appData.highlightSquareColor,
964 &appData.premoveHighlightColor,
965 &appData.lowTimeWarningColor,
976 // [HGM] font: keep a font for each square size, even non-stndard ones
979 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
980 char *fontTable[NUM_FONTS][MAX_SIZE];
983 ParseFont (char *name, int number)
984 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
986 if(sscanf(name, "size%d:", &size)) {
987 // [HGM] font: font is meant for specific boardSize (likely from settings file);
988 // defer processing it until we know if it matches our board size
989 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
990 fontTable[number][size] = strdup(strchr(name, ':')+1);
991 fontValid[number][size] = True;
996 case 0: // CLOCK_FONT
997 appData.clockFont = strdup(name);
999 case 1: // MESSAGE_FONT
1000 appData.font = strdup(name);
1002 case 2: // COORD_FONT
1003 appData.coordFont = strdup(name);
1008 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1013 { // only 2 fonts currently
1014 appData.clockFont = CLOCK_FONT_NAME;
1015 appData.coordFont = COORD_FONT_NAME;
1016 appData.font = DEFAULT_FONT_NAME;
1021 { // no-op, until we identify the code for this already in XBoard and move it here
1025 ParseColor (int n, char *name)
1026 { // in XBoard, just copy the color-name string
1027 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1031 ParseTextAttribs (ColorClass cc, char *s)
1033 (&appData.colorShout)[cc] = strdup(s);
1037 ParseBoardSize (void *addr, char *name)
1039 appData.boardSize = strdup(name);
1044 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1048 SetCommPortDefaults ()
1049 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1052 // [HGM] args: these three cases taken out to stay in front-end
1054 SaveFontArg (FILE *f, ArgDescriptor *ad)
1057 int i, n = (int)(intptr_t)ad->argLoc;
1059 case 0: // CLOCK_FONT
1060 name = appData.clockFont;
1062 case 1: // MESSAGE_FONT
1063 name = appData.font;
1065 case 2: // COORD_FONT
1066 name = appData.coordFont;
1071 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1072 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1073 fontTable[n][squareSize] = strdup(name);
1074 fontValid[n][squareSize] = True;
1077 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1078 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1083 { // nothing to do, as the sounds are at all times represented by their text-string names already
1087 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
1088 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1089 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1093 SaveColor (FILE *f, ArgDescriptor *ad)
1094 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1095 if(colorVariable[(int)(intptr_t)ad->argLoc])
1096 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1100 SaveBoardSize (FILE *f, char *name, void *addr)
1101 { // wrapper to shield back-end from BoardSize & sizeInfo
1102 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1106 ParseCommPortSettings (char *s)
1107 { // no such option in XBoard (yet)
1110 extern Widget engineOutputShell;
1114 GetActualPlacement (Widget wg, WindowPlacement *wp)
1119 XWindowAttributes winAt;
1126 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
1127 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
1128 wp->x = rx - winAt.x;
1129 wp->y = ry - winAt.y;
1130 wp->height = winAt.height;
1131 wp->width = winAt.width;
1132 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
1137 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1138 // In XBoard this will have to wait until awareness of window parameters is implemented
1139 GetActualPlacement(shellWidget, &wpMain);
1140 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1141 if(MoveHistoryIsUp()) GetActualPlacement(shells[7], &wpMoveHistory);
1142 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1143 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1144 if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1145 if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1149 PrintCommPortSettings (FILE *f, char *name)
1150 { // This option does not exist in XBoard
1154 MySearchPath (char *installDir, char *name, char *fullname)
1155 { // just append installDir and name. Perhaps ExpandPath should be used here?
1156 name = ExpandPathName(name);
1157 if(name && name[0] == '/')
1158 safeStrCpy(fullname, name, MSG_SIZ );
1160 sprintf(fullname, "%s%c%s", installDir, '/', name);
1166 MyGetFullPathName (char *name, char *fullname)
1167 { // should use ExpandPath?
1168 name = ExpandPathName(name);
1169 safeStrCpy(fullname, name, MSG_SIZ );
1174 EnsureOnScreen (int *x, int *y, int minX, int minY)
1181 { // [HGM] args: allows testing if main window is realized from back-end
1182 return xBoardWindow != 0;
1186 PopUpStartupDialog ()
1187 { // start menu not implemented in XBoard
1191 ConvertToLine (int argc, char **argv)
1193 static char line[128*1024], buf[1024];
1197 for(i=1; i<argc; i++)
1199 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1200 && argv[i][0] != '{' )
1201 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1203 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1204 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1207 line[strlen(line)-1] = NULLCHAR;
1211 //--------------------------------------------------------------------------------------------
1213 extern Boolean twoBoards, partnerUp;
1216 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1218 #define BoardSize int
1220 InitDrawingSizes (BoardSize boardSize, int flags)
1221 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1222 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1224 XtGeometryResult gres;
1226 static Dimension oldWidth, oldHeight;
1227 static VariantClass oldVariant;
1228 static int oldDual = -1, oldMono = -1;
1230 if(!formWidget) return;
1232 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1233 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1234 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1236 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1238 * Enable shell resizing.
1240 shellArgs[0].value = (XtArgVal) &w;
1241 shellArgs[1].value = (XtArgVal) &h;
1242 XtGetValues(shellWidget, shellArgs, 2);
1244 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1245 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1246 XtSetValues(shellWidget, &shellArgs[2], 4);
1248 XtSetArg(args[0], XtNdefaultDistance, &sep);
1249 XtGetValues(formWidget, args, 1);
1251 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1253 hOffset = boardWidth + 10;
1254 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1255 secondSegments[i] = gridSegments[i];
1256 secondSegments[i].x1 += hOffset;
1257 secondSegments[i].x2 += hOffset;
1260 XtSetArg(args[0], XtNwidth, boardWidth);
1261 XtSetArg(args[1], XtNheight, boardHeight);
1262 XtSetValues(boardWidget, args, 2);
1264 timerWidth = (boardWidth - sep) / 2;
1265 XtSetArg(args[0], XtNwidth, timerWidth);
1266 XtSetValues(whiteTimerWidget, args, 1);
1267 XtSetValues(blackTimerWidget, args, 1);
1269 XawFormDoLayout(formWidget, False);
1271 if (appData.titleInWindow) {
1273 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1274 XtSetArg(args[i], XtNheight, &h); i++;
1275 XtGetValues(titleWidget, args, i);
1277 w = boardWidth - 2*bor;
1279 XtSetArg(args[0], XtNwidth, &w);
1280 XtGetValues(menuBarWidget, args, 1);
1281 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1284 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1285 if (gres != XtGeometryYes && appData.debugMode) {
1287 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1288 programName, gres, w, h, wr, hr);
1292 XawFormDoLayout(formWidget, True);
1295 * Inhibit shell resizing.
1297 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1298 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1299 shellArgs[4].value = shellArgs[2].value = w;
1300 shellArgs[5].value = shellArgs[3].value = h;
1301 XtSetValues(shellWidget, &shellArgs[0], 6);
1303 XSync(xDisplay, False);
1307 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1310 if(gameInfo.variant != oldVariant) { // and only if variant changed
1313 for(i=0; i<4; i++) {
1315 for(p=0; p<=(int)WhiteKing; p++)
1316 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1317 if(gameInfo.variant == VariantShogi) {
1318 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1319 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1320 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1321 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1322 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1325 if(gameInfo.variant == VariantGothic) {
1326 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1329 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1330 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1331 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1334 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1335 for(p=0; p<=(int)WhiteKing; p++)
1336 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1337 if(gameInfo.variant == VariantShogi) {
1338 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1339 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1340 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1341 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1342 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1345 if(gameInfo.variant == VariantGothic) {
1346 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1349 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1350 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1351 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1356 for(i=0; i<2; i++) {
1358 for(p=0; p<=(int)WhiteKing; p++)
1359 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1360 if(gameInfo.variant == VariantShogi) {
1361 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1362 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1363 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1364 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1365 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1368 if(gameInfo.variant == VariantGothic) {
1369 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1372 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1373 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1374 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1378 oldMono = -10; // kludge to force recreation of animation masks
1379 oldVariant = gameInfo.variant;
1382 if(appData.monoMode != oldMono)
1385 oldMono = appData.monoMode;
1390 ParseIcsTextColors ()
1391 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1392 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1393 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1394 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1395 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1396 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1397 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1398 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1399 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1400 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1401 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1403 if (appData.colorize) {
1405 _("%s: can't parse color names; disabling colorization\n"),
1408 appData.colorize = FALSE;
1413 MakeOneColor (char *name, Pixel *color)
1415 XrmValue vFrom, vTo;
1416 if (!appData.monoMode) {
1417 vFrom.addr = (caddr_t) name;
1418 vFrom.size = strlen(name);
1419 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1420 if (vTo.addr == NULL) {
1421 appData.monoMode = True;
1424 *color = *(Pixel *) vTo.addr;
1432 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1433 int forceMono = False;
1435 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1436 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1437 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1438 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1439 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1440 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1447 { // [HGM] taken out of main
1449 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1450 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1451 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1453 if (appData.bitmapDirectory[0] != NULLCHAR) {
1457 CreateXPMBoard(appData.liteBackTextureFile, 1);
1458 CreateXPMBoard(appData.darkBackTextureFile, 0);
1462 /* Create regular pieces */
1463 if (!useImages) CreatePieces();
1468 main (int argc, char **argv)
1470 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1471 XSetWindowAttributes window_attributes;
1473 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1474 XrmValue vFrom, vTo;
1475 XtGeometryResult gres;
1478 int forceMono = False;
1480 srandom(time(0)); // [HGM] book: make random truly random
1482 setbuf(stdout, NULL);
1483 setbuf(stderr, NULL);
1486 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1487 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1491 programName = strrchr(argv[0], '/');
1492 if (programName == NULL)
1493 programName = argv[0];
1498 XtSetLanguageProc(NULL, NULL, NULL);
1499 bindtextdomain(PACKAGE, LOCALEDIR);
1500 textdomain(PACKAGE);
1504 XtAppInitialize(&appContext, "XBoard", shellOptions,
1505 XtNumber(shellOptions),
1506 &argc, argv, xboardResources, NULL, 0);
1507 appData.boardSize = "";
1508 InitAppData(ConvertToLine(argc, argv));
1510 if (p == NULL) p = "/tmp";
1511 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1512 gameCopyFilename = (char*) malloc(i);
1513 gamePasteFilename = (char*) malloc(i);
1514 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1515 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1517 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1518 clientResources, XtNumber(clientResources),
1521 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1522 static char buf[MSG_SIZ];
1523 EscapeExpand(buf, appData.firstInitString);
1524 appData.firstInitString = strdup(buf);
1525 EscapeExpand(buf, appData.secondInitString);
1526 appData.secondInitString = strdup(buf);
1527 EscapeExpand(buf, appData.firstComputerString);
1528 appData.firstComputerString = strdup(buf);
1529 EscapeExpand(buf, appData.secondComputerString);
1530 appData.secondComputerString = strdup(buf);
1533 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1536 if (chdir(chessDir) != 0) {
1537 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1543 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1544 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1545 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1546 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1549 setbuf(debugFP, NULL);
1553 if (appData.debugMode) {
1554 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1558 /* [HGM,HR] make sure board size is acceptable */
1559 if(appData.NrFiles > BOARD_FILES ||
1560 appData.NrRanks > BOARD_RANKS )
1561 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1564 /* This feature does not work; animation needs a rewrite */
1565 appData.highlightDragging = FALSE;
1569 xDisplay = XtDisplay(shellWidget);
1570 xScreen = DefaultScreen(xDisplay);
1571 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1573 gameInfo.variant = StringToVariant(appData.variant);
1574 InitPosition(FALSE);
1577 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1579 if (isdigit(appData.boardSize[0])) {
1580 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1581 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1582 &fontPxlSize, &smallLayout, &tinyLayout);
1584 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1585 programName, appData.boardSize);
1589 /* Find some defaults; use the nearest known size */
1590 SizeDefaults *szd, *nearest;
1591 int distance = 99999;
1592 nearest = szd = sizeDefaults;
1593 while (szd->name != NULL) {
1594 if (abs(szd->squareSize - squareSize) < distance) {
1596 distance = abs(szd->squareSize - squareSize);
1597 if (distance == 0) break;
1601 if (i < 2) lineGap = nearest->lineGap;
1602 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1603 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1604 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1605 if (i < 6) smallLayout = nearest->smallLayout;
1606 if (i < 7) tinyLayout = nearest->tinyLayout;
1609 SizeDefaults *szd = sizeDefaults;
1610 if (*appData.boardSize == NULLCHAR) {
1611 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1612 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1615 if (szd->name == NULL) szd--;
1616 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1618 while (szd->name != NULL &&
1619 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1620 if (szd->name == NULL) {
1621 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1622 programName, appData.boardSize);
1626 squareSize = szd->squareSize;
1627 lineGap = szd->lineGap;
1628 clockFontPxlSize = szd->clockFontPxlSize;
1629 coordFontPxlSize = szd->coordFontPxlSize;
1630 fontPxlSize = szd->fontPxlSize;
1631 smallLayout = szd->smallLayout;
1632 tinyLayout = szd->tinyLayout;
1633 // [HGM] font: use defaults from settings file if available and not overruled
1635 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1636 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1637 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1638 appData.font = fontTable[MESSAGE_FONT][squareSize];
1639 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1640 appData.coordFont = fontTable[COORD_FONT][squareSize];
1642 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1643 if (strlen(appData.pixmapDirectory) > 0) {
1644 p = ExpandPathName(appData.pixmapDirectory);
1646 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1647 appData.pixmapDirectory);
1650 if (appData.debugMode) {
1651 fprintf(stderr, _("\
1652 XBoard square size (hint): %d\n\
1653 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1655 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1656 if (appData.debugMode) {
1657 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1660 defaultLineGap = lineGap;
1661 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1663 /* [HR] height treated separately (hacked) */
1664 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1665 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1666 if (appData.showJail == 1) {
1667 /* Jail on top and bottom */
1668 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1669 XtSetArg(boardArgs[2], XtNheight,
1670 boardHeight + 2*(lineGap + squareSize));
1671 } else if (appData.showJail == 2) {
1673 XtSetArg(boardArgs[1], XtNwidth,
1674 boardWidth + 2*(lineGap + squareSize));
1675 XtSetArg(boardArgs[2], XtNheight, boardHeight);
1678 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1679 XtSetArg(boardArgs[2], XtNheight, boardHeight);
1683 * Determine what fonts to use.
1686 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1687 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1688 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1689 fontSet = CreateFontSet(appData.font);
1690 clockFontSet = CreateFontSet(appData.clockFont);
1692 /* For the coordFont, use the 0th font of the fontset. */
1693 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1694 XFontStruct **font_struct_list;
1695 XFontSetExtents *fontSize;
1696 char **font_name_list;
1697 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1698 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1699 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1700 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1701 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1704 appData.font = FindFont(appData.font, fontPxlSize);
1705 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1706 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1707 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1708 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1709 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1710 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1712 countFontID = coordFontID; // [HGM] holdings
1713 countFontStruct = coordFontStruct;
1715 xdb = XtDatabase(xDisplay);
1717 XrmPutLineResource(&xdb, "*international: True");
1718 vTo.size = sizeof(XFontSet);
1719 vTo.addr = (XtPointer) &fontSet;
1720 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1722 XrmPutStringResource(&xdb, "*font", appData.font);
1726 * Detect if there are not enough colors available and adapt.
1728 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1729 appData.monoMode = True;
1732 forceMono = MakeColors();
1735 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1737 appData.monoMode = True;
1740 if (appData.lowTimeWarning && !appData.monoMode) {
1741 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1742 vFrom.size = strlen(appData.lowTimeWarningColor);
1743 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1744 if (vTo.addr == NULL)
1745 appData.monoMode = True;
1747 lowTimeWarningColor = *(Pixel *) vTo.addr;
1750 if (appData.monoMode && appData.debugMode) {
1751 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1752 (unsigned long) XWhitePixel(xDisplay, xScreen),
1753 (unsigned long) XBlackPixel(xDisplay, xScreen));
1756 ParseIcsTextColors();
1757 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
1758 textColors[ColorNone].attr = 0;
1760 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1766 layoutName = "tinyLayout";
1767 } else if (smallLayout) {
1768 layoutName = "smallLayout";
1770 layoutName = "normalLayout";
1772 /* Outer layoutWidget is there only to provide a name for use in
1773 resources that depend on the layout style */
1775 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
1776 layoutArgs, XtNumber(layoutArgs));
1778 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
1779 formArgs, XtNumber(formArgs));
1780 XtSetArg(args[0], XtNdefaultDistance, &sep);
1781 XtGetValues(formWidget, args, 1);
1784 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
1785 XtSetArg(args[0], XtNtop, XtChainTop);
1786 XtSetArg(args[1], XtNbottom, XtChainTop);
1787 XtSetArg(args[2], XtNright, XtChainLeft);
1788 XtSetValues(menuBarWidget, args, 3);
1790 widgetList[j++] = whiteTimerWidget =
1791 XtCreateWidget("whiteTime", labelWidgetClass,
1792 formWidget, timerArgs, XtNumber(timerArgs));
1794 XtSetArg(args[0], XtNfontSet, clockFontSet);
1796 XtSetArg(args[0], XtNfont, clockFontStruct);
1798 XtSetArg(args[1], XtNtop, XtChainTop);
1799 XtSetArg(args[2], XtNbottom, XtChainTop);
1800 XtSetValues(whiteTimerWidget, args, 3);
1802 widgetList[j++] = blackTimerWidget =
1803 XtCreateWidget("blackTime", labelWidgetClass,
1804 formWidget, timerArgs, XtNumber(timerArgs));
1806 XtSetArg(args[0], XtNfontSet, clockFontSet);
1808 XtSetArg(args[0], XtNfont, clockFontStruct);
1810 XtSetArg(args[1], XtNtop, XtChainTop);
1811 XtSetArg(args[2], XtNbottom, XtChainTop);
1812 XtSetValues(blackTimerWidget, args, 3);
1814 if (appData.titleInWindow) {
1815 widgetList[j++] = titleWidget =
1816 XtCreateWidget("title", labelWidgetClass, formWidget,
1817 titleArgs, XtNumber(titleArgs));
1818 XtSetArg(args[0], XtNtop, XtChainTop);
1819 XtSetArg(args[1], XtNbottom, XtChainTop);
1820 XtSetValues(titleWidget, args, 2);
1823 if (appData.showButtonBar) {
1824 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
1825 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
1826 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
1827 XtSetArg(args[2], XtNtop, XtChainTop);
1828 XtSetArg(args[3], XtNbottom, XtChainTop);
1829 XtSetValues(buttonBarWidget, args, 4);
1832 widgetList[j++] = messageWidget =
1833 XtCreateWidget("message", labelWidgetClass, formWidget,
1834 messageArgs, XtNumber(messageArgs));
1835 XtSetArg(args[0], XtNtop, XtChainTop);
1836 XtSetArg(args[1], XtNbottom, XtChainTop);
1837 XtSetValues(messageWidget, args, 2);
1839 widgetList[j++] = boardWidget =
1840 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
1841 XtNumber(boardArgs));
1843 XtManageChildren(widgetList, j);
1845 timerWidth = (boardWidth - sep) / 2;
1846 XtSetArg(args[0], XtNwidth, timerWidth);
1847 XtSetValues(whiteTimerWidget, args, 1);
1848 XtSetValues(blackTimerWidget, args, 1);
1850 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1851 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1852 XtGetValues(whiteTimerWidget, args, 2);
1854 if (appData.showButtonBar) {
1855 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1856 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1857 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
1861 * formWidget uses these constraints but they are stored
1865 XtSetArg(args[i], XtNfromHoriz, 0); i++;
1866 XtSetValues(menuBarWidget, args, i);
1867 if (appData.titleInWindow) {
1870 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1871 XtSetValues(whiteTimerWidget, args, i);
1873 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1874 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1875 XtSetValues(blackTimerWidget, args, i);
1877 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1878 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
1879 XtSetValues(titleWidget, args, i);
1881 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1882 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1883 XtSetValues(messageWidget, args, i);
1884 if (appData.showButtonBar) {
1886 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1887 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1888 XtSetValues(buttonBarWidget, args, i);
1892 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1893 XtSetValues(whiteTimerWidget, args, i);
1895 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1896 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1897 XtSetValues(blackTimerWidget, args, i);
1899 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
1900 XtSetValues(titleWidget, args, i);
1902 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1903 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1904 XtSetValues(messageWidget, args, i);
1905 if (appData.showButtonBar) {
1907 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1908 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1909 XtSetValues(buttonBarWidget, args, i);
1914 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1915 XtSetValues(whiteTimerWidget, args, i);
1917 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1918 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1919 XtSetValues(blackTimerWidget, args, i);
1921 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1922 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1923 XtSetValues(messageWidget, args, i);
1924 if (appData.showButtonBar) {
1926 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1927 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1928 XtSetValues(buttonBarWidget, args, i);
1932 XtSetArg(args[0], XtNfromVert, messageWidget);
1933 XtSetArg(args[1], XtNtop, XtChainTop);
1934 XtSetArg(args[2], XtNbottom, XtChainBottom);
1935 XtSetArg(args[3], XtNleft, XtChainLeft);
1936 XtSetArg(args[4], XtNright, XtChainRight);
1937 XtSetValues(boardWidget, args, 5);
1939 XtRealizeWidget(shellWidget);
1942 XtSetArg(args[0], XtNx, wpMain.x);
1943 XtSetArg(args[1], XtNy, wpMain.y);
1944 XtSetValues(shellWidget, args, 2);
1948 * Correct the width of the message and title widgets.
1949 * It is not known why some systems need the extra fudge term.
1950 * The value "2" is probably larger than needed.
1952 XawFormDoLayout(formWidget, False);
1954 #define WIDTH_FUDGE 2
1956 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1957 XtSetArg(args[i], XtNheight, &h); i++;
1958 XtGetValues(messageWidget, args, i);
1959 if (appData.showButtonBar) {
1961 XtSetArg(args[i], XtNwidth, &w); i++;
1962 XtGetValues(buttonBarWidget, args, i);
1963 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1965 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
1968 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1969 if (gres != XtGeometryYes && appData.debugMode) {
1970 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1971 programName, gres, w, h, wr, hr);
1974 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
1975 /* The size used for the child widget in layout lags one resize behind
1976 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
1978 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1979 if (gres != XtGeometryYes && appData.debugMode) {
1980 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1981 programName, gres, w, h, wr, hr);
1984 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
1985 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
1986 XtSetArg(args[1], XtNright, XtChainRight);
1987 XtSetValues(messageWidget, args, 2);
1989 if (appData.titleInWindow) {
1991 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1992 XtSetArg(args[i], XtNheight, &h); i++;
1993 XtGetValues(titleWidget, args, i);
1995 w = boardWidth - 2*bor;
1997 XtSetArg(args[0], XtNwidth, &w);
1998 XtGetValues(menuBarWidget, args, 1);
1999 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2002 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2003 if (gres != XtGeometryYes && appData.debugMode) {
2005 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2006 programName, gres, w, h, wr, hr);
2009 XawFormDoLayout(formWidget, True);
2011 xBoardWindow = XtWindow(boardWidget);
2013 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2014 // not need to go into InitDrawingSizes().
2018 * Create X checkmark bitmap and initialize option menu checks.
2020 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2021 checkmark_bits, checkmark_width, checkmark_height);
2027 ReadBitmap(&wIconPixmap, "icon_white.bm",
2028 icon_white_bits, icon_white_width, icon_white_height);
2029 ReadBitmap(&bIconPixmap, "icon_black.bm",
2030 icon_black_bits, icon_black_width, icon_black_height);
2031 iconPixmap = wIconPixmap;
2033 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2034 XtSetValues(shellWidget, args, i);
2037 * Create a cursor for the board widget.
2039 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2040 XChangeWindowAttributes(xDisplay, xBoardWindow,
2041 CWCursor, &window_attributes);
2044 * Inhibit shell resizing.
2046 shellArgs[0].value = (XtArgVal) &w;
2047 shellArgs[1].value = (XtArgVal) &h;
2048 XtGetValues(shellWidget, shellArgs, 2);
2049 shellArgs[4].value = shellArgs[2].value = w;
2050 shellArgs[5].value = shellArgs[3].value = h;
2051 XtSetValues(shellWidget, &shellArgs[2], 4);
2052 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2053 marginH = h - boardHeight;
2055 CatchDeleteWindow(shellWidget, "QuitProc");
2063 if (appData.animate || appData.animateDragging)
2066 XtAugmentTranslations(formWidget,
2067 XtParseTranslationTable(globalTranslations));
2068 XtAugmentTranslations(boardWidget,
2069 XtParseTranslationTable(boardTranslations));
2070 XtAugmentTranslations(whiteTimerWidget,
2071 XtParseTranslationTable(whiteTranslations));
2072 XtAugmentTranslations(blackTimerWidget,
2073 XtParseTranslationTable(blackTranslations));
2075 /* Why is the following needed on some versions of X instead
2076 * of a translation? */
2077 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2078 (XtEventHandler) EventProc, NULL);
2080 XtAddEventHandler(formWidget, KeyPressMask, False,
2081 (XtEventHandler) MoveTypeInProc, NULL);
2082 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
2083 (XtEventHandler) EventProc, NULL);
2085 /* [AS] Restore layout */
2086 if( wpMoveHistory.visible ) {
2090 if( wpEvalGraph.visible )
2095 if( wpEngineOutput.visible ) {
2096 EngineOutputPopUp();
2101 if (errorExitStatus == -1) {
2102 if (appData.icsActive) {
2103 /* We now wait until we see "login:" from the ICS before
2104 sending the logon script (problems with timestamp otherwise) */
2105 /*ICSInitScript();*/
2106 if (appData.icsInputBox) ICSInputBoxPopUp();
2110 signal(SIGWINCH, TermSizeSigHandler);
2112 signal(SIGINT, IntSigHandler);
2113 signal(SIGTERM, IntSigHandler);
2114 if (*appData.cmailGameName != NULLCHAR) {
2115 signal(SIGUSR1, CmailSigHandler);
2119 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2121 // XtSetKeyboardFocus(shellWidget, formWidget);
2122 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2124 XtAppMainLoop(appContext);
2125 if (appData.debugMode) fclose(debugFP); // [DM] debug
2129 static Boolean noEcho;
2134 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2135 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2137 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2138 unlink(gameCopyFilename);
2139 unlink(gamePasteFilename);
2140 if(noEcho) EchoOn();
2144 TermSizeSigHandler (int sig)
2150 IntSigHandler (int sig)
2156 CmailSigHandler (int sig)
2161 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2163 /* Activate call-back function CmailSigHandlerCallBack() */
2164 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2166 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2170 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
2173 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2175 /**** end signal code ****/
2181 /* try to open the icsLogon script, either in the location given
2182 * or in the users HOME directory
2189 f = fopen(appData.icsLogon, "r");
2192 homedir = getenv("HOME");
2193 if (homedir != NULL)
2195 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2196 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2197 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2198 f = fopen(buf, "r");
2203 ProcessICSInitScript(f);
2205 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2218 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
2219 #define HISTORY_SIZE 64
2220 static char *history[HISTORY_SIZE];
2221 int histIn = 0, histP = 0;
2224 SaveInHistory (char *cmd)
2226 if (history[histIn] != NULL) {
2227 free(history[histIn]);
2228 history[histIn] = NULL;
2230 if (*cmd == NULLCHAR) return;
2231 history[histIn] = StrSave(cmd);
2232 histIn = (histIn + 1) % HISTORY_SIZE;
2233 if (history[histIn] != NULL) {
2234 free(history[histIn]);
2235 history[histIn] = NULL;
2241 PrevInHistory (char *cmd)
2244 if (histP == histIn) {
2245 if (history[histIn] != NULL) free(history[histIn]);
2246 history[histIn] = StrSave(cmd);
2248 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
2249 if (newhp == histIn || history[newhp] == NULL) return NULL;
2251 return history[histP];
2257 if (histP == histIn) return NULL;
2258 histP = (histP + 1) % HISTORY_SIZE;
2259 return history[histP];
2261 // end of borrowed code
2263 #define Abs(n) ((n)<0 ? -(n) : (n))
2267 InsertPxlSize (char *pattern, int targetPxlSize)
2269 char *base_fnt_lst, strInt[12], *p, *q;
2270 int alternatives, i, len, strIntLen;
2273 * Replace the "*" (if present) in the pixel-size slot of each
2274 * alternative with the targetPxlSize.
2278 while ((p = strchr(p, ',')) != NULL) {
2282 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
2283 strIntLen = strlen(strInt);
2284 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
2288 while (alternatives--) {
2289 char *comma = strchr(p, ',');
2290 for (i=0; i<14; i++) {
2291 char *hyphen = strchr(p, '-');
2293 if (comma && hyphen > comma) break;
2294 len = hyphen + 1 - p;
2295 if (i == 7 && *p == '*' && len == 2) {
2297 memcpy(q, strInt, strIntLen);
2307 len = comma + 1 - p;
2314 return base_fnt_lst;
2318 CreateFontSet (char *base_fnt_lst)
2321 char **missing_list;
2325 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
2326 &missing_list, &missing_count, &def_string);
2327 if (appData.debugMode) {
2329 XFontStruct **font_struct_list;
2330 char **font_name_list;
2331 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
2333 fprintf(debugFP, " got list %s, locale %s\n",
2334 XBaseFontNameListOfFontSet(fntSet),
2335 XLocaleOfFontSet(fntSet));
2336 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
2337 for (i = 0; i < count; i++) {
2338 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
2341 for (i = 0; i < missing_count; i++) {
2342 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
2345 if (fntSet == NULL) {
2346 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
2351 #else // not ENABLE_NLS
2353 * Find a font that matches "pattern" that is as close as
2354 * possible to the targetPxlSize. Prefer fonts that are k
2355 * pixels smaller to fonts that are k pixels larger. The
2356 * pattern must be in the X Consortium standard format,
2357 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2358 * The return value should be freed with XtFree when no
2362 FindFont (char *pattern, int targetPxlSize)
2364 char **fonts, *p, *best, *scalable, *scalableTail;
2365 int i, j, nfonts, minerr, err, pxlSize;
2367 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2369 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2370 programName, pattern);
2377 for (i=0; i<nfonts; i++) {
2380 if (*p != '-') continue;
2382 if (*p == NULLCHAR) break;
2383 if (*p++ == '-') j++;
2385 if (j < 7) continue;
2388 scalable = fonts[i];
2391 err = pxlSize - targetPxlSize;
2392 if (Abs(err) < Abs(minerr) ||
2393 (minerr > 0 && err < 0 && -err == minerr)) {
2399 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2400 /* If the error is too big and there is a scalable font,
2401 use the scalable font. */
2402 int headlen = scalableTail - scalable;
2403 p = (char *) XtMalloc(strlen(scalable) + 10);
2404 while (isdigit(*scalableTail)) scalableTail++;
2405 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2407 p = (char *) XtMalloc(strlen(best) + 2);
2408 safeStrCpy(p, best, strlen(best)+1 );
2410 if (appData.debugMode) {
2411 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2412 pattern, targetPxlSize, p);
2414 XFreeFontNames(fonts);
2421 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
2422 // must be called before all non-first callse to CreateGCs()
2423 XtReleaseGC(shellWidget, highlineGC);
2424 XtReleaseGC(shellWidget, lightSquareGC);
2425 XtReleaseGC(shellWidget, darkSquareGC);
2426 XtReleaseGC(shellWidget, lineGC);
2427 if (appData.monoMode) {
2428 if (DefaultDepth(xDisplay, xScreen) == 1) {
2429 XtReleaseGC(shellWidget, wbPieceGC);
2431 XtReleaseGC(shellWidget, bwPieceGC);
2434 XtReleaseGC(shellWidget, prelineGC);
2435 XtReleaseGC(shellWidget, jailSquareGC);
2436 XtReleaseGC(shellWidget, wdPieceGC);
2437 XtReleaseGC(shellWidget, wlPieceGC);
2438 XtReleaseGC(shellWidget, wjPieceGC);
2439 XtReleaseGC(shellWidget, bdPieceGC);
2440 XtReleaseGC(shellWidget, blPieceGC);
2441 XtReleaseGC(shellWidget, bjPieceGC);
2446 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
2448 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2449 | GCBackground | GCFunction | GCPlaneMask;
2450 gc_values->foreground = foreground;
2451 gc_values->background = background;
2452 return XtGetGC(shellWidget, value_mask, gc_values);
2456 CreateGCs (int redo)
2458 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2459 | GCBackground | GCFunction | GCPlaneMask;
2460 XGCValues gc_values;
2462 Pixel white = XWhitePixel(xDisplay, xScreen);
2463 Pixel black = XBlackPixel(xDisplay, xScreen);
2465 gc_values.plane_mask = AllPlanes;
2466 gc_values.line_width = lineGap;
2467 gc_values.line_style = LineSolid;
2468 gc_values.function = GXcopy;
2471 DeleteGCs(); // called a second time; clean up old GCs first
2472 } else { // [HGM] grid and font GCs created on first call only
2473 coordGC = CreateOneGC(&gc_values, black, white);
2474 XSetFont(xDisplay, coordGC, coordFontID);
2476 // [HGM] make font for holdings counts (white on black)
2477 countGC = CreateOneGC(&gc_values, white, black);
2478 XSetFont(xDisplay, countGC, countFontID);
2480 lineGC = CreateOneGC(&gc_values, black, black);
2482 if (appData.monoMode) {
2484 highlineGC = CreateOneGC(&gc_values, white, white);
2485 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
2486 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
2488 if (DefaultDepth(xDisplay, xScreen) == 1) {
2489 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2490 gc_values.function = GXcopyInverted;
2491 copyInvertedGC = CreateOneGC(&gc_values, black, white);
2492 gc_values.function = GXcopy;
2493 if (XBlackPixel(xDisplay, xScreen) == 1) {
2494 bwPieceGC = darkSquareGC;
2495 wbPieceGC = copyInvertedGC;
2497 bwPieceGC = copyInvertedGC;
2498 wbPieceGC = lightSquareGC;
2503 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
2504 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
2505 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
2506 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
2507 jailSquareGC = CreateOneGC(&gc_values, jailSquareColor, jailSquareColor);
2508 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
2509 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
2510 wjPieceGC = CreateOneGC(&gc_values, whitePieceColor, jailSquareColor);
2511 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
2512 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
2513 bjPieceGC = CreateOneGC(&gc_values, blackPieceColor, jailSquareColor);
2518 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
2526 fp = fopen(filename, "rb");
2528 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
2535 for (y=0; y<h; ++y) {
2536 for (x=0; x<h; ++x) {
2541 XPutPixel(xim, x, y, blackPieceColor);
2543 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2546 XPutPixel(xim, x, y, darkSquareColor);
2548 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2551 XPutPixel(xim, x, y, whitePieceColor);
2553 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2556 XPutPixel(xim, x, y, lightSquareColor);
2558 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2566 /* create Pixmap of piece */
2567 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2569 XPutImage(xDisplay, *dest, lightSquareGC, xim,
2572 /* create Pixmap of clipmask
2573 Note: We assume the white/black pieces have the same
2574 outline, so we make only 6 masks. This is okay
2575 since the XPM clipmask routines do the same. */
2577 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2579 XPutImage(xDisplay, temp, lightSquareGC, xmask,
2582 /* now create the 1-bit version */
2583 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2586 values.foreground = 1;
2587 values.background = 0;
2589 /* Don't use XtGetGC, not read only */
2590 maskGC = XCreateGC(xDisplay, *mask,
2591 GCForeground | GCBackground, &values);
2592 XCopyPlane(xDisplay, temp, *mask, maskGC,
2593 0, 0, squareSize, squareSize, 0, 0, 1);
2594 XFreePixmap(xDisplay, temp);
2599 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
2607 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
2612 /* The XSynchronize calls were copied from CreatePieces.
2613 Not sure if needed, but can't hurt */
2614 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2617 /* temp needed by loadXIM() */
2618 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2619 0, 0, ss, ss, AllPlanes, XYPixmap);
2621 if (strlen(appData.pixmapDirectory) == 0) {
2625 if (appData.monoMode) {
2626 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
2630 fprintf(stderr, _("\nLoading XIMs...\n"));
2632 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2633 fprintf(stderr, "%d", piece+1);
2634 for (kind=0; kind<4; kind++) {
2635 fprintf(stderr, ".");
2636 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2637 ExpandPathName(appData.pixmapDirectory),
2638 piece <= (int) WhiteKing ? "" : "w",
2639 pieceBitmapNames[piece],
2641 ximPieceBitmap[kind][piece] =
2642 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2643 0, 0, ss, ss, AllPlanes, XYPixmap);
2644 if (appData.debugMode)
2645 fprintf(stderr, _("(File:%s:) "), buf);
2646 loadXIM(ximPieceBitmap[kind][piece],
2648 &(xpmPieceBitmap2[kind][piece]),
2649 &(ximMaskPm2[piece]));
2650 if(piece <= (int)WhiteKing)
2651 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2653 fprintf(stderr," ");
2655 /* Load light and dark squares */
2656 /* If the LSQ and DSQ pieces don't exist, we will
2657 draw them with solid squares. */
2658 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2659 if (access(buf, 0) != 0) {
2663 fprintf(stderr, _("light square "));
2665 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2666 0, 0, ss, ss, AllPlanes, XYPixmap);
2667 if (appData.debugMode)
2668 fprintf(stderr, _("(File:%s:) "), buf);
2670 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2671 fprintf(stderr, _("dark square "));
2672 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2673 ExpandPathName(appData.pixmapDirectory), ss);
2674 if (appData.debugMode)
2675 fprintf(stderr, _("(File:%s:) "), buf);
2677 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2678 0, 0, ss, ss, AllPlanes, XYPixmap);
2679 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2680 xpmJailSquare = xpmLightSquare;
2682 fprintf(stderr, _("Done.\n"));
2684 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2687 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2691 CreateXPMBoard (char *s, int kind)
2695 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2696 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2697 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2703 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2704 // thisroutine has to be called t free the old piece pixmaps
2706 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2707 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2709 XFreePixmap(xDisplay, xpmLightSquare);
2710 XFreePixmap(xDisplay, xpmDarkSquare);
2719 u_int ss = squareSize;
2721 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2722 XpmColorSymbol symbols[4];
2723 static int redo = False;
2725 if(redo) FreeXPMPieces(); else redo = 1;
2727 /* The XSynchronize calls were copied from CreatePieces.
2728 Not sure if needed, but can't hurt */
2729 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2731 /* Setup translations so piece colors match square colors */
2732 symbols[0].name = "light_piece";
2733 symbols[0].value = appData.whitePieceColor;
2734 symbols[1].name = "dark_piece";
2735 symbols[1].value = appData.blackPieceColor;
2736 symbols[2].name = "light_square";
2737 symbols[2].value = appData.lightSquareColor;
2738 symbols[3].name = "dark_square";
2739 symbols[3].value = appData.darkSquareColor;
2741 attr.valuemask = XpmColorSymbols;
2742 attr.colorsymbols = symbols;
2743 attr.numsymbols = 4;
2745 if (appData.monoMode) {
2746 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2750 if (strlen(appData.pixmapDirectory) == 0) {
2751 XpmPieces* pieces = builtInXpms;
2754 while (pieces->size != squareSize && pieces->size) pieces++;
2755 if (!pieces->size) {
2756 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2759 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2760 for (kind=0; kind<4; kind++) {
2762 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2763 pieces->xpm[piece][kind],
2764 &(xpmPieceBitmap2[kind][piece]),
2765 NULL, &attr)) != 0) {
2766 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2770 if(piece <= (int) WhiteKing)
2771 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2775 xpmJailSquare = xpmLightSquare;
2779 fprintf(stderr, _("\nLoading XPMs...\n"));
2782 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2783 fprintf(stderr, "%d ", piece+1);
2784 for (kind=0; kind<4; kind++) {
2785 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2786 ExpandPathName(appData.pixmapDirectory),
2787 piece > (int) WhiteKing ? "w" : "",
2788 pieceBitmapNames[piece],
2790 if (appData.debugMode) {
2791 fprintf(stderr, _("(File:%s:) "), buf);
2793 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2794 &(xpmPieceBitmap2[kind][piece]),
2795 NULL, &attr)) != 0) {
2796 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2797 // [HGM] missing: read of unorthodox piece failed; substitute King.
2798 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2799 ExpandPathName(appData.pixmapDirectory),
2801 if (appData.debugMode) {
2802 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2804 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2805 &(xpmPieceBitmap2[kind][piece]),
2809 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2814 if(piece <= (int) WhiteKing)
2815 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2818 /* Load light and dark squares */
2819 /* If the LSQ and DSQ pieces don't exist, we will
2820 draw them with solid squares. */
2821 fprintf(stderr, _("light square "));
2822 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2823 if (access(buf, 0) != 0) {
2827 if (appData.debugMode)
2828 fprintf(stderr, _("(File:%s:) "), buf);
2830 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2831 &xpmLightSquare, NULL, &attr)) != 0) {
2832 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2835 fprintf(stderr, _("dark square "));
2836 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2837 ExpandPathName(appData.pixmapDirectory), ss);
2838 if (appData.debugMode) {
2839 fprintf(stderr, _("(File:%s:) "), buf);
2841 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2842 &xpmDarkSquare, NULL, &attr)) != 0) {
2843 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2847 xpmJailSquare = xpmLightSquare;
2848 fprintf(stderr, _("Done.\n"));
2850 oldVariant = -1; // kludge to force re-makig of animation masks
2851 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2854 #endif /* HAVE_LIBXPM */
2857 /* No built-in bitmaps */
2862 u_int ss = squareSize;
2864 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2867 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2868 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2869 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2870 pieceBitmapNames[piece],
2871 ss, kind == SOLID ? 's' : 'o');
2872 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2873 if(piece <= (int)WhiteKing)
2874 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2878 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2882 /* With built-in bitmaps */
2886 BuiltInBits* bib = builtInBits;
2889 u_int ss = squareSize;
2891 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2894 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2896 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2897 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2898 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2899 pieceBitmapNames[piece],
2900 ss, kind == SOLID ? 's' : 'o');
2901 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2902 bib->bits[kind][piece], ss, ss);
2903 if(piece <= (int)WhiteKing)
2904 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2908 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2914 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2919 char msg[MSG_SIZ], fullname[MSG_SIZ];
2921 if (*appData.bitmapDirectory != NULLCHAR) {
2922 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2923 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2924 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2925 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2926 &w, &h, pm, &x_hot, &y_hot);
2927 fprintf(stderr, "load %s\n", name);
2928 if (errcode != BitmapSuccess) {
2930 case BitmapOpenFailed:
2931 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2933 case BitmapFileInvalid:
2934 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2936 case BitmapNoMemory:
2937 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2941 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2945 fprintf(stderr, _("%s: %s...using built-in\n"),
2947 } else if (w != wreq || h != hreq) {
2949 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2950 programName, fullname, w, h, wreq, hreq);
2956 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2966 if (lineGap == 0) return;
2968 /* [HR] Split this into 2 loops for non-square boards. */
2970 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2971 gridSegments[i].x1 = 0;
2972 gridSegments[i].x2 =
2973 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2974 gridSegments[i].y1 = gridSegments[i].y2
2975 = lineGap / 2 + (i * (squareSize + lineGap));
2978 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2979 gridSegments[j + i].y1 = 0;
2980 gridSegments[j + i].y2 =
2981 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2982 gridSegments[j + i].x1 = gridSegments[j + i].x2
2983 = lineGap / 2 + (j * (squareSize + lineGap));
2987 int nrOfMenuItems = 7;
2988 Widget menuWidget[150];
2989 MenuListItem menuItemList[150] = {
2990 { "LoadNextGameProc", LoadNextGameProc },
2991 { "LoadPrevGameProc", LoadPrevGameProc },
2992 { "ReloadGameProc", ReloadGameProc },
2993 { "ReloadPositionProc", ReloadPositionProc },
2994 #ifndef OPTIONSDIALOG
2995 { "AlwaysQueenProc", AlwaysQueenProc },
2996 { "AnimateDraggingProc", AnimateDraggingProc },
2997 { "AnimateMovingProc", AnimateMovingProc },
2998 { "AutoflagProc", AutoflagProc },
2999 { "AutoflipProc", AutoflipProc },
3000 { "BlindfoldProc", BlindfoldProc },
3001 { "FlashMovesProc", FlashMovesProc },
3003 { "HighlightDraggingProc", HighlightDraggingProc },
3005 { "HighlightLastMoveProc", HighlightLastMoveProc },
3006 // { "IcsAlarmProc", IcsAlarmProc },
3007 { "MoveSoundProc", MoveSoundProc },
3008 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
3009 { "PopupExitMessageProc", PopupExitMessageProc },
3010 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
3011 // { "PremoveProc", PremoveProc },
3012 { "ShowCoordsProc", ShowCoordsProc },
3013 { "ShowThinkingProc", ShowThinkingProc },
3014 { "HideThinkingProc", HideThinkingProc },
3015 { "TestLegalityProc", TestLegalityProc },
3017 { "AboutGameProc", AboutGameEvent },
3018 { "DebugProc", DebugProc },
3019 { "NothingProc", NothingProc },
3024 MarkMenuItem (char *menuRef, int state)
3026 int nr = MenuToNumber(menuRef);
3029 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
3030 XtSetValues(menuWidget[nr], args, 1);
3035 EnableMenuItem (char *menuRef, int state)
3037 int nr = MenuToNumber(menuRef);
3038 if(nr >= 0) XtSetSensitive(menuWidget[nr], state);
3042 EnableButtonBar (int state)
3044 XtSetSensitive(buttonBarWidget, state);
3049 SetMenuEnables (Enables *enab)
3051 while (enab->name != NULL) {
3052 EnableMenuItem(enab->name, enab->value);
3058 Equal(char *p, char *s)
3059 { // compare strings skipping spaces in second
3061 if(*s == ' ') { s++; continue; }
3062 if(*s++ != *p++) return 0;
3068 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3069 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
3071 if(*nprms == 0) return;
3072 for(i=0; menuItemList[i].name; i++) {
3073 if(Equal(prms[0], menuItemList[i].name)) {
3074 (menuItemList[i].proc) ();
3081 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
3083 MenuProc *proc = (MenuProc *) addr;
3089 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
3091 RecentEngineEvent((int) (intptr_t) addr);
3094 // some stuff that must remain in front-end
3095 static Widget mainBar, currentMenu;
3096 static int wtot, nr = 0, widths[10];
3099 AppendMenuItem (char *text, char *name, MenuProc *action)
3106 XtSetArg(args[j], XtNleftMargin, 20); j++;
3107 XtSetArg(args[j], XtNrightMargin, 20); j++;
3109 if (strcmp(text, "----") == 0) {
3110 entry = XtCreateManagedWidget(text, smeLineObjectClass,
3111 currentMenu, args, j);
3113 XtSetArg(args[j], XtNlabel, XtNewString(_(text)));
3114 entry = XtCreateManagedWidget(name, smeBSBObjectClass,
3115 currentMenu, args, j+1);
3116 XtAddCallback(entry, XtNcallback,
3117 (XtCallbackProc) (strcmp(name, "recent") ? MenuBarSelect : MenuEngineSelect),
3119 menuWidget[nrOfMenuItems] = entry;
3124 CreateMenuButton (char *name, Menu *mb)
3125 { // create menu button on main bar, and shell for pull-down list
3131 XtSetArg(args[j], XtNmenuName, XtNewString(name)); j++;
3132 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3133 XtSetArg(args[j], XtNborderWidth, 0); j++;
3134 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3136 currentMenu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3139 XtSetArg(args[j], XtNwidth, &w); j++;
3140 XtGetValues(mb->subMenu, args, j);
3141 wtot += mb->textWidth = widths[nr++] = w;
3145 CreateMenuBar (Menu *mb, int boardWidth)
3149 char menuName[MSG_SIZ];
3153 // create bar itself
3155 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3156 XtSetArg(args[j], XtNvSpace, 0); j++;
3157 XtSetArg(args[j], XtNborderWidth, 0); j++;
3158 mainBar = XtCreateWidget("menuBar", boxWidgetClass,
3159 formWidget, args, j);
3161 CreateMainMenus(mb); // put menus in bar according to description in back-end
3163 // size buttons to make menu bar fit, clipping menu names where necessary
3164 while(wtot > boardWidth - 40) {
3166 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
3170 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
3172 XtSetArg(args[j], XtNwidth, widths[i]); j++;
3173 XtSetValues(ma[i].subMenu, args, j);
3180 CreateButtonBar (MenuItem *mi)
3183 Widget button, buttonBar;
3187 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3189 XtSetArg(args[j], XtNhSpace, 0); j++;
3191 XtSetArg(args[j], XtNborderWidth, 0); j++;
3192 XtSetArg(args[j], XtNvSpace, 0); j++;
3193 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3194 formWidget, args, j);
3196 while (mi->string != NULL) {
3199 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3200 XtSetArg(args[j], XtNborderWidth, 0); j++;
3202 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3203 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3204 buttonBar, args, j);
3205 XtAddCallback(button, XtNcallback,
3206 (XtCallbackProc) MenuBarSelect,
3207 (caddr_t) mi->proc);
3214 CreatePieceMenu (char *name, int color)
3219 ChessSquare selection;
3221 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3222 boardWidget, args, 0);
3224 for (i = 0; i < PIECE_MENU_SIZE; i++) {
3225 String item = pieceMenuStrings[color][i];
3227 if (strcmp(item, "----") == 0) {
3228 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3231 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3232 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3234 selection = pieceMenuTranslation[color][i];
3235 XtAddCallback(entry, XtNcallback,
3236 (XtCallbackProc) PieceMenuSelect,
3237 (caddr_t) selection);
3238 if (selection == WhitePawn || selection == BlackPawn) {
3239 XtSetArg(args[0], XtNpopupOnEntry, entry);
3240 XtSetValues(menu, args, 1);
3253 ChessSquare selection;
3255 whitePieceMenu = CreatePieceMenu("menuW", 0);
3256 blackPieceMenu = CreatePieceMenu("menuB", 1);
3258 if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
3259 XtRegisterGrabAction(PieceMenuPopup, True,
3260 (unsigned)(ButtonPressMask|ButtonReleaseMask),
3261 GrabModeAsync, GrabModeAsync);
3263 XtSetArg(args[0], XtNlabel, _("Drop"));
3264 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3265 boardWidget, args, 1);
3266 for (i = 0; i < DROP_MENU_SIZE; i++) {
3267 String item = dropMenuStrings[i];
3269 if (strcmp(item, "----") == 0) {
3270 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3273 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3274 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3276 selection = dropMenuTranslation[i];
3277 XtAddCallback(entry, XtNcallback,
3278 (XtCallbackProc) DropMenuSelect,
3279 (caddr_t) selection);
3293 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3294 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3295 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3296 dmEnables[i].piece);
3297 XtSetSensitive(entry, p != NULL || !appData.testLegality
3298 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3299 && !appData.icsActive));
3301 while (p && *p++ == dmEnables[i].piece) count++;
3302 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3304 XtSetArg(args[j], XtNlabel, label); j++;
3305 XtSetValues(entry, args, j);
3310 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
3312 String whichMenu; int menuNr = -2;
3313 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3314 if (event->type == ButtonRelease)
3315 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3316 else if (event->type == ButtonPress)
3317 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3319 case 0: whichMenu = params[0]; break;
3320 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3322 case -1: if (errorUp) ErrorPopDown();
3325 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3329 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3331 if (pmFromX < 0 || pmFromY < 0) return;
3332 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3336 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3338 if (pmFromX < 0 || pmFromY < 0) return;
3339 DropMenuEvent(piece, pmFromX, pmFromY);
3343 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3345 shiftKey = prms[0][0] & 1;
3350 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3352 shiftKey = prms[0][0] & 1;
3358 * If the user selects on a border boundary, return -1; if off the board,
3359 * return -2. Otherwise map the event coordinate to the square.
3362 EventToSquare (int x, int limit)
3369 if ((x % (squareSize + lineGap)) >= squareSize)
3371 x /= (squareSize + lineGap);
3378 do_flash_delay (unsigned long msec)
3384 drawHighlight (int file, int rank, GC gc)
3388 if (lineGap == 0) return;
3391 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
3392 (squareSize + lineGap);
3393 y = lineGap/2 + rank * (squareSize + lineGap);
3395 x = lineGap/2 + file * (squareSize + lineGap);
3396 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
3397 (squareSize + lineGap);
3400 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3401 squareSize+lineGap, squareSize+lineGap);
3404 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
3405 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
3408 SetHighlights (int fromX, int fromY, int toX, int toY)
3410 if (hi1X != fromX || hi1Y != fromY) {
3411 if (hi1X >= 0 && hi1Y >= 0) {
3412 drawHighlight(hi1X, hi1Y, lineGC);
3414 } // [HGM] first erase both, then draw new!
3416 if (hi2X != toX || hi2Y != toY) {
3417 if (hi2X >= 0 && hi2Y >= 0) {
3418 drawHighlight(hi2X, hi2Y, lineGC);
3421 if (hi1X != fromX || hi1Y != fromY) {
3422 if (fromX >= 0 && fromY >= 0) {
3423 drawHighlight(fromX, fromY, highlineGC);
3426 if (hi2X != toX || hi2Y != toY) {
3427 if (toX >= 0 && toY >= 0) {
3428 drawHighlight(toX, toY, highlineGC);
3432 if(toX<0) // clearing the highlights must have damaged arrow
3433 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y); // for now, redraw it (should really be cleared!)
3444 SetHighlights(-1, -1, -1, -1);
3449 SetPremoveHighlights (int fromX, int fromY, int toX, int toY)
3451 if (pm1X != fromX || pm1Y != fromY) {
3452 if (pm1X >= 0 && pm1Y >= 0) {
3453 drawHighlight(pm1X, pm1Y, lineGC);
3455 if (fromX >= 0 && fromY >= 0) {
3456 drawHighlight(fromX, fromY, prelineGC);
3459 if (pm2X != toX || pm2Y != toY) {
3460 if (pm2X >= 0 && pm2Y >= 0) {
3461 drawHighlight(pm2X, pm2Y, lineGC);
3463 if (toX >= 0 && toY >= 0) {
3464 drawHighlight(toX, toY, prelineGC);
3474 ClearPremoveHighlights ()
3476 SetPremoveHighlights(-1, -1, -1, -1);
3480 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
3482 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
3483 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
3485 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
3486 if(textureW[kind] < W*squareSize)
3487 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
3489 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
3490 if(textureH[kind] < H*squareSize)
3491 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
3493 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
3498 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
3499 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
3501 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
3502 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
3503 squareSize, squareSize, x*fac, y*fac);
3505 if (useImages && useImageSqs) {
3509 pm = xpmLightSquare;
3514 case 2: /* neutral */
3519 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3520 squareSize, squareSize, x*fac, y*fac);
3530 case 2: /* neutral */
3535 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
3540 I split out the routines to draw a piece so that I could
3541 make a generic flash routine.
3544 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3546 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3547 switch (square_color) {
3549 case 2: /* neutral */
3551 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3552 ? *pieceToOutline(piece)
3553 : *pieceToSolid(piece),
3554 dest, bwPieceGC, 0, 0,
3555 squareSize, squareSize, x, y);
3558 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3559 ? *pieceToSolid(piece)
3560 : *pieceToOutline(piece),
3561 dest, wbPieceGC, 0, 0,
3562 squareSize, squareSize, x, y);
3568 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3570 switch (square_color) {
3572 case 2: /* neutral */
3574 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3575 ? *pieceToOutline(piece)
3576 : *pieceToSolid(piece),
3577 dest, bwPieceGC, 0, 0,
3578 squareSize, squareSize, x, y, 1);
3581 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3582 ? *pieceToSolid(piece)
3583 : *pieceToOutline(piece),
3584 dest, wbPieceGC, 0, 0,
3585 squareSize, squareSize, x, y, 1);
3591 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3593 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
3594 switch (square_color) {
3596 XCopyPlane(xDisplay, *pieceToSolid(piece),
3597 dest, (int) piece < (int) BlackPawn
3598 ? wlPieceGC : blPieceGC, 0, 0,
3599 squareSize, squareSize, x, y, 1);
3602 XCopyPlane(xDisplay, *pieceToSolid(piece),
3603 dest, (int) piece < (int) BlackPawn
3604 ? wdPieceGC : bdPieceGC, 0, 0,
3605 squareSize, squareSize, x, y, 1);
3607 case 2: /* neutral */
3609 XCopyPlane(xDisplay, *pieceToSolid(piece),
3610 dest, (int) piece < (int) BlackPawn
3611 ? wjPieceGC : bjPieceGC, 0, 0,
3612 squareSize, squareSize, x, y, 1);
3618 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3620 int kind, p = piece;
3622 switch (square_color) {
3624 case 2: /* neutral */
3626 if ((int)piece < (int) BlackPawn) {
3634 if ((int)piece < (int) BlackPawn) {
3642 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
3643 if(useTexture & square_color+1) {
3644 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
3645 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
3646 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
3647 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
3648 XSetClipMask(xDisplay, wlPieceGC, None);
3649 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
3651 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3652 dest, wlPieceGC, 0, 0,
3653 squareSize, squareSize, x, y);
3656 typedef void (*DrawFunc)();
3661 if (appData.monoMode) {
3662 if (DefaultDepth(xDisplay, xScreen) == 1) {
3663 return monoDrawPiece_1bit;
3665 return monoDrawPiece;
3669 return colorDrawPieceImage;
3671 return colorDrawPiece;
3675 /* [HR] determine square color depending on chess variant. */
3677 SquareColor (int row, int column)
3681 if (gameInfo.variant == VariantXiangqi) {
3682 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
3684 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
3686 } else if (row <= 4) {
3692 square_color = ((column + row) % 2) == 1;
3695 /* [hgm] holdings: next line makes all holdings squares light */
3696 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
3698 return square_color;
3702 DrawSquare (int row, int column, ChessSquare piece, int do_flash)
3704 int square_color, x, y, direction, font_ascent, font_descent;
3707 XCharStruct overall;
3711 /* Calculate delay in milliseconds (2-delays per complete flash) */
3712 flash_delay = 500 / appData.flashRate;
3715 x = lineGap + ((BOARD_WIDTH-1)-column) *
3716 (squareSize + lineGap);
3717 y = lineGap + row * (squareSize + lineGap);
3719 x = lineGap + column * (squareSize + lineGap);
3720 y = lineGap + ((BOARD_HEIGHT-1)-row) *
3721 (squareSize + lineGap);
3724 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
3726 square_color = SquareColor(row, column);
3728 if ( // [HGM] holdings: blank out area between board and holdings
3729 column == BOARD_LEFT-1 || column == BOARD_RGHT
3730 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
3731 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
3732 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
3734 // [HGM] print piece counts next to holdings
3735 string[1] = NULLCHAR;
3736 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
3737 string[0] = '0' + piece;
3738 XTextExtents(countFontStruct, string, 1, &direction,
3739 &font_ascent, &font_descent, &overall);
3740 if (appData.monoMode) {
3741 XDrawImageString(xDisplay, xBoardWindow, countGC,
3742 x + squareSize - overall.width - 2,
3743 y + font_ascent + 1, string, 1);
3745 XDrawString(xDisplay, xBoardWindow, countGC,
3746 x + squareSize - overall.width - 2,
3747 y + font_ascent + 1, string, 1);
3750 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
3751 string[0] = '0' + piece;
3752 XTextExtents(countFontStruct, string, 1, &direction,
3753 &font_ascent, &font_descent, &overall);
3754 if (appData.monoMode) {
3755 XDrawImageString(xDisplay, xBoardWindow, countGC,
3756 x + 2, y + font_ascent + 1, string, 1);
3758 XDrawString(xDisplay, xBoardWindow, countGC,
3759 x + 2, y + font_ascent + 1, string, 1);
3763 if (piece == EmptySquare || appData.blindfold) {
3764 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
3766 drawfunc = ChooseDrawFunc();
3768 if (do_flash && appData.flashCount > 0) {
3769 for (i=0; i<appData.flashCount; ++i) {
3770 drawfunc(piece, square_color, x, y, xBoardWindow);
3771 XSync(xDisplay, False);
3772 do_flash_delay(flash_delay);
3774 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
3775 XSync(xDisplay, False);
3776 do_flash_delay(flash_delay);
3779 drawfunc(piece, square_color, x, y, xBoardWindow);
3783 string[1] = NULLCHAR;
3784 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
3785 && column >= BOARD_LEFT && column < BOARD_RGHT) {
3786 string[0] = 'a' + column - BOARD_LEFT;
3787 XTextExtents(coordFontStruct, string, 1, &direction,
3788 &font_ascent, &font_descent, &overall);
3789 if (appData.monoMode) {
3790 XDrawImageString(xDisplay, xBoardWindow, coordGC,
3791 x + squareSize - overall.width - 2,
3792 y + squareSize - font_descent - 1, string, 1);
3794 XDrawString(xDisplay, xBoardWindow, coordGC,
3795 x + squareSize - overall.width - 2,
3796 y + squareSize - font_descent - 1, string, 1);
3799 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
3800 string[0] = ONE + row;
3801 XTextExtents(coordFontStruct, string, 1, &direction,
3802 &font_ascent, &font_descent, &overall);
3803 if (appData.monoMode) {
3804 XDrawImageString(xDisplay, xBoardWindow, coordGC,
3805 x + 2, y + font_ascent + 1, string, 1);
3807 XDrawString(xDisplay, xBoardWindow, coordGC,
3808 x + 2, y + font_ascent + 1, string, 1);
3811 if(!partnerUp && marker[row][column]) {
3812 if(appData.monoMode) {
3813 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? darkSquareGC : lightSquareGC,
3814 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
3815 XDrawArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? lightSquareGC : darkSquareGC,
3816 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
3818 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
3819 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
3824 Fraction (int x, int start, int stop)
3826 double f = ((double) x - start)/(stop - start);
3827 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
3831 static WindowPlacement wpNew;
3834 CoDrag (Widget sh, WindowPlacement *wp)
3837 int j=0, touch=0, fudge = 2;
3838 GetActualPlacement(sh, wp);
3839 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
3840 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
3841 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
3842 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
3843 if(!touch ) return; // only windows that touch co-move
3844 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
3845 int heightInc = wpNew.height - wpMain.height;
3846 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3847 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3848 wp->y += fracTop * heightInc;
3849 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
3850 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
3851 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
3852 int widthInc = wpNew.width - wpMain.width;
3853 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3854 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3855 wp->y += fracLeft * widthInc;
3856 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
3857 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
3859 wp->x += wpNew.x - wpMain.x;
3860 wp->y += wpNew.y - wpMain.y;
3861 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
3862 if(touch == 3) wp->y += wpNew.height - wpMain.height;
3863 XtSetArg(args[j], XtNx, wp->x); j++;
3864 XtSetArg(args[j], XtNy, wp->y); j++;
3865 XtSetValues(sh, args, j);
3868 static XtIntervalId delayedDragID = 0;
3873 GetActualPlacement(shellWidget, &wpNew);
3874 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
3875 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
3876 return; // false alarm
3877 if(EngineOutputIsUp()) CoDrag(engineOutputShell, &wpEngineOutput);
3878 if(MoveHistoryIsUp()) CoDrag(shells[7], &wpMoveHistory);
3879 if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
3880 if(GameListIsUp()) CoDrag(gameListShell, &wpGameList);
3882 XDrawPosition(boardWidget, True, NULL);
3883 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
3890 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
3892 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
3895 /* Why is this needed on some versions of X? */
3897 EventProc (Widget widget, caddr_t unused, XEvent *event)
3899 if (!XtIsRealized(widget))
3901 switch (event->type) {
3902 case ConfigureNotify: // main window is being dragged: drag attached windows with it
3903 if(appData.useStickyWindows)
3904 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
3907 if (event->xexpose.count > 0) return; /* no clipping is done */
3908 XDrawPosition(widget, True, NULL);
3909 if(twoBoards) { // [HGM] dual: draw other board in other orientation
3910 flipView = !flipView; partnerUp = !partnerUp;
3911 XDrawPosition(widget, True, NULL);
3912 flipView = !flipView; partnerUp = !partnerUp;
3916 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
3924 DrawPosition (int fullRedraw, Board board)
3926 XDrawPosition(boardWidget, fullRedraw, board);
3929 /* Returns 1 if there are "too many" differences between b1 and b2
3930 (i.e. more than 1 move was made) */
3932 too_many_diffs (Board b1, Board b2)
3937 for (i=0; i<BOARD_HEIGHT; ++i) {
3938 for (j=0; j<BOARD_WIDTH; ++j) {
3939 if (b1[i][j] != b2[i][j]) {
3940 if (++c > 4) /* Castling causes 4 diffs */
3948 /* Matrix describing castling maneuvers */
3949 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
3950 static int castling_matrix[4][5] = {
3951 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
3952 { 0, 7, 4, 5, 6 }, /* 0-0, white */
3953 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
3954 { 7, 7, 4, 5, 6 } /* 0-0, black */
3957 /* Checks whether castling occurred. If it did, *rrow and *rcol
3958 are set to the destination (row,col) of the rook that moved.
3960 Returns 1 if castling occurred, 0 if not.
3962 Note: Only handles a max of 1 castling move, so be sure
3963 to call too_many_diffs() first.
3966 check_castle_draw (Board newb, Board oldb, int *rrow, int *rcol)
3971 /* For each type of castling... */
3972 for (i=0; i<4; ++i) {
3973 r = castling_matrix[i];
3975 /* Check the 4 squares involved in the castling move */
3977 for (j=1; j<=4; ++j) {
3978 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
3985 /* All 4 changed, so it must be a castling move */
3994 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
3996 DrawSeekAxis (int x, int y, int xTo, int yTo)
3998 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4002 DrawSeekBackground (int left, int top, int right, int bottom)
4004 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4008 DrawSeekText (char *buf, int x, int y)
4010 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4014 DrawSeekDot (int x, int y, int colorNr)
4016 int square = colorNr & 0x80;
4019 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4021 XFillRectangle(xDisplay, xBoardWindow, color,
4022 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4024 XFillArc(xDisplay, xBoardWindow, color,
4025 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4028 static int damage[2][BOARD_RANKS][BOARD_FILES];
4031 * event handler for redrawing the board
4034 XDrawPosition (Widget w, int repaint, Board board)
4037 static int lastFlipView = 0;
4038 static int lastBoardValid[2] = {0, 0};
4039 static Board lastBoard[2];
4042 int nr = twoBoards*partnerUp;
4044 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4046 if (board == NULL) {
4047 if (!lastBoardValid[nr]) return;
4048 board = lastBoard[nr];
4050 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4051 MarkMenuItem("Flip View", flipView);
4055 * It would be simpler to clear the window with XClearWindow()
4056 * but this causes a very distracting flicker.
4059 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4061 if ( lineGap && IsDrawArrowEnabled())
4062 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4063 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4065 /* If too much changes (begin observing new game, etc.), don't
4067 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4069 /* Special check for castling so we don't flash both the king
4070 and the rook (just flash the king). */
4072 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4073 /* Draw rook with NO flashing. King will be drawn flashing later */
4074 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4075 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4079 /* First pass -- Draw (newly) empty squares and repair damage.
4080 This prevents you from having a piece show up twice while it
4081 is flashing on its new square */
4082 for (i = 0; i < BOARD_HEIGHT; i++)
4083 for (j = 0; j < BOARD_WIDTH; j++)
4084 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4085 || damage[nr][i][j]) {
4086 DrawSquare(i, j, board[i][j], 0);
4087 damage[nr][i][j] = False;
4090 /* Second pass -- Draw piece(s) in new position and flash them */
4091 for (i = 0; i < BOARD_HEIGHT; i++)
4092 for (j = 0; j < BOARD_WIDTH; j++)
4093 if (board[i][j] != lastBoard[nr][i][j]) {
4094 DrawSquare(i, j, board[i][j], do_flash);
4098 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4099 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4100 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4102 for (i = 0; i < BOARD_HEIGHT; i++)
4103 for (j = 0; j < BOARD_WIDTH; j++) {
4104 DrawSquare(i, j, board[i][j], 0);
4105 damage[nr][i][j] = False;
4109 CopyBoard(lastBoard[nr], board);
4110 lastBoardValid[nr] = 1;
4111 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4112 lastFlipView = flipView;
4114 /* Draw highlights */
4115 if (pm1X >= 0 && pm1Y >= 0) {
4116 drawHighlight(pm1X, pm1Y, prelineGC);
4118 if (pm2X >= 0 && pm2Y >= 0) {
4119 drawHighlight(pm2X, pm2Y, prelineGC);
4121 if (hi1X >= 0 && hi1Y >= 0) {
4122 drawHighlight(hi1X, hi1Y, highlineGC);
4124 if (hi2X >= 0 && hi2Y >= 0) {
4125 drawHighlight(hi2X, hi2Y, highlineGC);
4127 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4129 /* If piece being dragged around board, must redraw that too */
4132 XSync(xDisplay, False);
4137 * event handler for redrawing the board
4140 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4142 XDrawPosition(w, True, NULL);
4147 * event handler for parsing user moves
4149 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4150 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4151 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4152 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4153 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4154 // and at the end FinishMove() to perform the move after optional promotion popups.
4155 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4157 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4159 if (w != boardWidget || errorExitStatus != -1) return;
4160 if(nprms) shiftKey = !strcmp(prms[0], "1");
4163 if (event->type == ButtonPress) {
4164 XtPopdown(promotionShell);
4165 XtDestroyWidget(promotionShell);
4166 promotionUp = False;
4174 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4175 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4176 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4180 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
4182 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4183 DragPieceMove(event->xmotion.x, event->xmotion.y);
4187 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
4188 { // [HGM] pv: walk PV
4189 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4192 static int savedIndex; /* gross that this is global */
4195 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4198 XawTextPosition index, dummy;
4201 XawTextGetSelectionPos(w, &index, &dummy);
4202 XtSetArg(arg, XtNstring, &val);
4203 XtGetValues(w, &arg, 1);
4204 ReplaceComment(savedIndex, val);
4205 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4206 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4210 EditCommentPopUp (int index, char *title, char *text)
4213 if (text == NULL) text = "";
4214 NewCommentPopup(title, text, index);
4223 extern Option boxOptions[];
4233 edit = boxOptions[0].handle;
4235 XtSetArg(args[j], XtNstring, &val); j++;
4236 XtGetValues(edit, args, j);
4238 SendMultiLineToICS(val);
4239 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4240 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4244 ICSInputBoxPopDown ()
4250 CommentPopUp (char *title, char *text)
4252 savedIndex = currentMove; // [HGM] vari
4253 NewCommentPopup(title, text, currentMove);
4262 static char *openName;
4268 (void) (*fileProc)(openFP, 0, openName);
4272 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
4274 fileProc = proc; /* I can't see a way not */
4275 fileOpenMode = openMode; /* to use globals here */
4276 { // [HGM] use file-selector dialog stolen from Ghostview
4277 int index; // this is not supported yet
4278 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
4279 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
4280 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
4281 ScheduleDelayedEvent(&DelayedLoad, 50);
4288 if (!filenameUp) return;
4289 XtPopdown(fileNameShell);
4290 XtDestroyWidget(fileNameShell);
4296 FileNameCallback (Widget w, XtPointer client_data, XtPointer call_data)
4301 XtSetArg(args[0], XtNlabel, &name);
4302 XtGetValues(w, args, 1);
4304 if (strcmp(name, _("cancel")) == 0) {
4309 FileNameAction(w, NULL, NULL, NULL);
4313 FileNameAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4321 name = XawDialogGetValueString(w = XtParent(w));
4323 if ((name != NULL) && (*name != NULLCHAR)) {
4324 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
4325 XtPopdown(w = XtParent(XtParent(w)));
4329 p = strrchr(buf, ' ');
4336 fullname = ExpandPathName(buf);
4338 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
4341 f = fopen(fullname, fileOpenMode);
4343 DisplayError(_("Failed to open file"), errno);
4345 (void) (*fileProc)(f, index, buf);
4352 XtPopdown(w = XtParent(XtParent(w)));
4362 Widget dialog, layout;
4364 Dimension bw_width, pw_width;
4366 char *PromoChars = "wglcqrbnkac+=\0";
4369 XtSetArg(args[j], XtNwidth, &bw_width); j++;
4370 XtGetValues(boardWidget, args, j);
4373 XtSetArg(args[j], XtNresizable, True); j++;
4374 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
4376 XtCreatePopupShell("Promotion", transientShellWidgetClass,
4377 shellWidget, args, j);
4379 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
4380 layoutArgs, XtNumber(layoutArgs));
4383 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
4384 XtSetArg(args[j], XtNborderWidth, 0); j++;
4385 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
4388 if(gameInfo.variant != VariantShogi) {
4389 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
4390 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
4391 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
4392 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
4393 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
4395 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
4396 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
4397 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
4398 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
4400 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
4401 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
4402 gameInfo.variant == VariantGiveaway) {
4403 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
4405 if(gameInfo.variant == VariantCapablanca ||
4406 gameInfo.variant == VariantGothic ||
4407 gameInfo.variant == VariantCapaRandom) {
4408 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
4409 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
4411 } else // [HGM] shogi
4413 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
4414 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
4416 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
4418 XtRealizeWidget(promotionShell);
4419 CatchDeleteWindow(promotionShell, "PromotionPopDown");
4422 XtSetArg(args[j], XtNwidth, &pw_width); j++;
4423 XtGetValues(promotionShell, args, j);
4425 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
4426 lineGap + squareSize/3 +
4427 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
4428 0 : 6*(squareSize + lineGap)), &x, &y);
4431 XtSetArg(args[j], XtNx, x); j++;
4432 XtSetArg(args[j], XtNy, y); j++;
4433 XtSetValues(promotionShell, args, j);
4435 XtPopup(promotionShell, XtGrabNone);
4443 if (!promotionUp) return;
4444 XtPopdown(promotionShell);
4445 XtDestroyWidget(promotionShell);
4446 promotionUp = False;
4450 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
4452 int promoChar = * (const char *) client_data;
4456 if (fromX == -1) return;
4463 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
4465 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
4466 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
4472 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
4474 dialogError = errorUp = False;
4475 XtPopdown(w = XtParent(XtParent(XtParent(w))));
4477 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
4484 if (!errorUp) return;
4485 dialogError = errorUp = False;
4486 XtPopdown(errorShell);
4487 XtDestroyWidget(errorShell);
4488 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
4492 ErrorPopUp (char *title, char *label, int modal)
4495 Widget dialog, layout;
4499 Dimension bw_width, pw_width;
4500 Dimension pw_height;
4504 XtSetArg(args[i], XtNresizable, True); i++;
4505 XtSetArg(args[i], XtNtitle, title); i++;
4507 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
4508 shellUp[0] ? (dialogError = modal = TRUE, shells[0]) : shellWidget, args, i);
4510 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
4511 layoutArgs, XtNumber(layoutArgs));
4514 XtSetArg(args[i], XtNlabel, label); i++;
4515 XtSetArg(args[i], XtNborderWidth, 0); i++;
4516 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
4519 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
4521 XtRealizeWidget(errorShell);
4522 CatchDeleteWindow(errorShell, "ErrorPopDown");
4525 XtSetArg(args[i], XtNwidth, &bw_width); i++;
4526 XtGetValues(boardWidget, args, i);
4528 XtSetArg(args[i], XtNwidth, &pw_width); i++;
4529 XtSetArg(args[i], XtNheight, &pw_height); i++;
4530 XtGetValues(errorShell, args, i);
4533 /* This code seems to tickle an X bug if it is executed too soon
4534 after xboard starts up. The coordinates get transformed as if
4535 the main window was positioned at (0, 0).
4537 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
4538 0 - pw_height + squareSize / 3, &x, &y);
4540 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
4541 RootWindowOfScreen(XtScreen(boardWidget)),
4542 (bw_width - pw_width) / 2,
4543 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
4547 if (y < 0) y = 0; /*avoid positioning top offscreen*/
4550 XtSetArg(args[i], XtNx, x); i++;
4551 XtSetArg(args[i], XtNy, y); i++;
4552 XtSetValues(errorShell, args, i);
4555 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
4558 /* Disable all user input other than deleting the window */
4559 static int frozen = 0;
4565 /* Grab by a widget that doesn't accept input */
4566 XtAddGrab(messageWidget, TRUE, FALSE);
4570 /* Undo a FreezeUI */
4574 if (!frozen) return;
4575 XtRemoveGrab(messageWidget);
4583 static int oldPausing = FALSE;
4584 static GameMode oldmode = (GameMode) -1;
4587 if (!boardWidget || !XtIsRealized(boardWidget)) return;
4589 if (pausing != oldPausing) {
4590 oldPausing = pausing;
4591 MarkMenuItem("Pause", pausing);
4593 if (appData.showButtonBar) {
4594 /* Always toggle, don't set. Previous code messes up when
4595 invoked while the button is pressed, as releasing it
4596 toggles the state again. */
4599 XtSetArg(args[0], XtNbackground, &oldbg);
4600 XtSetArg(args[1], XtNforeground, &oldfg);
4601 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
4603 XtSetArg(args[0], XtNbackground, oldfg);
4604 XtSetArg(args[1], XtNforeground, oldbg);
4606 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
4610 wname = ModeToWidgetName(oldmode);
4611 if (wname != NULL) {
4612 MarkMenuItem(wname, False);
4614 wname = ModeToWidgetName(gameMode);
4615 if (wname != NULL) {
4616 MarkMenuItem(wname, True);
4619 MarkMenuItem("Machine Match", matchMode && matchGame < appData.matchGames);
4621 /* Maybe all the enables should be handled here, not just this one */
4622 EnableMenuItem("Training", gameMode == Training || gameMode == PlayFromGameFile);
4627 * Button/menu procedures
4630 LoadGamePopUp (FILE *f, int gameNumber, char *title)
4632 cmailMsgLoaded = FALSE;
4633 if (gameNumber == 0) {
4634 int error = GameListBuild(f);
4636 DisplayError(_("Cannot build game list"), error);
4637 } else if (!ListEmpty(&gameList) &&
4638 ((ListGame *) gameList.tailPred)->number > 1) {
4639 GameListPopUp(f, title);
4645 return LoadGame(f, gameNumber, title, FALSE);
4648 /* this variable is shared between CopyPositionProc and SendPositionSelection */
4649 char *selected_fen_position=NULL;
4652 SendPositionSelection (Widget w, Atom *selection, Atom *target,
4653 Atom *type_return, XtPointer *value_return,
4654 unsigned long *length_return, int *format_return)
4656 char *selection_tmp;
4658 if (!selected_fen_position) return False; /* should never happen */
4659 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4660 /* note: since no XtSelectionDoneProc was registered, Xt will
4661 * automatically call XtFree on the value returned. So have to
4662 * make a copy of it allocated with XtMalloc */
4663 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
4664 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
4666 *value_return=selection_tmp;
4667 *length_return=strlen(selection_tmp);
4668 *type_return=*target;
4669 *format_return = 8; /* bits per byte */
4671 } else if (*target == XA_TARGETS(xDisplay)) {
4672 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4673 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4674 targets_tmp[1] = XA_STRING;
4675 *value_return = targets_tmp;
4676 *type_return = XA_ATOM;
4679 // This code leads to a read of value_return out of bounds on 64-bit systems.
4680 // Other code which I have seen always sets *format_return to 32 independent of
4681 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
4682 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
4683 *format_return = 8 * sizeof(Atom);
4684 if (*format_return > 32) {
4685 *length_return *= *format_return / 32;
4686 *format_return = 32;
4689 *format_return = 32;
4697 /* note: when called from menu all parameters are NULL, so no clue what the
4698 * Widget which was clicked on was, or what the click event was
4704 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4705 * have a notion of a position that is selected but not copied.
4706 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4708 if(gameMode == EditPosition) EditPositionDone(TRUE);
4709 if (selected_fen_position) free(selected_fen_position);
4710 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
4711 if (!selected_fen_position) return;
4712 XtOwnSelection(menuBarWidget, XA_PRIMARY,
4714 SendPositionSelection,
4715 NULL/* lose_ownership_proc */ ,
4716 NULL/* transfer_done_proc */);
4717 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4719 SendPositionSelection,
4720 NULL/* lose_ownership_proc */ ,
4721 NULL/* transfer_done_proc */);
4724 /* function called when the data to Paste is ready */
4726 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
4727 Atom *type, XtPointer value, unsigned long *len, int *format)
4730 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
4731 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
4732 EditPositionPasteFEN(fenstr);
4736 /* called when Paste Position button is pressed,
4737 * all parameters will be NULL */
4739 PastePositionProc ()
4741 XtGetSelectionValue(menuBarWidget,
4742 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4743 /* (XtSelectionCallbackProc) */ PastePositionCB,
4744 NULL, /* client_data passed to PastePositionCB */
4746 /* better to use the time field from the event that triggered the
4747 * call to this function, but that isn't trivial to get
4755 SendGameSelection (Widget w, Atom *selection, Atom *target,
4756 Atom *type_return, XtPointer *value_return,
4757 unsigned long *length_return, int *format_return)
4759 char *selection_tmp;
4761 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4762 FILE* f = fopen(gameCopyFilename, "r");
4765 if (f == NULL) return False;
4769 selection_tmp = XtMalloc(len + 1);
4770 count = fread(selection_tmp, 1, len, f);
4773 XtFree(selection_tmp);
4776 selection_tmp[len] = NULLCHAR;
4777 *value_return = selection_tmp;
4778 *length_return = len;
4779 *type_return = *target;
4780 *format_return = 8; /* bits per byte */
4782 } else if (*target == XA_TARGETS(xDisplay)) {
4783 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4784 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4785 targets_tmp[1] = XA_STRING;
4786 *value_return = targets_tmp;
4787 *type_return = XA_ATOM;
4790 // This code leads to a read of value_return out of bounds on 64-bit systems.
4791 // Other code which I have seen always sets *format_return to 32 independent of
4792 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
4793 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
4794 *format_return = 8 * sizeof(Atom);
4795 if (*format_return > 32) {
4796 *length_return *= *format_return / 32;
4797 *format_return = 32;
4800 *format_return = 32;
4812 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4813 * have a notion of a game that is selected but not copied.
4814 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4816 XtOwnSelection(menuBarWidget, XA_PRIMARY,
4819 NULL/* lose_ownership_proc */ ,
4820 NULL/* transfer_done_proc */);
4821 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4824 NULL/* lose_ownership_proc */ ,
4825 NULL/* transfer_done_proc */);
4828 /* note: when called from menu all parameters are NULL, so no clue what the
4829 * Widget which was clicked on was, or what the click event was
4831 /* function called when the data to Paste is ready */
4833 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
4834 Atom *type, XtPointer value, unsigned long *len, int *format)
4837 if (value == NULL || *len == 0) {
4838 return; /* nothing had been selected to copy */
4840 f = fopen(gamePasteFilename, "w");
4842 DisplayError(_("Can't open temp file"), errno);
4845 fwrite(value, 1, *len, f);
4848 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
4851 /* called when Paste Game button is pressed,
4852 * all parameters will be NULL */
4856 XtGetSelectionValue(menuBarWidget,
4857 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4858 /* (XtSelectionCallbackProc) */ PasteGameCB,
4859 NULL, /* client_data passed to PasteGameCB */
4861 /* better to use the time field from the event that triggered the
4862 * call to this function, but that isn't trivial to get
4871 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4877 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4878 { // [HGM] input: let up-arrow recall previous line from history
4885 if (!shellUp[4]) return;
4886 edit = boxOptions[0].handle;
4888 XtSetArg(args[j], XtNstring, &val); j++;
4889 XtGetValues(edit, args, j);
4890 val = PrevInHistory(val);
4891 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4892 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4894 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
4895 XawTextReplace(edit, 0, 0, &t);
4896 XawTextSetInsertionPoint(edit, 9999);
4901 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4902 { // [HGM] input: let down-arrow recall next line from history
4907 if (!shellUp[4]) return;
4908 edit = boxOptions[0].handle;
4909 val = NextInHistory();
4910 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4911 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4913 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
4914 XawTextReplace(edit, 0, 0, &t);
4915 XawTextSetInsertionPoint(edit, 9999);
4920 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4922 if (shellUp[4] == True)
4927 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4929 if (!TempBackwardActive) {
4930 TempBackwardActive = True;
4936 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4938 /* Check to see if triggered by a key release event for a repeating key.
4939 * If so the next queued event will be a key press of the same key at the same time */
4940 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
4942 XPeekEvent(xDisplay, &next);
4943 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
4944 next.xkey.keycode == event->xkey.keycode)
4948 TempBackwardActive = False;
4952 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4953 { // called as key binding
4956 if (nprms && *nprms > 0)
4960 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
4965 DisplayMessage (char *message, char *extMessage)
4967 /* display a message in the message widget */
4976 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
4981 message = extMessage;
4985 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
4987 /* need to test if messageWidget already exists, since this function
4988 can also be called during the startup, if for example a Xresource
4989 is not set up correctly */
4992 XtSetArg(arg, XtNlabel, message);
4993 XtSetValues(messageWidget, &arg, 1);
5000 DisplayTitle (char *text)
5004 char title[MSG_SIZ];
5007 if (text == NULL) text = "";
5009 if (appData.titleInWindow) {
5011 XtSetArg(args[i], XtNlabel, text); i++;
5012 XtSetValues(titleWidget, args, i);
5015 if (*text != NULLCHAR) {
5016 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
5017 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
5018 } else if (appData.icsActive) {
5019 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
5020 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
5021 } else if (appData.cmailGameName[0] != NULLCHAR) {
5022 snprintf(icon, sizeof(icon), "%s", "CMail");
5023 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
5025 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
5026 } else if (gameInfo.variant == VariantGothic) {
5027 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
5028 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
5031 } else if (gameInfo.variant == VariantFalcon) {
5032 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
5033 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
5035 } else if (appData.noChessProgram) {
5036 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
5037 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
5039 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
5040 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
5043 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
5044 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
5045 XtSetValues(shellWidget, args, i);
5046 XSync(xDisplay, False);
5051 DisplayError (String message, int error)
5056 if (appData.debugMode || appData.matchMode) {
5057 fprintf(stderr, "%s: %s\n", programName, message);
5060 if (appData.debugMode || appData.matchMode) {
5061 fprintf(stderr, "%s: %s: %s\n",
5062 programName, message, strerror(error));
5064 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
5067 ErrorPopUp(_("Error"), message, FALSE);
5072 DisplayMoveError (String message)
5076 DrawPosition(FALSE, NULL);
5077 if (appData.debugMode || appData.matchMode) {
5078 fprintf(stderr, "%s: %s\n", programName, message);
5080 if (appData.popupMoveErrors) {
5081 ErrorPopUp(_("Error"), message, FALSE);
5083 DisplayMessage(message, "");
5089 DisplayFatalError (String message, int error, int status)
5093 errorExitStatus = status;
5095 fprintf(stderr, "%s: %s\n", programName, message);
5097 fprintf(stderr, "%s: %s: %s\n",
5098 programName, message, strerror(error));
5099 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
5102 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
5103 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
5110 DisplayInformation (String message)
5113 ErrorPopUp(_("Information"), message, TRUE);
5117 DisplayNote (String message)
5120 ErrorPopUp(_("Note"), message, FALSE);
5124 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
5130 DisplayIcsInteractionTitle (String message)
5132 if (oldICSInteractionTitle == NULL) {
5133 /* Magic to find the old window title, adapted from vim */
5134 char *wina = getenv("WINDOWID");
5136 Window win = (Window) atoi(wina);
5137 Window root, parent, *children;
5138 unsigned int nchildren;
5139 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
5141 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
5142 if (!XQueryTree(xDisplay, win, &root, &parent,
5143 &children, &nchildren)) break;
5144 if (children) XFree((void *)children);
5145 if (parent == root || parent == 0) break;
5148 XSetErrorHandler(oldHandler);
5150 if (oldICSInteractionTitle == NULL) {
5151 oldICSInteractionTitle = "xterm";
5154 printf("\033]0;%s\007", message);
5158 char pendingReplyPrefix[MSG_SIZ];
5159 ProcRef pendingReplyPR;
5162 AskQuestionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5165 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
5169 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
5173 AskQuestionPopDown ()
5175 if (!askQuestionUp) return;
5176 XtPopdown(askQuestionShell);
5177 XtDestroyWidget(askQuestionShell);
5178 askQuestionUp = False;
5182 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5188 reply = XawDialogGetValueString(w = XtParent(w));
5189 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
5190 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
5191 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
5192 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
5193 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
5194 AskQuestionPopDown();
5196 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
5200 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
5205 XtSetArg(args[0], XtNlabel, &name);
5206 XtGetValues(w, args, 1);
5208 if (strcmp(name, _("cancel")) == 0) {
5209 AskQuestionPopDown();
5211 AskQuestionReplyAction(w, NULL, NULL, NULL);
5216 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
5219 Widget popup, layout, dialog, edit;
5225 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
5226 pendingReplyPR = pr;
5229 XtSetArg(args[i], XtNresizable, True); i++;
5230 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
5231 askQuestionShell = popup =
5232 XtCreatePopupShell(title, transientShellWidgetClass,
5233 shellWidget, args, i);
5236 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
5237 layoutArgs, XtNumber(layoutArgs));
5240 XtSetArg(args[i], XtNlabel, question); i++;
5241 XtSetArg(args[i], XtNvalue, ""); i++;
5242 XtSetArg(args[i], XtNborderWidth, 0); i++;
5243 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
5246 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
5247 (XtPointer) dialog);
5248 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
5249 (XtPointer) dialog);
5251 XtRealizeWidget(popup);
5252 CatchDeleteWindow(popup, "AskQuestionPopDown");
5254 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
5255 &x, &y, &win_x, &win_y, &mask);
5257 XtSetArg(args[0], XtNx, x - 10);
5258 XtSetArg(args[1], XtNy, y - 30);
5259 XtSetValues(popup, args, 2);
5261 XtPopup(popup, XtGrabExclusive);
5262 askQuestionUp = True;
5264 edit = XtNameToWidget(dialog, "*value");
5265 XtSetKeyboardFocus(popup, edit);
5270 PlaySound (char *name)
5272 if (*name == NULLCHAR) {
5274 } else if (strcmp(name, "$") == 0) {
5275 putc(BELLCHAR, stderr);
5278 char *prefix = "", *sep = "";
5279 if(appData.soundProgram[0] == NULLCHAR) return;
5280 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
5281 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
5289 PlaySound(appData.soundMove);
5295 PlaySound(appData.soundIcsWin);
5301 PlaySound(appData.soundIcsLoss);
5307 PlaySound(appData.soundIcsDraw);
5311 PlayIcsUnfinishedSound ()
5313 PlaySound(appData.soundIcsUnfinished);
5319 PlaySound(appData.soundIcsAlarm);
5325 PlaySound(appData.soundTell);
5331 system("stty echo");
5338 system("stty -echo");
5343 RunCommand (char *buf)
5349 Colorize (ColorClass cc, int continuation)
5352 int count, outCount, error;
5354 if (textColors[(int)cc].bg > 0) {
5355 if (textColors[(int)cc].fg > 0) {
5356 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
5357 textColors[(int)cc].fg, textColors[(int)cc].bg);
5359 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
5360 textColors[(int)cc].bg);
5363 if (textColors[(int)cc].fg > 0) {
5364 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
5365 textColors[(int)cc].fg);
5367 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
5370 count = strlen(buf);
5371 outCount = OutputToProcess(NoProc, buf, count, &error);
5372 if (outCount < count) {
5373 DisplayFatalError(_("Error writing to display"), error, 1);
5376 if (continuation) return;
5379 PlaySound(appData.soundShout);
5382 PlaySound(appData.soundSShout);
5385 PlaySound(appData.soundChannel1);
5388 PlaySound(appData.soundChannel);
5391 PlaySound(appData.soundKibitz);
5394 PlaySound(appData.soundTell);
5396 case ColorChallenge:
5397 PlaySound(appData.soundChallenge);
5400 PlaySound(appData.soundRequest);
5403 PlaySound(appData.soundSeek);
5415 return getpwuid(getuid())->pw_name;
5419 ExpandPathName (char *path)
5421 static char static_buf[4*MSG_SIZ];
5422 char *d, *s, buf[4*MSG_SIZ];
5428 while (*s && isspace(*s))
5437 if (*(s+1) == '/') {
5438 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
5442 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
5443 { char *p; if(p = strchr(buf, '/')) *p = 0; }
5444 pwd = getpwnam(buf);
5447 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
5451 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
5452 strcat(d, strchr(s+1, '/'));
5456 safeStrCpy(d, s, 4*MSG_SIZ );
5464 static char host_name[MSG_SIZ];
5466 #if HAVE_GETHOSTNAME
5467 gethostname(host_name, MSG_SIZ);
5469 #else /* not HAVE_GETHOSTNAME */
5470 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
5471 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
5473 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
5475 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
5476 #endif /* not HAVE_GETHOSTNAME */
5479 XtIntervalId delayedEventTimerXID = 0;
5480 DelayedEventCallback delayedEventCallback = 0;
5485 delayedEventTimerXID = 0;
5486 delayedEventCallback();
5490 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
5492 if(delayedEventTimerXID && delayedEventCallback == cb)
5493 // [HGM] alive: replace, rather than add or flush identical event
5494 XtRemoveTimeOut(delayedEventTimerXID);
5495 delayedEventCallback = cb;
5496 delayedEventTimerXID =
5497 XtAppAddTimeOut(appContext, millisec,
5498 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
5501 DelayedEventCallback
5504 if (delayedEventTimerXID) {
5505 return delayedEventCallback;
5512 CancelDelayedEvent ()
5514 if (delayedEventTimerXID) {
5515 XtRemoveTimeOut(delayedEventTimerXID);
5516 delayedEventTimerXID = 0;
5520 XtIntervalId loadGameTimerXID = 0;
5523 LoadGameTimerRunning ()
5525 return loadGameTimerXID != 0;
5529 StopLoadGameTimer ()
5531 if (loadGameTimerXID != 0) {
5532 XtRemoveTimeOut(loadGameTimerXID);
5533 loadGameTimerXID = 0;
5541 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
5543 loadGameTimerXID = 0;
5548 StartLoadGameTimer (long millisec)
5551 XtAppAddTimeOut(appContext, millisec,
5552 (XtTimerCallbackProc) LoadGameTimerCallback,
5556 XtIntervalId analysisClockXID = 0;
5559 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
5561 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
5562 || appData.icsEngineAnalyze) { // [DM]
5563 AnalysisPeriodicEvent(0);
5564 StartAnalysisClock();
5569 StartAnalysisClock ()
5572 XtAppAddTimeOut(appContext, 2000,
5573 (XtTimerCallbackProc) AnalysisClockCallback,
5577 XtIntervalId clockTimerXID = 0;
5580 ClockTimerRunning ()
5582 return clockTimerXID != 0;
5588 if (clockTimerXID != 0) {
5589 XtRemoveTimeOut(clockTimerXID);
5598 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
5605 StartClockTimer (long millisec)
5608 XtAppAddTimeOut(appContext, millisec,
5609 (XtTimerCallbackProc) ClockTimerCallback,
5614 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
5619 /* check for low time warning */
5620 Pixel foregroundOrWarningColor = timerForegroundPixel;
5623 appData.lowTimeWarning &&
5624 (timer / 1000) < appData.icsAlarmTime)
5625 foregroundOrWarningColor = lowTimeWarningColor;
5627 if (appData.clockMode) {
5628 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
5629 XtSetArg(args[0], XtNlabel, buf);
5631 snprintf(buf, MSG_SIZ, "%s ", color);
5632 XtSetArg(args[0], XtNlabel, buf);
5637 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
5638 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
5640 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
5641 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
5644 XtSetValues(w, args, 3);
5648 DisplayWhiteClock (long timeRemaining, int highlight)
5652 if(appData.noGUI) return;
5653 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
5654 if (highlight && iconPixmap == bIconPixmap) {
5655 iconPixmap = wIconPixmap;
5656 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
5657 XtSetValues(shellWidget, args, 1);
5662 DisplayBlackClock (long timeRemaining, int highlight)
5666 if(appData.noGUI) return;
5667 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
5668 if (highlight && iconPixmap == wIconPixmap) {
5669 iconPixmap = bIconPixmap;
5670 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
5671 XtSetValues(shellWidget, args, 1);
5690 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
5694 int to_prog[2], from_prog[2];
5698 if (appData.debugMode) {
5699 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
5702 /* We do NOT feed the cmdLine to the shell; we just
5703 parse it into blank-separated arguments in the
5704 most simple-minded way possible.
5707 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
5710 while(*p == ' ') p++;
5712 if(*p == '"' || *p == '\'')
5713 p = strchr(++argv[i-1], *p);
5714 else p = strchr(p, ' ');
5715 if (p == NULL) break;
5720 SetUpChildIO(to_prog, from_prog);
5722 if ((pid = fork()) == 0) {
5724 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
5725 close(to_prog[1]); // first close the unused pipe ends
5726 close(from_prog[0]);
5727 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
5728 dup2(from_prog[1], 1);
5729 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
5730 close(from_prog[1]); // and closing again loses one of the pipes!
5731 if(fileno(stderr) >= 2) // better safe than sorry...
5732 dup2(1, fileno(stderr)); /* force stderr to the pipe */
5734 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
5739 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
5741 execvp(argv[0], argv);
5743 /* If we get here, exec failed */
5748 /* Parent process */
5750 close(from_prog[1]);
5752 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5755 cp->fdFrom = from_prog[0];
5756 cp->fdTo = to_prog[1];
5761 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
5763 AlarmCallBack (int n)
5769 DestroyChildProcess (ProcRef pr, int signalType)
5771 ChildProc *cp = (ChildProc *) pr;
5773 if (cp->kind != CPReal) return;
5775 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
5776 signal(SIGALRM, AlarmCallBack);
5778 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
5779 kill(cp->pid, SIGKILL); // kill it forcefully
5780 wait((int *) 0); // and wait again
5784 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
5786 /* Process is exiting either because of the kill or because of
5787 a quit command sent by the backend; either way, wait for it to die.
5796 InterruptChildProcess (ProcRef pr)
5798 ChildProc *cp = (ChildProc *) pr;
5800 if (cp->kind != CPReal) return;
5801 (void) kill(cp->pid, SIGINT); /* stop it thinking */
5805 OpenTelnet (char *host, char *port, ProcRef *pr)
5807 char cmdLine[MSG_SIZ];
5809 if (port[0] == NULLCHAR) {
5810 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
5812 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
5814 return StartChildProcess(cmdLine, "", pr);
5818 OpenTCP (char *host, char *port, ProcRef *pr)
5821 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
5822 #else /* !OMIT_SOCKETS */
5823 struct addrinfo hints;
5824 struct addrinfo *ais, *ai;
5829 memset(&hints, 0, sizeof(hints));
5830 hints.ai_family = AF_UNSPEC;
5831 hints.ai_socktype = SOCK_STREAM;
5833 error = getaddrinfo(host, port, &hints, &ais);
5835 /* a getaddrinfo error is not an errno, so can't return it */
5836 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
5837 host, port, gai_strerror(error));
5841 for (ai = ais; ai != NULL; ai = ai->ai_next) {
5842 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
5846 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
5859 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5865 #endif /* !OMIT_SOCKETS */
5871 OpenCommPort (char *name, ProcRef *pr)
5876 fd = open(name, 2, 0);
5877 if (fd < 0) return errno;
5879 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5890 OpenLoopback (ProcRef *pr)
5895 SetUpChildIO(to, from);
5897 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5900 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
5908 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
5910 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
5914 #define INPUT_SOURCE_BUF_SIZE 8192
5923 char buf[INPUT_SOURCE_BUF_SIZE];
5928 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
5930 InputSource *is = (InputSource *) closure;
5935 if (is->lineByLine) {
5936 count = read(is->fd, is->unused,
5937 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
5939 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
5942 is->unused += count;
5944 while (p < is->unused) {
5945 q = memchr(p, '\n', is->unused - p);
5946 if (q == NULL) break;
5948 (is->func)(is, is->closure, p, q - p, 0);
5952 while (p < is->unused) {
5957 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
5962 (is->func)(is, is->closure, is->buf, count, error);
5967 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
5970 ChildProc *cp = (ChildProc *) pr;
5972 is = (InputSource *) calloc(1, sizeof(InputSource));
5973 is->lineByLine = lineByLine;
5977 is->fd = fileno(stdin);
5979 is->kind = cp->kind;
5980 is->fd = cp->fdFrom;
5983 is->unused = is->buf;
5986 is->xid = XtAppAddInput(appContext, is->fd,
5987 (XtPointer) (XtInputReadMask),
5988 (XtInputCallbackProc) DoInputCallback,
5990 is->closure = closure;
5991 return (InputSourceRef) is;
5995 RemoveInputSource (InputSourceRef isr)
5997 InputSource *is = (InputSource *) isr;
5999 if (is->xid == 0) return;
6000 XtRemoveInput(is->xid);
6005 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
6007 static int line = 0;
6008 ChildProc *cp = (ChildProc *) pr;
6013 if (appData.noJoin || !appData.useInternalWrap)
6014 outCount = fwrite(message, 1, count, stdout);
6017 int width = get_term_width();
6018 int len = wrap(NULL, message, count, width, &line);
6019 char *msg = malloc(len);
6023 outCount = fwrite(message, 1, count, stdout);
6026 dbgchk = wrap(msg, message, count, width, &line);
6027 if (dbgchk != len && appData.debugMode)
6028 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
6029 outCount = fwrite(msg, 1, dbgchk, stdout);
6035 outCount = write(cp->fdTo, message, count);
6045 /* Output message to process, with "ms" milliseconds of delay
6046 between each character. This is needed when sending the logon
6047 script to ICC, which for some reason doesn't like the
6048 instantaneous send. */
6050 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
6052 ChildProc *cp = (ChildProc *) pr;
6057 r = write(cp->fdTo, message++, 1);
6070 /**** Animation code by Hugh Fisher, DCS, ANU.
6072 Known problem: if a window overlapping the board is
6073 moved away while a piece is being animated underneath,
6074 the newly exposed area won't be updated properly.
6075 I can live with this.
6077 Known problem: if you look carefully at the animation
6078 of pieces in mono mode, they are being drawn as solid
6079 shapes without interior detail while moving. Fixing
6080 this would be a major complication for minimal return.
6083 /* Masks for XPM pieces. Black and white pieces can have
6084 different shapes, but in the interest of retaining my
6085 sanity pieces must have the same outline on both light
6086 and dark squares, and all pieces must use the same
6087 background square colors/images. */
6089 static int xpmDone = 0;
6092 CreateAnimMasks (int pieceDepth)
6098 unsigned long plane;
6101 /* Need a bitmap just to get a GC with right depth */
6102 buf = XCreatePixmap(xDisplay, xBoardWindow,
6104 values.foreground = 1;
6105 values.background = 0;
6106 /* Don't use XtGetGC, not read only */
6107 maskGC = XCreateGC(xDisplay, buf,
6108 GCForeground | GCBackground, &values);
6109 XFreePixmap(xDisplay, buf);
6111 buf = XCreatePixmap(xDisplay, xBoardWindow,
6112 squareSize, squareSize, pieceDepth);
6113 values.foreground = XBlackPixel(xDisplay, xScreen);
6114 values.background = XWhitePixel(xDisplay, xScreen);
6115 bufGC = XCreateGC(xDisplay, buf,
6116 GCForeground | GCBackground, &values);
6118 for (piece = WhitePawn; piece <= BlackKing; piece++) {
6119 /* Begin with empty mask */
6120 if(!xpmDone) // [HGM] pieces: keep using existing
6121 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
6122 squareSize, squareSize, 1);
6123 XSetFunction(xDisplay, maskGC, GXclear);
6124 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
6125 0, 0, squareSize, squareSize);
6127 /* Take a copy of the piece */
6132 XSetFunction(xDisplay, bufGC, GXcopy);
6133 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
6135 0, 0, squareSize, squareSize, 0, 0);
6137 /* XOR the background (light) over the piece */
6138 XSetFunction(xDisplay, bufGC, GXxor);
6140 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
6141 0, 0, squareSize, squareSize, 0, 0);
6143 XSetForeground(xDisplay, bufGC, lightSquareColor);
6144 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
6147 /* We now have an inverted piece image with the background
6148 erased. Construct mask by just selecting all the non-zero
6149 pixels - no need to reconstruct the original image. */
6150 XSetFunction(xDisplay, maskGC, GXor);
6152 /* Might be quicker to download an XImage and create bitmap
6153 data from it rather than this N copies per piece, but it
6154 only takes a fraction of a second and there is a much
6155 longer delay for loading the pieces. */
6156 for (n = 0; n < pieceDepth; n ++) {
6157 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
6158 0, 0, squareSize, squareSize,
6164 XFreePixmap(xDisplay, buf);
6165 XFreeGC(xDisplay, bufGC);
6166 XFreeGC(xDisplay, maskGC);
6170 InitAnimState (AnimState *anim, XWindowAttributes *info)
6175 /* Each buffer is square size, same depth as window */
6176 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
6177 squareSize, squareSize, info->depth);
6178 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
6179 squareSize, squareSize, info->depth);
6181 /* Create a plain GC for blitting */
6182 mask = GCForeground | GCBackground | GCFunction |
6183 GCPlaneMask | GCGraphicsExposures;
6184 values.foreground = XBlackPixel(xDisplay, xScreen);
6185 values.background = XWhitePixel(xDisplay, xScreen);
6186 values.function = GXcopy;
6187 values.plane_mask = AllPlanes;
6188 values.graphics_exposures = False;
6189 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
6191 /* Piece will be copied from an existing context at
6192 the start of each new animation/drag. */
6193 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
6195 /* Outline will be a read-only copy of an existing */
6196 anim->outlineGC = None;
6202 XWindowAttributes info;
6204 if (xpmDone && gameInfo.variant == oldVariant) return;
6205 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
6206 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
6208 InitAnimState(&game, &info);
6209 InitAnimState(&player, &info);
6211 /* For XPM pieces, we need bitmaps to use as masks. */
6213 CreateAnimMasks(info.depth), xpmDone = 1;
6218 static Boolean frameWaiting;
6221 FrameAlarm (int sig)
6223 frameWaiting = False;
6224 /* In case System-V style signals. Needed?? */
6225 signal(SIGALRM, FrameAlarm);
6229 FrameDelay (int time)
6231 struct itimerval delay;
6233 XSync(xDisplay, False);
6236 frameWaiting = True;
6237 signal(SIGALRM, FrameAlarm);
6238 delay.it_interval.tv_sec =
6239 delay.it_value.tv_sec = time / 1000;
6240 delay.it_interval.tv_usec =
6241 delay.it_value.tv_usec = (time % 1000) * 1000;
6242 setitimer(ITIMER_REAL, &delay, NULL);
6243 while (frameWaiting) pause();
6244 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
6245 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
6246 setitimer(ITIMER_REAL, &delay, NULL);
6253 FrameDelay (int time)
6255 XSync(xDisplay, False);
6257 usleep(time * 1000);
6268 /* Convert board position to corner of screen rect and color */
6271 ScreenSquare (int column, int row, XPoint *pt, int *color)
6274 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
6275 pt->y = lineGap + row * (squareSize + lineGap);
6277 pt->x = lineGap + column * (squareSize + lineGap);
6278 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
6280 *color = SquareColor(row, column);
6283 /* Convert window coords to square */
6286 BoardSquare (int x, int y, int *column, int *row)
6288 *column = EventToSquare(x, BOARD_WIDTH);
6289 if (flipView && *column >= 0)
6290 *column = BOARD_WIDTH - 1 - *column;
6291 *row = EventToSquare(y, BOARD_HEIGHT);
6292 if (!flipView && *row >= 0)
6293 *row = BOARD_HEIGHT - 1 - *row;
6298 #undef Max /* just in case */
6300 #define Max(a, b) ((a) > (b) ? (a) : (b))
6301 #define Min(a, b) ((a) < (b) ? (a) : (b))
6304 SetRect (XRectangle *rect, int x, int y, int width, int height)
6308 rect->width = width;
6309 rect->height = height;
6312 /* Test if two frames overlap. If they do, return
6313 intersection rect within old and location of
6314 that rect within new. */
6317 Intersect ( XPoint *old, XPoint *new, int size, XRectangle *area, XPoint *pt)
6319 if (old->x > new->x + size || new->x > old->x + size ||
6320 old->y > new->y + size || new->y > old->y + size) {
6323 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
6324 size - abs(old->x - new->x), size - abs(old->y - new->y));
6325 pt->x = Max(old->x - new->x, 0);
6326 pt->y = Max(old->y - new->y, 0);
6331 /* For two overlapping frames, return the rect(s)
6332 in the old that do not intersect with the new. */
6335 CalcUpdateRects (XPoint *old, XPoint *new, int size, XRectangle update[], int *nUpdates)
6339 /* If old = new (shouldn't happen) then nothing to draw */
6340 if (old->x == new->x && old->y == new->y) {
6344 /* Work out what bits overlap. Since we know the rects
6345 are the same size we don't need a full intersect calc. */
6347 /* Top or bottom edge? */
6348 if (new->y > old->y) {
6349 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
6351 } else if (old->y > new->y) {
6352 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
6353 size, old->y - new->y);
6356 /* Left or right edge - don't overlap any update calculated above. */
6357 if (new->x > old->x) {
6358 SetRect(&(update[count]), old->x, Max(new->y, old->y),
6359 new->x - old->x, size - abs(new->y - old->y));
6361 } else if (old->x > new->x) {
6362 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
6363 old->x - new->x, size - abs(new->y - old->y));
6370 /* Generate a series of frame coords from start->mid->finish.
6371 The movement rate doubles until the half way point is
6372 reached, then halves back down to the final destination,
6373 which gives a nice slow in/out effect. The algorithmn
6374 may seem to generate too many intermediates for short
6375 moves, but remember that the purpose is to attract the
6376 viewers attention to the piece about to be moved and
6377 then to where it ends up. Too few frames would be less
6381 Tween (XPoint *start, XPoint *mid, XPoint *finish, int factor, XPoint frames[], int *nFrames)
6383 int fraction, n, count;
6387 /* Slow in, stepping 1/16th, then 1/8th, ... */
6389 for (n = 0; n < factor; n++)
6391 for (n = 0; n < factor; n++) {
6392 frames[count].x = start->x + (mid->x - start->x) / fraction;
6393 frames[count].y = start->y + (mid->y - start->y) / fraction;
6395 fraction = fraction / 2;
6399 frames[count] = *mid;
6402 /* Slow out, stepping 1/2, then 1/4, ... */
6404 for (n = 0; n < factor; n++) {
6405 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
6406 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
6408 fraction = fraction * 2;
6413 /* Draw a piece on the screen without disturbing what's there */
6416 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
6420 /* Bitmap for piece being moved. */
6421 if (appData.monoMode) {
6422 *mask = *pieceToSolid(piece);
6423 } else if (useImages) {
6425 *mask = xpmMask[piece];
6427 *mask = ximMaskPm[piece];
6430 *mask = *pieceToSolid(piece);
6433 /* GC for piece being moved. Square color doesn't matter, but
6434 since it gets modified we make a copy of the original. */
6436 if (appData.monoMode)
6441 if (appData.monoMode)
6446 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
6448 /* Outline only used in mono mode and is not modified */
6450 *outline = bwPieceGC;
6452 *outline = wbPieceGC;
6456 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
6461 /* Draw solid rectangle which will be clipped to shape of piece */
6462 XFillRectangle(xDisplay, dest, clip,
6463 0, 0, squareSize, squareSize);
6464 if (appData.monoMode)
6465 /* Also draw outline in contrasting color for black
6466 on black / white on white cases */
6467 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
6468 0, 0, squareSize, squareSize, 0, 0, 1);
6470 /* Copy the piece */
6475 if(appData.upsideDown && flipView) kind ^= 2;
6476 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
6478 0, 0, squareSize, squareSize,
6483 /* Animate the movement of a single piece */
6486 BeginAnimation (AnimState *anim, ChessSquare piece, int startColor, XPoint *start)
6490 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
6491 /* The old buffer is initialised with the start square (empty) */
6492 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
6493 anim->prevFrame = *start;
6495 /* The piece will be drawn using its own bitmap as a matte */
6496 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
6497 XSetClipMask(xDisplay, anim->pieceGC, mask);
6501 AnimationFrame (AnimState *anim, XPoint *frame, ChessSquare piece)
6503 XRectangle updates[4];
6508 /* Save what we are about to draw into the new buffer */
6509 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
6510 frame->x, frame->y, squareSize, squareSize,
6513 /* Erase bits of the previous frame */
6514 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
6515 /* Where the new frame overlapped the previous,
6516 the contents in newBuf are wrong. */
6517 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
6518 overlap.x, overlap.y,
6519 overlap.width, overlap.height,
6521 /* Repaint the areas in the old that don't overlap new */
6522 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
6523 for (i = 0; i < count; i++)
6524 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6525 updates[i].x - anim->prevFrame.x,
6526 updates[i].y - anim->prevFrame.y,
6527 updates[i].width, updates[i].height,
6528 updates[i].x, updates[i].y);
6530 /* Easy when no overlap */
6531 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6532 0, 0, squareSize, squareSize,
6533 anim->prevFrame.x, anim->prevFrame.y);
6536 /* Save this frame for next time round */
6537 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
6538 0, 0, squareSize, squareSize,
6540 anim->prevFrame = *frame;
6542 /* Draw piece over original screen contents, not current,
6543 and copy entire rect. Wipes out overlapping piece images. */
6544 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
6545 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
6546 0, 0, squareSize, squareSize,
6547 frame->x, frame->y);
6551 EndAnimation (AnimState *anim, XPoint *finish)
6553 XRectangle updates[4];
6558 /* The main code will redraw the final square, so we
6559 only need to erase the bits that don't overlap. */
6560 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
6561 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
6562 for (i = 0; i < count; i++)
6563 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6564 updates[i].x - anim->prevFrame.x,
6565 updates[i].y - anim->prevFrame.y,
6566 updates[i].width, updates[i].height,
6567 updates[i].x, updates[i].y);
6569 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6570 0, 0, squareSize, squareSize,
6571 anim->prevFrame.x, anim->prevFrame.y);
6576 FrameSequence (AnimState *anim, ChessSquare piece, int startColor, XPoint *start, XPoint *finish, XPoint frames[], int nFrames)
6580 BeginAnimation(anim, piece, startColor, start);
6581 for (n = 0; n < nFrames; n++) {
6582 AnimationFrame(anim, &(frames[n]), piece);
6583 FrameDelay(appData.animSpeed);
6585 EndAnimation(anim, finish);
6589 AnimateAtomicCapture (Board board, int fromX, int fromY, int toX, int toY)
6592 ChessSquare piece = board[fromY][toY];
6593 board[fromY][toY] = EmptySquare;
6594 DrawPosition(FALSE, board);
6596 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
6597 y = lineGap + toY * (squareSize + lineGap);
6599 x = lineGap + toX * (squareSize + lineGap);
6600 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
6602 for(i=1; i<4*kFactor; i++) {
6603 int r = squareSize * 9 * i/(20*kFactor - 5);
6604 XFillArc(xDisplay, xBoardWindow, highlineGC,
6605 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
6606 FrameDelay(appData.animSpeed);
6608 board[fromY][toY] = piece;
6611 /* Main control logic for deciding what to animate and how */
6614 AnimateMove (Board board, int fromX, int fromY, int toX, int toY)
6618 XPoint start, finish, mid;
6619 XPoint frames[kFactor * 2 + 1];
6620 int nFrames, startColor, endColor;
6622 /* Are we animating? */
6623 if (!appData.animate || appData.blindfold)
6626 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
6627 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
6628 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
6630 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
6631 piece = board[fromY][fromX];
6632 if (piece >= EmptySquare) return;
6637 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
6640 ScreenSquare(fromX, fromY, &start, &startColor);
6641 ScreenSquare(toX, toY, &finish, &endColor);
6644 /* Knight: make straight movement then diagonal */
6645 if (abs(toY - fromY) < abs(toX - fromX)) {
6646 mid.x = start.x + (finish.x - start.x) / 2;
6650 mid.y = start.y + (finish.y - start.y) / 2;
6653 mid.x = start.x + (finish.x - start.x) / 2;
6654 mid.y = start.y + (finish.y - start.y) / 2;
6657 /* Don't use as many frames for very short moves */
6658 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
6659 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
6661 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
6662 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
6663 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
6665 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
6666 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
6669 /* Be sure end square is redrawn */
6670 damage[0][toY][toX] = True;
6674 DragPieceBegin (int x, int y, Boolean instantly)
6676 int boardX, boardY, color;
6679 /* Are we animating? */
6680 if (!appData.animateDragging || appData.blindfold)
6683 /* Figure out which square we start in and the
6684 mouse position relative to top left corner. */
6685 BoardSquare(x, y, &boardX, &boardY);
6686 player.startBoardX = boardX;
6687 player.startBoardY = boardY;
6688 ScreenSquare(boardX, boardY, &corner, &color);
6689 player.startSquare = corner;
6690 player.startColor = color;
6691 /* As soon as we start dragging, the piece will jump slightly to
6692 be centered over the mouse pointer. */
6693 player.mouseDelta.x = squareSize/2;
6694 player.mouseDelta.y = squareSize/2;
6695 /* Initialise animation */
6696 player.dragPiece = PieceForSquare(boardX, boardY);
6698 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
6699 player.dragActive = True;
6700 BeginAnimation(&player, player.dragPiece, color, &corner);
6701 /* Mark this square as needing to be redrawn. Note that
6702 we don't remove the piece though, since logically (ie
6703 as seen by opponent) the move hasn't been made yet. */
6704 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
6705 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
6706 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
6707 corner.x, corner.y, squareSize, squareSize,
6708 0, 0); // [HGM] zh: unstack in stead of grab
6709 if(gatingPiece != EmptySquare) {
6710 /* Kludge alert: When gating we want the introduced
6711 piece to appear on the from square. To generate an
6712 image of it, we draw it on the board, copy the image,
6713 and draw the original piece again. */
6714 ChessSquare piece = boards[currentMove][boardY][boardX];
6715 DrawSquare(boardY, boardX, gatingPiece, 0);
6716 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
6717 corner.x, corner.y, squareSize, squareSize, 0, 0);
6718 DrawSquare(boardY, boardX, piece, 0);
6720 damage[0][boardY][boardX] = True;
6722 player.dragActive = False;
6727 ChangeDragPiece (ChessSquare piece)
6730 player.dragPiece = piece;
6731 /* The piece will be drawn using its own bitmap as a matte */
6732 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
6733 XSetClipMask(xDisplay, player.pieceGC, mask);
6737 DragPieceMove (int x, int y)
6741 /* Are we animating? */
6742 if (!appData.animateDragging || appData.blindfold)
6746 if (! player.dragActive)
6748 /* Move piece, maintaining same relative position
6749 of mouse within square */
6750 corner.x = x - player.mouseDelta.x;
6751 corner.y = y - player.mouseDelta.y;
6752 AnimationFrame(&player, &corner, player.dragPiece);
6754 if (appData.highlightDragging) {
6756 BoardSquare(x, y, &boardX, &boardY);
6757 SetHighlights(fromX, fromY, boardX, boardY);
6763 DragPieceEnd (int x, int y)
6765 int boardX, boardY, color;
6768 /* Are we animating? */
6769 if (!appData.animateDragging || appData.blindfold)
6773 if (! player.dragActive)
6775 /* Last frame in sequence is square piece is
6776 placed on, which may not match mouse exactly. */
6777 BoardSquare(x, y, &boardX, &boardY);
6778 ScreenSquare(boardX, boardY, &corner, &color);
6779 EndAnimation(&player, &corner);
6781 /* Be sure end square is redrawn */
6782 damage[0][boardY][boardX] = True;
6784 /* This prevents weird things happening with fast successive
6785 clicks which on my Sun at least can cause motion events
6786 without corresponding press/release. */
6787 player.dragActive = False;
6790 /* Handle expose event while piece being dragged */
6795 if (!player.dragActive || appData.blindfold)
6798 /* What we're doing: logically, the move hasn't been made yet,
6799 so the piece is still in it's original square. But visually
6800 it's being dragged around the board. So we erase the square
6801 that the piece is on and draw it at the last known drag point. */
6802 BlankSquare(player.startSquare.x, player.startSquare.y,
6803 player.startColor, EmptySquare, xBoardWindow, 1);
6804 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
6805 damage[0][player.startBoardY][player.startBoardX] = TRUE;
6808 #include <sys/ioctl.h>
6812 int fd, default_width;
6815 default_width = 79; // this is FICS default anyway...
6817 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
6819 if (!ioctl(fd, TIOCGSIZE, &win))
6820 default_width = win.ts_cols;
6821 #elif defined(TIOCGWINSZ)
6823 if (!ioctl(fd, TIOCGWINSZ, &win))
6824 default_width = win.ws_col;
6826 return default_width;
6832 static int old_width = 0;
6833 int new_width = get_term_width();
6835 if (old_width != new_width)
6836 ics_printf("set width %d\n", new_width);
6837 old_width = new_width;
6841 NotifyFrontendLogin ()
6846 /* [AS] Arrow highlighting support */
6848 static double A_WIDTH = 5; /* Width of arrow body */
6850 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
6851 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
6862 return (int) (x + 0.5);
6866 SquareToPos (int rank, int file, int *x, int *y)
6869 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
6870 *y = lineGap + rank * (squareSize + lineGap);
6872 *x = lineGap + file * (squareSize + lineGap);
6873 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
6877 /* Draw an arrow between two points using current settings */
6879 DrawArrowBetweenPoints (int s_x, int s_y, int d_x, int d_y)
6882 double dx, dy, j, k, x, y;
6885 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
6887 arrow[0].x = s_x + A_WIDTH + 0.5;
6890 arrow[1].x = s_x + A_WIDTH + 0.5;
6891 arrow[1].y = d_y - h;
6893 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
6894 arrow[2].y = d_y - h;
6899 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
6900 arrow[5].y = d_y - h;
6902 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
6903 arrow[4].y = d_y - h;
6905 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
6908 else if( d_y == s_y ) {
6909 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
6912 arrow[0].y = s_y + A_WIDTH + 0.5;
6914 arrow[1].x = d_x - w;
6915 arrow[1].y = s_y + A_WIDTH + 0.5;
6917 arrow[2].x = d_x - w;
6918 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
6923 arrow[5].x = d_x - w;
6924 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
6926 arrow[4].x = d_x - w;
6927 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
6930 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
6933 /* [AS] Needed a lot of paper for this! :-) */
6934 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
6935 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
6937 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
6939 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
6944 arrow[0].x = Round(x - j);
6945 arrow[0].y = Round(y + j*dx);
6947 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
6948 arrow[1].y = Round(arrow[0].y - 2*j*dx);
6951 x = (double) d_x - k;
6952 y = (double) d_y - k*dy;
6955 x = (double) d_x + k;
6956 y = (double) d_y + k*dy;
6959 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
6961 arrow[6].x = Round(x - j);
6962 arrow[6].y = Round(y + j*dx);
6964 arrow[2].x = Round(arrow[6].x + 2*j);
6965 arrow[2].y = Round(arrow[6].y - 2*j*dx);
6967 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
6968 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
6973 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
6974 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
6977 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
6978 if(appData.monoMode) arrow[7] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, arrow, 8, CoordModeOrigin);
6979 // Polygon( hdc, arrow, 7 );
6983 ArrowDamage (int s_col, int s_row, int d_col, int d_row)
6986 hor = 64*s_col + 32; vert = 64*s_row + 32;
6987 for(i=0; i<= 64; i++) {
6988 damage[0][vert+6>>6][hor+6>>6] = True;
6989 damage[0][vert-6>>6][hor+6>>6] = True;
6990 damage[0][vert+6>>6][hor-6>>6] = True;
6991 damage[0][vert-6>>6][hor-6>>6] = True;
6992 hor += d_col - s_col; vert += d_row - s_row;
6996 /* [AS] Draw an arrow between two squares */
6998 DrawArrowBetweenSquares (int s_col, int s_row, int d_col, int d_row)
7000 int s_x, s_y, d_x, d_y;
7002 if( s_col == d_col && s_row == d_row ) {
7006 /* Get source and destination points */
7007 SquareToPos( s_row, s_col, &s_x, &s_y);
7008 SquareToPos( d_row, d_col, &d_x, &d_y);
7011 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
7013 else if( d_y < s_y ) {
7014 d_y += squareSize / 2 + squareSize / 4;
7017 d_y += squareSize / 2;
7021 d_x += squareSize / 2 - squareSize / 4;
7023 else if( d_x < s_x ) {
7024 d_x += squareSize / 2 + squareSize / 4;
7027 d_x += squareSize / 2;
7030 s_x += squareSize / 2;
7031 s_y += squareSize / 2;
7034 A_WIDTH = squareSize / 14.; //[HGM] make float
7036 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
7037 ArrowDamage(s_col, s_row, d_col, d_row);
7041 IsDrawArrowEnabled ()
7043 return appData.highlightMoveWithArrow && squareSize >= 32;
7047 DrawArrowHighlight (int fromX, int fromY, int toX,int toY)
7049 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
7050 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
7054 UpdateLogos (int displ)
7056 return; // no logos in XBoard yet