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;
1414 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1415 XrmValue vFrom, vTo;
1416 int forceMono = False;
1418 if (!appData.monoMode) {
1419 vFrom.addr = (caddr_t) appData.lightSquareColor;
1420 vFrom.size = strlen(appData.lightSquareColor);
1421 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1422 if (vTo.addr == NULL) {
1423 appData.monoMode = True;
1426 lightSquareColor = *(Pixel *) vTo.addr;
1429 if (!appData.monoMode) {
1430 vFrom.addr = (caddr_t) appData.darkSquareColor;
1431 vFrom.size = strlen(appData.darkSquareColor);
1432 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1433 if (vTo.addr == NULL) {
1434 appData.monoMode = True;
1437 darkSquareColor = *(Pixel *) vTo.addr;
1440 if (!appData.monoMode) {
1441 vFrom.addr = (caddr_t) appData.whitePieceColor;
1442 vFrom.size = strlen(appData.whitePieceColor);
1443 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1444 if (vTo.addr == NULL) {
1445 appData.monoMode = True;
1448 whitePieceColor = *(Pixel *) vTo.addr;
1451 if (!appData.monoMode) {
1452 vFrom.addr = (caddr_t) appData.blackPieceColor;
1453 vFrom.size = strlen(appData.blackPieceColor);
1454 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1455 if (vTo.addr == NULL) {
1456 appData.monoMode = True;
1459 blackPieceColor = *(Pixel *) vTo.addr;
1463 if (!appData.monoMode) {
1464 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1465 vFrom.size = strlen(appData.highlightSquareColor);
1466 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1467 if (vTo.addr == NULL) {
1468 appData.monoMode = True;
1471 highlightSquareColor = *(Pixel *) vTo.addr;
1475 if (!appData.monoMode) {
1476 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1477 vFrom.size = strlen(appData.premoveHighlightColor);
1478 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1479 if (vTo.addr == NULL) {
1480 appData.monoMode = True;
1483 premoveHighlightColor = *(Pixel *) vTo.addr;
1491 { // [HGM] taken out of main
1493 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1494 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1495 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1497 if (appData.bitmapDirectory[0] != NULLCHAR) {
1501 CreateXPMBoard(appData.liteBackTextureFile, 1);
1502 CreateXPMBoard(appData.darkBackTextureFile, 0);
1506 /* Create regular pieces */
1507 if (!useImages) CreatePieces();
1512 main (int argc, char **argv)
1514 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1515 XSetWindowAttributes window_attributes;
1517 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1518 XrmValue vFrom, vTo;
1519 XtGeometryResult gres;
1522 int forceMono = False;
1524 srandom(time(0)); // [HGM] book: make random truly random
1526 setbuf(stdout, NULL);
1527 setbuf(stderr, NULL);
1530 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1531 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1535 programName = strrchr(argv[0], '/');
1536 if (programName == NULL)
1537 programName = argv[0];
1542 XtSetLanguageProc(NULL, NULL, NULL);
1543 bindtextdomain(PACKAGE, LOCALEDIR);
1544 textdomain(PACKAGE);
1548 XtAppInitialize(&appContext, "XBoard", shellOptions,
1549 XtNumber(shellOptions),
1550 &argc, argv, xboardResources, NULL, 0);
1551 appData.boardSize = "";
1552 InitAppData(ConvertToLine(argc, argv));
1554 if (p == NULL) p = "/tmp";
1555 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1556 gameCopyFilename = (char*) malloc(i);
1557 gamePasteFilename = (char*) malloc(i);
1558 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1559 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1561 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1562 clientResources, XtNumber(clientResources),
1565 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1566 static char buf[MSG_SIZ];
1567 EscapeExpand(buf, appData.firstInitString);
1568 appData.firstInitString = strdup(buf);
1569 EscapeExpand(buf, appData.secondInitString);
1570 appData.secondInitString = strdup(buf);
1571 EscapeExpand(buf, appData.firstComputerString);
1572 appData.firstComputerString = strdup(buf);
1573 EscapeExpand(buf, appData.secondComputerString);
1574 appData.secondComputerString = strdup(buf);
1577 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1580 if (chdir(chessDir) != 0) {
1581 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1587 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1588 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1589 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1590 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1593 setbuf(debugFP, NULL);
1597 if (appData.debugMode) {
1598 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1602 /* [HGM,HR] make sure board size is acceptable */
1603 if(appData.NrFiles > BOARD_FILES ||
1604 appData.NrRanks > BOARD_RANKS )
1605 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1608 /* This feature does not work; animation needs a rewrite */
1609 appData.highlightDragging = FALSE;
1613 xDisplay = XtDisplay(shellWidget);
1614 xScreen = DefaultScreen(xDisplay);
1615 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1617 gameInfo.variant = StringToVariant(appData.variant);
1618 InitPosition(FALSE);
1621 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1623 if (isdigit(appData.boardSize[0])) {
1624 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1625 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1626 &fontPxlSize, &smallLayout, &tinyLayout);
1628 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1629 programName, appData.boardSize);
1633 /* Find some defaults; use the nearest known size */
1634 SizeDefaults *szd, *nearest;
1635 int distance = 99999;
1636 nearest = szd = sizeDefaults;
1637 while (szd->name != NULL) {
1638 if (abs(szd->squareSize - squareSize) < distance) {
1640 distance = abs(szd->squareSize - squareSize);
1641 if (distance == 0) break;
1645 if (i < 2) lineGap = nearest->lineGap;
1646 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1647 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1648 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1649 if (i < 6) smallLayout = nearest->smallLayout;
1650 if (i < 7) tinyLayout = nearest->tinyLayout;
1653 SizeDefaults *szd = sizeDefaults;
1654 if (*appData.boardSize == NULLCHAR) {
1655 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1656 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1659 if (szd->name == NULL) szd--;
1660 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1662 while (szd->name != NULL &&
1663 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1664 if (szd->name == NULL) {
1665 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1666 programName, appData.boardSize);
1670 squareSize = szd->squareSize;
1671 lineGap = szd->lineGap;
1672 clockFontPxlSize = szd->clockFontPxlSize;
1673 coordFontPxlSize = szd->coordFontPxlSize;
1674 fontPxlSize = szd->fontPxlSize;
1675 smallLayout = szd->smallLayout;
1676 tinyLayout = szd->tinyLayout;
1677 // [HGM] font: use defaults from settings file if available and not overruled
1679 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1680 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1681 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1682 appData.font = fontTable[MESSAGE_FONT][squareSize];
1683 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1684 appData.coordFont = fontTable[COORD_FONT][squareSize];
1686 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1687 if (strlen(appData.pixmapDirectory) > 0) {
1688 p = ExpandPathName(appData.pixmapDirectory);
1690 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1691 appData.pixmapDirectory);
1694 if (appData.debugMode) {
1695 fprintf(stderr, _("\
1696 XBoard square size (hint): %d\n\
1697 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1699 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1700 if (appData.debugMode) {
1701 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1704 defaultLineGap = lineGap;
1705 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1707 /* [HR] height treated separately (hacked) */
1708 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1709 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1710 if (appData.showJail == 1) {
1711 /* Jail on top and bottom */
1712 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1713 XtSetArg(boardArgs[2], XtNheight,
1714 boardHeight + 2*(lineGap + squareSize));
1715 } else if (appData.showJail == 2) {
1717 XtSetArg(boardArgs[1], XtNwidth,
1718 boardWidth + 2*(lineGap + squareSize));
1719 XtSetArg(boardArgs[2], XtNheight, boardHeight);
1722 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1723 XtSetArg(boardArgs[2], XtNheight, boardHeight);
1727 * Determine what fonts to use.
1730 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1731 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1732 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1733 fontSet = CreateFontSet(appData.font);
1734 clockFontSet = CreateFontSet(appData.clockFont);
1736 /* For the coordFont, use the 0th font of the fontset. */
1737 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1738 XFontStruct **font_struct_list;
1739 XFontSetExtents *fontSize;
1740 char **font_name_list;
1741 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1742 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1743 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1744 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1745 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1748 appData.font = FindFont(appData.font, fontPxlSize);
1749 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1750 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1751 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1752 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1753 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1754 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1756 countFontID = coordFontID; // [HGM] holdings
1757 countFontStruct = coordFontStruct;
1759 xdb = XtDatabase(xDisplay);
1761 XrmPutLineResource(&xdb, "*international: True");
1762 vTo.size = sizeof(XFontSet);
1763 vTo.addr = (XtPointer) &fontSet;
1764 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1766 XrmPutStringResource(&xdb, "*font", appData.font);
1770 * Detect if there are not enough colors available and adapt.
1772 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1773 appData.monoMode = True;
1776 forceMono = MakeColors();
1779 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1781 appData.monoMode = True;
1784 if (appData.lowTimeWarning && !appData.monoMode) {
1785 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1786 vFrom.size = strlen(appData.lowTimeWarningColor);
1787 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1788 if (vTo.addr == NULL)
1789 appData.monoMode = True;
1791 lowTimeWarningColor = *(Pixel *) vTo.addr;
1794 if (appData.monoMode && appData.debugMode) {
1795 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1796 (unsigned long) XWhitePixel(xDisplay, xScreen),
1797 (unsigned long) XBlackPixel(xDisplay, xScreen));
1800 ParseIcsTextColors();
1801 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
1802 textColors[ColorNone].attr = 0;
1804 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1810 layoutName = "tinyLayout";
1811 } else if (smallLayout) {
1812 layoutName = "smallLayout";
1814 layoutName = "normalLayout";
1816 /* Outer layoutWidget is there only to provide a name for use in
1817 resources that depend on the layout style */
1819 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
1820 layoutArgs, XtNumber(layoutArgs));
1822 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
1823 formArgs, XtNumber(formArgs));
1824 XtSetArg(args[0], XtNdefaultDistance, &sep);
1825 XtGetValues(formWidget, args, 1);
1828 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
1829 XtSetArg(args[0], XtNtop, XtChainTop);
1830 XtSetArg(args[1], XtNbottom, XtChainTop);
1831 XtSetArg(args[2], XtNright, XtChainLeft);
1832 XtSetValues(menuBarWidget, args, 3);
1834 widgetList[j++] = whiteTimerWidget =
1835 XtCreateWidget("whiteTime", labelWidgetClass,
1836 formWidget, timerArgs, XtNumber(timerArgs));
1838 XtSetArg(args[0], XtNfontSet, clockFontSet);
1840 XtSetArg(args[0], XtNfont, clockFontStruct);
1842 XtSetArg(args[1], XtNtop, XtChainTop);
1843 XtSetArg(args[2], XtNbottom, XtChainTop);
1844 XtSetValues(whiteTimerWidget, args, 3);
1846 widgetList[j++] = blackTimerWidget =
1847 XtCreateWidget("blackTime", labelWidgetClass,
1848 formWidget, timerArgs, XtNumber(timerArgs));
1850 XtSetArg(args[0], XtNfontSet, clockFontSet);
1852 XtSetArg(args[0], XtNfont, clockFontStruct);
1854 XtSetArg(args[1], XtNtop, XtChainTop);
1855 XtSetArg(args[2], XtNbottom, XtChainTop);
1856 XtSetValues(blackTimerWidget, args, 3);
1858 if (appData.titleInWindow) {
1859 widgetList[j++] = titleWidget =
1860 XtCreateWidget("title", labelWidgetClass, formWidget,
1861 titleArgs, XtNumber(titleArgs));
1862 XtSetArg(args[0], XtNtop, XtChainTop);
1863 XtSetArg(args[1], XtNbottom, XtChainTop);
1864 XtSetValues(titleWidget, args, 2);
1867 if (appData.showButtonBar) {
1868 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
1869 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
1870 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
1871 XtSetArg(args[2], XtNtop, XtChainTop);
1872 XtSetArg(args[3], XtNbottom, XtChainTop);
1873 XtSetValues(buttonBarWidget, args, 4);
1876 widgetList[j++] = messageWidget =
1877 XtCreateWidget("message", labelWidgetClass, formWidget,
1878 messageArgs, XtNumber(messageArgs));
1879 XtSetArg(args[0], XtNtop, XtChainTop);
1880 XtSetArg(args[1], XtNbottom, XtChainTop);
1881 XtSetValues(messageWidget, args, 2);
1883 widgetList[j++] = boardWidget =
1884 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
1885 XtNumber(boardArgs));
1887 XtManageChildren(widgetList, j);
1889 timerWidth = (boardWidth - sep) / 2;
1890 XtSetArg(args[0], XtNwidth, timerWidth);
1891 XtSetValues(whiteTimerWidget, args, 1);
1892 XtSetValues(blackTimerWidget, args, 1);
1894 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1895 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1896 XtGetValues(whiteTimerWidget, args, 2);
1898 if (appData.showButtonBar) {
1899 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1900 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1901 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
1905 * formWidget uses these constraints but they are stored
1909 XtSetArg(args[i], XtNfromHoriz, 0); i++;
1910 XtSetValues(menuBarWidget, args, i);
1911 if (appData.titleInWindow) {
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], XtNjustify, XtJustifyLeft); i++;
1923 XtSetValues(titleWidget, args, i);
1925 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1926 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1927 XtSetValues(messageWidget, args, i);
1928 if (appData.showButtonBar) {
1930 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1931 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1932 XtSetValues(buttonBarWidget, args, i);
1936 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1937 XtSetValues(whiteTimerWidget, args, i);
1939 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1940 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1941 XtSetValues(blackTimerWidget, args, i);
1943 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
1944 XtSetValues(titleWidget, args, i);
1946 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1947 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1948 XtSetValues(messageWidget, args, i);
1949 if (appData.showButtonBar) {
1951 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1952 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1953 XtSetValues(buttonBarWidget, args, i);
1958 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1959 XtSetValues(whiteTimerWidget, args, i);
1961 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1962 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1963 XtSetValues(blackTimerWidget, args, i);
1965 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1966 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1967 XtSetValues(messageWidget, args, i);
1968 if (appData.showButtonBar) {
1970 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1971 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1972 XtSetValues(buttonBarWidget, args, i);
1976 XtSetArg(args[0], XtNfromVert, messageWidget);
1977 XtSetArg(args[1], XtNtop, XtChainTop);
1978 XtSetArg(args[2], XtNbottom, XtChainBottom);
1979 XtSetArg(args[3], XtNleft, XtChainLeft);
1980 XtSetArg(args[4], XtNright, XtChainRight);
1981 XtSetValues(boardWidget, args, 5);
1983 XtRealizeWidget(shellWidget);
1986 XtSetArg(args[0], XtNx, wpMain.x);
1987 XtSetArg(args[1], XtNy, wpMain.y);
1988 XtSetValues(shellWidget, args, 2);
1992 * Correct the width of the message and title widgets.
1993 * It is not known why some systems need the extra fudge term.
1994 * The value "2" is probably larger than needed.
1996 XawFormDoLayout(formWidget, False);
1998 #define WIDTH_FUDGE 2
2000 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2001 XtSetArg(args[i], XtNheight, &h); i++;
2002 XtGetValues(messageWidget, args, i);
2003 if (appData.showButtonBar) {
2005 XtSetArg(args[i], XtNwidth, &w); i++;
2006 XtGetValues(buttonBarWidget, args, i);
2007 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2009 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2012 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2013 if (gres != XtGeometryYes && appData.debugMode) {
2014 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2015 programName, gres, w, h, wr, hr);
2018 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2019 /* The size used for the child widget in layout lags one resize behind
2020 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2022 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2023 if (gres != XtGeometryYes && appData.debugMode) {
2024 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2025 programName, gres, w, h, wr, hr);
2028 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
2029 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2030 XtSetArg(args[1], XtNright, XtChainRight);
2031 XtSetValues(messageWidget, args, 2);
2033 if (appData.titleInWindow) {
2035 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2036 XtSetArg(args[i], XtNheight, &h); i++;
2037 XtGetValues(titleWidget, args, i);
2039 w = boardWidth - 2*bor;
2041 XtSetArg(args[0], XtNwidth, &w);
2042 XtGetValues(menuBarWidget, args, 1);
2043 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2046 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2047 if (gres != XtGeometryYes && appData.debugMode) {
2049 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2050 programName, gres, w, h, wr, hr);
2053 XawFormDoLayout(formWidget, True);
2055 xBoardWindow = XtWindow(boardWidget);
2057 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2058 // not need to go into InitDrawingSizes().
2062 * Create X checkmark bitmap and initialize option menu checks.
2064 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2065 checkmark_bits, checkmark_width, checkmark_height);
2071 ReadBitmap(&wIconPixmap, "icon_white.bm",
2072 icon_white_bits, icon_white_width, icon_white_height);
2073 ReadBitmap(&bIconPixmap, "icon_black.bm",
2074 icon_black_bits, icon_black_width, icon_black_height);
2075 iconPixmap = wIconPixmap;
2077 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2078 XtSetValues(shellWidget, args, i);
2081 * Create a cursor for the board widget.
2083 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2084 XChangeWindowAttributes(xDisplay, xBoardWindow,
2085 CWCursor, &window_attributes);
2088 * Inhibit shell resizing.
2090 shellArgs[0].value = (XtArgVal) &w;
2091 shellArgs[1].value = (XtArgVal) &h;
2092 XtGetValues(shellWidget, shellArgs, 2);
2093 shellArgs[4].value = shellArgs[2].value = w;
2094 shellArgs[5].value = shellArgs[3].value = h;
2095 XtSetValues(shellWidget, &shellArgs[2], 4);
2096 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2097 marginH = h - boardHeight;
2099 CatchDeleteWindow(shellWidget, "QuitProc");
2107 if (appData.animate || appData.animateDragging)
2110 XtAugmentTranslations(formWidget,
2111 XtParseTranslationTable(globalTranslations));
2112 XtAugmentTranslations(boardWidget,
2113 XtParseTranslationTable(boardTranslations));
2114 XtAugmentTranslations(whiteTimerWidget,
2115 XtParseTranslationTable(whiteTranslations));
2116 XtAugmentTranslations(blackTimerWidget,
2117 XtParseTranslationTable(blackTranslations));
2119 /* Why is the following needed on some versions of X instead
2120 * of a translation? */
2121 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2122 (XtEventHandler) EventProc, NULL);
2124 XtAddEventHandler(formWidget, KeyPressMask, False,
2125 (XtEventHandler) MoveTypeInProc, NULL);
2126 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
2127 (XtEventHandler) EventProc, NULL);
2129 /* [AS] Restore layout */
2130 if( wpMoveHistory.visible ) {
2134 if( wpEvalGraph.visible )
2139 if( wpEngineOutput.visible ) {
2140 EngineOutputPopUp();
2145 if (errorExitStatus == -1) {
2146 if (appData.icsActive) {
2147 /* We now wait until we see "login:" from the ICS before
2148 sending the logon script (problems with timestamp otherwise) */
2149 /*ICSInitScript();*/
2150 if (appData.icsInputBox) ICSInputBoxPopUp();
2154 signal(SIGWINCH, TermSizeSigHandler);
2156 signal(SIGINT, IntSigHandler);
2157 signal(SIGTERM, IntSigHandler);
2158 if (*appData.cmailGameName != NULLCHAR) {
2159 signal(SIGUSR1, CmailSigHandler);
2163 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2165 // XtSetKeyboardFocus(shellWidget, formWidget);
2166 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2168 XtAppMainLoop(appContext);
2169 if (appData.debugMode) fclose(debugFP); // [DM] debug
2173 static Boolean noEcho;
2178 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2179 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2181 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2182 unlink(gameCopyFilename);
2183 unlink(gamePasteFilename);
2184 if(noEcho) EchoOn();
2188 TermSizeSigHandler (int sig)
2194 IntSigHandler (int sig)
2200 CmailSigHandler (int sig)
2205 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2207 /* Activate call-back function CmailSigHandlerCallBack() */
2208 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2210 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2214 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
2217 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2219 /**** end signal code ****/
2225 /* try to open the icsLogon script, either in the location given
2226 * or in the users HOME directory
2233 f = fopen(appData.icsLogon, "r");
2236 homedir = getenv("HOME");
2237 if (homedir != NULL)
2239 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2240 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2241 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2242 f = fopen(buf, "r");
2247 ProcessICSInitScript(f);
2249 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2262 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
2263 #define HISTORY_SIZE 64
2264 static char *history[HISTORY_SIZE];
2265 int histIn = 0, histP = 0;
2268 SaveInHistory (char *cmd)
2270 if (history[histIn] != NULL) {
2271 free(history[histIn]);
2272 history[histIn] = NULL;
2274 if (*cmd == NULLCHAR) return;
2275 history[histIn] = StrSave(cmd);
2276 histIn = (histIn + 1) % HISTORY_SIZE;
2277 if (history[histIn] != NULL) {
2278 free(history[histIn]);
2279 history[histIn] = NULL;
2285 PrevInHistory (char *cmd)
2288 if (histP == histIn) {
2289 if (history[histIn] != NULL) free(history[histIn]);
2290 history[histIn] = StrSave(cmd);
2292 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
2293 if (newhp == histIn || history[newhp] == NULL) return NULL;
2295 return history[histP];
2301 if (histP == histIn) return NULL;
2302 histP = (histP + 1) % HISTORY_SIZE;
2303 return history[histP];
2305 // end of borrowed code
2307 #define Abs(n) ((n)<0 ? -(n) : (n))
2311 InsertPxlSize (char *pattern, int targetPxlSize)
2313 char *base_fnt_lst, strInt[12], *p, *q;
2314 int alternatives, i, len, strIntLen;
2317 * Replace the "*" (if present) in the pixel-size slot of each
2318 * alternative with the targetPxlSize.
2322 while ((p = strchr(p, ',')) != NULL) {
2326 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
2327 strIntLen = strlen(strInt);
2328 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
2332 while (alternatives--) {
2333 char *comma = strchr(p, ',');
2334 for (i=0; i<14; i++) {
2335 char *hyphen = strchr(p, '-');
2337 if (comma && hyphen > comma) break;
2338 len = hyphen + 1 - p;
2339 if (i == 7 && *p == '*' && len == 2) {
2341 memcpy(q, strInt, strIntLen);
2351 len = comma + 1 - p;
2358 return base_fnt_lst;
2362 CreateFontSet (char *base_fnt_lst)
2365 char **missing_list;
2369 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
2370 &missing_list, &missing_count, &def_string);
2371 if (appData.debugMode) {
2373 XFontStruct **font_struct_list;
2374 char **font_name_list;
2375 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
2377 fprintf(debugFP, " got list %s, locale %s\n",
2378 XBaseFontNameListOfFontSet(fntSet),
2379 XLocaleOfFontSet(fntSet));
2380 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
2381 for (i = 0; i < count; i++) {
2382 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
2385 for (i = 0; i < missing_count; i++) {
2386 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
2389 if (fntSet == NULL) {
2390 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
2395 #else // not ENABLE_NLS
2397 * Find a font that matches "pattern" that is as close as
2398 * possible to the targetPxlSize. Prefer fonts that are k
2399 * pixels smaller to fonts that are k pixels larger. The
2400 * pattern must be in the X Consortium standard format,
2401 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2402 * The return value should be freed with XtFree when no
2406 FindFont (char *pattern, int targetPxlSize)
2408 char **fonts, *p, *best, *scalable, *scalableTail;
2409 int i, j, nfonts, minerr, err, pxlSize;
2411 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2413 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2414 programName, pattern);
2421 for (i=0; i<nfonts; i++) {
2424 if (*p != '-') continue;
2426 if (*p == NULLCHAR) break;
2427 if (*p++ == '-') j++;
2429 if (j < 7) continue;
2432 scalable = fonts[i];
2435 err = pxlSize - targetPxlSize;
2436 if (Abs(err) < Abs(minerr) ||
2437 (minerr > 0 && err < 0 && -err == minerr)) {
2443 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2444 /* If the error is too big and there is a scalable font,
2445 use the scalable font. */
2446 int headlen = scalableTail - scalable;
2447 p = (char *) XtMalloc(strlen(scalable) + 10);
2448 while (isdigit(*scalableTail)) scalableTail++;
2449 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2451 p = (char *) XtMalloc(strlen(best) + 2);
2452 safeStrCpy(p, best, strlen(best)+1 );
2454 if (appData.debugMode) {
2455 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2456 pattern, targetPxlSize, p);
2458 XFreeFontNames(fonts);
2465 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
2466 // must be called before all non-first callse to CreateGCs()
2467 XtReleaseGC(shellWidget, highlineGC);
2468 XtReleaseGC(shellWidget, lightSquareGC);
2469 XtReleaseGC(shellWidget, darkSquareGC);
2470 XtReleaseGC(shellWidget, lineGC);
2471 if (appData.monoMode) {
2472 if (DefaultDepth(xDisplay, xScreen) == 1) {
2473 XtReleaseGC(shellWidget, wbPieceGC);
2475 XtReleaseGC(shellWidget, bwPieceGC);
2478 XtReleaseGC(shellWidget, prelineGC);
2479 XtReleaseGC(shellWidget, jailSquareGC);
2480 XtReleaseGC(shellWidget, wdPieceGC);
2481 XtReleaseGC(shellWidget, wlPieceGC);
2482 XtReleaseGC(shellWidget, wjPieceGC);
2483 XtReleaseGC(shellWidget, bdPieceGC);
2484 XtReleaseGC(shellWidget, blPieceGC);
2485 XtReleaseGC(shellWidget, bjPieceGC);
2490 CreateGCs (int redo)
2492 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2493 | GCBackground | GCFunction | GCPlaneMask;
2494 XGCValues gc_values;
2497 gc_values.plane_mask = AllPlanes;
2498 gc_values.line_width = lineGap;
2499 gc_values.line_style = LineSolid;
2500 gc_values.function = GXcopy;
2503 DeleteGCs(); // called a second time; clean up old GCs first
2504 } else { // [HGM] grid and font GCs created on first call only
2505 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
2506 gc_values.background = XWhitePixel(xDisplay, xScreen);
2507 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
2508 XSetFont(xDisplay, coordGC, coordFontID);
2510 // [HGM] make font for holdings counts (white on black)
2511 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
2512 gc_values.background = XBlackPixel(xDisplay, xScreen);
2513 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
2514 XSetFont(xDisplay, countGC, countFontID);
2516 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
2517 gc_values.background = XBlackPixel(xDisplay, xScreen);
2518 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
2520 if (appData.monoMode) {
2521 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
2522 gc_values.background = XWhitePixel(xDisplay, xScreen);
2523 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
2525 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
2526 gc_values.background = XBlackPixel(xDisplay, xScreen);
2527 lightSquareGC = wbPieceGC
2528 = XtGetGC(shellWidget, value_mask, &gc_values);
2530 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
2531 gc_values.background = XWhitePixel(xDisplay, xScreen);
2532 darkSquareGC = bwPieceGC
2533 = XtGetGC(shellWidget, value_mask, &gc_values);
2535 if (DefaultDepth(xDisplay, xScreen) == 1) {
2536 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2537 gc_values.function = GXcopyInverted;
2538 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
2539 gc_values.function = GXcopy;
2540 if (XBlackPixel(xDisplay, xScreen) == 1) {
2541 bwPieceGC = darkSquareGC;
2542 wbPieceGC = copyInvertedGC;
2544 bwPieceGC = copyInvertedGC;
2545 wbPieceGC = lightSquareGC;
2549 gc_values.foreground = highlightSquareColor;
2550 gc_values.background = highlightSquareColor;
2551 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
2553 gc_values.foreground = premoveHighlightColor;
2554 gc_values.background = premoveHighlightColor;
2555 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
2557 gc_values.foreground = lightSquareColor;
2558 gc_values.background = darkSquareColor;
2559 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
2561 gc_values.foreground = darkSquareColor;
2562 gc_values.background = lightSquareColor;
2563 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
2565 gc_values.foreground = jailSquareColor;
2566 gc_values.background = jailSquareColor;
2567 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
2569 gc_values.foreground = whitePieceColor;
2570 gc_values.background = darkSquareColor;
2571 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2573 gc_values.foreground = whitePieceColor;
2574 gc_values.background = lightSquareColor;
2575 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2577 gc_values.foreground = whitePieceColor;
2578 gc_values.background = jailSquareColor;
2579 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2581 gc_values.foreground = blackPieceColor;
2582 gc_values.background = darkSquareColor;
2583 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2585 gc_values.foreground = blackPieceColor;
2586 gc_values.background = lightSquareColor;
2587 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2589 gc_values.foreground = blackPieceColor;
2590 gc_values.background = jailSquareColor;
2591 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2596 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
2604 fp = fopen(filename, "rb");
2606 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
2613 for (y=0; y<h; ++y) {
2614 for (x=0; x<h; ++x) {
2619 XPutPixel(xim, x, y, blackPieceColor);
2621 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2624 XPutPixel(xim, x, y, darkSquareColor);
2626 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2629 XPutPixel(xim, x, y, whitePieceColor);
2631 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2634 XPutPixel(xim, x, y, lightSquareColor);
2636 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2644 /* create Pixmap of piece */
2645 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2647 XPutImage(xDisplay, *dest, lightSquareGC, xim,
2650 /* create Pixmap of clipmask
2651 Note: We assume the white/black pieces have the same
2652 outline, so we make only 6 masks. This is okay
2653 since the XPM clipmask routines do the same. */
2655 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2657 XPutImage(xDisplay, temp, lightSquareGC, xmask,
2660 /* now create the 1-bit version */
2661 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2664 values.foreground = 1;
2665 values.background = 0;
2667 /* Don't use XtGetGC, not read only */
2668 maskGC = XCreateGC(xDisplay, *mask,
2669 GCForeground | GCBackground, &values);
2670 XCopyPlane(xDisplay, temp, *mask, maskGC,
2671 0, 0, squareSize, squareSize, 0, 0, 1);
2672 XFreePixmap(xDisplay, temp);
2677 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
2685 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
2690 /* The XSynchronize calls were copied from CreatePieces.
2691 Not sure if needed, but can't hurt */
2692 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2695 /* temp needed by loadXIM() */
2696 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2697 0, 0, ss, ss, AllPlanes, XYPixmap);
2699 if (strlen(appData.pixmapDirectory) == 0) {
2703 if (appData.monoMode) {
2704 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
2708 fprintf(stderr, _("\nLoading XIMs...\n"));
2710 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2711 fprintf(stderr, "%d", piece+1);
2712 for (kind=0; kind<4; kind++) {
2713 fprintf(stderr, ".");
2714 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2715 ExpandPathName(appData.pixmapDirectory),
2716 piece <= (int) WhiteKing ? "" : "w",
2717 pieceBitmapNames[piece],
2719 ximPieceBitmap[kind][piece] =
2720 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2721 0, 0, ss, ss, AllPlanes, XYPixmap);
2722 if (appData.debugMode)
2723 fprintf(stderr, _("(File:%s:) "), buf);
2724 loadXIM(ximPieceBitmap[kind][piece],
2726 &(xpmPieceBitmap2[kind][piece]),
2727 &(ximMaskPm2[piece]));
2728 if(piece <= (int)WhiteKing)
2729 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2731 fprintf(stderr," ");
2733 /* Load light and dark squares */
2734 /* If the LSQ and DSQ pieces don't exist, we will
2735 draw them with solid squares. */
2736 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2737 if (access(buf, 0) != 0) {
2741 fprintf(stderr, _("light square "));
2743 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2744 0, 0, ss, ss, AllPlanes, XYPixmap);
2745 if (appData.debugMode)
2746 fprintf(stderr, _("(File:%s:) "), buf);
2748 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2749 fprintf(stderr, _("dark square "));
2750 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2751 ExpandPathName(appData.pixmapDirectory), ss);
2752 if (appData.debugMode)
2753 fprintf(stderr, _("(File:%s:) "), buf);
2755 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2756 0, 0, ss, ss, AllPlanes, XYPixmap);
2757 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2758 xpmJailSquare = xpmLightSquare;
2760 fprintf(stderr, _("Done.\n"));
2762 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2765 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2769 CreateXPMBoard (char *s, int kind)
2773 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2774 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2775 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2781 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2782 // thisroutine has to be called t free the old piece pixmaps
2784 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2785 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2787 XFreePixmap(xDisplay, xpmLightSquare);
2788 XFreePixmap(xDisplay, xpmDarkSquare);
2797 u_int ss = squareSize;
2799 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2800 XpmColorSymbol symbols[4];
2801 static int redo = False;
2803 if(redo) FreeXPMPieces(); else redo = 1;
2805 /* The XSynchronize calls were copied from CreatePieces.
2806 Not sure if needed, but can't hurt */
2807 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2809 /* Setup translations so piece colors match square colors */
2810 symbols[0].name = "light_piece";
2811 symbols[0].value = appData.whitePieceColor;
2812 symbols[1].name = "dark_piece";
2813 symbols[1].value = appData.blackPieceColor;
2814 symbols[2].name = "light_square";
2815 symbols[2].value = appData.lightSquareColor;
2816 symbols[3].name = "dark_square";
2817 symbols[3].value = appData.darkSquareColor;
2819 attr.valuemask = XpmColorSymbols;
2820 attr.colorsymbols = symbols;
2821 attr.numsymbols = 4;
2823 if (appData.monoMode) {
2824 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2828 if (strlen(appData.pixmapDirectory) == 0) {
2829 XpmPieces* pieces = builtInXpms;
2832 while (pieces->size != squareSize && pieces->size) pieces++;
2833 if (!pieces->size) {
2834 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2837 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2838 for (kind=0; kind<4; kind++) {
2840 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2841 pieces->xpm[piece][kind],
2842 &(xpmPieceBitmap2[kind][piece]),
2843 NULL, &attr)) != 0) {
2844 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2848 if(piece <= (int) WhiteKing)
2849 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2853 xpmJailSquare = xpmLightSquare;
2857 fprintf(stderr, _("\nLoading XPMs...\n"));
2860 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2861 fprintf(stderr, "%d ", piece+1);
2862 for (kind=0; kind<4; kind++) {
2863 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2864 ExpandPathName(appData.pixmapDirectory),
2865 piece > (int) WhiteKing ? "w" : "",
2866 pieceBitmapNames[piece],
2868 if (appData.debugMode) {
2869 fprintf(stderr, _("(File:%s:) "), buf);
2871 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2872 &(xpmPieceBitmap2[kind][piece]),
2873 NULL, &attr)) != 0) {
2874 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2875 // [HGM] missing: read of unorthodox piece failed; substitute King.
2876 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2877 ExpandPathName(appData.pixmapDirectory),
2879 if (appData.debugMode) {
2880 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2882 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2883 &(xpmPieceBitmap2[kind][piece]),
2887 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2892 if(piece <= (int) WhiteKing)
2893 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2896 /* Load light and dark squares */
2897 /* If the LSQ and DSQ pieces don't exist, we will
2898 draw them with solid squares. */
2899 fprintf(stderr, _("light square "));
2900 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2901 if (access(buf, 0) != 0) {
2905 if (appData.debugMode)
2906 fprintf(stderr, _("(File:%s:) "), buf);
2908 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2909 &xpmLightSquare, NULL, &attr)) != 0) {
2910 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2913 fprintf(stderr, _("dark square "));
2914 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2915 ExpandPathName(appData.pixmapDirectory), ss);
2916 if (appData.debugMode) {
2917 fprintf(stderr, _("(File:%s:) "), buf);
2919 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2920 &xpmDarkSquare, NULL, &attr)) != 0) {
2921 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2925 xpmJailSquare = xpmLightSquare;
2926 fprintf(stderr, _("Done.\n"));
2928 oldVariant = -1; // kludge to force re-makig of animation masks
2929 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2932 #endif /* HAVE_LIBXPM */
2935 /* No built-in bitmaps */
2940 u_int ss = squareSize;
2942 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2945 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2946 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2947 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2948 pieceBitmapNames[piece],
2949 ss, kind == SOLID ? 's' : 'o');
2950 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2951 if(piece <= (int)WhiteKing)
2952 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2956 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2960 /* With built-in bitmaps */
2964 BuiltInBits* bib = builtInBits;
2967 u_int ss = squareSize;
2969 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2972 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2974 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2975 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2976 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2977 pieceBitmapNames[piece],
2978 ss, kind == SOLID ? 's' : 'o');
2979 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2980 bib->bits[kind][piece], ss, ss);
2981 if(piece <= (int)WhiteKing)
2982 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2986 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2992 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2997 char msg[MSG_SIZ], fullname[MSG_SIZ];
2999 if (*appData.bitmapDirectory != NULLCHAR) {
3000 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3001 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3002 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3003 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3004 &w, &h, pm, &x_hot, &y_hot);
3005 fprintf(stderr, "load %s\n", name);
3006 if (errcode != BitmapSuccess) {
3008 case BitmapOpenFailed:
3009 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3011 case BitmapFileInvalid:
3012 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3014 case BitmapNoMemory:
3015 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3019 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3023 fprintf(stderr, _("%s: %s...using built-in\n"),
3025 } else if (w != wreq || h != hreq) {
3027 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3028 programName, fullname, w, h, wreq, hreq);
3034 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3044 if (lineGap == 0) return;
3046 /* [HR] Split this into 2 loops for non-square boards. */
3048 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3049 gridSegments[i].x1 = 0;
3050 gridSegments[i].x2 =
3051 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3052 gridSegments[i].y1 = gridSegments[i].y2
3053 = lineGap / 2 + (i * (squareSize + lineGap));
3056 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3057 gridSegments[j + i].y1 = 0;
3058 gridSegments[j + i].y2 =
3059 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3060 gridSegments[j + i].x1 = gridSegments[j + i].x2
3061 = lineGap / 2 + (j * (squareSize + lineGap));
3065 int nrOfMenuItems = 7;
3066 Widget menuWidget[150];
3067 MenuListItem menuItemList[150] = {
3068 { "LoadNextGameProc", LoadNextGameProc },
3069 { "LoadPrevGameProc", LoadPrevGameProc },
3070 { "ReloadGameProc", ReloadGameProc },
3071 { "ReloadPositionProc", ReloadPositionProc },
3072 #ifndef OPTIONSDIALOG
3073 { "AlwaysQueenProc", AlwaysQueenProc },
3074 { "AnimateDraggingProc", AnimateDraggingProc },
3075 { "AnimateMovingProc", AnimateMovingProc },
3076 { "AutoflagProc", AutoflagProc },
3077 { "AutoflipProc", AutoflipProc },
3078 { "BlindfoldProc", BlindfoldProc },
3079 { "FlashMovesProc", FlashMovesProc },
3081 { "HighlightDraggingProc", HighlightDraggingProc },
3083 { "HighlightLastMoveProc", HighlightLastMoveProc },
3084 // { "IcsAlarmProc", IcsAlarmProc },
3085 { "MoveSoundProc", MoveSoundProc },
3086 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
3087 { "PopupExitMessageProc", PopupExitMessageProc },
3088 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
3089 // { "PremoveProc", PremoveProc },
3090 { "ShowCoordsProc", ShowCoordsProc },
3091 { "ShowThinkingProc", ShowThinkingProc },
3092 { "HideThinkingProc", HideThinkingProc },
3093 { "TestLegalityProc", TestLegalityProc },
3095 { "AboutGameProc", AboutGameEvent },
3096 { "DebugProc", DebugProc },
3097 { "NothingProc", NothingProc },
3102 MarkMenuItem (char *menuRef, int state)
3104 int nr = MenuToNumber(menuRef);
3107 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
3108 XtSetValues(menuWidget[nr], args, 1);
3113 EnableMenuItem (char *menuRef, int state)
3115 int nr = MenuToNumber(menuRef);
3116 if(nr >= 0) XtSetSensitive(menuWidget[nr], state);
3120 EnableButtonBar (int state)
3122 XtSetSensitive(buttonBarWidget, state);
3127 SetMenuEnables (Enables *enab)
3129 while (enab->name != NULL) {
3130 EnableMenuItem(enab->name, enab->value);
3136 Equal(char *p, char *s)
3137 { // compare strings skipping spaces in second
3139 if(*s == ' ') { s++; continue; }
3140 if(*s++ != *p++) return 0;
3146 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3147 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
3149 if(*nprms == 0) return;
3150 for(i=0; menuItemList[i].name; i++) {
3151 if(Equal(prms[0], menuItemList[i].name)) {
3152 (menuItemList[i].proc) ();
3159 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
3161 MenuProc *proc = (MenuProc *) addr;
3167 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
3169 RecentEngineEvent((int) (intptr_t) addr);
3172 // some stuff that must remain in front-end
3173 static Widget mainBar, currentMenu;
3174 static int wtot, nr = 0, widths[10];
3177 AppendMenuItem (char *text, char *name, MenuProc *action)
3184 XtSetArg(args[j], XtNleftMargin, 20); j++;
3185 XtSetArg(args[j], XtNrightMargin, 20); j++;
3187 if (strcmp(text, "----") == 0) {
3188 entry = XtCreateManagedWidget(text, smeLineObjectClass,
3189 currentMenu, args, j);
3191 XtSetArg(args[j], XtNlabel, XtNewString(_(text)));
3192 entry = XtCreateManagedWidget(name, smeBSBObjectClass,
3193 currentMenu, args, j+1);
3194 XtAddCallback(entry, XtNcallback,
3195 (XtCallbackProc) (strcmp(name, "recent") ? MenuBarSelect : MenuEngineSelect),
3197 menuWidget[nrOfMenuItems] = entry;
3202 CreateMenuButton (char *name, Menu *mb)
3203 { // create menu button on main bar, and shell for pull-down list
3209 XtSetArg(args[j], XtNmenuName, XtNewString(name)); j++;
3210 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3211 XtSetArg(args[j], XtNborderWidth, 0); j++;
3212 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3214 currentMenu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3217 XtSetArg(args[j], XtNwidth, &w); j++;
3218 XtGetValues(mb->subMenu, args, j);
3219 wtot += mb->textWidth = widths[nr++] = w;
3223 CreateMenuBar (Menu *mb, int boardWidth)
3227 char menuName[MSG_SIZ];
3231 // create bar itself
3233 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3234 XtSetArg(args[j], XtNvSpace, 0); j++;
3235 XtSetArg(args[j], XtNborderWidth, 0); j++;
3236 mainBar = XtCreateWidget("menuBar", boxWidgetClass,
3237 formWidget, args, j);
3239 CreateMainMenus(mb); // put menus in bar according to description in back-end
3241 // size buttons to make menu bar fit, clipping menu names where necessary
3242 while(wtot > boardWidth - 40) {
3244 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
3248 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
3250 XtSetArg(args[j], XtNwidth, widths[i]); j++;
3251 XtSetValues(ma[i].subMenu, args, j);
3258 CreateButtonBar (MenuItem *mi)
3261 Widget button, buttonBar;
3265 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3267 XtSetArg(args[j], XtNhSpace, 0); j++;
3269 XtSetArg(args[j], XtNborderWidth, 0); j++;
3270 XtSetArg(args[j], XtNvSpace, 0); j++;
3271 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3272 formWidget, args, j);
3274 while (mi->string != NULL) {
3277 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3278 XtSetArg(args[j], XtNborderWidth, 0); j++;
3280 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3281 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3282 buttonBar, args, j);
3283 XtAddCallback(button, XtNcallback,
3284 (XtCallbackProc) MenuBarSelect,
3285 (caddr_t) mi->proc);
3292 CreatePieceMenu (char *name, int color)
3297 ChessSquare selection;
3299 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3300 boardWidget, args, 0);
3302 for (i = 0; i < PIECE_MENU_SIZE; i++) {
3303 String item = pieceMenuStrings[color][i];
3305 if (strcmp(item, "----") == 0) {
3306 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3309 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3310 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3312 selection = pieceMenuTranslation[color][i];
3313 XtAddCallback(entry, XtNcallback,
3314 (XtCallbackProc) PieceMenuSelect,
3315 (caddr_t) selection);
3316 if (selection == WhitePawn || selection == BlackPawn) {
3317 XtSetArg(args[0], XtNpopupOnEntry, entry);
3318 XtSetValues(menu, args, 1);
3331 ChessSquare selection;
3333 whitePieceMenu = CreatePieceMenu("menuW", 0);
3334 blackPieceMenu = CreatePieceMenu("menuB", 1);
3336 if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
3337 XtRegisterGrabAction(PieceMenuPopup, True,
3338 (unsigned)(ButtonPressMask|ButtonReleaseMask),
3339 GrabModeAsync, GrabModeAsync);
3341 XtSetArg(args[0], XtNlabel, _("Drop"));
3342 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3343 boardWidget, args, 1);
3344 for (i = 0; i < DROP_MENU_SIZE; i++) {
3345 String item = dropMenuStrings[i];
3347 if (strcmp(item, "----") == 0) {
3348 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3351 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3352 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3354 selection = dropMenuTranslation[i];
3355 XtAddCallback(entry, XtNcallback,
3356 (XtCallbackProc) DropMenuSelect,
3357 (caddr_t) selection);
3371 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3372 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3373 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3374 dmEnables[i].piece);
3375 XtSetSensitive(entry, p != NULL || !appData.testLegality
3376 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3377 && !appData.icsActive));
3379 while (p && *p++ == dmEnables[i].piece) count++;
3380 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3382 XtSetArg(args[j], XtNlabel, label); j++;
3383 XtSetValues(entry, args, j);
3388 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
3390 String whichMenu; int menuNr = -2;
3391 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3392 if (event->type == ButtonRelease)
3393 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3394 else if (event->type == ButtonPress)
3395 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3397 case 0: whichMenu = params[0]; break;
3398 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3400 case -1: if (errorUp) ErrorPopDown();
3403 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3407 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3409 if (pmFromX < 0 || pmFromY < 0) return;
3410 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3414 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3416 if (pmFromX < 0 || pmFromY < 0) return;
3417 DropMenuEvent(piece, pmFromX, pmFromY);
3421 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3423 shiftKey = prms[0][0] & 1;
3428 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3430 shiftKey = prms[0][0] & 1;
3436 * If the user selects on a border boundary, return -1; if off the board,
3437 * return -2. Otherwise map the event coordinate to the square.
3440 EventToSquare (int x, int limit)
3447 if ((x % (squareSize + lineGap)) >= squareSize)
3449 x /= (squareSize + lineGap);
3456 do_flash_delay (unsigned long msec)
3462 drawHighlight (int file, int rank, GC gc)
3466 if (lineGap == 0) return;
3469 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
3470 (squareSize + lineGap);
3471 y = lineGap/2 + rank * (squareSize + lineGap);
3473 x = lineGap/2 + file * (squareSize + lineGap);
3474 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
3475 (squareSize + lineGap);
3478 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3479 squareSize+lineGap, squareSize+lineGap);
3482 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
3483 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
3486 SetHighlights (int fromX, int fromY, int toX, int toY)
3488 if (hi1X != fromX || hi1Y != fromY) {
3489 if (hi1X >= 0 && hi1Y >= 0) {
3490 drawHighlight(hi1X, hi1Y, lineGC);
3492 } // [HGM] first erase both, then draw new!
3494 if (hi2X != toX || hi2Y != toY) {
3495 if (hi2X >= 0 && hi2Y >= 0) {
3496 drawHighlight(hi2X, hi2Y, lineGC);
3499 if (hi1X != fromX || hi1Y != fromY) {
3500 if (fromX >= 0 && fromY >= 0) {
3501 drawHighlight(fromX, fromY, highlineGC);
3504 if (hi2X != toX || hi2Y != toY) {
3505 if (toX >= 0 && toY >= 0) {
3506 drawHighlight(toX, toY, highlineGC);
3510 if(toX<0) // clearing the highlights must have damaged arrow
3511 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y); // for now, redraw it (should really be cleared!)
3522 SetHighlights(-1, -1, -1, -1);
3527 SetPremoveHighlights (int fromX, int fromY, int toX, int toY)
3529 if (pm1X != fromX || pm1Y != fromY) {
3530 if (pm1X >= 0 && pm1Y >= 0) {
3531 drawHighlight(pm1X, pm1Y, lineGC);
3533 if (fromX >= 0 && fromY >= 0) {
3534 drawHighlight(fromX, fromY, prelineGC);
3537 if (pm2X != toX || pm2Y != toY) {
3538 if (pm2X >= 0 && pm2Y >= 0) {
3539 drawHighlight(pm2X, pm2Y, lineGC);
3541 if (toX >= 0 && toY >= 0) {
3542 drawHighlight(toX, toY, prelineGC);
3552 ClearPremoveHighlights ()
3554 SetPremoveHighlights(-1, -1, -1, -1);
3558 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
3560 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
3561 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
3563 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
3564 if(textureW[kind] < W*squareSize)
3565 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
3567 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
3568 if(textureH[kind] < H*squareSize)
3569 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
3571 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
3576 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
3577 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
3579 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
3580 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
3581 squareSize, squareSize, x*fac, y*fac);
3583 if (useImages && useImageSqs) {
3587 pm = xpmLightSquare;
3592 case 2: /* neutral */
3597 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3598 squareSize, squareSize, x*fac, y*fac);
3608 case 2: /* neutral */
3613 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
3618 I split out the routines to draw a piece so that I could
3619 make a generic flash routine.
3622 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3624 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3625 switch (square_color) {
3627 case 2: /* neutral */
3629 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3630 ? *pieceToOutline(piece)
3631 : *pieceToSolid(piece),
3632 dest, bwPieceGC, 0, 0,
3633 squareSize, squareSize, x, y);
3636 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3637 ? *pieceToSolid(piece)
3638 : *pieceToOutline(piece),
3639 dest, wbPieceGC, 0, 0,
3640 squareSize, squareSize, x, y);
3646 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3648 switch (square_color) {
3650 case 2: /* neutral */
3652 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3653 ? *pieceToOutline(piece)
3654 : *pieceToSolid(piece),
3655 dest, bwPieceGC, 0, 0,
3656 squareSize, squareSize, x, y, 1);
3659 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3660 ? *pieceToSolid(piece)
3661 : *pieceToOutline(piece),
3662 dest, wbPieceGC, 0, 0,
3663 squareSize, squareSize, x, y, 1);
3669 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3671 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
3672 switch (square_color) {
3674 XCopyPlane(xDisplay, *pieceToSolid(piece),
3675 dest, (int) piece < (int) BlackPawn
3676 ? wlPieceGC : blPieceGC, 0, 0,
3677 squareSize, squareSize, x, y, 1);
3680 XCopyPlane(xDisplay, *pieceToSolid(piece),
3681 dest, (int) piece < (int) BlackPawn
3682 ? wdPieceGC : bdPieceGC, 0, 0,
3683 squareSize, squareSize, x, y, 1);
3685 case 2: /* neutral */
3687 XCopyPlane(xDisplay, *pieceToSolid(piece),
3688 dest, (int) piece < (int) BlackPawn
3689 ? wjPieceGC : bjPieceGC, 0, 0,
3690 squareSize, squareSize, x, y, 1);
3696 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3698 int kind, p = piece;
3700 switch (square_color) {
3702 case 2: /* neutral */
3704 if ((int)piece < (int) BlackPawn) {
3712 if ((int)piece < (int) BlackPawn) {
3720 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
3721 if(useTexture & square_color+1) {
3722 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
3723 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
3724 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
3725 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
3726 XSetClipMask(xDisplay, wlPieceGC, None);
3727 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
3729 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3730 dest, wlPieceGC, 0, 0,
3731 squareSize, squareSize, x, y);
3734 typedef void (*DrawFunc)();
3739 if (appData.monoMode) {
3740 if (DefaultDepth(xDisplay, xScreen) == 1) {
3741 return monoDrawPiece_1bit;
3743 return monoDrawPiece;
3747 return colorDrawPieceImage;
3749 return colorDrawPiece;
3753 /* [HR] determine square color depending on chess variant. */
3755 SquareColor (int row, int column)
3759 if (gameInfo.variant == VariantXiangqi) {
3760 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
3762 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
3764 } else if (row <= 4) {
3770 square_color = ((column + row) % 2) == 1;
3773 /* [hgm] holdings: next line makes all holdings squares light */
3774 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
3776 return square_color;
3780 DrawSquare (int row, int column, ChessSquare piece, int do_flash)
3782 int square_color, x, y, direction, font_ascent, font_descent;
3785 XCharStruct overall;
3789 /* Calculate delay in milliseconds (2-delays per complete flash) */
3790 flash_delay = 500 / appData.flashRate;
3793 x = lineGap + ((BOARD_WIDTH-1)-column) *
3794 (squareSize + lineGap);
3795 y = lineGap + row * (squareSize + lineGap);
3797 x = lineGap + column * (squareSize + lineGap);
3798 y = lineGap + ((BOARD_HEIGHT-1)-row) *
3799 (squareSize + lineGap);
3802 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
3804 square_color = SquareColor(row, column);
3806 if ( // [HGM] holdings: blank out area between board and holdings
3807 column == BOARD_LEFT-1 || column == BOARD_RGHT
3808 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
3809 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
3810 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
3812 // [HGM] print piece counts next to holdings
3813 string[1] = NULLCHAR;
3814 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
3815 string[0] = '0' + piece;
3816 XTextExtents(countFontStruct, string, 1, &direction,
3817 &font_ascent, &font_descent, &overall);
3818 if (appData.monoMode) {
3819 XDrawImageString(xDisplay, xBoardWindow, countGC,
3820 x + squareSize - overall.width - 2,
3821 y + font_ascent + 1, string, 1);
3823 XDrawString(xDisplay, xBoardWindow, countGC,
3824 x + squareSize - overall.width - 2,
3825 y + font_ascent + 1, string, 1);
3828 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
3829 string[0] = '0' + piece;
3830 XTextExtents(countFontStruct, string, 1, &direction,
3831 &font_ascent, &font_descent, &overall);
3832 if (appData.monoMode) {
3833 XDrawImageString(xDisplay, xBoardWindow, countGC,
3834 x + 2, y + font_ascent + 1, string, 1);
3836 XDrawString(xDisplay, xBoardWindow, countGC,
3837 x + 2, y + font_ascent + 1, string, 1);
3841 if (piece == EmptySquare || appData.blindfold) {
3842 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
3844 drawfunc = ChooseDrawFunc();
3846 if (do_flash && appData.flashCount > 0) {
3847 for (i=0; i<appData.flashCount; ++i) {
3848 drawfunc(piece, square_color, x, y, xBoardWindow);
3849 XSync(xDisplay, False);
3850 do_flash_delay(flash_delay);
3852 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
3853 XSync(xDisplay, False);
3854 do_flash_delay(flash_delay);
3857 drawfunc(piece, square_color, x, y, xBoardWindow);
3861 string[1] = NULLCHAR;
3862 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
3863 && column >= BOARD_LEFT && column < BOARD_RGHT) {
3864 string[0] = 'a' + column - BOARD_LEFT;
3865 XTextExtents(coordFontStruct, string, 1, &direction,
3866 &font_ascent, &font_descent, &overall);
3867 if (appData.monoMode) {
3868 XDrawImageString(xDisplay, xBoardWindow, coordGC,
3869 x + squareSize - overall.width - 2,
3870 y + squareSize - font_descent - 1, string, 1);
3872 XDrawString(xDisplay, xBoardWindow, coordGC,
3873 x + squareSize - overall.width - 2,
3874 y + squareSize - font_descent - 1, string, 1);
3877 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
3878 string[0] = ONE + row;
3879 XTextExtents(coordFontStruct, string, 1, &direction,
3880 &font_ascent, &font_descent, &overall);
3881 if (appData.monoMode) {
3882 XDrawImageString(xDisplay, xBoardWindow, coordGC,
3883 x + 2, y + font_ascent + 1, string, 1);
3885 XDrawString(xDisplay, xBoardWindow, coordGC,
3886 x + 2, y + font_ascent + 1, string, 1);
3889 if(!partnerUp && marker[row][column]) {
3890 if(appData.monoMode) {
3891 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? darkSquareGC : lightSquareGC,
3892 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
3893 XDrawArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? lightSquareGC : darkSquareGC,
3894 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
3896 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
3897 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
3902 Fraction (int x, int start, int stop)
3904 double f = ((double) x - start)/(stop - start);
3905 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
3909 static WindowPlacement wpNew;
3912 CoDrag (Widget sh, WindowPlacement *wp)
3915 int j=0, touch=0, fudge = 2;
3916 GetActualPlacement(sh, wp);
3917 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
3918 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
3919 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
3920 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
3921 if(!touch ) return; // only windows that touch co-move
3922 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
3923 int heightInc = wpNew.height - wpMain.height;
3924 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3925 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3926 wp->y += fracTop * heightInc;
3927 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
3928 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
3929 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
3930 int widthInc = wpNew.width - wpMain.width;
3931 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3932 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3933 wp->y += fracLeft * widthInc;
3934 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
3935 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
3937 wp->x += wpNew.x - wpMain.x;
3938 wp->y += wpNew.y - wpMain.y;
3939 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
3940 if(touch == 3) wp->y += wpNew.height - wpMain.height;
3941 XtSetArg(args[j], XtNx, wp->x); j++;
3942 XtSetArg(args[j], XtNy, wp->y); j++;
3943 XtSetValues(sh, args, j);
3946 static XtIntervalId delayedDragID = 0;
3951 GetActualPlacement(shellWidget, &wpNew);
3952 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
3953 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
3954 return; // false alarm
3955 if(EngineOutputIsUp()) CoDrag(engineOutputShell, &wpEngineOutput);
3956 if(MoveHistoryIsUp()) CoDrag(shells[7], &wpMoveHistory);
3957 if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
3958 if(GameListIsUp()) CoDrag(gameListShell, &wpGameList);
3960 XDrawPosition(boardWidget, True, NULL);
3961 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
3968 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
3970 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
3973 /* Why is this needed on some versions of X? */
3975 EventProc (Widget widget, caddr_t unused, XEvent *event)
3977 if (!XtIsRealized(widget))
3979 switch (event->type) {
3980 case ConfigureNotify: // main window is being dragged: drag attached windows with it
3981 if(appData.useStickyWindows)
3982 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
3985 if (event->xexpose.count > 0) return; /* no clipping is done */
3986 XDrawPosition(widget, True, NULL);
3987 if(twoBoards) { // [HGM] dual: draw other board in other orientation
3988 flipView = !flipView; partnerUp = !partnerUp;
3989 XDrawPosition(widget, True, NULL);
3990 flipView = !flipView; partnerUp = !partnerUp;
3994 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4002 DrawPosition (int fullRedraw, Board board)
4004 XDrawPosition(boardWidget, fullRedraw, board);
4007 /* Returns 1 if there are "too many" differences between b1 and b2
4008 (i.e. more than 1 move was made) */
4010 too_many_diffs (Board b1, Board b2)
4015 for (i=0; i<BOARD_HEIGHT; ++i) {
4016 for (j=0; j<BOARD_WIDTH; ++j) {
4017 if (b1[i][j] != b2[i][j]) {
4018 if (++c > 4) /* Castling causes 4 diffs */
4026 /* Matrix describing castling maneuvers */
4027 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4028 static int castling_matrix[4][5] = {
4029 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4030 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4031 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4032 { 7, 7, 4, 5, 6 } /* 0-0, black */
4035 /* Checks whether castling occurred. If it did, *rrow and *rcol
4036 are set to the destination (row,col) of the rook that moved.
4038 Returns 1 if castling occurred, 0 if not.
4040 Note: Only handles a max of 1 castling move, so be sure
4041 to call too_many_diffs() first.
4044 check_castle_draw (Board newb, Board oldb, int *rrow, int *rcol)
4049 /* For each type of castling... */
4050 for (i=0; i<4; ++i) {
4051 r = castling_matrix[i];
4053 /* Check the 4 squares involved in the castling move */
4055 for (j=1; j<=4; ++j) {
4056 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4063 /* All 4 changed, so it must be a castling move */
4072 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4074 DrawSeekAxis (int x, int y, int xTo, int yTo)
4076 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4080 DrawSeekBackground (int left, int top, int right, int bottom)
4082 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4086 DrawSeekText (char *buf, int x, int y)
4088 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4092 DrawSeekDot (int x, int y, int colorNr)
4094 int square = colorNr & 0x80;
4097 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4099 XFillRectangle(xDisplay, xBoardWindow, color,
4100 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4102 XFillArc(xDisplay, xBoardWindow, color,
4103 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4106 static int damage[2][BOARD_RANKS][BOARD_FILES];
4109 * event handler for redrawing the board
4112 XDrawPosition (Widget w, int repaint, Board board)
4115 static int lastFlipView = 0;
4116 static int lastBoardValid[2] = {0, 0};
4117 static Board lastBoard[2];
4120 int nr = twoBoards*partnerUp;
4122 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4124 if (board == NULL) {
4125 if (!lastBoardValid[nr]) return;
4126 board = lastBoard[nr];
4128 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4129 MarkMenuItem("Flip View", flipView);
4133 * It would be simpler to clear the window with XClearWindow()
4134 * but this causes a very distracting flicker.
4137 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4139 if ( lineGap && IsDrawArrowEnabled())
4140 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4141 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4143 /* If too much changes (begin observing new game, etc.), don't
4145 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4147 /* Special check for castling so we don't flash both the king
4148 and the rook (just flash the king). */
4150 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4151 /* Draw rook with NO flashing. King will be drawn flashing later */
4152 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4153 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4157 /* First pass -- Draw (newly) empty squares and repair damage.
4158 This prevents you from having a piece show up twice while it
4159 is flashing on its new square */
4160 for (i = 0; i < BOARD_HEIGHT; i++)
4161 for (j = 0; j < BOARD_WIDTH; j++)
4162 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4163 || damage[nr][i][j]) {
4164 DrawSquare(i, j, board[i][j], 0);
4165 damage[nr][i][j] = False;
4168 /* Second pass -- Draw piece(s) in new position and flash them */
4169 for (i = 0; i < BOARD_HEIGHT; i++)
4170 for (j = 0; j < BOARD_WIDTH; j++)
4171 if (board[i][j] != lastBoard[nr][i][j]) {
4172 DrawSquare(i, j, board[i][j], do_flash);
4176 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4177 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4178 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4180 for (i = 0; i < BOARD_HEIGHT; i++)
4181 for (j = 0; j < BOARD_WIDTH; j++) {
4182 DrawSquare(i, j, board[i][j], 0);
4183 damage[nr][i][j] = False;
4187 CopyBoard(lastBoard[nr], board);
4188 lastBoardValid[nr] = 1;
4189 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4190 lastFlipView = flipView;
4192 /* Draw highlights */
4193 if (pm1X >= 0 && pm1Y >= 0) {
4194 drawHighlight(pm1X, pm1Y, prelineGC);
4196 if (pm2X >= 0 && pm2Y >= 0) {
4197 drawHighlight(pm2X, pm2Y, prelineGC);
4199 if (hi1X >= 0 && hi1Y >= 0) {
4200 drawHighlight(hi1X, hi1Y, highlineGC);
4202 if (hi2X >= 0 && hi2Y >= 0) {
4203 drawHighlight(hi2X, hi2Y, highlineGC);
4205 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4207 /* If piece being dragged around board, must redraw that too */
4210 XSync(xDisplay, False);
4215 * event handler for redrawing the board
4218 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4220 XDrawPosition(w, True, NULL);
4225 * event handler for parsing user moves
4227 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4228 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4229 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4230 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4231 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4232 // and at the end FinishMove() to perform the move after optional promotion popups.
4233 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4235 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4237 if (w != boardWidget || errorExitStatus != -1) return;
4238 if(nprms) shiftKey = !strcmp(prms[0], "1");
4241 if (event->type == ButtonPress) {
4242 XtPopdown(promotionShell);
4243 XtDestroyWidget(promotionShell);
4244 promotionUp = False;
4252 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4253 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4254 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4258 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
4260 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4261 DragPieceMove(event->xmotion.x, event->xmotion.y);
4265 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
4266 { // [HGM] pv: walk PV
4267 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4270 static int savedIndex; /* gross that this is global */
4273 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4276 XawTextPosition index, dummy;
4279 XawTextGetSelectionPos(w, &index, &dummy);
4280 XtSetArg(arg, XtNstring, &val);
4281 XtGetValues(w, &arg, 1);
4282 ReplaceComment(savedIndex, val);
4283 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4284 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4288 EditCommentPopUp (int index, char *title, char *text)
4291 if (text == NULL) text = "";
4292 NewCommentPopup(title, text, index);
4301 extern Option boxOptions[];
4311 edit = boxOptions[0].handle;
4313 XtSetArg(args[j], XtNstring, &val); j++;
4314 XtGetValues(edit, args, j);
4316 SendMultiLineToICS(val);
4317 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4318 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4322 ICSInputBoxPopDown ()
4328 CommentPopUp (char *title, char *text)
4330 savedIndex = currentMove; // [HGM] vari
4331 NewCommentPopup(title, text, currentMove);
4340 static char *openName;
4346 (void) (*fileProc)(openFP, 0, openName);
4350 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
4352 fileProc = proc; /* I can't see a way not */
4353 fileOpenMode = openMode; /* to use globals here */
4354 { // [HGM] use file-selector dialog stolen from Ghostview
4355 int index; // this is not supported yet
4356 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
4357 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
4358 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
4359 ScheduleDelayedEvent(&DelayedLoad, 50);
4366 if (!filenameUp) return;
4367 XtPopdown(fileNameShell);
4368 XtDestroyWidget(fileNameShell);
4374 FileNameCallback (Widget w, XtPointer client_data, XtPointer call_data)
4379 XtSetArg(args[0], XtNlabel, &name);
4380 XtGetValues(w, args, 1);
4382 if (strcmp(name, _("cancel")) == 0) {
4387 FileNameAction(w, NULL, NULL, NULL);
4391 FileNameAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4399 name = XawDialogGetValueString(w = XtParent(w));
4401 if ((name != NULL) && (*name != NULLCHAR)) {
4402 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
4403 XtPopdown(w = XtParent(XtParent(w)));
4407 p = strrchr(buf, ' ');
4414 fullname = ExpandPathName(buf);
4416 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
4419 f = fopen(fullname, fileOpenMode);
4421 DisplayError(_("Failed to open file"), errno);
4423 (void) (*fileProc)(f, index, buf);
4430 XtPopdown(w = XtParent(XtParent(w)));
4440 Widget dialog, layout;
4442 Dimension bw_width, pw_width;
4444 char *PromoChars = "wglcqrbnkac+=\0";
4447 XtSetArg(args[j], XtNwidth, &bw_width); j++;
4448 XtGetValues(boardWidget, args, j);
4451 XtSetArg(args[j], XtNresizable, True); j++;
4452 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
4454 XtCreatePopupShell("Promotion", transientShellWidgetClass,
4455 shellWidget, args, j);
4457 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
4458 layoutArgs, XtNumber(layoutArgs));
4461 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
4462 XtSetArg(args[j], XtNborderWidth, 0); j++;
4463 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
4466 if(gameInfo.variant != VariantShogi) {
4467 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
4468 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
4469 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
4470 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
4471 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
4473 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
4474 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
4475 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
4476 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
4478 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
4479 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
4480 gameInfo.variant == VariantGiveaway) {
4481 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
4483 if(gameInfo.variant == VariantCapablanca ||
4484 gameInfo.variant == VariantGothic ||
4485 gameInfo.variant == VariantCapaRandom) {
4486 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
4487 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
4489 } else // [HGM] shogi
4491 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
4492 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
4494 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
4496 XtRealizeWidget(promotionShell);
4497 CatchDeleteWindow(promotionShell, "PromotionPopDown");
4500 XtSetArg(args[j], XtNwidth, &pw_width); j++;
4501 XtGetValues(promotionShell, args, j);
4503 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
4504 lineGap + squareSize/3 +
4505 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
4506 0 : 6*(squareSize + lineGap)), &x, &y);
4509 XtSetArg(args[j], XtNx, x); j++;
4510 XtSetArg(args[j], XtNy, y); j++;
4511 XtSetValues(promotionShell, args, j);
4513 XtPopup(promotionShell, XtGrabNone);
4521 if (!promotionUp) return;
4522 XtPopdown(promotionShell);
4523 XtDestroyWidget(promotionShell);
4524 promotionUp = False;
4528 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
4530 int promoChar = * (const char *) client_data;
4534 if (fromX == -1) return;
4541 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
4543 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
4544 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
4550 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
4552 dialogError = errorUp = False;
4553 XtPopdown(w = XtParent(XtParent(XtParent(w))));
4555 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
4562 if (!errorUp) return;
4563 dialogError = errorUp = False;
4564 XtPopdown(errorShell);
4565 XtDestroyWidget(errorShell);
4566 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
4570 ErrorPopUp (char *title, char *label, int modal)
4573 Widget dialog, layout;
4577 Dimension bw_width, pw_width;
4578 Dimension pw_height;
4582 XtSetArg(args[i], XtNresizable, True); i++;
4583 XtSetArg(args[i], XtNtitle, title); i++;
4585 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
4586 shellUp[0] ? (dialogError = modal = TRUE, shells[0]) : shellWidget, args, i);
4588 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
4589 layoutArgs, XtNumber(layoutArgs));
4592 XtSetArg(args[i], XtNlabel, label); i++;
4593 XtSetArg(args[i], XtNborderWidth, 0); i++;
4594 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
4597 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
4599 XtRealizeWidget(errorShell);
4600 CatchDeleteWindow(errorShell, "ErrorPopDown");
4603 XtSetArg(args[i], XtNwidth, &bw_width); i++;
4604 XtGetValues(boardWidget, args, i);
4606 XtSetArg(args[i], XtNwidth, &pw_width); i++;
4607 XtSetArg(args[i], XtNheight, &pw_height); i++;
4608 XtGetValues(errorShell, args, i);
4611 /* This code seems to tickle an X bug if it is executed too soon
4612 after xboard starts up. The coordinates get transformed as if
4613 the main window was positioned at (0, 0).
4615 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
4616 0 - pw_height + squareSize / 3, &x, &y);
4618 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
4619 RootWindowOfScreen(XtScreen(boardWidget)),
4620 (bw_width - pw_width) / 2,
4621 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
4625 if (y < 0) y = 0; /*avoid positioning top offscreen*/
4628 XtSetArg(args[i], XtNx, x); i++;
4629 XtSetArg(args[i], XtNy, y); i++;
4630 XtSetValues(errorShell, args, i);
4633 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
4636 /* Disable all user input other than deleting the window */
4637 static int frozen = 0;
4643 /* Grab by a widget that doesn't accept input */
4644 XtAddGrab(messageWidget, TRUE, FALSE);
4648 /* Undo a FreezeUI */
4652 if (!frozen) return;
4653 XtRemoveGrab(messageWidget);
4661 static int oldPausing = FALSE;
4662 static GameMode oldmode = (GameMode) -1;
4665 if (!boardWidget || !XtIsRealized(boardWidget)) return;
4667 if (pausing != oldPausing) {
4668 oldPausing = pausing;
4669 MarkMenuItem("Pause", pausing);
4671 if (appData.showButtonBar) {
4672 /* Always toggle, don't set. Previous code messes up when
4673 invoked while the button is pressed, as releasing it
4674 toggles the state again. */
4677 XtSetArg(args[0], XtNbackground, &oldbg);
4678 XtSetArg(args[1], XtNforeground, &oldfg);
4679 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
4681 XtSetArg(args[0], XtNbackground, oldfg);
4682 XtSetArg(args[1], XtNforeground, oldbg);
4684 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
4688 wname = ModeToWidgetName(oldmode);
4689 if (wname != NULL) {
4690 MarkMenuItem(wname, False);
4692 wname = ModeToWidgetName(gameMode);
4693 if (wname != NULL) {
4694 MarkMenuItem(wname, True);
4697 MarkMenuItem("Machine Match", matchMode && matchGame < appData.matchGames);
4699 /* Maybe all the enables should be handled here, not just this one */
4700 EnableMenuItem("Training", gameMode == Training || gameMode == PlayFromGameFile);
4705 * Button/menu procedures
4708 LoadGamePopUp (FILE *f, int gameNumber, char *title)
4710 cmailMsgLoaded = FALSE;
4711 if (gameNumber == 0) {
4712 int error = GameListBuild(f);
4714 DisplayError(_("Cannot build game list"), error);
4715 } else if (!ListEmpty(&gameList) &&
4716 ((ListGame *) gameList.tailPred)->number > 1) {
4717 GameListPopUp(f, title);
4723 return LoadGame(f, gameNumber, title, FALSE);
4726 /* this variable is shared between CopyPositionProc and SendPositionSelection */
4727 char *selected_fen_position=NULL;
4730 SendPositionSelection (Widget w, Atom *selection, Atom *target,
4731 Atom *type_return, XtPointer *value_return,
4732 unsigned long *length_return, int *format_return)
4734 char *selection_tmp;
4736 if (!selected_fen_position) return False; /* should never happen */
4737 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4738 /* note: since no XtSelectionDoneProc was registered, Xt will
4739 * automatically call XtFree on the value returned. So have to
4740 * make a copy of it allocated with XtMalloc */
4741 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
4742 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
4744 *value_return=selection_tmp;
4745 *length_return=strlen(selection_tmp);
4746 *type_return=*target;
4747 *format_return = 8; /* bits per byte */
4749 } else if (*target == XA_TARGETS(xDisplay)) {
4750 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4751 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4752 targets_tmp[1] = XA_STRING;
4753 *value_return = targets_tmp;
4754 *type_return = XA_ATOM;
4757 // This code leads to a read of value_return out of bounds on 64-bit systems.
4758 // Other code which I have seen always sets *format_return to 32 independent of
4759 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
4760 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
4761 *format_return = 8 * sizeof(Atom);
4762 if (*format_return > 32) {
4763 *length_return *= *format_return / 32;
4764 *format_return = 32;
4767 *format_return = 32;
4775 /* note: when called from menu all parameters are NULL, so no clue what the
4776 * Widget which was clicked on was, or what the click event was
4782 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4783 * have a notion of a position that is selected but not copied.
4784 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4786 if(gameMode == EditPosition) EditPositionDone(TRUE);
4787 if (selected_fen_position) free(selected_fen_position);
4788 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
4789 if (!selected_fen_position) return;
4790 XtOwnSelection(menuBarWidget, XA_PRIMARY,
4792 SendPositionSelection,
4793 NULL/* lose_ownership_proc */ ,
4794 NULL/* transfer_done_proc */);
4795 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4797 SendPositionSelection,
4798 NULL/* lose_ownership_proc */ ,
4799 NULL/* transfer_done_proc */);
4802 /* function called when the data to Paste is ready */
4804 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
4805 Atom *type, XtPointer value, unsigned long *len, int *format)
4808 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
4809 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
4810 EditPositionPasteFEN(fenstr);
4814 /* called when Paste Position button is pressed,
4815 * all parameters will be NULL */
4817 PastePositionProc ()
4819 XtGetSelectionValue(menuBarWidget,
4820 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4821 /* (XtSelectionCallbackProc) */ PastePositionCB,
4822 NULL, /* client_data passed to PastePositionCB */
4824 /* better to use the time field from the event that triggered the
4825 * call to this function, but that isn't trivial to get
4833 SendGameSelection (Widget w, Atom *selection, Atom *target,
4834 Atom *type_return, XtPointer *value_return,
4835 unsigned long *length_return, int *format_return)
4837 char *selection_tmp;
4839 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4840 FILE* f = fopen(gameCopyFilename, "r");
4843 if (f == NULL) return False;
4847 selection_tmp = XtMalloc(len + 1);
4848 count = fread(selection_tmp, 1, len, f);
4851 XtFree(selection_tmp);
4854 selection_tmp[len] = NULLCHAR;
4855 *value_return = selection_tmp;
4856 *length_return = len;
4857 *type_return = *target;
4858 *format_return = 8; /* bits per byte */
4860 } else if (*target == XA_TARGETS(xDisplay)) {
4861 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4862 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4863 targets_tmp[1] = XA_STRING;
4864 *value_return = targets_tmp;
4865 *type_return = XA_ATOM;
4868 // This code leads to a read of value_return out of bounds on 64-bit systems.
4869 // Other code which I have seen always sets *format_return to 32 independent of
4870 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
4871 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
4872 *format_return = 8 * sizeof(Atom);
4873 if (*format_return > 32) {
4874 *length_return *= *format_return / 32;
4875 *format_return = 32;
4878 *format_return = 32;
4890 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4891 * have a notion of a game that is selected but not copied.
4892 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4894 XtOwnSelection(menuBarWidget, XA_PRIMARY,
4897 NULL/* lose_ownership_proc */ ,
4898 NULL/* transfer_done_proc */);
4899 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4902 NULL/* lose_ownership_proc */ ,
4903 NULL/* transfer_done_proc */);
4906 /* note: when called from menu all parameters are NULL, so no clue what the
4907 * Widget which was clicked on was, or what the click event was
4909 /* function called when the data to Paste is ready */
4911 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
4912 Atom *type, XtPointer value, unsigned long *len, int *format)
4915 if (value == NULL || *len == 0) {
4916 return; /* nothing had been selected to copy */
4918 f = fopen(gamePasteFilename, "w");
4920 DisplayError(_("Can't open temp file"), errno);
4923 fwrite(value, 1, *len, f);
4926 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
4929 /* called when Paste Game button is pressed,
4930 * all parameters will be NULL */
4934 XtGetSelectionValue(menuBarWidget,
4935 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4936 /* (XtSelectionCallbackProc) */ PasteGameCB,
4937 NULL, /* client_data passed to PasteGameCB */
4939 /* better to use the time field from the event that triggered the
4940 * call to this function, but that isn't trivial to get
4949 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4955 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4956 { // [HGM] input: let up-arrow recall previous line from history
4963 if (!shellUp[4]) return;
4964 edit = boxOptions[0].handle;
4966 XtSetArg(args[j], XtNstring, &val); j++;
4967 XtGetValues(edit, args, j);
4968 val = PrevInHistory(val);
4969 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4970 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4972 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
4973 XawTextReplace(edit, 0, 0, &t);
4974 XawTextSetInsertionPoint(edit, 9999);
4979 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4980 { // [HGM] input: let down-arrow recall next line from history
4985 if (!shellUp[4]) return;
4986 edit = boxOptions[0].handle;
4987 val = NextInHistory();
4988 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4989 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4991 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
4992 XawTextReplace(edit, 0, 0, &t);
4993 XawTextSetInsertionPoint(edit, 9999);
4998 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5000 if (shellUp[4] == True)
5005 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5007 if (!TempBackwardActive) {
5008 TempBackwardActive = True;
5014 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5016 /* Check to see if triggered by a key release event for a repeating key.
5017 * If so the next queued event will be a key press of the same key at the same time */
5018 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
5020 XPeekEvent(xDisplay, &next);
5021 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
5022 next.xkey.keycode == event->xkey.keycode)
5026 TempBackwardActive = False;
5030 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5031 { // called as key binding
5034 if (nprms && *nprms > 0)
5038 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
5043 DisplayMessage (char *message, char *extMessage)
5045 /* display a message in the message widget */
5054 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
5059 message = extMessage;
5063 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
5065 /* need to test if messageWidget already exists, since this function
5066 can also be called during the startup, if for example a Xresource
5067 is not set up correctly */
5070 XtSetArg(arg, XtNlabel, message);
5071 XtSetValues(messageWidget, &arg, 1);
5078 DisplayTitle (char *text)
5082 char title[MSG_SIZ];
5085 if (text == NULL) text = "";
5087 if (appData.titleInWindow) {
5089 XtSetArg(args[i], XtNlabel, text); i++;
5090 XtSetValues(titleWidget, args, i);
5093 if (*text != NULLCHAR) {
5094 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
5095 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
5096 } else if (appData.icsActive) {
5097 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
5098 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
5099 } else if (appData.cmailGameName[0] != NULLCHAR) {
5100 snprintf(icon, sizeof(icon), "%s", "CMail");
5101 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
5103 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
5104 } else if (gameInfo.variant == VariantGothic) {
5105 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
5106 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
5109 } else if (gameInfo.variant == VariantFalcon) {
5110 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
5111 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
5113 } else if (appData.noChessProgram) {
5114 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
5115 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
5117 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
5118 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
5121 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
5122 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
5123 XtSetValues(shellWidget, args, i);
5124 XSync(xDisplay, False);
5129 DisplayError (String message, int error)
5134 if (appData.debugMode || appData.matchMode) {
5135 fprintf(stderr, "%s: %s\n", programName, message);
5138 if (appData.debugMode || appData.matchMode) {
5139 fprintf(stderr, "%s: %s: %s\n",
5140 programName, message, strerror(error));
5142 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
5145 ErrorPopUp(_("Error"), message, FALSE);
5150 DisplayMoveError (String message)
5154 DrawPosition(FALSE, NULL);
5155 if (appData.debugMode || appData.matchMode) {
5156 fprintf(stderr, "%s: %s\n", programName, message);
5158 if (appData.popupMoveErrors) {
5159 ErrorPopUp(_("Error"), message, FALSE);
5161 DisplayMessage(message, "");
5167 DisplayFatalError (String message, int error, int status)
5171 errorExitStatus = status;
5173 fprintf(stderr, "%s: %s\n", programName, message);
5175 fprintf(stderr, "%s: %s: %s\n",
5176 programName, message, strerror(error));
5177 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
5180 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
5181 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
5188 DisplayInformation (String message)
5191 ErrorPopUp(_("Information"), message, TRUE);
5195 DisplayNote (String message)
5198 ErrorPopUp(_("Note"), message, FALSE);
5202 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
5208 DisplayIcsInteractionTitle (String message)
5210 if (oldICSInteractionTitle == NULL) {
5211 /* Magic to find the old window title, adapted from vim */
5212 char *wina = getenv("WINDOWID");
5214 Window win = (Window) atoi(wina);
5215 Window root, parent, *children;
5216 unsigned int nchildren;
5217 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
5219 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
5220 if (!XQueryTree(xDisplay, win, &root, &parent,
5221 &children, &nchildren)) break;
5222 if (children) XFree((void *)children);
5223 if (parent == root || parent == 0) break;
5226 XSetErrorHandler(oldHandler);
5228 if (oldICSInteractionTitle == NULL) {
5229 oldICSInteractionTitle = "xterm";
5232 printf("\033]0;%s\007", message);
5236 char pendingReplyPrefix[MSG_SIZ];
5237 ProcRef pendingReplyPR;
5240 AskQuestionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5243 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
5247 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
5251 AskQuestionPopDown ()
5253 if (!askQuestionUp) return;
5254 XtPopdown(askQuestionShell);
5255 XtDestroyWidget(askQuestionShell);
5256 askQuestionUp = False;
5260 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5266 reply = XawDialogGetValueString(w = XtParent(w));
5267 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
5268 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
5269 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
5270 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
5271 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
5272 AskQuestionPopDown();
5274 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
5278 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
5283 XtSetArg(args[0], XtNlabel, &name);
5284 XtGetValues(w, args, 1);
5286 if (strcmp(name, _("cancel")) == 0) {
5287 AskQuestionPopDown();
5289 AskQuestionReplyAction(w, NULL, NULL, NULL);
5294 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
5297 Widget popup, layout, dialog, edit;
5303 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
5304 pendingReplyPR = pr;
5307 XtSetArg(args[i], XtNresizable, True); i++;
5308 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
5309 askQuestionShell = popup =
5310 XtCreatePopupShell(title, transientShellWidgetClass,
5311 shellWidget, args, i);
5314 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
5315 layoutArgs, XtNumber(layoutArgs));
5318 XtSetArg(args[i], XtNlabel, question); i++;
5319 XtSetArg(args[i], XtNvalue, ""); i++;
5320 XtSetArg(args[i], XtNborderWidth, 0); i++;
5321 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
5324 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
5325 (XtPointer) dialog);
5326 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
5327 (XtPointer) dialog);
5329 XtRealizeWidget(popup);
5330 CatchDeleteWindow(popup, "AskQuestionPopDown");
5332 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
5333 &x, &y, &win_x, &win_y, &mask);
5335 XtSetArg(args[0], XtNx, x - 10);
5336 XtSetArg(args[1], XtNy, y - 30);
5337 XtSetValues(popup, args, 2);
5339 XtPopup(popup, XtGrabExclusive);
5340 askQuestionUp = True;
5342 edit = XtNameToWidget(dialog, "*value");
5343 XtSetKeyboardFocus(popup, edit);
5348 PlaySound (char *name)
5350 if (*name == NULLCHAR) {
5352 } else if (strcmp(name, "$") == 0) {
5353 putc(BELLCHAR, stderr);
5356 char *prefix = "", *sep = "";
5357 if(appData.soundProgram[0] == NULLCHAR) return;
5358 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
5359 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
5367 PlaySound(appData.soundMove);
5373 PlaySound(appData.soundIcsWin);
5379 PlaySound(appData.soundIcsLoss);
5385 PlaySound(appData.soundIcsDraw);
5389 PlayIcsUnfinishedSound ()
5391 PlaySound(appData.soundIcsUnfinished);
5397 PlaySound(appData.soundIcsAlarm);
5403 PlaySound(appData.soundTell);
5409 system("stty echo");
5416 system("stty -echo");
5421 RunCommand (char *buf)
5427 Colorize (ColorClass cc, int continuation)
5430 int count, outCount, error;
5432 if (textColors[(int)cc].bg > 0) {
5433 if (textColors[(int)cc].fg > 0) {
5434 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
5435 textColors[(int)cc].fg, textColors[(int)cc].bg);
5437 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
5438 textColors[(int)cc].bg);
5441 if (textColors[(int)cc].fg > 0) {
5442 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
5443 textColors[(int)cc].fg);
5445 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
5448 count = strlen(buf);
5449 outCount = OutputToProcess(NoProc, buf, count, &error);
5450 if (outCount < count) {
5451 DisplayFatalError(_("Error writing to display"), error, 1);
5454 if (continuation) return;
5457 PlaySound(appData.soundShout);
5460 PlaySound(appData.soundSShout);
5463 PlaySound(appData.soundChannel1);
5466 PlaySound(appData.soundChannel);
5469 PlaySound(appData.soundKibitz);
5472 PlaySound(appData.soundTell);
5474 case ColorChallenge:
5475 PlaySound(appData.soundChallenge);
5478 PlaySound(appData.soundRequest);
5481 PlaySound(appData.soundSeek);
5493 return getpwuid(getuid())->pw_name;
5497 ExpandPathName (char *path)
5499 static char static_buf[4*MSG_SIZ];
5500 char *d, *s, buf[4*MSG_SIZ];
5506 while (*s && isspace(*s))
5515 if (*(s+1) == '/') {
5516 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
5520 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
5521 { char *p; if(p = strchr(buf, '/')) *p = 0; }
5522 pwd = getpwnam(buf);
5525 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
5529 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
5530 strcat(d, strchr(s+1, '/'));
5534 safeStrCpy(d, s, 4*MSG_SIZ );
5542 static char host_name[MSG_SIZ];
5544 #if HAVE_GETHOSTNAME
5545 gethostname(host_name, MSG_SIZ);
5547 #else /* not HAVE_GETHOSTNAME */
5548 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
5549 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
5551 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
5553 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
5554 #endif /* not HAVE_GETHOSTNAME */
5557 XtIntervalId delayedEventTimerXID = 0;
5558 DelayedEventCallback delayedEventCallback = 0;
5563 delayedEventTimerXID = 0;
5564 delayedEventCallback();
5568 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
5570 if(delayedEventTimerXID && delayedEventCallback == cb)
5571 // [HGM] alive: replace, rather than add or flush identical event
5572 XtRemoveTimeOut(delayedEventTimerXID);
5573 delayedEventCallback = cb;
5574 delayedEventTimerXID =
5575 XtAppAddTimeOut(appContext, millisec,
5576 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
5579 DelayedEventCallback
5582 if (delayedEventTimerXID) {
5583 return delayedEventCallback;
5590 CancelDelayedEvent ()
5592 if (delayedEventTimerXID) {
5593 XtRemoveTimeOut(delayedEventTimerXID);
5594 delayedEventTimerXID = 0;
5598 XtIntervalId loadGameTimerXID = 0;
5601 LoadGameTimerRunning ()
5603 return loadGameTimerXID != 0;
5607 StopLoadGameTimer ()
5609 if (loadGameTimerXID != 0) {
5610 XtRemoveTimeOut(loadGameTimerXID);
5611 loadGameTimerXID = 0;
5619 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
5621 loadGameTimerXID = 0;
5626 StartLoadGameTimer (long millisec)
5629 XtAppAddTimeOut(appContext, millisec,
5630 (XtTimerCallbackProc) LoadGameTimerCallback,
5634 XtIntervalId analysisClockXID = 0;
5637 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
5639 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
5640 || appData.icsEngineAnalyze) { // [DM]
5641 AnalysisPeriodicEvent(0);
5642 StartAnalysisClock();
5647 StartAnalysisClock ()
5650 XtAppAddTimeOut(appContext, 2000,
5651 (XtTimerCallbackProc) AnalysisClockCallback,
5655 XtIntervalId clockTimerXID = 0;
5658 ClockTimerRunning ()
5660 return clockTimerXID != 0;
5666 if (clockTimerXID != 0) {
5667 XtRemoveTimeOut(clockTimerXID);
5676 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
5683 StartClockTimer (long millisec)
5686 XtAppAddTimeOut(appContext, millisec,
5687 (XtTimerCallbackProc) ClockTimerCallback,
5692 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
5697 /* check for low time warning */
5698 Pixel foregroundOrWarningColor = timerForegroundPixel;
5701 appData.lowTimeWarning &&
5702 (timer / 1000) < appData.icsAlarmTime)
5703 foregroundOrWarningColor = lowTimeWarningColor;
5705 if (appData.clockMode) {
5706 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
5707 XtSetArg(args[0], XtNlabel, buf);
5709 snprintf(buf, MSG_SIZ, "%s ", color);
5710 XtSetArg(args[0], XtNlabel, buf);
5715 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
5716 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
5718 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
5719 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
5722 XtSetValues(w, args, 3);
5726 DisplayWhiteClock (long timeRemaining, int highlight)
5730 if(appData.noGUI) return;
5731 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
5732 if (highlight && iconPixmap == bIconPixmap) {
5733 iconPixmap = wIconPixmap;
5734 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
5735 XtSetValues(shellWidget, args, 1);
5740 DisplayBlackClock (long timeRemaining, int highlight)
5744 if(appData.noGUI) return;
5745 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
5746 if (highlight && iconPixmap == wIconPixmap) {
5747 iconPixmap = bIconPixmap;
5748 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
5749 XtSetValues(shellWidget, args, 1);
5768 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
5772 int to_prog[2], from_prog[2];
5776 if (appData.debugMode) {
5777 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
5780 /* We do NOT feed the cmdLine to the shell; we just
5781 parse it into blank-separated arguments in the
5782 most simple-minded way possible.
5785 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
5788 while(*p == ' ') p++;
5790 if(*p == '"' || *p == '\'')
5791 p = strchr(++argv[i-1], *p);
5792 else p = strchr(p, ' ');
5793 if (p == NULL) break;
5798 SetUpChildIO(to_prog, from_prog);
5800 if ((pid = fork()) == 0) {
5802 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
5803 close(to_prog[1]); // first close the unused pipe ends
5804 close(from_prog[0]);
5805 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
5806 dup2(from_prog[1], 1);
5807 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
5808 close(from_prog[1]); // and closing again loses one of the pipes!
5809 if(fileno(stderr) >= 2) // better safe than sorry...
5810 dup2(1, fileno(stderr)); /* force stderr to the pipe */
5812 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
5817 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
5819 execvp(argv[0], argv);
5821 /* If we get here, exec failed */
5826 /* Parent process */
5828 close(from_prog[1]);
5830 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5833 cp->fdFrom = from_prog[0];
5834 cp->fdTo = to_prog[1];
5839 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
5841 AlarmCallBack (int n)
5847 DestroyChildProcess (ProcRef pr, int signalType)
5849 ChildProc *cp = (ChildProc *) pr;
5851 if (cp->kind != CPReal) return;
5853 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
5854 signal(SIGALRM, AlarmCallBack);
5856 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
5857 kill(cp->pid, SIGKILL); // kill it forcefully
5858 wait((int *) 0); // and wait again
5862 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
5864 /* Process is exiting either because of the kill or because of
5865 a quit command sent by the backend; either way, wait for it to die.
5874 InterruptChildProcess (ProcRef pr)
5876 ChildProc *cp = (ChildProc *) pr;
5878 if (cp->kind != CPReal) return;
5879 (void) kill(cp->pid, SIGINT); /* stop it thinking */
5883 OpenTelnet (char *host, char *port, ProcRef *pr)
5885 char cmdLine[MSG_SIZ];
5887 if (port[0] == NULLCHAR) {
5888 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
5890 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
5892 return StartChildProcess(cmdLine, "", pr);
5896 OpenTCP (char *host, char *port, ProcRef *pr)
5899 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
5900 #else /* !OMIT_SOCKETS */
5901 struct addrinfo hints;
5902 struct addrinfo *ais, *ai;
5907 memset(&hints, 0, sizeof(hints));
5908 hints.ai_family = AF_UNSPEC;
5909 hints.ai_socktype = SOCK_STREAM;
5911 error = getaddrinfo(host, port, &hints, &ais);
5913 /* a getaddrinfo error is not an errno, so can't return it */
5914 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
5915 host, port, gai_strerror(error));
5919 for (ai = ais; ai != NULL; ai = ai->ai_next) {
5920 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
5924 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
5937 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5943 #endif /* !OMIT_SOCKETS */
5949 OpenCommPort (char *name, ProcRef *pr)
5954 fd = open(name, 2, 0);
5955 if (fd < 0) return errno;
5957 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5968 OpenLoopback (ProcRef *pr)
5973 SetUpChildIO(to, from);
5975 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5978 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
5986 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
5988 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
5992 #define INPUT_SOURCE_BUF_SIZE 8192
6001 char buf[INPUT_SOURCE_BUF_SIZE];
6006 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
6008 InputSource *is = (InputSource *) closure;
6013 if (is->lineByLine) {
6014 count = read(is->fd, is->unused,
6015 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
6017 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
6020 is->unused += count;
6022 while (p < is->unused) {
6023 q = memchr(p, '\n', is->unused - p);
6024 if (q == NULL) break;
6026 (is->func)(is, is->closure, p, q - p, 0);
6030 while (p < is->unused) {
6035 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
6040 (is->func)(is, is->closure, is->buf, count, error);
6045 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
6048 ChildProc *cp = (ChildProc *) pr;
6050 is = (InputSource *) calloc(1, sizeof(InputSource));
6051 is->lineByLine = lineByLine;
6055 is->fd = fileno(stdin);
6057 is->kind = cp->kind;
6058 is->fd = cp->fdFrom;
6061 is->unused = is->buf;
6064 is->xid = XtAppAddInput(appContext, is->fd,
6065 (XtPointer) (XtInputReadMask),
6066 (XtInputCallbackProc) DoInputCallback,
6068 is->closure = closure;
6069 return (InputSourceRef) is;
6073 RemoveInputSource (InputSourceRef isr)
6075 InputSource *is = (InputSource *) isr;
6077 if (is->xid == 0) return;
6078 XtRemoveInput(is->xid);
6083 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
6085 static int line = 0;
6086 ChildProc *cp = (ChildProc *) pr;
6091 if (appData.noJoin || !appData.useInternalWrap)
6092 outCount = fwrite(message, 1, count, stdout);
6095 int width = get_term_width();
6096 int len = wrap(NULL, message, count, width, &line);
6097 char *msg = malloc(len);
6101 outCount = fwrite(message, 1, count, stdout);
6104 dbgchk = wrap(msg, message, count, width, &line);
6105 if (dbgchk != len && appData.debugMode)
6106 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
6107 outCount = fwrite(msg, 1, dbgchk, stdout);
6113 outCount = write(cp->fdTo, message, count);
6123 /* Output message to process, with "ms" milliseconds of delay
6124 between each character. This is needed when sending the logon
6125 script to ICC, which for some reason doesn't like the
6126 instantaneous send. */
6128 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
6130 ChildProc *cp = (ChildProc *) pr;
6135 r = write(cp->fdTo, message++, 1);
6148 /**** Animation code by Hugh Fisher, DCS, ANU.
6150 Known problem: if a window overlapping the board is
6151 moved away while a piece is being animated underneath,
6152 the newly exposed area won't be updated properly.
6153 I can live with this.
6155 Known problem: if you look carefully at the animation
6156 of pieces in mono mode, they are being drawn as solid
6157 shapes without interior detail while moving. Fixing
6158 this would be a major complication for minimal return.
6161 /* Masks for XPM pieces. Black and white pieces can have
6162 different shapes, but in the interest of retaining my
6163 sanity pieces must have the same outline on both light
6164 and dark squares, and all pieces must use the same
6165 background square colors/images. */
6167 static int xpmDone = 0;
6170 CreateAnimMasks (int pieceDepth)
6176 unsigned long plane;
6179 /* Need a bitmap just to get a GC with right depth */
6180 buf = XCreatePixmap(xDisplay, xBoardWindow,
6182 values.foreground = 1;
6183 values.background = 0;
6184 /* Don't use XtGetGC, not read only */
6185 maskGC = XCreateGC(xDisplay, buf,
6186 GCForeground | GCBackground, &values);
6187 XFreePixmap(xDisplay, buf);
6189 buf = XCreatePixmap(xDisplay, xBoardWindow,
6190 squareSize, squareSize, pieceDepth);
6191 values.foreground = XBlackPixel(xDisplay, xScreen);
6192 values.background = XWhitePixel(xDisplay, xScreen);
6193 bufGC = XCreateGC(xDisplay, buf,
6194 GCForeground | GCBackground, &values);
6196 for (piece = WhitePawn; piece <= BlackKing; piece++) {
6197 /* Begin with empty mask */
6198 if(!xpmDone) // [HGM] pieces: keep using existing
6199 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
6200 squareSize, squareSize, 1);
6201 XSetFunction(xDisplay, maskGC, GXclear);
6202 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
6203 0, 0, squareSize, squareSize);
6205 /* Take a copy of the piece */
6210 XSetFunction(xDisplay, bufGC, GXcopy);
6211 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
6213 0, 0, squareSize, squareSize, 0, 0);
6215 /* XOR the background (light) over the piece */
6216 XSetFunction(xDisplay, bufGC, GXxor);
6218 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
6219 0, 0, squareSize, squareSize, 0, 0);
6221 XSetForeground(xDisplay, bufGC, lightSquareColor);
6222 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
6225 /* We now have an inverted piece image with the background
6226 erased. Construct mask by just selecting all the non-zero
6227 pixels - no need to reconstruct the original image. */
6228 XSetFunction(xDisplay, maskGC, GXor);
6230 /* Might be quicker to download an XImage and create bitmap
6231 data from it rather than this N copies per piece, but it
6232 only takes a fraction of a second and there is a much
6233 longer delay for loading the pieces. */
6234 for (n = 0; n < pieceDepth; n ++) {
6235 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
6236 0, 0, squareSize, squareSize,
6242 XFreePixmap(xDisplay, buf);
6243 XFreeGC(xDisplay, bufGC);
6244 XFreeGC(xDisplay, maskGC);
6248 InitAnimState (AnimState *anim, XWindowAttributes *info)
6253 /* Each buffer is square size, same depth as window */
6254 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
6255 squareSize, squareSize, info->depth);
6256 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
6257 squareSize, squareSize, info->depth);
6259 /* Create a plain GC for blitting */
6260 mask = GCForeground | GCBackground | GCFunction |
6261 GCPlaneMask | GCGraphicsExposures;
6262 values.foreground = XBlackPixel(xDisplay, xScreen);
6263 values.background = XWhitePixel(xDisplay, xScreen);
6264 values.function = GXcopy;
6265 values.plane_mask = AllPlanes;
6266 values.graphics_exposures = False;
6267 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
6269 /* Piece will be copied from an existing context at
6270 the start of each new animation/drag. */
6271 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
6273 /* Outline will be a read-only copy of an existing */
6274 anim->outlineGC = None;
6280 XWindowAttributes info;
6282 if (xpmDone && gameInfo.variant == oldVariant) return;
6283 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
6284 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
6286 InitAnimState(&game, &info);
6287 InitAnimState(&player, &info);
6289 /* For XPM pieces, we need bitmaps to use as masks. */
6291 CreateAnimMasks(info.depth), xpmDone = 1;
6296 static Boolean frameWaiting;
6299 FrameAlarm (int sig)
6301 frameWaiting = False;
6302 /* In case System-V style signals. Needed?? */
6303 signal(SIGALRM, FrameAlarm);
6307 FrameDelay (int time)
6309 struct itimerval delay;
6311 XSync(xDisplay, False);
6314 frameWaiting = True;
6315 signal(SIGALRM, FrameAlarm);
6316 delay.it_interval.tv_sec =
6317 delay.it_value.tv_sec = time / 1000;
6318 delay.it_interval.tv_usec =
6319 delay.it_value.tv_usec = (time % 1000) * 1000;
6320 setitimer(ITIMER_REAL, &delay, NULL);
6321 while (frameWaiting) pause();
6322 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
6323 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
6324 setitimer(ITIMER_REAL, &delay, NULL);
6331 FrameDelay (int time)
6333 XSync(xDisplay, False);
6335 usleep(time * 1000);
6346 /* Convert board position to corner of screen rect and color */
6349 ScreenSquare (int column, int row, XPoint *pt, int *color)
6352 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
6353 pt->y = lineGap + row * (squareSize + lineGap);
6355 pt->x = lineGap + column * (squareSize + lineGap);
6356 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
6358 *color = SquareColor(row, column);
6361 /* Convert window coords to square */
6364 BoardSquare (int x, int y, int *column, int *row)
6366 *column = EventToSquare(x, BOARD_WIDTH);
6367 if (flipView && *column >= 0)
6368 *column = BOARD_WIDTH - 1 - *column;
6369 *row = EventToSquare(y, BOARD_HEIGHT);
6370 if (!flipView && *row >= 0)
6371 *row = BOARD_HEIGHT - 1 - *row;
6376 #undef Max /* just in case */
6378 #define Max(a, b) ((a) > (b) ? (a) : (b))
6379 #define Min(a, b) ((a) < (b) ? (a) : (b))
6382 SetRect (XRectangle *rect, int x, int y, int width, int height)
6386 rect->width = width;
6387 rect->height = height;
6390 /* Test if two frames overlap. If they do, return
6391 intersection rect within old and location of
6392 that rect within new. */
6395 Intersect ( XPoint *old, XPoint *new, int size, XRectangle *area, XPoint *pt)
6397 if (old->x > new->x + size || new->x > old->x + size ||
6398 old->y > new->y + size || new->y > old->y + size) {
6401 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
6402 size - abs(old->x - new->x), size - abs(old->y - new->y));
6403 pt->x = Max(old->x - new->x, 0);
6404 pt->y = Max(old->y - new->y, 0);
6409 /* For two overlapping frames, return the rect(s)
6410 in the old that do not intersect with the new. */
6413 CalcUpdateRects (XPoint *old, XPoint *new, int size, XRectangle update[], int *nUpdates)
6417 /* If old = new (shouldn't happen) then nothing to draw */
6418 if (old->x == new->x && old->y == new->y) {
6422 /* Work out what bits overlap. Since we know the rects
6423 are the same size we don't need a full intersect calc. */
6425 /* Top or bottom edge? */
6426 if (new->y > old->y) {
6427 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
6429 } else if (old->y > new->y) {
6430 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
6431 size, old->y - new->y);
6434 /* Left or right edge - don't overlap any update calculated above. */
6435 if (new->x > old->x) {
6436 SetRect(&(update[count]), old->x, Max(new->y, old->y),
6437 new->x - old->x, size - abs(new->y - old->y));
6439 } else if (old->x > new->x) {
6440 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
6441 old->x - new->x, size - abs(new->y - old->y));
6448 /* Generate a series of frame coords from start->mid->finish.
6449 The movement rate doubles until the half way point is
6450 reached, then halves back down to the final destination,
6451 which gives a nice slow in/out effect. The algorithmn
6452 may seem to generate too many intermediates for short
6453 moves, but remember that the purpose is to attract the
6454 viewers attention to the piece about to be moved and
6455 then to where it ends up. Too few frames would be less
6459 Tween (XPoint *start, XPoint *mid, XPoint *finish, int factor, XPoint frames[], int *nFrames)
6461 int fraction, n, count;
6465 /* Slow in, stepping 1/16th, then 1/8th, ... */
6467 for (n = 0; n < factor; n++)
6469 for (n = 0; n < factor; n++) {
6470 frames[count].x = start->x + (mid->x - start->x) / fraction;
6471 frames[count].y = start->y + (mid->y - start->y) / fraction;
6473 fraction = fraction / 2;
6477 frames[count] = *mid;
6480 /* Slow out, stepping 1/2, then 1/4, ... */
6482 for (n = 0; n < factor; n++) {
6483 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
6484 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
6486 fraction = fraction * 2;
6491 /* Draw a piece on the screen without disturbing what's there */
6494 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
6498 /* Bitmap for piece being moved. */
6499 if (appData.monoMode) {
6500 *mask = *pieceToSolid(piece);
6501 } else if (useImages) {
6503 *mask = xpmMask[piece];
6505 *mask = ximMaskPm[piece];
6508 *mask = *pieceToSolid(piece);
6511 /* GC for piece being moved. Square color doesn't matter, but
6512 since it gets modified we make a copy of the original. */
6514 if (appData.monoMode)
6519 if (appData.monoMode)
6524 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
6526 /* Outline only used in mono mode and is not modified */
6528 *outline = bwPieceGC;
6530 *outline = wbPieceGC;
6534 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
6539 /* Draw solid rectangle which will be clipped to shape of piece */
6540 XFillRectangle(xDisplay, dest, clip,
6541 0, 0, squareSize, squareSize);
6542 if (appData.monoMode)
6543 /* Also draw outline in contrasting color for black
6544 on black / white on white cases */
6545 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
6546 0, 0, squareSize, squareSize, 0, 0, 1);
6548 /* Copy the piece */
6553 if(appData.upsideDown && flipView) kind ^= 2;
6554 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
6556 0, 0, squareSize, squareSize,
6561 /* Animate the movement of a single piece */
6564 BeginAnimation (AnimState *anim, ChessSquare piece, int startColor, XPoint *start)
6568 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
6569 /* The old buffer is initialised with the start square (empty) */
6570 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
6571 anim->prevFrame = *start;
6573 /* The piece will be drawn using its own bitmap as a matte */
6574 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
6575 XSetClipMask(xDisplay, anim->pieceGC, mask);
6579 AnimationFrame (AnimState *anim, XPoint *frame, ChessSquare piece)
6581 XRectangle updates[4];
6586 /* Save what we are about to draw into the new buffer */
6587 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
6588 frame->x, frame->y, squareSize, squareSize,
6591 /* Erase bits of the previous frame */
6592 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
6593 /* Where the new frame overlapped the previous,
6594 the contents in newBuf are wrong. */
6595 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
6596 overlap.x, overlap.y,
6597 overlap.width, overlap.height,
6599 /* Repaint the areas in the old that don't overlap new */
6600 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
6601 for (i = 0; i < count; i++)
6602 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6603 updates[i].x - anim->prevFrame.x,
6604 updates[i].y - anim->prevFrame.y,
6605 updates[i].width, updates[i].height,
6606 updates[i].x, updates[i].y);
6608 /* Easy when no overlap */
6609 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6610 0, 0, squareSize, squareSize,
6611 anim->prevFrame.x, anim->prevFrame.y);
6614 /* Save this frame for next time round */
6615 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
6616 0, 0, squareSize, squareSize,
6618 anim->prevFrame = *frame;
6620 /* Draw piece over original screen contents, not current,
6621 and copy entire rect. Wipes out overlapping piece images. */
6622 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
6623 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
6624 0, 0, squareSize, squareSize,
6625 frame->x, frame->y);
6629 EndAnimation (AnimState *anim, XPoint *finish)
6631 XRectangle updates[4];
6636 /* The main code will redraw the final square, so we
6637 only need to erase the bits that don't overlap. */
6638 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
6639 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
6640 for (i = 0; i < count; i++)
6641 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6642 updates[i].x - anim->prevFrame.x,
6643 updates[i].y - anim->prevFrame.y,
6644 updates[i].width, updates[i].height,
6645 updates[i].x, updates[i].y);
6647 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6648 0, 0, squareSize, squareSize,
6649 anim->prevFrame.x, anim->prevFrame.y);
6654 FrameSequence (AnimState *anim, ChessSquare piece, int startColor, XPoint *start, XPoint *finish, XPoint frames[], int nFrames)
6658 BeginAnimation(anim, piece, startColor, start);
6659 for (n = 0; n < nFrames; n++) {
6660 AnimationFrame(anim, &(frames[n]), piece);
6661 FrameDelay(appData.animSpeed);
6663 EndAnimation(anim, finish);
6667 AnimateAtomicCapture (Board board, int fromX, int fromY, int toX, int toY)
6670 ChessSquare piece = board[fromY][toY];
6671 board[fromY][toY] = EmptySquare;
6672 DrawPosition(FALSE, board);
6674 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
6675 y = lineGap + toY * (squareSize + lineGap);
6677 x = lineGap + toX * (squareSize + lineGap);
6678 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
6680 for(i=1; i<4*kFactor; i++) {
6681 int r = squareSize * 9 * i/(20*kFactor - 5);
6682 XFillArc(xDisplay, xBoardWindow, highlineGC,
6683 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
6684 FrameDelay(appData.animSpeed);
6686 board[fromY][toY] = piece;
6689 /* Main control logic for deciding what to animate and how */
6692 AnimateMove (Board board, int fromX, int fromY, int toX, int toY)
6696 XPoint start, finish, mid;
6697 XPoint frames[kFactor * 2 + 1];
6698 int nFrames, startColor, endColor;
6700 /* Are we animating? */
6701 if (!appData.animate || appData.blindfold)
6704 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
6705 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
6706 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
6708 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
6709 piece = board[fromY][fromX];
6710 if (piece >= EmptySquare) return;
6715 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
6718 ScreenSquare(fromX, fromY, &start, &startColor);
6719 ScreenSquare(toX, toY, &finish, &endColor);
6722 /* Knight: make straight movement then diagonal */
6723 if (abs(toY - fromY) < abs(toX - fromX)) {
6724 mid.x = start.x + (finish.x - start.x) / 2;
6728 mid.y = start.y + (finish.y - start.y) / 2;
6731 mid.x = start.x + (finish.x - start.x) / 2;
6732 mid.y = start.y + (finish.y - start.y) / 2;
6735 /* Don't use as many frames for very short moves */
6736 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
6737 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
6739 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
6740 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
6741 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
6743 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
6744 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
6747 /* Be sure end square is redrawn */
6748 damage[0][toY][toX] = True;
6752 DragPieceBegin (int x, int y, Boolean instantly)
6754 int boardX, boardY, color;
6757 /* Are we animating? */
6758 if (!appData.animateDragging || appData.blindfold)
6761 /* Figure out which square we start in and the
6762 mouse position relative to top left corner. */
6763 BoardSquare(x, y, &boardX, &boardY);
6764 player.startBoardX = boardX;
6765 player.startBoardY = boardY;
6766 ScreenSquare(boardX, boardY, &corner, &color);
6767 player.startSquare = corner;
6768 player.startColor = color;
6769 /* As soon as we start dragging, the piece will jump slightly to
6770 be centered over the mouse pointer. */
6771 player.mouseDelta.x = squareSize/2;
6772 player.mouseDelta.y = squareSize/2;
6773 /* Initialise animation */
6774 player.dragPiece = PieceForSquare(boardX, boardY);
6776 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
6777 player.dragActive = True;
6778 BeginAnimation(&player, player.dragPiece, color, &corner);
6779 /* Mark this square as needing to be redrawn. Note that
6780 we don't remove the piece though, since logically (ie
6781 as seen by opponent) the move hasn't been made yet. */
6782 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
6783 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
6784 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
6785 corner.x, corner.y, squareSize, squareSize,
6786 0, 0); // [HGM] zh: unstack in stead of grab
6787 if(gatingPiece != EmptySquare) {
6788 /* Kludge alert: When gating we want the introduced
6789 piece to appear on the from square. To generate an
6790 image of it, we draw it on the board, copy the image,
6791 and draw the original piece again. */
6792 ChessSquare piece = boards[currentMove][boardY][boardX];
6793 DrawSquare(boardY, boardX, gatingPiece, 0);
6794 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
6795 corner.x, corner.y, squareSize, squareSize, 0, 0);
6796 DrawSquare(boardY, boardX, piece, 0);
6798 damage[0][boardY][boardX] = True;
6800 player.dragActive = False;
6805 ChangeDragPiece (ChessSquare piece)
6808 player.dragPiece = piece;
6809 /* The piece will be drawn using its own bitmap as a matte */
6810 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
6811 XSetClipMask(xDisplay, player.pieceGC, mask);
6815 DragPieceMove (int x, int y)
6819 /* Are we animating? */
6820 if (!appData.animateDragging || appData.blindfold)
6824 if (! player.dragActive)
6826 /* Move piece, maintaining same relative position
6827 of mouse within square */
6828 corner.x = x - player.mouseDelta.x;
6829 corner.y = y - player.mouseDelta.y;
6830 AnimationFrame(&player, &corner, player.dragPiece);
6832 if (appData.highlightDragging) {
6834 BoardSquare(x, y, &boardX, &boardY);
6835 SetHighlights(fromX, fromY, boardX, boardY);
6841 DragPieceEnd (int x, int y)
6843 int boardX, boardY, color;
6846 /* Are we animating? */
6847 if (!appData.animateDragging || appData.blindfold)
6851 if (! player.dragActive)
6853 /* Last frame in sequence is square piece is
6854 placed on, which may not match mouse exactly. */
6855 BoardSquare(x, y, &boardX, &boardY);
6856 ScreenSquare(boardX, boardY, &corner, &color);
6857 EndAnimation(&player, &corner);
6859 /* Be sure end square is redrawn */
6860 damage[0][boardY][boardX] = True;
6862 /* This prevents weird things happening with fast successive
6863 clicks which on my Sun at least can cause motion events
6864 without corresponding press/release. */
6865 player.dragActive = False;
6868 /* Handle expose event while piece being dragged */
6873 if (!player.dragActive || appData.blindfold)
6876 /* What we're doing: logically, the move hasn't been made yet,
6877 so the piece is still in it's original square. But visually
6878 it's being dragged around the board. So we erase the square
6879 that the piece is on and draw it at the last known drag point. */
6880 BlankSquare(player.startSquare.x, player.startSquare.y,
6881 player.startColor, EmptySquare, xBoardWindow, 1);
6882 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
6883 damage[0][player.startBoardY][player.startBoardX] = TRUE;
6886 #include <sys/ioctl.h>
6890 int fd, default_width;
6893 default_width = 79; // this is FICS default anyway...
6895 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
6897 if (!ioctl(fd, TIOCGSIZE, &win))
6898 default_width = win.ts_cols;
6899 #elif defined(TIOCGWINSZ)
6901 if (!ioctl(fd, TIOCGWINSZ, &win))
6902 default_width = win.ws_col;
6904 return default_width;
6910 static int old_width = 0;
6911 int new_width = get_term_width();
6913 if (old_width != new_width)
6914 ics_printf("set width %d\n", new_width);
6915 old_width = new_width;
6919 NotifyFrontendLogin ()
6924 /* [AS] Arrow highlighting support */
6926 static double A_WIDTH = 5; /* Width of arrow body */
6928 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
6929 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
6940 return (int) (x + 0.5);
6944 SquareToPos (int rank, int file, int *x, int *y)
6947 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
6948 *y = lineGap + rank * (squareSize + lineGap);
6950 *x = lineGap + file * (squareSize + lineGap);
6951 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
6955 /* Draw an arrow between two points using current settings */
6957 DrawArrowBetweenPoints (int s_x, int s_y, int d_x, int d_y)
6960 double dx, dy, j, k, x, y;
6963 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
6965 arrow[0].x = s_x + A_WIDTH + 0.5;
6968 arrow[1].x = s_x + A_WIDTH + 0.5;
6969 arrow[1].y = d_y - h;
6971 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
6972 arrow[2].y = d_y - h;
6977 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
6978 arrow[5].y = d_y - h;
6980 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
6981 arrow[4].y = d_y - h;
6983 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
6986 else if( d_y == s_y ) {
6987 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
6990 arrow[0].y = s_y + A_WIDTH + 0.5;
6992 arrow[1].x = d_x - w;
6993 arrow[1].y = s_y + A_WIDTH + 0.5;
6995 arrow[2].x = d_x - w;
6996 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
7001 arrow[5].x = d_x - w;
7002 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
7004 arrow[4].x = d_x - w;
7005 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
7008 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
7011 /* [AS] Needed a lot of paper for this! :-) */
7012 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
7013 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
7015 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
7017 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
7022 arrow[0].x = Round(x - j);
7023 arrow[0].y = Round(y + j*dx);
7025 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
7026 arrow[1].y = Round(arrow[0].y - 2*j*dx);
7029 x = (double) d_x - k;
7030 y = (double) d_y - k*dy;
7033 x = (double) d_x + k;
7034 y = (double) d_y + k*dy;
7037 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
7039 arrow[6].x = Round(x - j);
7040 arrow[6].y = Round(y + j*dx);
7042 arrow[2].x = Round(arrow[6].x + 2*j);
7043 arrow[2].y = Round(arrow[6].y - 2*j*dx);
7045 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
7046 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
7051 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
7052 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
7055 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
7056 if(appData.monoMode) arrow[7] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, arrow, 8, CoordModeOrigin);
7057 // Polygon( hdc, arrow, 7 );
7061 ArrowDamage (int s_col, int s_row, int d_col, int d_row)
7064 hor = 64*s_col + 32; vert = 64*s_row + 32;
7065 for(i=0; i<= 64; i++) {
7066 damage[0][vert+6>>6][hor+6>>6] = True;
7067 damage[0][vert-6>>6][hor+6>>6] = True;
7068 damage[0][vert+6>>6][hor-6>>6] = True;
7069 damage[0][vert-6>>6][hor-6>>6] = True;
7070 hor += d_col - s_col; vert += d_row - s_row;
7074 /* [AS] Draw an arrow between two squares */
7076 DrawArrowBetweenSquares (int s_col, int s_row, int d_col, int d_row)
7078 int s_x, s_y, d_x, d_y;
7080 if( s_col == d_col && s_row == d_row ) {
7084 /* Get source and destination points */
7085 SquareToPos( s_row, s_col, &s_x, &s_y);
7086 SquareToPos( d_row, d_col, &d_x, &d_y);
7089 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
7091 else if( d_y < s_y ) {
7092 d_y += squareSize / 2 + squareSize / 4;
7095 d_y += squareSize / 2;
7099 d_x += squareSize / 2 - squareSize / 4;
7101 else if( d_x < s_x ) {
7102 d_x += squareSize / 2 + squareSize / 4;
7105 d_x += squareSize / 2;
7108 s_x += squareSize / 2;
7109 s_y += squareSize / 2;
7112 A_WIDTH = squareSize / 14.; //[HGM] make float
7114 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
7115 ArrowDamage(s_col, s_row, d_col, d_row);
7119 IsDrawArrowEnabled ()
7121 return appData.highlightMoveWithArrow && squareSize >= 32;
7125 DrawArrowHighlight (int fromX, int fromY, int toX,int toY)
7127 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
7128 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
7132 UpdateLogos (int displ)
7134 return; // no logos in XBoard yet