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 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
312 void ErrorPopUp P((char *title, char *text, int modal));
313 void ErrorPopDown P((void));
314 static char *ExpandPathName P((char *path));
315 static void CreateAnimVars P((void));
316 static void DragPieceMove P((int x, int y));
317 static void DrawDragPiece P((void));
318 char *ModeToWidgetName P((GameMode mode));
319 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
320 void GameListOptionsPopDown P(());
321 void GenericPopDown P(());
322 void update_ics_width P(());
323 int get_term_width P(());
324 int CopyMemoProc P(());
325 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
326 Boolean IsDrawArrowEnabled P(());
329 * XBoard depends on Xt R4 or higher
331 int xtVersion = XtSpecificationRelease;
336 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
337 jailSquareColor, highlightSquareColor, premoveHighlightColor;
338 Pixel lowTimeWarningColor;
339 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
340 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
341 wjPieceGC, bjPieceGC, prelineGC, countGC;
342 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
343 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
344 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
345 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
346 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
347 ICSInputShell, fileNameShell, askQuestionShell;
348 Widget historyShell, evalGraphShell, gameListShell;
349 int hOffset; // [HGM] dual
350 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
351 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
352 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
354 XFontSet fontSet, clockFontSet;
357 XFontStruct *clockFontStruct;
359 Font coordFontID, countFontID;
360 XFontStruct *coordFontStruct, *countFontStruct;
361 XtAppContext appContext;
363 char *oldICSInteractionTitle;
367 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
369 Position commentX = -1, commentY = -1;
370 Dimension commentW, commentH;
371 typedef unsigned int BoardSize;
373 Boolean chessProgram;
375 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
376 int squareSize, smallLayout = 0, tinyLayout = 0,
377 marginW, marginH, // [HGM] for run-time resizing
378 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
379 ICSInputBoxUp = False, askQuestionUp = False,
380 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
381 errorUp = False, errorExitStatus = -1, lineGap, defaultLineGap;
382 Dimension textHeight;
383 Pixel timerForegroundPixel, timerBackgroundPixel;
384 Pixel buttonForegroundPixel, buttonBackgroundPixel;
385 char *chessDir, *programName, *programVersion,
386 *gameCopyFilename, *gamePasteFilename;
387 Boolean alwaysOnTop = False;
388 Boolean saveSettingsOnExit;
389 char *settingsFileName;
390 char *icsTextMenuString;
392 char *firstChessProgramNames;
393 char *secondChessProgramNames;
395 WindowPlacement wpMain;
396 WindowPlacement wpConsole;
397 WindowPlacement wpComment;
398 WindowPlacement wpMoveHistory;
399 WindowPlacement wpEvalGraph;
400 WindowPlacement wpEngineOutput;
401 WindowPlacement wpGameList;
402 WindowPlacement wpTags;
404 extern Widget shells[];
405 extern Boolean shellUp[];
409 Pixmap pieceBitmap[2][(int)BlackPawn];
410 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
411 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
412 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
413 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
414 Pixmap xpmBoardBitmap[2];
415 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
416 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
417 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
418 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
419 XImage *ximLightSquare, *ximDarkSquare;
422 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
423 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
425 #define White(piece) ((int)(piece) < (int)BlackPawn)
427 /* Variables for doing smooth animation. This whole thing
428 would be much easier if the board was double-buffered,
429 but that would require a fairly major rewrite. */
434 GC blitGC, pieceGC, outlineGC;
435 XPoint startSquare, prevFrame, mouseDelta;
439 int startBoardX, startBoardY;
442 /* There can be two pieces being animated at once: a player
443 can begin dragging a piece before the remote opponent has moved. */
445 static AnimState game, player;
447 /* Bitmaps for use as masks when drawing XPM pieces.
448 Need one for each black and white piece. */
449 static Pixmap xpmMask[BlackKing + 1];
451 /* This magic number is the number of intermediate frames used
452 in each half of the animation. For short moves it's reduced
453 by 1. The total number of frames will be factor * 2 + 1. */
456 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
458 #define PAUSE_BUTTON "P"
459 MenuItem buttonBar[] = {
460 {"<<", "<<", ToStartEvent},
461 {"<", "<", BackwardEvent},
462 {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseEvent},
463 {">", ">", ForwardEvent},
464 {">>", ">>", ToEndEvent},
468 #define PIECE_MENU_SIZE 18
469 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
470 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
471 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
472 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
473 N_("Empty square"), N_("Clear board") },
474 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
475 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
476 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
477 N_("Empty square"), N_("Clear board") }
479 /* must be in same order as pieceMenuStrings! */
480 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
481 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
482 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
483 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
484 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
485 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
486 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
487 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
488 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
491 #define DROP_MENU_SIZE 6
492 String dropMenuStrings[DROP_MENU_SIZE] = {
493 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
495 /* must be in same order as dropMenuStrings! */
496 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
497 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
498 WhiteRook, WhiteQueen
506 DropMenuEnables dmEnables[] = {
524 { XtNborderWidth, 0 },
525 { XtNdefaultDistance, 0 },
529 { XtNborderWidth, 0 },
530 { XtNresizable, (XtArgVal) True },
534 { XtNborderWidth, 0 },
540 { XtNjustify, (XtArgVal) XtJustifyRight },
541 { XtNlabel, (XtArgVal) "..." },
542 { XtNresizable, (XtArgVal) True },
543 { XtNresize, (XtArgVal) False }
546 Arg messageArgs[] = {
547 { XtNjustify, (XtArgVal) XtJustifyLeft },
548 { XtNlabel, (XtArgVal) "..." },
549 { XtNresizable, (XtArgVal) True },
550 { XtNresize, (XtArgVal) False }
554 { XtNborderWidth, 0 },
555 { XtNjustify, (XtArgVal) XtJustifyLeft }
558 XtResource clientResources[] = {
559 { "flashCount", "flashCount", XtRInt, sizeof(int),
560 XtOffset(AppDataPtr, flashCount), XtRImmediate,
561 (XtPointer) FLASH_COUNT },
564 XrmOptionDescRec shellOptions[] = {
565 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
566 { "-flash", "flashCount", XrmoptionNoArg, "3" },
567 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
570 XtActionsRec boardActions[] = {
571 { "DrawPosition", DrawPositionProc },
572 { "HandleUserMove", HandleUserMove },
573 { "AnimateUserMove", AnimateUserMove },
574 { "HandlePV", HandlePV },
575 { "SelectPV", SelectPV },
576 { "StopPV", StopPV },
577 { "FileNameAction", FileNameAction },
578 { "AskQuestionProc", AskQuestionProc },
579 { "AskQuestionReplyAction", AskQuestionReplyAction },
580 { "PieceMenuPopup", PieceMenuPopup },
581 { "WhiteClock", WhiteClock },
582 { "BlackClock", BlackClock },
583 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
584 { "QuitProc", QuitWrapper },
585 { "ManProc", ManInner },
586 { "TempBackwardProc", TempBackwardProc },
587 { "TempForwardProc", TempForwardProc },
588 { "CommentClick", (XtActionProc) CommentClick },
589 { "CommentPopDown", (XtActionProc) CommentPopDown },
590 { "TagsPopDown", (XtActionProc) TagsPopDown },
591 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
592 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
593 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
594 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
595 { "GameListPopDown", (XtActionProc) GameListPopDown },
596 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
597 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
598 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
599 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
600 { "GenericPopDown", (XtActionProc) GenericPopDown },
601 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
602 { "SelectMove", (XtActionProc) SelectMove },
603 { "LoadSelectedProc", LoadSelectedProc },
604 { "SetFilterProc", SetFilterProc },
605 { "TypeInProc", TypeInProc },
606 { "EnterKeyProc", EnterKeyProc },
607 { "UpKeyProc", UpKeyProc },
608 { "DownKeyProc", DownKeyProc },
611 char globalTranslations[] =
612 ":<Key>F9: MenuItem(ResignProc) \n \
613 :Ctrl<Key>n: MenuItem(NewGame) \n \
614 :Meta<Key>V: MenuItem(NewVariant) \n \
615 :Ctrl<Key>o: MenuItem(LoadGame) \n \
616 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
617 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
618 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
619 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
620 :Ctrl<Key>s: MenuItem(SaveGame) \n \
621 :Ctrl<Key>c: MenuItem(CopyGame) \n \
622 :Ctrl<Key>v: MenuItem(PasteGame) \n \
623 :Ctrl<Key>O: MenuItem(LoadPosition) \n \
624 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
625 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
626 :Ctrl<Key>S: MenuItem(SavePosition) \n \
627 :Ctrl<Key>C: MenuItem(CopyPosition) \n \
628 :Ctrl<Key>V: MenuItem(PastePosition) \n \
629 :Ctrl<Key>q: MenuItem(Exit) \n \
630 :Ctrl<Key>w: MenuItem(MachineWhite) \n \
631 :Ctrl<Key>b: MenuItem(MachineBlack) \n \
632 :Ctrl<Key>t: MenuItem(TwoMachines) \n \
633 :Ctrl<Key>a: MenuItem(AnalysisMode) \n \
634 :Ctrl<Key>g: MenuItem(AnalyzeFile) \n \
635 :Ctrl<Key>e: MenuItem(EditGame) \n \
636 :Ctrl<Key>E: MenuItem(EditPosition) \n \
637 :Meta<Key>O: MenuItem(ShowEngineOutput) \n \
638 :Meta<Key>E: MenuItem(ShowEvaluationGraph) \n \
639 :Meta<Key>G: MenuItem(ShowGameList) \n \
640 :Meta<Key>H: MenuItem(ShowMoveHistory) \n \
641 :<Key>Pause: MenuItem(Pause) \n \
642 :<Key>F3: MenuItem(Accept) \n \
643 :<Key>F4: MenuItem(Decline) \n \
644 :<Key>F12: MenuItem(Rematch) \n \
645 :<Key>F5: MenuItem(CallFlag) \n \
646 :<Key>F6: MenuItem(Draw) \n \
647 :<Key>F7: MenuItem(Adjourn) \n \
648 :<Key>F8: MenuItem(Abort) \n \
649 :<Key>F10: MenuItem(StopObserving) \n \
650 :<Key>F11: MenuItem(StopExamining) \n \
651 :Ctrl<Key>d: MenuItem(DebugProc) \n \
652 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
653 :Meta<Key>End: MenuItem(ToEnd) \n \
654 :Meta<Key>Right: MenuItem(Forward) \n \
655 :Meta<Key>Home: MenuItem(ToStart) \n \
656 :Meta<Key>Left: MenuItem(Backward) \n \
657 :<Key>Left: MenuItem(Backward) \n \
658 :<Key>Right: MenuItem(Forward) \n \
659 :<Key>Home: MenuItem(Revert) \n \
660 :<Key>End: MenuItem(TruncateGame) \n \
661 :Ctrl<Key>m: MenuItem(MoveNow) \n \
662 :Ctrl<Key>x: MenuItem(RetractMove) \n \
663 :Meta<Key>J: MenuItem(Adjudications) \n \
664 :Meta<Key>U: MenuItem(CommonEngine) \n \
665 :Meta<Key>T: MenuItem(TimeControl) \n \
666 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
667 #ifndef OPTIONSDIALOG
669 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
670 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
671 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
672 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
673 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
676 :<Key>F1: MenuItem(Manual) \n \
677 :<Key>F2: MenuItem(FlipView) \n \
678 :<KeyDown>Return: TempBackwardProc() \n \
679 :<KeyUp>Return: TempForwardProc() \n";
681 char boardTranslations[] =
682 "<Btn1Down>: HandleUserMove(0) \n \
683 Shift<Btn1Up>: HandleUserMove(1) \n \
684 <Btn1Up>: HandleUserMove(0) \n \
685 <Btn1Motion>: AnimateUserMove() \n \
686 <Btn3Motion>: HandlePV() \n \
687 <Btn2Motion>: HandlePV() \n \
688 <Btn3Up>: PieceMenuPopup(menuB) \n \
689 <Btn2Up>: PieceMenuPopup(menuB) \n \
690 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
691 PieceMenuPopup(menuB) \n \
692 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
693 PieceMenuPopup(menuW) \n \
694 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
695 PieceMenuPopup(menuW) \n \
696 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
697 PieceMenuPopup(menuB) \n";
699 char whiteTranslations[] =
700 "Shift<BtnDown>: WhiteClock(1)\n \
701 <BtnDown>: WhiteClock(0)\n";
702 char blackTranslations[] =
703 "Shift<BtnDown>: BlackClock(1)\n \
704 <BtnDown>: BlackClock(0)\n";
706 char ICSInputTranslations[] =
707 "<Key>Up: UpKeyProc() \n "
708 "<Key>Down: DownKeyProc() \n "
709 "<Key>Return: EnterKeyProc() \n";
711 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
712 // as the widget is destroyed before the up-click can call extend-end
713 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
715 String xboardResources[] = {
716 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
717 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
718 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
723 /* Max possible square size */
724 #define MAXSQSIZE 256
726 static int xpm_avail[MAXSQSIZE];
728 #ifdef HAVE_DIR_STRUCT
730 /* Extract piece size from filename */
732 xpm_getsize (char *name, int len, char *ext)
740 if ((p=strchr(name, '.')) == NULL ||
741 StrCaseCmp(p+1, ext) != 0)
747 while (*p && isdigit(*p))
754 /* Setup xpm_avail */
756 xpm_getavail (char *dirname, char *ext)
762 for (i=0; i<MAXSQSIZE; ++i)
765 if (appData.debugMode)
766 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
768 dir = opendir(dirname);
771 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
772 programName, dirname);
776 while ((ent=readdir(dir)) != NULL) {
777 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
778 if (i > 0 && i < MAXSQSIZE)
788 xpm_print_avail (FILE *fp, char *ext)
792 fprintf(fp, _("Available `%s' sizes:\n"), ext);
793 for (i=1; i<MAXSQSIZE; ++i) {
799 /* Return XPM piecesize closest to size */
801 xpm_closest_to (char *dirname, int size, char *ext)
804 int sm_diff = MAXSQSIZE;
808 xpm_getavail(dirname, ext);
810 if (appData.debugMode)
811 xpm_print_avail(stderr, ext);
813 for (i=1; i<MAXSQSIZE; ++i) {
816 diff = (diff<0) ? -diff : diff;
817 if (diff < sm_diff) {
825 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
831 #else /* !HAVE_DIR_STRUCT */
832 /* If we are on a system without a DIR struct, we can't
833 read the directory, so we can't collect a list of
834 filenames, etc., so we can't do any size-fitting. */
836 xpm_closest_to (char *dirname, int size, char *ext)
839 Warning: No DIR structure found on this system --\n\
840 Unable to autosize for XPM/XIM pieces.\n\
841 Please report this error to %s.\n\
842 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
845 #endif /* HAVE_DIR_STRUCT */
847 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
848 "magenta", "cyan", "white" };
852 TextColors textColors[(int)NColorClasses];
854 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
856 parse_color (char *str, int which)
858 char *p, buf[100], *d;
861 if (strlen(str) > 99) /* watch bounds on buf */
866 for (i=0; i<which; ++i) {
873 /* Could be looking at something like:
875 .. in which case we want to stop on a comma also */
876 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
880 return -1; /* Use default for empty field */
883 if (which == 2 || isdigit(*p))
886 while (*p && isalpha(*p))
891 for (i=0; i<8; ++i) {
892 if (!StrCaseCmp(buf, cnames[i]))
893 return which? (i+40) : (i+30);
895 if (!StrCaseCmp(buf, "default")) return -1;
897 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
902 parse_cpair (ColorClass cc, char *str)
904 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
905 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
910 /* bg and attr are optional */
911 textColors[(int)cc].bg = parse_color(str, 1);
912 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
913 textColors[(int)cc].attr = 0;
919 /* Arrange to catch delete-window events */
920 Atom wm_delete_window;
922 CatchDeleteWindow (Widget w, String procname)
925 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
926 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
927 XtAugmentTranslations(w, XtParseTranslationTable(buf));
934 XtSetArg(args[0], XtNiconic, False);
935 XtSetValues(shellWidget, args, 1);
937 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
940 //---------------------------------------------------------------------------------------------------------
941 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
944 #define CW_USEDEFAULT (1<<31)
945 #define ICS_TEXT_MENU_SIZE 90
946 #define DEBUG_FILE "xboard.debug"
947 #define SetCurrentDirectory chdir
948 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
952 // these two must some day move to frontend.h, when they are implemented
953 Boolean GameListIsUp();
955 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
958 // front-end part of option handling
960 // [HGM] This platform-dependent table provides the location for storing the color info
961 extern char *crWhite, * crBlack;
965 &appData.whitePieceColor,
966 &appData.blackPieceColor,
967 &appData.lightSquareColor,
968 &appData.darkSquareColor,
969 &appData.highlightSquareColor,
970 &appData.premoveHighlightColor,
971 &appData.lowTimeWarningColor,
982 // [HGM] font: keep a font for each square size, even non-stndard ones
985 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
986 char *fontTable[NUM_FONTS][MAX_SIZE];
989 ParseFont (char *name, int number)
990 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
992 if(sscanf(name, "size%d:", &size)) {
993 // [HGM] font: font is meant for specific boardSize (likely from settings file);
994 // defer processing it until we know if it matches our board size
995 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
996 fontTable[number][size] = strdup(strchr(name, ':')+1);
997 fontValid[number][size] = True;
1002 case 0: // CLOCK_FONT
1003 appData.clockFont = strdup(name);
1005 case 1: // MESSAGE_FONT
1006 appData.font = strdup(name);
1008 case 2: // COORD_FONT
1009 appData.coordFont = strdup(name);
1014 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1019 { // only 2 fonts currently
1020 appData.clockFont = CLOCK_FONT_NAME;
1021 appData.coordFont = COORD_FONT_NAME;
1022 appData.font = DEFAULT_FONT_NAME;
1027 { // no-op, until we identify the code for this already in XBoard and move it here
1031 ParseColor (int n, char *name)
1032 { // in XBoard, just copy the color-name string
1033 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1037 ParseTextAttribs (ColorClass cc, char *s)
1039 (&appData.colorShout)[cc] = strdup(s);
1043 ParseBoardSize (void *addr, char *name)
1045 appData.boardSize = strdup(name);
1050 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1054 SetCommPortDefaults ()
1055 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1058 // [HGM] args: these three cases taken out to stay in front-end
1060 SaveFontArg (FILE *f, ArgDescriptor *ad)
1063 int i, n = (int)(intptr_t)ad->argLoc;
1065 case 0: // CLOCK_FONT
1066 name = appData.clockFont;
1068 case 1: // MESSAGE_FONT
1069 name = appData.font;
1071 case 2: // COORD_FONT
1072 name = appData.coordFont;
1077 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1078 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1079 fontTable[n][squareSize] = strdup(name);
1080 fontValid[n][squareSize] = True;
1083 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1084 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1089 { // nothing to do, as the sounds are at all times represented by their text-string names already
1093 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
1094 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1095 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1099 SaveColor (FILE *f, ArgDescriptor *ad)
1100 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1101 if(colorVariable[(int)(intptr_t)ad->argLoc])
1102 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1106 SaveBoardSize (FILE *f, char *name, void *addr)
1107 { // wrapper to shield back-end from BoardSize & sizeInfo
1108 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1112 ParseCommPortSettings (char *s)
1113 { // no such option in XBoard (yet)
1116 extern Widget engineOutputShell;
1120 GetActualPlacement (Widget wg, WindowPlacement *wp)
1125 XWindowAttributes winAt;
1132 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
1133 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
1134 wp->x = rx - winAt.x;
1135 wp->y = ry - winAt.y;
1136 wp->height = winAt.height;
1137 wp->width = winAt.width;
1138 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
1143 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1144 // In XBoard this will have to wait until awareness of window parameters is implemented
1145 GetActualPlacement(shellWidget, &wpMain);
1146 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1147 if(MoveHistoryIsUp()) GetActualPlacement(shells[7], &wpMoveHistory);
1148 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1149 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1150 if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1151 if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1155 PrintCommPortSettings (FILE *f, char *name)
1156 { // This option does not exist in XBoard
1160 MySearchPath (char *installDir, char *name, char *fullname)
1161 { // just append installDir and name. Perhaps ExpandPath should be used here?
1162 name = ExpandPathName(name);
1163 if(name && name[0] == '/')
1164 safeStrCpy(fullname, name, MSG_SIZ );
1166 sprintf(fullname, "%s%c%s", installDir, '/', name);
1172 MyGetFullPathName (char *name, char *fullname)
1173 { // should use ExpandPath?
1174 name = ExpandPathName(name);
1175 safeStrCpy(fullname, name, MSG_SIZ );
1180 EnsureOnScreen (int *x, int *y, int minX, int minY)
1187 { // [HGM] args: allows testing if main window is realized from back-end
1188 return xBoardWindow != 0;
1192 PopUpStartupDialog ()
1193 { // start menu not implemented in XBoard
1197 ConvertToLine (int argc, char **argv)
1199 static char line[128*1024], buf[1024];
1203 for(i=1; i<argc; i++)
1205 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1206 && argv[i][0] != '{' )
1207 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1209 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1210 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1213 line[strlen(line)-1] = NULLCHAR;
1217 //--------------------------------------------------------------------------------------------
1219 extern Boolean twoBoards, partnerUp;
1222 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1224 #define BoardSize int
1226 InitDrawingSizes (BoardSize boardSize, int flags)
1227 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1228 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1230 XtGeometryResult gres;
1232 static Dimension oldWidth, oldHeight;
1233 static VariantClass oldVariant;
1234 static int oldDual = -1, oldMono = -1;
1236 if(!formWidget) return;
1238 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1239 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1240 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1242 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1244 * Enable shell resizing.
1246 shellArgs[0].value = (XtArgVal) &w;
1247 shellArgs[1].value = (XtArgVal) &h;
1248 XtGetValues(shellWidget, shellArgs, 2);
1250 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1251 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1252 XtSetValues(shellWidget, &shellArgs[2], 4);
1254 XtSetArg(args[0], XtNdefaultDistance, &sep);
1255 XtGetValues(formWidget, args, 1);
1257 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1259 hOffset = boardWidth + 10;
1260 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1261 secondSegments[i] = gridSegments[i];
1262 secondSegments[i].x1 += hOffset;
1263 secondSegments[i].x2 += hOffset;
1266 XtSetArg(args[0], XtNwidth, boardWidth);
1267 XtSetArg(args[1], XtNheight, boardHeight);
1268 XtSetValues(boardWidget, args, 2);
1270 timerWidth = (boardWidth - sep) / 2;
1271 XtSetArg(args[0], XtNwidth, timerWidth);
1272 XtSetValues(whiteTimerWidget, args, 1);
1273 XtSetValues(blackTimerWidget, args, 1);
1275 XawFormDoLayout(formWidget, False);
1277 if (appData.titleInWindow) {
1279 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1280 XtSetArg(args[i], XtNheight, &h); i++;
1281 XtGetValues(titleWidget, args, i);
1283 w = boardWidth - 2*bor;
1285 XtSetArg(args[0], XtNwidth, &w);
1286 XtGetValues(menuBarWidget, args, 1);
1287 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1290 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1291 if (gres != XtGeometryYes && appData.debugMode) {
1293 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1294 programName, gres, w, h, wr, hr);
1298 XawFormDoLayout(formWidget, True);
1301 * Inhibit shell resizing.
1303 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1304 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1305 shellArgs[4].value = shellArgs[2].value = w;
1306 shellArgs[5].value = shellArgs[3].value = h;
1307 XtSetValues(shellWidget, &shellArgs[0], 6);
1309 XSync(xDisplay, False);
1313 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1316 if(gameInfo.variant != oldVariant) { // and only if variant changed
1319 for(i=0; i<4; i++) {
1321 for(p=0; p<=(int)WhiteKing; p++)
1322 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1323 if(gameInfo.variant == VariantShogi) {
1324 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1325 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1326 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1327 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1328 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1331 if(gameInfo.variant == VariantGothic) {
1332 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1335 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1336 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1337 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1340 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1341 for(p=0; p<=(int)WhiteKing; p++)
1342 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1343 if(gameInfo.variant == VariantShogi) {
1344 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1345 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1346 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1347 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1348 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1351 if(gameInfo.variant == VariantGothic) {
1352 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1355 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1356 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1357 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1362 for(i=0; i<2; i++) {
1364 for(p=0; p<=(int)WhiteKing; p++)
1365 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1366 if(gameInfo.variant == VariantShogi) {
1367 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1368 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1369 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1370 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1371 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1374 if(gameInfo.variant == VariantGothic) {
1375 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1378 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1379 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1380 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1384 oldMono = -10; // kludge to force recreation of animation masks
1385 oldVariant = gameInfo.variant;
1388 if(appData.monoMode != oldMono)
1391 oldMono = appData.monoMode;
1396 ParseIcsTextColors ()
1397 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1398 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1399 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1400 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1401 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1402 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1403 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1404 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1405 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1406 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1407 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1409 if (appData.colorize) {
1411 _("%s: can't parse color names; disabling colorization\n"),
1414 appData.colorize = FALSE;
1420 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1421 XrmValue vFrom, vTo;
1422 int forceMono = False;
1424 if (!appData.monoMode) {
1425 vFrom.addr = (caddr_t) appData.lightSquareColor;
1426 vFrom.size = strlen(appData.lightSquareColor);
1427 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1428 if (vTo.addr == NULL) {
1429 appData.monoMode = True;
1432 lightSquareColor = *(Pixel *) vTo.addr;
1435 if (!appData.monoMode) {
1436 vFrom.addr = (caddr_t) appData.darkSquareColor;
1437 vFrom.size = strlen(appData.darkSquareColor);
1438 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1439 if (vTo.addr == NULL) {
1440 appData.monoMode = True;
1443 darkSquareColor = *(Pixel *) vTo.addr;
1446 if (!appData.monoMode) {
1447 vFrom.addr = (caddr_t) appData.whitePieceColor;
1448 vFrom.size = strlen(appData.whitePieceColor);
1449 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1450 if (vTo.addr == NULL) {
1451 appData.monoMode = True;
1454 whitePieceColor = *(Pixel *) vTo.addr;
1457 if (!appData.monoMode) {
1458 vFrom.addr = (caddr_t) appData.blackPieceColor;
1459 vFrom.size = strlen(appData.blackPieceColor);
1460 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1461 if (vTo.addr == NULL) {
1462 appData.monoMode = True;
1465 blackPieceColor = *(Pixel *) vTo.addr;
1469 if (!appData.monoMode) {
1470 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1471 vFrom.size = strlen(appData.highlightSquareColor);
1472 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1473 if (vTo.addr == NULL) {
1474 appData.monoMode = True;
1477 highlightSquareColor = *(Pixel *) vTo.addr;
1481 if (!appData.monoMode) {
1482 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1483 vFrom.size = strlen(appData.premoveHighlightColor);
1484 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1485 if (vTo.addr == NULL) {
1486 appData.monoMode = True;
1489 premoveHighlightColor = *(Pixel *) vTo.addr;
1497 { // [HGM] taken out of main
1499 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1500 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1501 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1503 if (appData.bitmapDirectory[0] != NULLCHAR) {
1507 CreateXPMBoard(appData.liteBackTextureFile, 1);
1508 CreateXPMBoard(appData.darkBackTextureFile, 0);
1512 /* Create regular pieces */
1513 if (!useImages) CreatePieces();
1518 main (int argc, char **argv)
1520 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1521 XSetWindowAttributes window_attributes;
1523 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1524 XrmValue vFrom, vTo;
1525 XtGeometryResult gres;
1528 int forceMono = False;
1530 srandom(time(0)); // [HGM] book: make random truly random
1532 setbuf(stdout, NULL);
1533 setbuf(stderr, NULL);
1536 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1537 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1541 programName = strrchr(argv[0], '/');
1542 if (programName == NULL)
1543 programName = argv[0];
1548 XtSetLanguageProc(NULL, NULL, NULL);
1549 bindtextdomain(PACKAGE, LOCALEDIR);
1550 textdomain(PACKAGE);
1554 XtAppInitialize(&appContext, "XBoard", shellOptions,
1555 XtNumber(shellOptions),
1556 &argc, argv, xboardResources, NULL, 0);
1557 appData.boardSize = "";
1558 InitAppData(ConvertToLine(argc, argv));
1560 if (p == NULL) p = "/tmp";
1561 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1562 gameCopyFilename = (char*) malloc(i);
1563 gamePasteFilename = (char*) malloc(i);
1564 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1565 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1567 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1568 clientResources, XtNumber(clientResources),
1571 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1572 static char buf[MSG_SIZ];
1573 EscapeExpand(buf, appData.firstInitString);
1574 appData.firstInitString = strdup(buf);
1575 EscapeExpand(buf, appData.secondInitString);
1576 appData.secondInitString = strdup(buf);
1577 EscapeExpand(buf, appData.firstComputerString);
1578 appData.firstComputerString = strdup(buf);
1579 EscapeExpand(buf, appData.secondComputerString);
1580 appData.secondComputerString = strdup(buf);
1583 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1586 if (chdir(chessDir) != 0) {
1587 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1593 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1594 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1595 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1596 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1599 setbuf(debugFP, NULL);
1603 if (appData.debugMode) {
1604 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1608 /* [HGM,HR] make sure board size is acceptable */
1609 if(appData.NrFiles > BOARD_FILES ||
1610 appData.NrRanks > BOARD_RANKS )
1611 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1614 /* This feature does not work; animation needs a rewrite */
1615 appData.highlightDragging = FALSE;
1619 xDisplay = XtDisplay(shellWidget);
1620 xScreen = DefaultScreen(xDisplay);
1621 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1623 gameInfo.variant = StringToVariant(appData.variant);
1624 InitPosition(FALSE);
1627 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1629 if (isdigit(appData.boardSize[0])) {
1630 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1631 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1632 &fontPxlSize, &smallLayout, &tinyLayout);
1634 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1635 programName, appData.boardSize);
1639 /* Find some defaults; use the nearest known size */
1640 SizeDefaults *szd, *nearest;
1641 int distance = 99999;
1642 nearest = szd = sizeDefaults;
1643 while (szd->name != NULL) {
1644 if (abs(szd->squareSize - squareSize) < distance) {
1646 distance = abs(szd->squareSize - squareSize);
1647 if (distance == 0) break;
1651 if (i < 2) lineGap = nearest->lineGap;
1652 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1653 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1654 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1655 if (i < 6) smallLayout = nearest->smallLayout;
1656 if (i < 7) tinyLayout = nearest->tinyLayout;
1659 SizeDefaults *szd = sizeDefaults;
1660 if (*appData.boardSize == NULLCHAR) {
1661 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1662 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1665 if (szd->name == NULL) szd--;
1666 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1668 while (szd->name != NULL &&
1669 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1670 if (szd->name == NULL) {
1671 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1672 programName, appData.boardSize);
1676 squareSize = szd->squareSize;
1677 lineGap = szd->lineGap;
1678 clockFontPxlSize = szd->clockFontPxlSize;
1679 coordFontPxlSize = szd->coordFontPxlSize;
1680 fontPxlSize = szd->fontPxlSize;
1681 smallLayout = szd->smallLayout;
1682 tinyLayout = szd->tinyLayout;
1683 // [HGM] font: use defaults from settings file if available and not overruled
1685 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1686 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1687 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1688 appData.font = fontTable[MESSAGE_FONT][squareSize];
1689 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1690 appData.coordFont = fontTable[COORD_FONT][squareSize];
1692 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1693 if (strlen(appData.pixmapDirectory) > 0) {
1694 p = ExpandPathName(appData.pixmapDirectory);
1696 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1697 appData.pixmapDirectory);
1700 if (appData.debugMode) {
1701 fprintf(stderr, _("\
1702 XBoard square size (hint): %d\n\
1703 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1705 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1706 if (appData.debugMode) {
1707 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1710 defaultLineGap = lineGap;
1711 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1713 /* [HR] height treated separately (hacked) */
1714 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1715 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1716 if (appData.showJail == 1) {
1717 /* Jail on top and bottom */
1718 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1719 XtSetArg(boardArgs[2], XtNheight,
1720 boardHeight + 2*(lineGap + squareSize));
1721 } else if (appData.showJail == 2) {
1723 XtSetArg(boardArgs[1], XtNwidth,
1724 boardWidth + 2*(lineGap + squareSize));
1725 XtSetArg(boardArgs[2], XtNheight, boardHeight);
1728 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1729 XtSetArg(boardArgs[2], XtNheight, boardHeight);
1733 * Determine what fonts to use.
1736 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1737 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1738 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1739 fontSet = CreateFontSet(appData.font);
1740 clockFontSet = CreateFontSet(appData.clockFont);
1742 /* For the coordFont, use the 0th font of the fontset. */
1743 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1744 XFontStruct **font_struct_list;
1745 XFontSetExtents *fontSize;
1746 char **font_name_list;
1747 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1748 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1749 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1750 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1751 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1754 appData.font = FindFont(appData.font, fontPxlSize);
1755 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1756 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1757 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1758 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1759 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1760 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1762 countFontID = coordFontID; // [HGM] holdings
1763 countFontStruct = coordFontStruct;
1765 xdb = XtDatabase(xDisplay);
1767 XrmPutLineResource(&xdb, "*international: True");
1768 vTo.size = sizeof(XFontSet);
1769 vTo.addr = (XtPointer) &fontSet;
1770 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1772 XrmPutStringResource(&xdb, "*font", appData.font);
1776 * Detect if there are not enough colors available and adapt.
1778 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1779 appData.monoMode = True;
1782 forceMono = MakeColors();
1785 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1787 appData.monoMode = True;
1790 if (appData.lowTimeWarning && !appData.monoMode) {
1791 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1792 vFrom.size = strlen(appData.lowTimeWarningColor);
1793 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1794 if (vTo.addr == NULL)
1795 appData.monoMode = True;
1797 lowTimeWarningColor = *(Pixel *) vTo.addr;
1800 if (appData.monoMode && appData.debugMode) {
1801 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1802 (unsigned long) XWhitePixel(xDisplay, xScreen),
1803 (unsigned long) XBlackPixel(xDisplay, xScreen));
1806 ParseIcsTextColors();
1807 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
1808 textColors[ColorNone].attr = 0;
1810 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1816 layoutName = "tinyLayout";
1817 } else if (smallLayout) {
1818 layoutName = "smallLayout";
1820 layoutName = "normalLayout";
1822 /* Outer layoutWidget is there only to provide a name for use in
1823 resources that depend on the layout style */
1825 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
1826 layoutArgs, XtNumber(layoutArgs));
1828 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
1829 formArgs, XtNumber(formArgs));
1830 XtSetArg(args[0], XtNdefaultDistance, &sep);
1831 XtGetValues(formWidget, args, 1);
1834 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
1835 XtSetArg(args[0], XtNtop, XtChainTop);
1836 XtSetArg(args[1], XtNbottom, XtChainTop);
1837 XtSetArg(args[2], XtNright, XtChainLeft);
1838 XtSetValues(menuBarWidget, args, 3);
1840 widgetList[j++] = whiteTimerWidget =
1841 XtCreateWidget("whiteTime", labelWidgetClass,
1842 formWidget, timerArgs, XtNumber(timerArgs));
1844 XtSetArg(args[0], XtNfontSet, clockFontSet);
1846 XtSetArg(args[0], XtNfont, clockFontStruct);
1848 XtSetArg(args[1], XtNtop, XtChainTop);
1849 XtSetArg(args[2], XtNbottom, XtChainTop);
1850 XtSetValues(whiteTimerWidget, args, 3);
1852 widgetList[j++] = blackTimerWidget =
1853 XtCreateWidget("blackTime", labelWidgetClass,
1854 formWidget, timerArgs, XtNumber(timerArgs));
1856 XtSetArg(args[0], XtNfontSet, clockFontSet);
1858 XtSetArg(args[0], XtNfont, clockFontStruct);
1860 XtSetArg(args[1], XtNtop, XtChainTop);
1861 XtSetArg(args[2], XtNbottom, XtChainTop);
1862 XtSetValues(blackTimerWidget, args, 3);
1864 if (appData.titleInWindow) {
1865 widgetList[j++] = titleWidget =
1866 XtCreateWidget("title", labelWidgetClass, formWidget,
1867 titleArgs, XtNumber(titleArgs));
1868 XtSetArg(args[0], XtNtop, XtChainTop);
1869 XtSetArg(args[1], XtNbottom, XtChainTop);
1870 XtSetValues(titleWidget, args, 2);
1873 if (appData.showButtonBar) {
1874 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
1875 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
1876 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
1877 XtSetArg(args[2], XtNtop, XtChainTop);
1878 XtSetArg(args[3], XtNbottom, XtChainTop);
1879 XtSetValues(buttonBarWidget, args, 4);
1882 widgetList[j++] = messageWidget =
1883 XtCreateWidget("message", labelWidgetClass, formWidget,
1884 messageArgs, XtNumber(messageArgs));
1885 XtSetArg(args[0], XtNtop, XtChainTop);
1886 XtSetArg(args[1], XtNbottom, XtChainTop);
1887 XtSetValues(messageWidget, args, 2);
1889 widgetList[j++] = boardWidget =
1890 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
1891 XtNumber(boardArgs));
1893 XtManageChildren(widgetList, j);
1895 timerWidth = (boardWidth - sep) / 2;
1896 XtSetArg(args[0], XtNwidth, timerWidth);
1897 XtSetValues(whiteTimerWidget, args, 1);
1898 XtSetValues(blackTimerWidget, args, 1);
1900 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1901 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1902 XtGetValues(whiteTimerWidget, args, 2);
1904 if (appData.showButtonBar) {
1905 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1906 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1907 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
1911 * formWidget uses these constraints but they are stored
1915 XtSetArg(args[i], XtNfromHoriz, 0); i++;
1916 XtSetValues(menuBarWidget, args, i);
1917 if (appData.titleInWindow) {
1920 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1921 XtSetValues(whiteTimerWidget, args, i);
1923 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1924 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1925 XtSetValues(blackTimerWidget, args, i);
1927 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1928 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
1929 XtSetValues(titleWidget, args, i);
1931 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1932 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1933 XtSetValues(messageWidget, args, i);
1934 if (appData.showButtonBar) {
1936 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1937 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1938 XtSetValues(buttonBarWidget, args, i);
1942 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1943 XtSetValues(whiteTimerWidget, args, i);
1945 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1946 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1947 XtSetValues(blackTimerWidget, args, i);
1949 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
1950 XtSetValues(titleWidget, args, i);
1952 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1953 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1954 XtSetValues(messageWidget, args, i);
1955 if (appData.showButtonBar) {
1957 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1958 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1959 XtSetValues(buttonBarWidget, args, i);
1964 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1965 XtSetValues(whiteTimerWidget, args, i);
1967 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1968 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1969 XtSetValues(blackTimerWidget, args, i);
1971 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1972 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1973 XtSetValues(messageWidget, args, i);
1974 if (appData.showButtonBar) {
1976 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1977 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1978 XtSetValues(buttonBarWidget, args, i);
1982 XtSetArg(args[0], XtNfromVert, messageWidget);
1983 XtSetArg(args[1], XtNtop, XtChainTop);
1984 XtSetArg(args[2], XtNbottom, XtChainBottom);
1985 XtSetArg(args[3], XtNleft, XtChainLeft);
1986 XtSetArg(args[4], XtNright, XtChainRight);
1987 XtSetValues(boardWidget, args, 5);
1989 XtRealizeWidget(shellWidget);
1992 XtSetArg(args[0], XtNx, wpMain.x);
1993 XtSetArg(args[1], XtNy, wpMain.y);
1994 XtSetValues(shellWidget, args, 2);
1998 * Correct the width of the message and title widgets.
1999 * It is not known why some systems need the extra fudge term.
2000 * The value "2" is probably larger than needed.
2002 XawFormDoLayout(formWidget, False);
2004 #define WIDTH_FUDGE 2
2006 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2007 XtSetArg(args[i], XtNheight, &h); i++;
2008 XtGetValues(messageWidget, args, i);
2009 if (appData.showButtonBar) {
2011 XtSetArg(args[i], XtNwidth, &w); i++;
2012 XtGetValues(buttonBarWidget, args, i);
2013 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2015 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2018 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2019 if (gres != XtGeometryYes && appData.debugMode) {
2020 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2021 programName, gres, w, h, wr, hr);
2024 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2025 /* The size used for the child widget in layout lags one resize behind
2026 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2028 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2029 if (gres != XtGeometryYes && appData.debugMode) {
2030 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2031 programName, gres, w, h, wr, hr);
2034 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
2035 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2036 XtSetArg(args[1], XtNright, XtChainRight);
2037 XtSetValues(messageWidget, args, 2);
2039 if (appData.titleInWindow) {
2041 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2042 XtSetArg(args[i], XtNheight, &h); i++;
2043 XtGetValues(titleWidget, args, i);
2045 w = boardWidth - 2*bor;
2047 XtSetArg(args[0], XtNwidth, &w);
2048 XtGetValues(menuBarWidget, args, 1);
2049 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2052 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2053 if (gres != XtGeometryYes && appData.debugMode) {
2055 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2056 programName, gres, w, h, wr, hr);
2059 XawFormDoLayout(formWidget, True);
2061 xBoardWindow = XtWindow(boardWidget);
2063 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2064 // not need to go into InitDrawingSizes().
2068 * Create X checkmark bitmap and initialize option menu checks.
2070 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2071 checkmark_bits, checkmark_width, checkmark_height);
2072 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2073 #ifndef OPTIONSDIALOG
2074 if (appData.alwaysPromoteToQueen) {
2075 XtSetValues(XtNameToWidget(menuBarWidget, "Always Queen"),
2078 if (appData.animateDragging) {
2079 XtSetValues(XtNameToWidget(menuBarWidget,
2080 "Animate Dragging"),
2083 if (appData.animate) {
2084 XtSetValues(XtNameToWidget(menuBarWidget, "Animate Moving"),
2087 if (appData.autoCallFlag) {
2088 XtSetValues(XtNameToWidget(menuBarWidget, "Auto Flag"),
2091 if (appData.autoFlipView) {
2092 XtSetValues(XtNameToWidget(menuBarWidget,"Auto Flip View"),
2095 if (appData.blindfold) {
2096 XtSetValues(XtNameToWidget(menuBarWidget,
2097 "Blindfold"), args, 1);
2099 if (appData.flashCount > 0) {
2100 XtSetValues(XtNameToWidget(menuBarWidget,
2105 if (appData.highlightDragging) {
2106 XtSetValues(XtNameToWidget(menuBarWidget,
2107 "Highlight Dragging"),
2111 if (appData.highlightLastMove) {
2112 XtSetValues(XtNameToWidget(menuBarWidget,
2113 "Highlight Last Move"),
2116 if (appData.highlightMoveWithArrow) {
2117 XtSetValues(XtNameToWidget(menuBarWidget,
2121 // if (appData.icsAlarm) {
2122 // XtSetValues(XtNameToWidget(menuBarWidget, "ICS Alarm"),
2125 if (appData.ringBellAfterMoves) {
2126 XtSetValues(XtNameToWidget(menuBarWidget, "Move Sound"),
2129 if (appData.oneClick) {
2130 XtSetValues(XtNameToWidget(menuBarWidget,
2131 "OneClick"), args, 1);
2133 if (appData.periodicUpdates) {
2134 XtSetValues(XtNameToWidget(menuBarWidget,
2135 "Periodic Updates"), args, 1);
2137 if (appData.ponderNextMove) {
2138 XtSetValues(XtNameToWidget(menuBarWidget,
2139 "Ponder Next Move"), args, 1);
2141 if (appData.popupExitMessage) {
2142 XtSetValues(XtNameToWidget(menuBarWidget,
2143 "Popup Exit Message"), args, 1);
2145 if (appData.popupMoveErrors) {
2146 XtSetValues(XtNameToWidget(menuBarWidget,
2147 "Popup Move Errors"), args, 1);
2149 // if (appData.premove) {
2150 // XtSetValues(XtNameToWidget(menuBarWidget,
2151 // "Premove"), args, 1);
2153 if (appData.showCoords) {
2154 XtSetValues(XtNameToWidget(menuBarWidget, "Show Coords"),
2157 if (appData.hideThinkingFromHuman) {
2158 XtSetValues(XtNameToWidget(menuBarWidget, "Hide Thinking"),
2161 if (appData.testLegality) {
2162 XtSetValues(XtNameToWidget(menuBarWidget,"Test Legality"),
2166 if (saveSettingsOnExit) {
2167 MarkMenuItem("Save Settings on Exit", True);
2173 ReadBitmap(&wIconPixmap, "icon_white.bm",
2174 icon_white_bits, icon_white_width, icon_white_height);
2175 ReadBitmap(&bIconPixmap, "icon_black.bm",
2176 icon_black_bits, icon_black_width, icon_black_height);
2177 iconPixmap = wIconPixmap;
2179 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2180 XtSetValues(shellWidget, args, i);
2183 * Create a cursor for the board widget.
2185 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2186 XChangeWindowAttributes(xDisplay, xBoardWindow,
2187 CWCursor, &window_attributes);
2190 * Inhibit shell resizing.
2192 shellArgs[0].value = (XtArgVal) &w;
2193 shellArgs[1].value = (XtArgVal) &h;
2194 XtGetValues(shellWidget, shellArgs, 2);
2195 shellArgs[4].value = shellArgs[2].value = w;
2196 shellArgs[5].value = shellArgs[3].value = h;
2197 XtSetValues(shellWidget, &shellArgs[2], 4);
2198 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2199 marginH = h - boardHeight;
2201 CatchDeleteWindow(shellWidget, "QuitProc");
2209 if (appData.animate || appData.animateDragging)
2212 XtAugmentTranslations(formWidget,
2213 XtParseTranslationTable(globalTranslations));
2214 XtAugmentTranslations(boardWidget,
2215 XtParseTranslationTable(boardTranslations));
2216 XtAugmentTranslations(whiteTimerWidget,
2217 XtParseTranslationTable(whiteTranslations));
2218 XtAugmentTranslations(blackTimerWidget,
2219 XtParseTranslationTable(blackTranslations));
2221 /* Why is the following needed on some versions of X instead
2222 * of a translation? */
2223 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2224 (XtEventHandler) EventProc, NULL);
2226 XtAddEventHandler(formWidget, KeyPressMask, False,
2227 (XtEventHandler) MoveTypeInProc, NULL);
2228 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
2229 (XtEventHandler) EventProc, NULL);
2231 /* [AS] Restore layout */
2232 if( wpMoveHistory.visible ) {
2236 if( wpEvalGraph.visible )
2241 if( wpEngineOutput.visible ) {
2242 EngineOutputPopUp();
2247 if (errorExitStatus == -1) {
2248 if (appData.icsActive) {
2249 /* We now wait until we see "login:" from the ICS before
2250 sending the logon script (problems with timestamp otherwise) */
2251 /*ICSInitScript();*/
2252 if (appData.icsInputBox) ICSInputBoxPopUp();
2256 signal(SIGWINCH, TermSizeSigHandler);
2258 signal(SIGINT, IntSigHandler);
2259 signal(SIGTERM, IntSigHandler);
2260 if (*appData.cmailGameName != NULLCHAR) {
2261 signal(SIGUSR1, CmailSigHandler);
2265 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2267 // XtSetKeyboardFocus(shellWidget, formWidget);
2268 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2270 XtAppMainLoop(appContext);
2271 if (appData.debugMode) fclose(debugFP); // [DM] debug
2275 static Boolean noEcho;
2280 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2281 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2283 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2284 unlink(gameCopyFilename);
2285 unlink(gamePasteFilename);
2286 if(noEcho) EchoOn();
2290 TermSizeSigHandler (int sig)
2296 IntSigHandler (int sig)
2302 CmailSigHandler (int sig)
2307 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2309 /* Activate call-back function CmailSigHandlerCallBack() */
2310 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2312 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2316 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
2319 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2321 /**** end signal code ****/
2327 /* try to open the icsLogon script, either in the location given
2328 * or in the users HOME directory
2335 f = fopen(appData.icsLogon, "r");
2338 homedir = getenv("HOME");
2339 if (homedir != NULL)
2341 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2342 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2343 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2344 f = fopen(buf, "r");
2349 ProcessICSInitScript(f);
2351 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2365 GreyRevert (Boolean grey)
2367 MarkMenuItem("Revert", !grey);
2368 MarkMenuItem("Annotate", !grey);
2371 Enables icsEnables[] = {
2372 { "Mail Move", False },
2373 { "Reload CMail Message", False },
2374 { "Machine Black", False },
2375 { "Machine White", False },
2376 { "Analysis Mode", False },
2377 { "Analyze File", False },
2378 { "Two Machines", False },
2379 { "Machine Match", False },
2383 { "Move Now", False },
2384 #ifndef OPTIONSDIALOG
2385 { "Periodic Updates", False },
2386 { "Hide Thinking", False },
2387 { "Ponder Next Move", False },
2390 { "Engine #1 Settings", False },
2391 { "Engine #2 Settings", False },
2392 { "Load Engine", False },
2393 { "Annotate", False },
2398 Enables ncpEnables[] = {
2399 { "Mail Move", False },
2400 { "Reload CMail Message", False },
2401 { "Machine White", False },
2402 { "Machine Black", False },
2403 { "Analysis Mode", False },
2404 { "Analyze File", False },
2405 { "Two Machines", False },
2406 { "Machine Match", False },
2407 { "ICS Client", False },
2408 { "ICStex", False },
2409 { "ICS Input Box", False },
2410 { "Action", False },
2411 { "Revert", False },
2412 { "Annotate", False },
2413 { "Engine #1 Settings", False },
2414 { "Engine #2 Settings", False },
2415 { "Move Now", False },
2416 { "Retract Move", False },
2418 #ifndef OPTIONSDIALOG
2419 { "Auto Flag", False },
2420 { "Auto Flip View", False },
2421 // { "ICS Alarm", False },
2422 { "Move Sound", False },
2423 { "Hide Thinking", False },
2424 { "Periodic Updates", False },
2425 { "Ponder Next Move", False },
2432 Enables gnuEnables[] = {
2433 { "ICS Client", False },
2434 { "ICStex", False },
2435 { "ICS Input Box", False },
2436 { "Accept", False },
2437 { "Decline", False },
2438 { "Rematch", False },
2439 { "Adjourn", False },
2440 { "Stop Examining", False },
2441 { "Stop Observing", False },
2442 { "Upload to Examine", False },
2443 { "Revert", False },
2444 { "Annotate", False },
2447 /* The next two options rely on SetCmailMode being called *after* */
2448 /* SetGNUMode so that when GNU is being used to give hints these */
2449 /* menu options are still available */
2451 { "Mail Move", False },
2452 { "Reload CMail Message", False },
2453 // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2454 { "Machine White", True },
2455 { "Machine Black", True },
2456 { "Analysis Mode", True },
2457 { "Analyze File", True },
2458 { "Two Machines", True },
2459 { "Machine Match", True },
2460 { "Engine #1 Settings", True },
2461 { "Engine #2 Settings", True },
2464 { "Move Now", True },
2465 { "Retract Move", True },
2470 Enables cmailEnables[] = {
2472 { "Call Flag", False },
2474 { "Adjourn", False },
2476 { "Stop Observing", False },
2477 { "Stop Examining", False },
2478 { "Mail Move", True },
2479 { "Reload CMail Message", True },
2483 Enables trainingOnEnables[] = {
2484 { "Edit Comment", False },
2486 { "Forward", False },
2487 { "Backward", False },
2488 { "Forward to End", False },
2489 { "Back to Start", False },
2490 { "Move Now", False },
2491 { "Truncate Game", False },
2495 Enables trainingOffEnables[] = {
2496 { "Edit Comment", True },
2498 { "Forward", True },
2499 { "Backward", True },
2500 { "Forward to End", True },
2501 { "Back to Start", True },
2502 { "Move Now", True },
2503 { "Truncate Game", True },
2507 Enables machineThinkingEnables[] = {
2508 { "Load Game", False },
2509 // { "Load Next Game", False },
2510 // { "Load Previous Game", False },
2511 // { "Reload Same Game", False },
2512 { "Paste Game", False },
2513 { "Load Position", False },
2514 // { "Load Next Position", False },
2515 // { "Load Previous Position", False },
2516 // { "Reload Same Position", False },
2517 { "Paste Position", False },
2518 { "Machine White", False },
2519 { "Machine Black", False },
2520 { "Two Machines", False },
2521 // { "Machine Match", False },
2522 { "Retract Move", False },
2526 Enables userThinkingEnables[] = {
2527 { "Load Game", True },
2528 // { "Load Next Game", True },
2529 // { "Load Previous Game", True },
2530 // { "Reload Same Game", True },
2531 { "Paste Game", True },
2532 { "Load Position", True },
2533 // { "Load Next Position", True },
2534 // { "Load Previous Position", True },
2535 // { "Reload Same Position", True },
2536 { "Paste Position", True },
2537 { "Machine White", True },
2538 { "Machine Black", True },
2539 { "Two Machines", True },
2540 // { "Machine Match", True },
2541 { "Retract Move", True },
2548 SetMenuEnables(icsEnables);
2551 if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
2552 EnableMenuItem("Analysis Mode", True);
2553 EnableMenuItem("Engine #1 Settings", True);
2561 SetMenuEnables(ncpEnables);
2567 SetMenuEnables(gnuEnables);
2573 SetMenuEnables(cmailEnables);
2577 SetTrainingModeOn ()
2579 SetMenuEnables(trainingOnEnables);
2580 if (appData.showButtonBar) {
2581 XtSetSensitive(buttonBarWidget, False);
2587 SetTrainingModeOff ()
2589 SetMenuEnables(trainingOffEnables);
2590 if (appData.showButtonBar) {
2591 XtSetSensitive(buttonBarWidget, True);
2596 SetUserThinkingEnables ()
2598 if (appData.noChessProgram) return;
2599 SetMenuEnables(userThinkingEnables);
2603 SetMachineThinkingEnables ()
2605 if (appData.noChessProgram) return;
2606 SetMenuEnables(machineThinkingEnables);
2608 case MachinePlaysBlack:
2609 case MachinePlaysWhite:
2610 case TwoMachinesPlay:
2611 EnableMenuItem(ModeToWidgetName(gameMode), True);
2618 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
2619 #define HISTORY_SIZE 64
2620 static char *history[HISTORY_SIZE];
2621 int histIn = 0, histP = 0;
2624 SaveInHistory (char *cmd)
2626 if (history[histIn] != NULL) {
2627 free(history[histIn]);
2628 history[histIn] = NULL;
2630 if (*cmd == NULLCHAR) return;
2631 history[histIn] = StrSave(cmd);
2632 histIn = (histIn + 1) % HISTORY_SIZE;
2633 if (history[histIn] != NULL) {
2634 free(history[histIn]);
2635 history[histIn] = NULL;
2641 PrevInHistory (char *cmd)
2644 if (histP == histIn) {
2645 if (history[histIn] != NULL) free(history[histIn]);
2646 history[histIn] = StrSave(cmd);
2648 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
2649 if (newhp == histIn || history[newhp] == NULL) return NULL;
2651 return history[histP];
2657 if (histP == histIn) return NULL;
2658 histP = (histP + 1) % HISTORY_SIZE;
2659 return history[histP];
2661 // end of borrowed code
2663 #define Abs(n) ((n)<0 ? -(n) : (n))
2667 InsertPxlSize (char *pattern, int targetPxlSize)
2669 char *base_fnt_lst, strInt[12], *p, *q;
2670 int alternatives, i, len, strIntLen;
2673 * Replace the "*" (if present) in the pixel-size slot of each
2674 * alternative with the targetPxlSize.
2678 while ((p = strchr(p, ',')) != NULL) {
2682 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
2683 strIntLen = strlen(strInt);
2684 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
2688 while (alternatives--) {
2689 char *comma = strchr(p, ',');
2690 for (i=0; i<14; i++) {
2691 char *hyphen = strchr(p, '-');
2693 if (comma && hyphen > comma) break;
2694 len = hyphen + 1 - p;
2695 if (i == 7 && *p == '*' && len == 2) {
2697 memcpy(q, strInt, strIntLen);
2707 len = comma + 1 - p;
2714 return base_fnt_lst;
2718 CreateFontSet (char *base_fnt_lst)
2721 char **missing_list;
2725 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
2726 &missing_list, &missing_count, &def_string);
2727 if (appData.debugMode) {
2729 XFontStruct **font_struct_list;
2730 char **font_name_list;
2731 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
2733 fprintf(debugFP, " got list %s, locale %s\n",
2734 XBaseFontNameListOfFontSet(fntSet),
2735 XLocaleOfFontSet(fntSet));
2736 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
2737 for (i = 0; i < count; i++) {
2738 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
2741 for (i = 0; i < missing_count; i++) {
2742 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
2745 if (fntSet == NULL) {
2746 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
2751 #else // not ENABLE_NLS
2753 * Find a font that matches "pattern" that is as close as
2754 * possible to the targetPxlSize. Prefer fonts that are k
2755 * pixels smaller to fonts that are k pixels larger. The
2756 * pattern must be in the X Consortium standard format,
2757 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2758 * The return value should be freed with XtFree when no
2762 FindFont (char *pattern, int targetPxlSize)
2764 char **fonts, *p, *best, *scalable, *scalableTail;
2765 int i, j, nfonts, minerr, err, pxlSize;
2767 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2769 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2770 programName, pattern);
2777 for (i=0; i<nfonts; i++) {
2780 if (*p != '-') continue;
2782 if (*p == NULLCHAR) break;
2783 if (*p++ == '-') j++;
2785 if (j < 7) continue;
2788 scalable = fonts[i];
2791 err = pxlSize - targetPxlSize;
2792 if (Abs(err) < Abs(minerr) ||
2793 (minerr > 0 && err < 0 && -err == minerr)) {
2799 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2800 /* If the error is too big and there is a scalable font,
2801 use the scalable font. */
2802 int headlen = scalableTail - scalable;
2803 p = (char *) XtMalloc(strlen(scalable) + 10);
2804 while (isdigit(*scalableTail)) scalableTail++;
2805 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2807 p = (char *) XtMalloc(strlen(best) + 2);
2808 safeStrCpy(p, best, strlen(best)+1 );
2810 if (appData.debugMode) {
2811 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2812 pattern, targetPxlSize, p);
2814 XFreeFontNames(fonts);
2821 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
2822 // must be called before all non-first callse to CreateGCs()
2823 XtReleaseGC(shellWidget, highlineGC);
2824 XtReleaseGC(shellWidget, lightSquareGC);
2825 XtReleaseGC(shellWidget, darkSquareGC);
2826 XtReleaseGC(shellWidget, lineGC);
2827 if (appData.monoMode) {
2828 if (DefaultDepth(xDisplay, xScreen) == 1) {
2829 XtReleaseGC(shellWidget, wbPieceGC);
2831 XtReleaseGC(shellWidget, bwPieceGC);
2834 XtReleaseGC(shellWidget, prelineGC);
2835 XtReleaseGC(shellWidget, jailSquareGC);
2836 XtReleaseGC(shellWidget, wdPieceGC);
2837 XtReleaseGC(shellWidget, wlPieceGC);
2838 XtReleaseGC(shellWidget, wjPieceGC);
2839 XtReleaseGC(shellWidget, bdPieceGC);
2840 XtReleaseGC(shellWidget, blPieceGC);
2841 XtReleaseGC(shellWidget, bjPieceGC);
2846 CreateGCs (int redo)
2848 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2849 | GCBackground | GCFunction | GCPlaneMask;
2850 XGCValues gc_values;
2853 gc_values.plane_mask = AllPlanes;
2854 gc_values.line_width = lineGap;
2855 gc_values.line_style = LineSolid;
2856 gc_values.function = GXcopy;
2859 DeleteGCs(); // called a second time; clean up old GCs first
2860 } else { // [HGM] grid and font GCs created on first call only
2861 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
2862 gc_values.background = XWhitePixel(xDisplay, xScreen);
2863 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
2864 XSetFont(xDisplay, coordGC, coordFontID);
2866 // [HGM] make font for holdings counts (white on black)
2867 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
2868 gc_values.background = XBlackPixel(xDisplay, xScreen);
2869 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
2870 XSetFont(xDisplay, countGC, countFontID);
2872 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
2873 gc_values.background = XBlackPixel(xDisplay, xScreen);
2874 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
2876 if (appData.monoMode) {
2877 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
2878 gc_values.background = XWhitePixel(xDisplay, xScreen);
2879 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
2881 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
2882 gc_values.background = XBlackPixel(xDisplay, xScreen);
2883 lightSquareGC = wbPieceGC
2884 = XtGetGC(shellWidget, value_mask, &gc_values);
2886 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
2887 gc_values.background = XWhitePixel(xDisplay, xScreen);
2888 darkSquareGC = bwPieceGC
2889 = XtGetGC(shellWidget, value_mask, &gc_values);
2891 if (DefaultDepth(xDisplay, xScreen) == 1) {
2892 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2893 gc_values.function = GXcopyInverted;
2894 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
2895 gc_values.function = GXcopy;
2896 if (XBlackPixel(xDisplay, xScreen) == 1) {
2897 bwPieceGC = darkSquareGC;
2898 wbPieceGC = copyInvertedGC;
2900 bwPieceGC = copyInvertedGC;
2901 wbPieceGC = lightSquareGC;
2905 gc_values.foreground = highlightSquareColor;
2906 gc_values.background = highlightSquareColor;
2907 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
2909 gc_values.foreground = premoveHighlightColor;
2910 gc_values.background = premoveHighlightColor;
2911 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
2913 gc_values.foreground = lightSquareColor;
2914 gc_values.background = darkSquareColor;
2915 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
2917 gc_values.foreground = darkSquareColor;
2918 gc_values.background = lightSquareColor;
2919 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
2921 gc_values.foreground = jailSquareColor;
2922 gc_values.background = jailSquareColor;
2923 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
2925 gc_values.foreground = whitePieceColor;
2926 gc_values.background = darkSquareColor;
2927 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2929 gc_values.foreground = whitePieceColor;
2930 gc_values.background = lightSquareColor;
2931 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2933 gc_values.foreground = whitePieceColor;
2934 gc_values.background = jailSquareColor;
2935 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2937 gc_values.foreground = blackPieceColor;
2938 gc_values.background = darkSquareColor;
2939 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2941 gc_values.foreground = blackPieceColor;
2942 gc_values.background = lightSquareColor;
2943 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2945 gc_values.foreground = blackPieceColor;
2946 gc_values.background = jailSquareColor;
2947 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2952 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
2960 fp = fopen(filename, "rb");
2962 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
2969 for (y=0; y<h; ++y) {
2970 for (x=0; x<h; ++x) {
2975 XPutPixel(xim, x, y, blackPieceColor);
2977 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2980 XPutPixel(xim, x, y, darkSquareColor);
2982 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2985 XPutPixel(xim, x, y, whitePieceColor);
2987 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2990 XPutPixel(xim, x, y, lightSquareColor);
2992 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3000 /* create Pixmap of piece */
3001 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3003 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3006 /* create Pixmap of clipmask
3007 Note: We assume the white/black pieces have the same
3008 outline, so we make only 6 masks. This is okay
3009 since the XPM clipmask routines do the same. */
3011 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3013 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3016 /* now create the 1-bit version */
3017 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3020 values.foreground = 1;
3021 values.background = 0;
3023 /* Don't use XtGetGC, not read only */
3024 maskGC = XCreateGC(xDisplay, *mask,
3025 GCForeground | GCBackground, &values);
3026 XCopyPlane(xDisplay, temp, *mask, maskGC,
3027 0, 0, squareSize, squareSize, 0, 0, 1);
3028 XFreePixmap(xDisplay, temp);
3033 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3041 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3046 /* The XSynchronize calls were copied from CreatePieces.
3047 Not sure if needed, but can't hurt */
3048 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3051 /* temp needed by loadXIM() */
3052 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3053 0, 0, ss, ss, AllPlanes, XYPixmap);
3055 if (strlen(appData.pixmapDirectory) == 0) {
3059 if (appData.monoMode) {
3060 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3064 fprintf(stderr, _("\nLoading XIMs...\n"));
3066 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3067 fprintf(stderr, "%d", piece+1);
3068 for (kind=0; kind<4; kind++) {
3069 fprintf(stderr, ".");
3070 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3071 ExpandPathName(appData.pixmapDirectory),
3072 piece <= (int) WhiteKing ? "" : "w",
3073 pieceBitmapNames[piece],
3075 ximPieceBitmap[kind][piece] =
3076 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3077 0, 0, ss, ss, AllPlanes, XYPixmap);
3078 if (appData.debugMode)
3079 fprintf(stderr, _("(File:%s:) "), buf);
3080 loadXIM(ximPieceBitmap[kind][piece],
3082 &(xpmPieceBitmap2[kind][piece]),
3083 &(ximMaskPm2[piece]));
3084 if(piece <= (int)WhiteKing)
3085 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3087 fprintf(stderr," ");
3089 /* Load light and dark squares */
3090 /* If the LSQ and DSQ pieces don't exist, we will
3091 draw them with solid squares. */
3092 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3093 if (access(buf, 0) != 0) {
3097 fprintf(stderr, _("light square "));
3099 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3100 0, 0, ss, ss, AllPlanes, XYPixmap);
3101 if (appData.debugMode)
3102 fprintf(stderr, _("(File:%s:) "), buf);
3104 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3105 fprintf(stderr, _("dark square "));
3106 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3107 ExpandPathName(appData.pixmapDirectory), ss);
3108 if (appData.debugMode)
3109 fprintf(stderr, _("(File:%s:) "), buf);
3111 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3112 0, 0, ss, ss, AllPlanes, XYPixmap);
3113 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3114 xpmJailSquare = xpmLightSquare;
3116 fprintf(stderr, _("Done.\n"));
3118 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3121 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3125 CreateXPMBoard (char *s, int kind)
3129 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3130 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3131 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3137 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3138 // thisroutine has to be called t free the old piece pixmaps
3140 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3141 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3143 XFreePixmap(xDisplay, xpmLightSquare);
3144 XFreePixmap(xDisplay, xpmDarkSquare);
3153 u_int ss = squareSize;
3155 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3156 XpmColorSymbol symbols[4];
3157 static int redo = False;
3159 if(redo) FreeXPMPieces(); else redo = 1;
3161 /* The XSynchronize calls were copied from CreatePieces.
3162 Not sure if needed, but can't hurt */
3163 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3165 /* Setup translations so piece colors match square colors */
3166 symbols[0].name = "light_piece";
3167 symbols[0].value = appData.whitePieceColor;
3168 symbols[1].name = "dark_piece";
3169 symbols[1].value = appData.blackPieceColor;
3170 symbols[2].name = "light_square";
3171 symbols[2].value = appData.lightSquareColor;
3172 symbols[3].name = "dark_square";
3173 symbols[3].value = appData.darkSquareColor;
3175 attr.valuemask = XpmColorSymbols;
3176 attr.colorsymbols = symbols;
3177 attr.numsymbols = 4;
3179 if (appData.monoMode) {
3180 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3184 if (strlen(appData.pixmapDirectory) == 0) {
3185 XpmPieces* pieces = builtInXpms;
3188 while (pieces->size != squareSize && pieces->size) pieces++;
3189 if (!pieces->size) {
3190 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3193 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3194 for (kind=0; kind<4; kind++) {
3196 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3197 pieces->xpm[piece][kind],
3198 &(xpmPieceBitmap2[kind][piece]),
3199 NULL, &attr)) != 0) {
3200 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3204 if(piece <= (int) WhiteKing)
3205 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3209 xpmJailSquare = xpmLightSquare;
3213 fprintf(stderr, _("\nLoading XPMs...\n"));
3216 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3217 fprintf(stderr, "%d ", piece+1);
3218 for (kind=0; kind<4; kind++) {
3219 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3220 ExpandPathName(appData.pixmapDirectory),
3221 piece > (int) WhiteKing ? "w" : "",
3222 pieceBitmapNames[piece],
3224 if (appData.debugMode) {
3225 fprintf(stderr, _("(File:%s:) "), buf);
3227 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3228 &(xpmPieceBitmap2[kind][piece]),
3229 NULL, &attr)) != 0) {
3230 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3231 // [HGM] missing: read of unorthodox piece failed; substitute King.
3232 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3233 ExpandPathName(appData.pixmapDirectory),
3235 if (appData.debugMode) {
3236 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3238 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3239 &(xpmPieceBitmap2[kind][piece]),
3243 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3248 if(piece <= (int) WhiteKing)
3249 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3252 /* Load light and dark squares */
3253 /* If the LSQ and DSQ pieces don't exist, we will
3254 draw them with solid squares. */
3255 fprintf(stderr, _("light square "));
3256 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3257 if (access(buf, 0) != 0) {
3261 if (appData.debugMode)
3262 fprintf(stderr, _("(File:%s:) "), buf);
3264 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3265 &xpmLightSquare, NULL, &attr)) != 0) {
3266 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3269 fprintf(stderr, _("dark square "));
3270 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3271 ExpandPathName(appData.pixmapDirectory), ss);
3272 if (appData.debugMode) {
3273 fprintf(stderr, _("(File:%s:) "), buf);
3275 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3276 &xpmDarkSquare, NULL, &attr)) != 0) {
3277 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3281 xpmJailSquare = xpmLightSquare;
3282 fprintf(stderr, _("Done.\n"));
3284 oldVariant = -1; // kludge to force re-makig of animation masks
3285 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3288 #endif /* HAVE_LIBXPM */
3291 /* No built-in bitmaps */
3296 u_int ss = squareSize;
3298 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3301 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3302 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3303 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3304 pieceBitmapNames[piece],
3305 ss, kind == SOLID ? 's' : 'o');
3306 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3307 if(piece <= (int)WhiteKing)
3308 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3312 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3316 /* With built-in bitmaps */
3320 BuiltInBits* bib = builtInBits;
3323 u_int ss = squareSize;
3325 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3328 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3330 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3331 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3332 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3333 pieceBitmapNames[piece],
3334 ss, kind == SOLID ? 's' : 'o');
3335 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3336 bib->bits[kind][piece], ss, ss);
3337 if(piece <= (int)WhiteKing)
3338 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3342 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3348 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
3353 char msg[MSG_SIZ], fullname[MSG_SIZ];
3355 if (*appData.bitmapDirectory != NULLCHAR) {
3356 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3357 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3358 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3359 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3360 &w, &h, pm, &x_hot, &y_hot);
3361 fprintf(stderr, "load %s\n", name);
3362 if (errcode != BitmapSuccess) {
3364 case BitmapOpenFailed:
3365 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3367 case BitmapFileInvalid:
3368 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3370 case BitmapNoMemory:
3371 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3375 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3379 fprintf(stderr, _("%s: %s...using built-in\n"),
3381 } else if (w != wreq || h != hreq) {
3383 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3384 programName, fullname, w, h, wreq, hreq);
3390 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3400 if (lineGap == 0) return;
3402 /* [HR] Split this into 2 loops for non-square boards. */
3404 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3405 gridSegments[i].x1 = 0;
3406 gridSegments[i].x2 =
3407 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3408 gridSegments[i].y1 = gridSegments[i].y2
3409 = lineGap / 2 + (i * (squareSize + lineGap));
3412 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3413 gridSegments[j + i].y1 = 0;
3414 gridSegments[j + i].y2 =
3415 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3416 gridSegments[j + i].x1 = gridSegments[j + i].x2
3417 = lineGap / 2 + (j * (squareSize + lineGap));
3421 int nrOfMenuItems = 7;
3422 Widget menuWidget[150];
3423 MenuListItem menuItemList[150] = {
3424 { "LoadNextGameProc", LoadNextGameProc },
3425 { "LoadPrevGameProc", LoadPrevGameProc },
3426 { "ReloadGameProc", ReloadGameProc },
3427 { "ReloadPositionProc", ReloadPositionProc },
3428 #ifndef OPTIONSDIALOG
3429 { "AlwaysQueenProc", AlwaysQueenProc },
3430 { "AnimateDraggingProc", AnimateDraggingProc },
3431 { "AnimateMovingProc", AnimateMovingProc },
3432 { "AutoflagProc", AutoflagProc },
3433 { "AutoflipProc", AutoflipProc },
3434 { "BlindfoldProc", BlindfoldProc },
3435 { "FlashMovesProc", FlashMovesProc },
3437 { "HighlightDraggingProc", HighlightDraggingProc },
3439 { "HighlightLastMoveProc", HighlightLastMoveProc },
3440 // { "IcsAlarmProc", IcsAlarmProc },
3441 { "MoveSoundProc", MoveSoundProc },
3442 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
3443 { "PopupExitMessageProc", PopupExitMessageProc },
3444 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
3445 // { "PremoveProc", PremoveProc },
3446 { "ShowCoordsProc", ShowCoordsProc },
3447 { "ShowThinkingProc", ShowThinkingProc },
3448 { "HideThinkingProc", HideThinkingProc },
3449 { "TestLegalityProc", TestLegalityProc },
3451 { "AboutGameProc", AboutGameEvent },
3452 { "DebugProc", DebugProc },
3453 { "NothingProc", NothingProc },
3458 MarkMenuItem (char *menuRef, int state)
3460 int nr = MenuToNumber(menuRef);
3463 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
3464 XtSetValues(menuWidget[nr], args, 1);
3469 EnableMenuItem (char *menuRef, int state)
3471 int nr = MenuToNumber(menuRef);
3472 if(nr >= 0) XtSetSensitive(menuWidget[nr], state);
3476 SetMenuEnables (Enables *enab)
3478 while (enab->name != NULL) {
3479 EnableMenuItem(enab->name, enab->value);
3485 Equal(char *p, char *s)
3486 { // compare strings skipping spaces in second
3488 if(*s == ' ') { s++; continue; }
3489 if(*s++ != *p++) return 0;
3495 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3496 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
3498 if(*nprms == 0) return;
3499 for(i=0; menuItemList[i].name; i++) {
3500 if(Equal(prms[0], menuItemList[i].name)) {
3501 (menuItemList[i].proc) ();
3508 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
3510 MenuProc *proc = (MenuProc *) addr;
3516 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
3518 RecentEngineEvent((int) (intptr_t) addr);
3521 // some stuff that must remain in front-end
3522 static Widget mainBar, currentMenu;
3523 static int wtot, nr = 0, widths[10];
3526 AppendMenuItem (char *text, char *name, MenuProc *action)
3533 XtSetArg(args[j], XtNleftMargin, 20); j++;
3534 XtSetArg(args[j], XtNrightMargin, 20); j++;
3536 if (strcmp(text, "----") == 0) {
3537 entry = XtCreateManagedWidget(text, smeLineObjectClass,
3538 currentMenu, args, j);
3540 XtSetArg(args[j], XtNlabel, XtNewString(_(text)));
3541 entry = XtCreateManagedWidget(name, smeBSBObjectClass,
3542 currentMenu, args, j+1);
3543 XtAddCallback(entry, XtNcallback,
3544 (XtCallbackProc) (strcmp(name, "recent") ? MenuBarSelect : MenuEngineSelect),
3546 menuWidget[nrOfMenuItems] = entry;
3551 CreateMenuButton (char *name, Menu *mb)
3552 { // create menu button on main bar, and shell for pull-down list
3558 XtSetArg(args[j], XtNmenuName, XtNewString(name)); j++;
3559 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3560 XtSetArg(args[j], XtNborderWidth, 0); j++;
3561 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3563 currentMenu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3566 XtSetArg(args[j], XtNwidth, &w); j++;
3567 XtGetValues(mb->subMenu, args, j);
3568 wtot += mb->textWidth = widths[nr++] = w;
3572 CreateMenuBar (Menu *mb, int boardWidth)
3576 char menuName[MSG_SIZ];
3580 // create bar itself
3582 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3583 XtSetArg(args[j], XtNvSpace, 0); j++;
3584 XtSetArg(args[j], XtNborderWidth, 0); j++;
3585 mainBar = XtCreateWidget("menuBar", boxWidgetClass,
3586 formWidget, args, j);
3588 CreateMainMenus(mb); // put menus in bar according to description in back-end
3590 // size buttons to make menu bar fit, clipping menu names where necessary
3591 while(wtot > boardWidth - 40) {
3593 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
3597 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
3599 XtSetArg(args[j], XtNwidth, widths[i]); j++;
3600 XtSetValues(ma[i].subMenu, args, j);
3607 CreateButtonBar (MenuItem *mi)
3610 Widget button, buttonBar;
3614 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3616 XtSetArg(args[j], XtNhSpace, 0); j++;
3618 XtSetArg(args[j], XtNborderWidth, 0); j++;
3619 XtSetArg(args[j], XtNvSpace, 0); j++;
3620 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3621 formWidget, args, j);
3623 while (mi->string != NULL) {
3626 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3627 XtSetArg(args[j], XtNborderWidth, 0); j++;
3629 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3630 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3631 buttonBar, args, j);
3632 XtAddCallback(button, XtNcallback,
3633 (XtCallbackProc) MenuBarSelect,
3634 (caddr_t) mi->proc);
3641 CreatePieceMenu (char *name, int color)
3646 ChessSquare selection;
3648 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3649 boardWidget, args, 0);
3651 for (i = 0; i < PIECE_MENU_SIZE; i++) {
3652 String item = pieceMenuStrings[color][i];
3654 if (strcmp(item, "----") == 0) {
3655 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3658 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3659 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3661 selection = pieceMenuTranslation[color][i];
3662 XtAddCallback(entry, XtNcallback,
3663 (XtCallbackProc) PieceMenuSelect,
3664 (caddr_t) selection);
3665 if (selection == WhitePawn || selection == BlackPawn) {
3666 XtSetArg(args[0], XtNpopupOnEntry, entry);
3667 XtSetValues(menu, args, 1);
3680 ChessSquare selection;
3682 whitePieceMenu = CreatePieceMenu("menuW", 0);
3683 blackPieceMenu = CreatePieceMenu("menuB", 1);
3685 if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
3686 XtRegisterGrabAction(PieceMenuPopup, True,
3687 (unsigned)(ButtonPressMask|ButtonReleaseMask),
3688 GrabModeAsync, GrabModeAsync);
3690 XtSetArg(args[0], XtNlabel, _("Drop"));
3691 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3692 boardWidget, args, 1);
3693 for (i = 0; i < DROP_MENU_SIZE; i++) {
3694 String item = dropMenuStrings[i];
3696 if (strcmp(item, "----") == 0) {
3697 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3700 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3701 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3703 selection = dropMenuTranslation[i];
3704 XtAddCallback(entry, XtNcallback,
3705 (XtCallbackProc) DropMenuSelect,
3706 (caddr_t) selection);
3720 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3721 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3722 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3723 dmEnables[i].piece);
3724 XtSetSensitive(entry, p != NULL || !appData.testLegality
3725 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3726 && !appData.icsActive));
3728 while (p && *p++ == dmEnables[i].piece) count++;
3729 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3731 XtSetArg(args[j], XtNlabel, label); j++;
3732 XtSetValues(entry, args, j);
3737 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
3739 String whichMenu; int menuNr = -2;
3740 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3741 if (event->type == ButtonRelease)
3742 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3743 else if (event->type == ButtonPress)
3744 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3746 case 0: whichMenu = params[0]; break;
3747 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3749 case -1: if (errorUp) ErrorPopDown();
3752 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3756 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3758 if (pmFromX < 0 || pmFromY < 0) return;
3759 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3763 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3765 if (pmFromX < 0 || pmFromY < 0) return;
3766 DropMenuEvent(piece, pmFromX, pmFromY);
3770 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3772 shiftKey = prms[0][0] & 1;
3777 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3779 shiftKey = prms[0][0] & 1;
3785 * If the user selects on a border boundary, return -1; if off the board,
3786 * return -2. Otherwise map the event coordinate to the square.
3789 EventToSquare (int x, int limit)
3796 if ((x % (squareSize + lineGap)) >= squareSize)
3798 x /= (squareSize + lineGap);
3805 do_flash_delay (unsigned long msec)
3811 drawHighlight (int file, int rank, GC gc)
3815 if (lineGap == 0) return;
3818 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
3819 (squareSize + lineGap);
3820 y = lineGap/2 + rank * (squareSize + lineGap);
3822 x = lineGap/2 + file * (squareSize + lineGap);
3823 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
3824 (squareSize + lineGap);
3827 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3828 squareSize+lineGap, squareSize+lineGap);
3831 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
3832 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
3835 SetHighlights (int fromX, int fromY, int toX, int toY)
3837 if (hi1X != fromX || hi1Y != fromY) {
3838 if (hi1X >= 0 && hi1Y >= 0) {
3839 drawHighlight(hi1X, hi1Y, lineGC);
3841 } // [HGM] first erase both, then draw new!
3843 if (hi2X != toX || hi2Y != toY) {
3844 if (hi2X >= 0 && hi2Y >= 0) {
3845 drawHighlight(hi2X, hi2Y, lineGC);
3848 if (hi1X != fromX || hi1Y != fromY) {
3849 if (fromX >= 0 && fromY >= 0) {
3850 drawHighlight(fromX, fromY, highlineGC);
3853 if (hi2X != toX || hi2Y != toY) {
3854 if (toX >= 0 && toY >= 0) {
3855 drawHighlight(toX, toY, highlineGC);
3859 if(toX<0) // clearing the highlights must have damaged arrow
3860 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y); // for now, redraw it (should really be cleared!)
3871 SetHighlights(-1, -1, -1, -1);
3876 SetPremoveHighlights (int fromX, int fromY, int toX, int toY)
3878 if (pm1X != fromX || pm1Y != fromY) {
3879 if (pm1X >= 0 && pm1Y >= 0) {
3880 drawHighlight(pm1X, pm1Y, lineGC);
3882 if (fromX >= 0 && fromY >= 0) {
3883 drawHighlight(fromX, fromY, prelineGC);
3886 if (pm2X != toX || pm2Y != toY) {
3887 if (pm2X >= 0 && pm2Y >= 0) {
3888 drawHighlight(pm2X, pm2Y, lineGC);
3890 if (toX >= 0 && toY >= 0) {
3891 drawHighlight(toX, toY, prelineGC);
3901 ClearPremoveHighlights ()
3903 SetPremoveHighlights(-1, -1, -1, -1);
3907 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
3909 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
3910 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
3912 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
3913 if(textureW[kind] < W*squareSize)
3914 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
3916 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
3917 if(textureH[kind] < H*squareSize)
3918 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
3920 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
3925 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
3926 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
3928 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
3929 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
3930 squareSize, squareSize, x*fac, y*fac);
3932 if (useImages && useImageSqs) {
3936 pm = xpmLightSquare;
3941 case 2: /* neutral */
3946 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3947 squareSize, squareSize, x*fac, y*fac);
3957 case 2: /* neutral */
3962 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
3967 I split out the routines to draw a piece so that I could
3968 make a generic flash routine.
3971 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3973 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3974 switch (square_color) {
3976 case 2: /* neutral */
3978 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3979 ? *pieceToOutline(piece)
3980 : *pieceToSolid(piece),
3981 dest, bwPieceGC, 0, 0,
3982 squareSize, squareSize, x, y);
3985 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3986 ? *pieceToSolid(piece)
3987 : *pieceToOutline(piece),
3988 dest, wbPieceGC, 0, 0,
3989 squareSize, squareSize, x, y);
3995 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3997 switch (square_color) {
3999 case 2: /* neutral */
4001 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4002 ? *pieceToOutline(piece)
4003 : *pieceToSolid(piece),
4004 dest, bwPieceGC, 0, 0,
4005 squareSize, squareSize, x, y, 1);
4008 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4009 ? *pieceToSolid(piece)
4010 : *pieceToOutline(piece),
4011 dest, wbPieceGC, 0, 0,
4012 squareSize, squareSize, x, y, 1);
4018 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4020 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4021 switch (square_color) {
4023 XCopyPlane(xDisplay, *pieceToSolid(piece),
4024 dest, (int) piece < (int) BlackPawn
4025 ? wlPieceGC : blPieceGC, 0, 0,
4026 squareSize, squareSize, x, y, 1);
4029 XCopyPlane(xDisplay, *pieceToSolid(piece),
4030 dest, (int) piece < (int) BlackPawn
4031 ? wdPieceGC : bdPieceGC, 0, 0,
4032 squareSize, squareSize, x, y, 1);
4034 case 2: /* neutral */
4036 XCopyPlane(xDisplay, *pieceToSolid(piece),
4037 dest, (int) piece < (int) BlackPawn
4038 ? wjPieceGC : bjPieceGC, 0, 0,
4039 squareSize, squareSize, x, y, 1);
4045 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4047 int kind, p = piece;
4049 switch (square_color) {
4051 case 2: /* neutral */
4053 if ((int)piece < (int) BlackPawn) {
4061 if ((int)piece < (int) BlackPawn) {
4069 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4070 if(useTexture & square_color+1) {
4071 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4072 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4073 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4074 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4075 XSetClipMask(xDisplay, wlPieceGC, None);
4076 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4078 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4079 dest, wlPieceGC, 0, 0,
4080 squareSize, squareSize, x, y);
4083 typedef void (*DrawFunc)();
4088 if (appData.monoMode) {
4089 if (DefaultDepth(xDisplay, xScreen) == 1) {
4090 return monoDrawPiece_1bit;
4092 return monoDrawPiece;
4096 return colorDrawPieceImage;
4098 return colorDrawPiece;
4102 /* [HR] determine square color depending on chess variant. */
4104 SquareColor (int row, int column)
4108 if (gameInfo.variant == VariantXiangqi) {
4109 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4111 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4113 } else if (row <= 4) {
4119 square_color = ((column + row) % 2) == 1;
4122 /* [hgm] holdings: next line makes all holdings squares light */
4123 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4125 return square_color;
4129 DrawSquare (int row, int column, ChessSquare piece, int do_flash)
4131 int square_color, x, y, direction, font_ascent, font_descent;
4134 XCharStruct overall;
4138 /* Calculate delay in milliseconds (2-delays per complete flash) */
4139 flash_delay = 500 / appData.flashRate;
4142 x = lineGap + ((BOARD_WIDTH-1)-column) *
4143 (squareSize + lineGap);
4144 y = lineGap + row * (squareSize + lineGap);
4146 x = lineGap + column * (squareSize + lineGap);
4147 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4148 (squareSize + lineGap);
4151 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4153 square_color = SquareColor(row, column);
4155 if ( // [HGM] holdings: blank out area between board and holdings
4156 column == BOARD_LEFT-1 || column == BOARD_RGHT
4157 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4158 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4159 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4161 // [HGM] print piece counts next to holdings
4162 string[1] = NULLCHAR;
4163 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4164 string[0] = '0' + piece;
4165 XTextExtents(countFontStruct, string, 1, &direction,
4166 &font_ascent, &font_descent, &overall);
4167 if (appData.monoMode) {
4168 XDrawImageString(xDisplay, xBoardWindow, countGC,
4169 x + squareSize - overall.width - 2,
4170 y + font_ascent + 1, string, 1);
4172 XDrawString(xDisplay, xBoardWindow, countGC,
4173 x + squareSize - overall.width - 2,
4174 y + font_ascent + 1, string, 1);
4177 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4178 string[0] = '0' + piece;
4179 XTextExtents(countFontStruct, string, 1, &direction,
4180 &font_ascent, &font_descent, &overall);
4181 if (appData.monoMode) {
4182 XDrawImageString(xDisplay, xBoardWindow, countGC,
4183 x + 2, y + font_ascent + 1, string, 1);
4185 XDrawString(xDisplay, xBoardWindow, countGC,
4186 x + 2, y + font_ascent + 1, string, 1);
4190 if (piece == EmptySquare || appData.blindfold) {
4191 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4193 drawfunc = ChooseDrawFunc();
4195 if (do_flash && appData.flashCount > 0) {
4196 for (i=0; i<appData.flashCount; ++i) {
4197 drawfunc(piece, square_color, x, y, xBoardWindow);
4198 XSync(xDisplay, False);
4199 do_flash_delay(flash_delay);
4201 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4202 XSync(xDisplay, False);
4203 do_flash_delay(flash_delay);
4206 drawfunc(piece, square_color, x, y, xBoardWindow);
4210 string[1] = NULLCHAR;
4211 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4212 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4213 string[0] = 'a' + column - BOARD_LEFT;
4214 XTextExtents(coordFontStruct, string, 1, &direction,
4215 &font_ascent, &font_descent, &overall);
4216 if (appData.monoMode) {
4217 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4218 x + squareSize - overall.width - 2,
4219 y + squareSize - font_descent - 1, string, 1);
4221 XDrawString(xDisplay, xBoardWindow, coordGC,
4222 x + squareSize - overall.width - 2,
4223 y + squareSize - font_descent - 1, string, 1);
4226 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4227 string[0] = ONE + row;
4228 XTextExtents(coordFontStruct, string, 1, &direction,
4229 &font_ascent, &font_descent, &overall);
4230 if (appData.monoMode) {
4231 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4232 x + 2, y + font_ascent + 1, string, 1);
4234 XDrawString(xDisplay, xBoardWindow, coordGC,
4235 x + 2, y + font_ascent + 1, string, 1);
4238 if(!partnerUp && marker[row][column]) {
4239 if(appData.monoMode) {
4240 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? darkSquareGC : lightSquareGC,
4241 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4242 XDrawArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? lightSquareGC : darkSquareGC,
4243 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4245 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4246 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4251 Fraction (int x, int start, int stop)
4253 double f = ((double) x - start)/(stop - start);
4254 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
4258 static WindowPlacement wpNew;
4261 CoDrag (Widget sh, WindowPlacement *wp)
4264 int j=0, touch=0, fudge = 2;
4265 GetActualPlacement(sh, wp);
4266 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
4267 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
4268 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
4269 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
4270 if(!touch ) return; // only windows that touch co-move
4271 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
4272 int heightInc = wpNew.height - wpMain.height;
4273 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
4274 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
4275 wp->y += fracTop * heightInc;
4276 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
4277 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
4278 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
4279 int widthInc = wpNew.width - wpMain.width;
4280 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
4281 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
4282 wp->y += fracLeft * widthInc;
4283 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
4284 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
4286 wp->x += wpNew.x - wpMain.x;
4287 wp->y += wpNew.y - wpMain.y;
4288 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
4289 if(touch == 3) wp->y += wpNew.height - wpMain.height;
4290 XtSetArg(args[j], XtNx, wp->x); j++;
4291 XtSetArg(args[j], XtNy, wp->y); j++;
4292 XtSetValues(sh, args, j);
4295 static XtIntervalId delayedDragID = 0;
4300 GetActualPlacement(shellWidget, &wpNew);
4301 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
4302 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
4303 return; // false alarm
4304 if(EngineOutputIsUp()) CoDrag(engineOutputShell, &wpEngineOutput);
4305 if(MoveHistoryIsUp()) CoDrag(shells[7], &wpMoveHistory);
4306 if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
4307 if(GameListIsUp()) CoDrag(gameListShell, &wpGameList);
4309 XDrawPosition(boardWidget, True, NULL);
4310 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
4317 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
4319 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
4322 /* Why is this needed on some versions of X? */
4324 EventProc (Widget widget, caddr_t unused, XEvent *event)
4326 if (!XtIsRealized(widget))
4328 switch (event->type) {
4329 case ConfigureNotify: // main window is being dragged: drag attached windows with it
4330 if(appData.useStickyWindows)
4331 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
4334 if (event->xexpose.count > 0) return; /* no clipping is done */
4335 XDrawPosition(widget, True, NULL);
4336 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4337 flipView = !flipView; partnerUp = !partnerUp;
4338 XDrawPosition(widget, True, NULL);
4339 flipView = !flipView; partnerUp = !partnerUp;
4343 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4351 DrawPosition (int fullRedraw, Board board)
4353 XDrawPosition(boardWidget, fullRedraw, board);
4356 /* Returns 1 if there are "too many" differences between b1 and b2
4357 (i.e. more than 1 move was made) */
4359 too_many_diffs (Board b1, Board b2)
4364 for (i=0; i<BOARD_HEIGHT; ++i) {
4365 for (j=0; j<BOARD_WIDTH; ++j) {
4366 if (b1[i][j] != b2[i][j]) {
4367 if (++c > 4) /* Castling causes 4 diffs */
4375 /* Matrix describing castling maneuvers */
4376 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4377 static int castling_matrix[4][5] = {
4378 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4379 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4380 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4381 { 7, 7, 4, 5, 6 } /* 0-0, black */
4384 /* Checks whether castling occurred. If it did, *rrow and *rcol
4385 are set to the destination (row,col) of the rook that moved.
4387 Returns 1 if castling occurred, 0 if not.
4389 Note: Only handles a max of 1 castling move, so be sure
4390 to call too_many_diffs() first.
4393 check_castle_draw (Board newb, Board oldb, int *rrow, int *rcol)
4398 /* For each type of castling... */
4399 for (i=0; i<4; ++i) {
4400 r = castling_matrix[i];
4402 /* Check the 4 squares involved in the castling move */
4404 for (j=1; j<=4; ++j) {
4405 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4412 /* All 4 changed, so it must be a castling move */
4421 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4423 DrawSeekAxis (int x, int y, int xTo, int yTo)
4425 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4429 DrawSeekBackground (int left, int top, int right, int bottom)
4431 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4435 DrawSeekText (char *buf, int x, int y)
4437 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4441 DrawSeekDot (int x, int y, int colorNr)
4443 int square = colorNr & 0x80;
4446 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4448 XFillRectangle(xDisplay, xBoardWindow, color,
4449 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4451 XFillArc(xDisplay, xBoardWindow, color,
4452 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4455 static int damage[2][BOARD_RANKS][BOARD_FILES];
4458 * event handler for redrawing the board
4461 XDrawPosition (Widget w, int repaint, Board board)
4464 static int lastFlipView = 0;
4465 static int lastBoardValid[2] = {0, 0};
4466 static Board lastBoard[2];
4469 int nr = twoBoards*partnerUp;
4471 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4473 if (board == NULL) {
4474 if (!lastBoardValid[nr]) return;
4475 board = lastBoard[nr];
4477 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4478 MarkMenuItem("Flip View", flipView);
4482 * It would be simpler to clear the window with XClearWindow()
4483 * but this causes a very distracting flicker.
4486 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4488 if ( lineGap && IsDrawArrowEnabled())
4489 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4490 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4492 /* If too much changes (begin observing new game, etc.), don't
4494 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4496 /* Special check for castling so we don't flash both the king
4497 and the rook (just flash the king). */
4499 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4500 /* Draw rook with NO flashing. King will be drawn flashing later */
4501 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4502 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4506 /* First pass -- Draw (newly) empty squares and repair damage.
4507 This prevents you from having a piece show up twice while it
4508 is flashing on its new square */
4509 for (i = 0; i < BOARD_HEIGHT; i++)
4510 for (j = 0; j < BOARD_WIDTH; j++)
4511 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4512 || damage[nr][i][j]) {
4513 DrawSquare(i, j, board[i][j], 0);
4514 damage[nr][i][j] = False;
4517 /* Second pass -- Draw piece(s) in new position and flash them */
4518 for (i = 0; i < BOARD_HEIGHT; i++)
4519 for (j = 0; j < BOARD_WIDTH; j++)
4520 if (board[i][j] != lastBoard[nr][i][j]) {
4521 DrawSquare(i, j, board[i][j], do_flash);
4525 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4526 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4527 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4529 for (i = 0; i < BOARD_HEIGHT; i++)
4530 for (j = 0; j < BOARD_WIDTH; j++) {
4531 DrawSquare(i, j, board[i][j], 0);
4532 damage[nr][i][j] = False;
4536 CopyBoard(lastBoard[nr], board);
4537 lastBoardValid[nr] = 1;
4538 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4539 lastFlipView = flipView;
4541 /* Draw highlights */
4542 if (pm1X >= 0 && pm1Y >= 0) {
4543 drawHighlight(pm1X, pm1Y, prelineGC);
4545 if (pm2X >= 0 && pm2Y >= 0) {
4546 drawHighlight(pm2X, pm2Y, prelineGC);
4548 if (hi1X >= 0 && hi1Y >= 0) {
4549 drawHighlight(hi1X, hi1Y, highlineGC);
4551 if (hi2X >= 0 && hi2Y >= 0) {
4552 drawHighlight(hi2X, hi2Y, highlineGC);
4554 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4556 /* If piece being dragged around board, must redraw that too */
4559 XSync(xDisplay, False);
4564 * event handler for redrawing the board
4567 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4569 XDrawPosition(w, True, NULL);
4574 * event handler for parsing user moves
4576 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4577 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4578 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4579 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4580 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4581 // and at the end FinishMove() to perform the move after optional promotion popups.
4582 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4584 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4586 if (w != boardWidget || errorExitStatus != -1) return;
4587 if(nprms) shiftKey = !strcmp(prms[0], "1");
4590 if (event->type == ButtonPress) {
4591 XtPopdown(promotionShell);
4592 XtDestroyWidget(promotionShell);
4593 promotionUp = False;
4601 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4602 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4603 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4607 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
4609 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4610 DragPieceMove(event->xmotion.x, event->xmotion.y);
4614 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
4615 { // [HGM] pv: walk PV
4616 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4619 static int savedIndex; /* gross that this is global */
4622 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4625 XawTextPosition index, dummy;
4628 XawTextGetSelectionPos(w, &index, &dummy);
4629 XtSetArg(arg, XtNstring, &val);
4630 XtGetValues(w, &arg, 1);
4631 ReplaceComment(savedIndex, val);
4632 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4633 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4637 EditCommentPopUp (int index, char *title, char *text)
4640 if (text == NULL) text = "";
4641 NewCommentPopup(title, text, index);
4650 extern Option boxOptions[];
4660 edit = boxOptions[0].handle;
4662 XtSetArg(args[j], XtNstring, &val); j++;
4663 XtGetValues(edit, args, j);
4665 SendMultiLineToICS(val);
4666 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4667 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4671 ICSInputBoxPopDown ()
4677 CommentPopUp (char *title, char *text)
4679 savedIndex = currentMove; // [HGM] vari
4680 NewCommentPopup(title, text, currentMove);
4689 static char *openName;
4695 (void) (*fileProc)(openFP, 0, openName);
4699 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
4701 fileProc = proc; /* I can't see a way not */
4702 fileOpenMode = openMode; /* to use globals here */
4703 { // [HGM] use file-selector dialog stolen from Ghostview
4704 int index; // this is not supported yet
4705 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
4706 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
4707 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
4708 ScheduleDelayedEvent(&DelayedLoad, 50);
4715 if (!filenameUp) return;
4716 XtPopdown(fileNameShell);
4717 XtDestroyWidget(fileNameShell);
4723 FileNameCallback (Widget w, XtPointer client_data, XtPointer call_data)
4728 XtSetArg(args[0], XtNlabel, &name);
4729 XtGetValues(w, args, 1);
4731 if (strcmp(name, _("cancel")) == 0) {
4736 FileNameAction(w, NULL, NULL, NULL);
4740 FileNameAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4748 name = XawDialogGetValueString(w = XtParent(w));
4750 if ((name != NULL) && (*name != NULLCHAR)) {
4751 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
4752 XtPopdown(w = XtParent(XtParent(w)));
4756 p = strrchr(buf, ' ');
4763 fullname = ExpandPathName(buf);
4765 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
4768 f = fopen(fullname, fileOpenMode);
4770 DisplayError(_("Failed to open file"), errno);
4772 (void) (*fileProc)(f, index, buf);
4779 XtPopdown(w = XtParent(XtParent(w)));
4789 Widget dialog, layout;
4791 Dimension bw_width, pw_width;
4793 char *PromoChars = "wglcqrbnkac+=\0";
4796 XtSetArg(args[j], XtNwidth, &bw_width); j++;
4797 XtGetValues(boardWidget, args, j);
4800 XtSetArg(args[j], XtNresizable, True); j++;
4801 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
4803 XtCreatePopupShell("Promotion", transientShellWidgetClass,
4804 shellWidget, args, j);
4806 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
4807 layoutArgs, XtNumber(layoutArgs));
4810 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
4811 XtSetArg(args[j], XtNborderWidth, 0); j++;
4812 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
4815 if(gameInfo.variant != VariantShogi) {
4816 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
4817 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
4818 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
4819 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
4820 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
4822 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
4823 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
4824 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
4825 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
4827 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
4828 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
4829 gameInfo.variant == VariantGiveaway) {
4830 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
4832 if(gameInfo.variant == VariantCapablanca ||
4833 gameInfo.variant == VariantGothic ||
4834 gameInfo.variant == VariantCapaRandom) {
4835 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
4836 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
4838 } else // [HGM] shogi
4840 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
4841 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
4843 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
4845 XtRealizeWidget(promotionShell);
4846 CatchDeleteWindow(promotionShell, "PromotionPopDown");
4849 XtSetArg(args[j], XtNwidth, &pw_width); j++;
4850 XtGetValues(promotionShell, args, j);
4852 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
4853 lineGap + squareSize/3 +
4854 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
4855 0 : 6*(squareSize + lineGap)), &x, &y);
4858 XtSetArg(args[j], XtNx, x); j++;
4859 XtSetArg(args[j], XtNy, y); j++;
4860 XtSetValues(promotionShell, args, j);
4862 XtPopup(promotionShell, XtGrabNone);
4870 if (!promotionUp) return;
4871 XtPopdown(promotionShell);
4872 XtDestroyWidget(promotionShell);
4873 promotionUp = False;
4877 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
4879 int promoChar = * (const char *) client_data;
4883 if (fromX == -1) return;
4890 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
4892 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
4893 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
4899 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
4901 dialogError = errorUp = False;
4902 XtPopdown(w = XtParent(XtParent(XtParent(w))));
4904 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
4911 if (!errorUp) return;
4912 dialogError = errorUp = False;
4913 XtPopdown(errorShell);
4914 XtDestroyWidget(errorShell);
4915 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
4919 ErrorPopUp (char *title, char *label, int modal)
4922 Widget dialog, layout;
4926 Dimension bw_width, pw_width;
4927 Dimension pw_height;
4931 XtSetArg(args[i], XtNresizable, True); i++;
4932 XtSetArg(args[i], XtNtitle, title); i++;
4934 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
4935 shellUp[0] ? (dialogError = modal = TRUE, shells[0]) : shellWidget, args, i);
4937 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
4938 layoutArgs, XtNumber(layoutArgs));
4941 XtSetArg(args[i], XtNlabel, label); i++;
4942 XtSetArg(args[i], XtNborderWidth, 0); i++;
4943 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
4946 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
4948 XtRealizeWidget(errorShell);
4949 CatchDeleteWindow(errorShell, "ErrorPopDown");
4952 XtSetArg(args[i], XtNwidth, &bw_width); i++;
4953 XtGetValues(boardWidget, args, i);
4955 XtSetArg(args[i], XtNwidth, &pw_width); i++;
4956 XtSetArg(args[i], XtNheight, &pw_height); i++;
4957 XtGetValues(errorShell, args, i);
4960 /* This code seems to tickle an X bug if it is executed too soon
4961 after xboard starts up. The coordinates get transformed as if
4962 the main window was positioned at (0, 0).
4964 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
4965 0 - pw_height + squareSize / 3, &x, &y);
4967 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
4968 RootWindowOfScreen(XtScreen(boardWidget)),
4969 (bw_width - pw_width) / 2,
4970 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
4974 if (y < 0) y = 0; /*avoid positioning top offscreen*/
4977 XtSetArg(args[i], XtNx, x); i++;
4978 XtSetArg(args[i], XtNy, y); i++;
4979 XtSetValues(errorShell, args, i);
4982 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
4985 /* Disable all user input other than deleting the window */
4986 static int frozen = 0;
4992 /* Grab by a widget that doesn't accept input */
4993 XtAddGrab(messageWidget, TRUE, FALSE);
4997 /* Undo a FreezeUI */
5001 if (!frozen) return;
5002 XtRemoveGrab(messageWidget);
5007 ModeToWidgetName (GameMode mode)
5010 case BeginningOfGame:
5011 if (appData.icsActive)
5012 return "ICS Client";
5013 else if (appData.noChessProgram ||
5014 *appData.cmailGameName != NULLCHAR)
5017 return "Machine Black";
5018 case MachinePlaysBlack:
5019 return "Machine Black";
5020 case MachinePlaysWhite:
5021 return "Machine White";
5023 return "Analysis Mode";
5025 return "Analyze File";
5026 case TwoMachinesPlay:
5027 return "Two Machines";
5030 case PlayFromGameFile:
5033 return "Edit Position";
5036 case IcsPlayingWhite:
5037 case IcsPlayingBlack:
5041 return "ICS Client";
5052 static int oldPausing = FALSE;
5053 static GameMode oldmode = (GameMode) -1;
5056 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5058 if (pausing != oldPausing) {
5059 oldPausing = pausing;
5060 MarkMenuItem("Pause", pausing);
5062 if (appData.showButtonBar) {
5063 /* Always toggle, don't set. Previous code messes up when
5064 invoked while the button is pressed, as releasing it
5065 toggles the state again. */
5068 XtSetArg(args[0], XtNbackground, &oldbg);
5069 XtSetArg(args[1], XtNforeground, &oldfg);
5070 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5072 XtSetArg(args[0], XtNbackground, oldfg);
5073 XtSetArg(args[1], XtNforeground, oldbg);
5075 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5079 wname = ModeToWidgetName(oldmode);
5080 if (wname != NULL) {
5081 MarkMenuItem(wname, False);
5083 wname = ModeToWidgetName(gameMode);
5084 if (wname != NULL) {
5085 MarkMenuItem(wname, True);
5088 MarkMenuItem("Machine Match", matchMode && matchGame < appData.matchGames);
5090 /* Maybe all the enables should be handled here, not just this one */
5091 EnableMenuItem("Training", gameMode == Training || gameMode == PlayFromGameFile);
5096 * Button/menu procedures
5099 LoadGamePopUp (FILE *f, int gameNumber, char *title)
5101 cmailMsgLoaded = FALSE;
5102 if (gameNumber == 0) {
5103 int error = GameListBuild(f);
5105 DisplayError(_("Cannot build game list"), error);
5106 } else if (!ListEmpty(&gameList) &&
5107 ((ListGame *) gameList.tailPred)->number > 1) {
5108 GameListPopUp(f, title);
5114 return LoadGame(f, gameNumber, title, FALSE);
5120 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5123 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5145 LoadNextPositionProc ()
5151 LoadPrevPositionProc ()
5157 ReloadPositionProc ()
5165 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5168 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5174 FileNamePopUp(_("Save game file name?"),
5175 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5176 appData.oldSaveStyle ? ".game" : ".pgn",
5183 FileNamePopUp(_("Save position file name?"),
5184 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5185 appData.oldSaveStyle ? ".pos" : ".fen",
5190 ReloadCmailMsgProc ()
5192 ReloadCmailMsgEvent(FALSE);
5195 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5196 char *selected_fen_position=NULL;
5199 SendPositionSelection (Widget w, Atom *selection, Atom *target,
5200 Atom *type_return, XtPointer *value_return,
5201 unsigned long *length_return, int *format_return)
5203 char *selection_tmp;
5205 if (!selected_fen_position) return False; /* should never happen */
5206 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5207 /* note: since no XtSelectionDoneProc was registered, Xt will
5208 * automatically call XtFree on the value returned. So have to
5209 * make a copy of it allocated with XtMalloc */
5210 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5211 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5213 *value_return=selection_tmp;
5214 *length_return=strlen(selection_tmp);
5215 *type_return=*target;
5216 *format_return = 8; /* bits per byte */
5218 } else if (*target == XA_TARGETS(xDisplay)) {
5219 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5220 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5221 targets_tmp[1] = XA_STRING;
5222 *value_return = targets_tmp;
5223 *type_return = XA_ATOM;
5226 // This code leads to a read of value_return out of bounds on 64-bit systems.
5227 // Other code which I have seen always sets *format_return to 32 independent of
5228 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5229 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5230 *format_return = 8 * sizeof(Atom);
5231 if (*format_return > 32) {
5232 *length_return *= *format_return / 32;
5233 *format_return = 32;
5236 *format_return = 32;
5244 /* note: when called from menu all parameters are NULL, so no clue what the
5245 * Widget which was clicked on was, or what the click event was
5251 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5252 * have a notion of a position that is selected but not copied.
5253 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5255 if(gameMode == EditPosition) EditPositionDone(TRUE);
5256 if (selected_fen_position) free(selected_fen_position);
5257 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5258 if (!selected_fen_position) return;
5259 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5261 SendPositionSelection,
5262 NULL/* lose_ownership_proc */ ,
5263 NULL/* transfer_done_proc */);
5264 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5266 SendPositionSelection,
5267 NULL/* lose_ownership_proc */ ,
5268 NULL/* transfer_done_proc */);
5272 CopyFENToClipboard ()
5273 { // wrapper to make call from back-end possible
5277 /* function called when the data to Paste is ready */
5279 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
5280 Atom *type, XtPointer value, unsigned long *len, int *format)
5283 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5284 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5285 EditPositionPasteFEN(fenstr);
5289 /* called when Paste Position button is pressed,
5290 * all parameters will be NULL */
5292 PastePositionProc ()
5294 XtGetSelectionValue(menuBarWidget,
5295 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5296 /* (XtSelectionCallbackProc) */ PastePositionCB,
5297 NULL, /* client_data passed to PastePositionCB */
5299 /* better to use the time field from the event that triggered the
5300 * call to this function, but that isn't trivial to get
5308 SendGameSelection (Widget w, Atom *selection, Atom *target,
5309 Atom *type_return, XtPointer *value_return,
5310 unsigned long *length_return, int *format_return)
5312 char *selection_tmp;
5314 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5315 FILE* f = fopen(gameCopyFilename, "r");
5318 if (f == NULL) return False;
5322 selection_tmp = XtMalloc(len + 1);
5323 count = fread(selection_tmp, 1, len, f);
5326 XtFree(selection_tmp);
5329 selection_tmp[len] = NULLCHAR;
5330 *value_return = selection_tmp;
5331 *length_return = len;
5332 *type_return = *target;
5333 *format_return = 8; /* bits per byte */
5335 } else if (*target == XA_TARGETS(xDisplay)) {
5336 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5337 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5338 targets_tmp[1] = XA_STRING;
5339 *value_return = targets_tmp;
5340 *type_return = XA_ATOM;
5343 // This code leads to a read of value_return out of bounds on 64-bit systems.
5344 // Other code which I have seen always sets *format_return to 32 independent of
5345 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5346 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5347 *format_return = 8 * sizeof(Atom);
5348 if (*format_return > 32) {
5349 *length_return *= *format_return / 32;
5350 *format_return = 32;
5353 *format_return = 32;
5365 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5366 * have a notion of a game that is selected but not copied.
5367 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5369 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5372 NULL/* lose_ownership_proc */ ,
5373 NULL/* transfer_done_proc */);
5374 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5377 NULL/* lose_ownership_proc */ ,
5378 NULL/* transfer_done_proc */);
5381 /* note: when called from menu all parameters are NULL, so no clue what the
5382 * Widget which was clicked on was, or what the click event was
5389 ret = SaveGameToFile(gameCopyFilename, FALSE);
5398 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5402 /* function called when the data to Paste is ready */
5404 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
5405 Atom *type, XtPointer value, unsigned long *len, int *format)
5408 if (value == NULL || *len == 0) {
5409 return; /* nothing had been selected to copy */
5411 f = fopen(gamePasteFilename, "w");
5413 DisplayError(_("Can't open temp file"), errno);
5416 fwrite(value, 1, *len, f);
5419 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5422 /* called when Paste Game button is pressed,
5423 * all parameters will be NULL */
5427 XtGetSelectionValue(menuBarWidget,
5428 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5429 /* (XtSelectionCallbackProc) */ PasteGameCB,
5430 NULL, /* client_data passed to PasteGameCB */
5432 /* better to use the time field from the event that triggered the
5433 * call to this function, but that isn't trivial to get
5455 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5465 if (!first.analysisSupport) {
5466 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5467 DisplayError(buf, 0);
5470 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5471 if (appData.icsActive) {
5472 if (gameMode != IcsObserving) {
5473 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5474 DisplayError(buf, 0);
5476 if (appData.icsEngineAnalyze) {
5477 if (appData.debugMode)
5478 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5484 /* if enable, use want disable icsEngineAnalyze */
5485 if (appData.icsEngineAnalyze) {
5490 appData.icsEngineAnalyze = TRUE;
5491 if (appData.debugMode)
5492 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5494 #ifndef OPTIONSDIALOG
5495 if (!appData.showThinking)
5505 if (!first.analysisSupport) {
5507 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5508 DisplayError(buf, 0);
5511 // Reset(FALSE, TRUE);
5512 #ifndef OPTIONSDIALOG
5513 if (!appData.showThinking)
5517 // FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5518 AnalysisPeriodicEvent(1);
5532 if (PopDown(1)) { // popdown succesful
5533 MarkMenuItem("Edit Comment", False);
5534 MarkMenuItem("Show Comments", False);
5535 } else // was not up
5542 if (!PopDown(4)) ICSInputBoxPopUp();
5548 UserAdjudicationEvent(+1);
5554 UserAdjudicationEvent(-1);
5560 UserAdjudicationEvent(0);
5564 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5566 if (shellUp[4] == True)
5571 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5572 { // [HGM] input: let up-arrow recall previous line from history
5579 if (!shellUp[4]) return;
5580 edit = boxOptions[0].handle;
5582 XtSetArg(args[j], XtNstring, &val); j++;
5583 XtGetValues(edit, args, j);
5584 val = PrevInHistory(val);
5585 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5586 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5588 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
5589 XawTextReplace(edit, 0, 0, &t);
5590 XawTextSetInsertionPoint(edit, 9999);
5595 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5596 { // [HGM] input: let down-arrow recall next line from history
5601 if (!shellUp[4]) return;
5602 edit = boxOptions[0].handle;
5603 val = NextInHistory();
5604 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5605 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5607 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
5608 XawTextReplace(edit, 0, 0, &t);
5609 XawTextSetInsertionPoint(edit, 9999);
5614 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5616 if (!TempBackwardActive) {
5617 TempBackwardActive = True;
5623 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5625 /* Check to see if triggered by a key release event for a repeating key.
5626 * If so the next queued event will be a key press of the same key at the same time */
5627 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
5629 XPeekEvent(xDisplay, &next);
5630 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
5631 next.xkey.keycode == event->xkey.keycode)
5635 TempBackwardActive = False;
5653 flipView = !flipView;
5654 DrawPosition(True, NULL);
5658 PonderNextMoveProc ()
5662 PonderNextMoveEvent(!appData.ponderNextMove);
5663 #ifndef OPTIONSDIALOG
5664 if (appData.ponderNextMove) {
5665 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5667 XtSetArg(args[0], XtNleftBitmap, None);
5669 XtSetValues(XtNameToWidget(menuBarWidget, "Ponder Next Move"),
5674 #ifndef OPTIONSDIALOG
5680 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
5682 if (appData.alwaysPromoteToQueen) {
5683 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5685 XtSetArg(args[0], XtNleftBitmap, None);
5687 XtSetValues(XtNameToWidget(menuBarWidget, "Always Queen"),
5692 AnimateDraggingProc ()
5696 appData.animateDragging = !appData.animateDragging;
5698 if (appData.animateDragging) {
5699 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5702 XtSetArg(args[0], XtNleftBitmap, None);
5704 XtSetValues(XtNameToWidget(menuBarWidget, "Animate Dragging"),
5709 AnimateMovingProc ()
5713 appData.animate = !appData.animate;
5715 if (appData.animate) {
5716 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5719 XtSetArg(args[0], XtNleftBitmap, None);
5721 XtSetValues(XtNameToWidget(menuBarWidget, "Animate Moving"),
5730 appData.autoCallFlag = !appData.autoCallFlag;
5732 if (appData.autoCallFlag) {
5733 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5735 XtSetArg(args[0], XtNleftBitmap, None);
5737 XtSetValues(XtNameToWidget(menuBarWidget, "Auto Flag"),
5746 appData.autoFlipView = !appData.autoFlipView;
5748 if (appData.autoFlipView) {
5749 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5751 XtSetArg(args[0], XtNleftBitmap, None);
5753 XtSetValues(XtNameToWidget(menuBarWidget, "Auto Flip View"),
5762 appData.blindfold = !appData.blindfold;
5764 if (appData.blindfold) {
5765 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5767 XtSetArg(args[0], XtNleftBitmap, None);
5769 XtSetValues(XtNameToWidget(menuBarWidget, "Blindfold"),
5772 DrawPosition(True, NULL);
5780 appData.testLegality = !appData.testLegality;
5782 if (appData.testLegality) {
5783 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5785 XtSetArg(args[0], XtNleftBitmap, None);
5787 XtSetValues(XtNameToWidget(menuBarWidget, "Test Legality"),
5797 if (appData.flashCount == 0) {
5798 appData.flashCount = 3;
5800 appData.flashCount = -appData.flashCount;
5803 if (appData.flashCount > 0) {
5804 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5806 XtSetArg(args[0], XtNleftBitmap, None);
5808 XtSetValues(XtNameToWidget(menuBarWidget, "Flash Moves"),
5814 HighlightDraggingProc ()
5818 appData.highlightDragging = !appData.highlightDragging;
5820 if (appData.highlightDragging) {
5821 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5823 XtSetArg(args[0], XtNleftBitmap, None);
5825 XtSetValues(XtNameToWidget(menuBarWidget,
5826 "Highlight Dragging"), args, 1);
5831 HighlightLastMoveProc ()
5835 appData.highlightLastMove = !appData.highlightLastMove;
5837 if (appData.highlightLastMove) {
5838 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5840 XtSetArg(args[0], XtNleftBitmap, None);
5842 XtSetValues(XtNameToWidget(menuBarWidget,
5843 "Highlight Last Move"), args, 1);
5847 HighlightArrowProc ()
5851 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
5853 if (appData.highlightMoveWithArrow) {
5854 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5856 XtSetArg(args[0], XtNleftBitmap, None);
5858 XtSetValues(XtNameToWidget(menuBarWidget,
5868 appData.icsAlarm = !appData.icsAlarm;
5870 if (appData.icsAlarm) {
5871 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5873 XtSetArg(args[0], XtNleftBitmap, None);
5875 XtSetValues(XtNameToWidget(menuBarWidget,
5876 "ICS Alarm"), args, 1);
5885 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
5887 if (appData.ringBellAfterMoves) {
5888 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5890 XtSetArg(args[0], XtNleftBitmap, None);
5892 XtSetValues(XtNameToWidget(menuBarWidget, "Move Sound"),
5901 appData.oneClick = !appData.oneClick;
5903 if (appData.oneClick) {
5904 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5906 XtSetArg(args[0], XtNleftBitmap, None);
5908 XtSetValues(XtNameToWidget(menuBarWidget, "OneClick"),
5913 PeriodicUpdatesProc ()
5917 PeriodicUpdatesEvent(!appData.periodicUpdates);
5919 if (appData.periodicUpdates) {
5920 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5922 XtSetArg(args[0], XtNleftBitmap, None);
5924 XtSetValues(XtNameToWidget(menuBarWidget, "Periodic Updates"),
5929 PopupExitMessageProc ()
5933 appData.popupExitMessage = !appData.popupExitMessage;
5935 if (appData.popupExitMessage) {
5936 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5938 XtSetArg(args[0], XtNleftBitmap, None);
5940 XtSetValues(XtNameToWidget(menuBarWidget,
5941 "Popup Exit Message"), args, 1);
5945 PopupMoveErrorsProc ()
5949 appData.popupMoveErrors = !appData.popupMoveErrors;
5951 if (appData.popupMoveErrors) {
5952 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5954 XtSetArg(args[0], XtNleftBitmap, None);
5956 XtSetValues(XtNameToWidget(menuBarWidget, "Popup Move Errors"),
5966 appData.premove = !appData.premove;
5968 if (appData.premove) {
5969 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5971 XtSetArg(args[0], XtNleftBitmap, None);
5973 XtSetValues(XtNameToWidget(menuBarWidget,
5974 "Premove"), args, 1);
5983 appData.showCoords = !appData.showCoords;
5985 if (appData.showCoords) {
5986 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5988 XtSetArg(args[0], XtNleftBitmap, None);
5990 XtSetValues(XtNameToWidget(menuBarWidget, "Show Coords"),
5993 DrawPosition(True, NULL);
5999 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6000 ShowThinkingEvent();
6008 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6009 ShowThinkingEvent();
6011 MarkMenuItem("Hide Thinking", appData.hideThinkingFromHuman);
6020 saveSettingsOnExit = !saveSettingsOnExit;
6022 MarkMenuItem("Save Settings on Exit", saveSettingsOnExit);
6028 SaveSettings(settingsFileName);
6035 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6041 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6042 { // called as key binding
6045 if (nprms && *nprms > 0)
6049 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6055 { // called from menu
6056 ManInner(NULL, NULL, NULL, NULL);
6063 snprintf(buf, MSG_SIZ, "%s mailto:bug-xboard@gnu.org", appData.sysOpen);
6071 snprintf(buf, MSG_SIZ, "%s http://www.gnu.org/software/xboard/user_guide/UserGuide.html", appData.sysOpen);
6079 snprintf(buf, MSG_SIZ, "%s http://www.gnu.org/software/xboard/", appData.sysOpen);
6087 snprintf(buf, MSG_SIZ, "%s http://www.gnu.org/software/xboard/whats_new/portal.html", appData.sysOpen);
6094 char buf[2 * MSG_SIZ];
6096 char *zippy = _(" (with Zippy code)");
6100 snprintf(buf, sizeof(buf),
6102 "Copyright 1991 Digital Equipment Corporation\n"
6103 "Enhancements Copyright 1992-2012 Free Software Foundation\n"
6104 "Enhancements Copyright 2005 Alessandro Scotti\n\n"
6105 "%s is free software and carries NO WARRANTY;"
6106 "see the file COPYING for more information.\n\n"
6107 "Visit XBoard on the web at: http://www.gnu.org/software/xboard/\n"
6108 "Check out the newest features at: http://www.gnu.org/software/xboard/whats_new.html\n\n"
6109 "Report bugs via email at: <bug-xboard@gnu.org>\n\n"
6111 programVersion, zippy, PACKAGE);
6112 ErrorPopUp(_("About XBoard"), buf, FALSE);
6118 appData.debugMode = !appData.debugMode;
6128 DisplayMessage (char *message, char *extMessage)
6130 /* display a message in the message widget */
6139 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6144 message = extMessage;
6148 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6150 /* need to test if messageWidget already exists, since this function
6151 can also be called during the startup, if for example a Xresource
6152 is not set up correctly */
6155 XtSetArg(arg, XtNlabel, message);
6156 XtSetValues(messageWidget, &arg, 1);
6163 DisplayTitle (char *text)
6167 char title[MSG_SIZ];
6170 if (text == NULL) text = "";
6172 if (appData.titleInWindow) {
6174 XtSetArg(args[i], XtNlabel, text); i++;
6175 XtSetValues(titleWidget, args, i);
6178 if (*text != NULLCHAR) {
6179 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6180 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6181 } else if (appData.icsActive) {
6182 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6183 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6184 } else if (appData.cmailGameName[0] != NULLCHAR) {
6185 snprintf(icon, sizeof(icon), "%s", "CMail");
6186 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6188 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6189 } else if (gameInfo.variant == VariantGothic) {
6190 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6191 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6194 } else if (gameInfo.variant == VariantFalcon) {
6195 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6196 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6198 } else if (appData.noChessProgram) {
6199 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6200 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6202 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6203 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6206 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6207 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6208 XtSetValues(shellWidget, args, i);
6209 XSync(xDisplay, False);
6214 DisplayError (String message, int error)
6219 if (appData.debugMode || appData.matchMode) {
6220 fprintf(stderr, "%s: %s\n", programName, message);
6223 if (appData.debugMode || appData.matchMode) {
6224 fprintf(stderr, "%s: %s: %s\n",
6225 programName, message, strerror(error));
6227 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6230 ErrorPopUp(_("Error"), message, FALSE);
6235 DisplayMoveError (String message)
6239 DrawPosition(FALSE, NULL);
6240 if (appData.debugMode || appData.matchMode) {
6241 fprintf(stderr, "%s: %s\n", programName, message);
6243 if (appData.popupMoveErrors) {
6244 ErrorPopUp(_("Error"), message, FALSE);
6246 DisplayMessage(message, "");
6252 DisplayFatalError (String message, int error, int status)
6256 errorExitStatus = status;
6258 fprintf(stderr, "%s: %s\n", programName, message);
6260 fprintf(stderr, "%s: %s: %s\n",
6261 programName, message, strerror(error));
6262 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6265 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6266 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6273 DisplayInformation (String message)
6276 ErrorPopUp(_("Information"), message, TRUE);
6280 DisplayNote (String message)
6283 ErrorPopUp(_("Note"), message, FALSE);
6287 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
6293 DisplayIcsInteractionTitle (String message)
6295 if (oldICSInteractionTitle == NULL) {
6296 /* Magic to find the old window title, adapted from vim */
6297 char *wina = getenv("WINDOWID");
6299 Window win = (Window) atoi(wina);
6300 Window root, parent, *children;
6301 unsigned int nchildren;
6302 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6304 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6305 if (!XQueryTree(xDisplay, win, &root, &parent,
6306 &children, &nchildren)) break;
6307 if (children) XFree((void *)children);
6308 if (parent == root || parent == 0) break;
6311 XSetErrorHandler(oldHandler);
6313 if (oldICSInteractionTitle == NULL) {
6314 oldICSInteractionTitle = "xterm";
6317 printf("\033]0;%s\007", message);
6321 char pendingReplyPrefix[MSG_SIZ];
6322 ProcRef pendingReplyPR;
6325 AskQuestionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6328 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
6332 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6336 AskQuestionPopDown ()
6338 if (!askQuestionUp) return;
6339 XtPopdown(askQuestionShell);
6340 XtDestroyWidget(askQuestionShell);
6341 askQuestionUp = False;
6345 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6351 reply = XawDialogGetValueString(w = XtParent(w));
6352 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
6353 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
6354 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
6355 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
6356 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6357 AskQuestionPopDown();
6359 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
6363 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
6368 XtSetArg(args[0], XtNlabel, &name);
6369 XtGetValues(w, args, 1);
6371 if (strcmp(name, _("cancel")) == 0) {
6372 AskQuestionPopDown();
6374 AskQuestionReplyAction(w, NULL, NULL, NULL);
6379 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
6382 Widget popup, layout, dialog, edit;
6388 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
6389 pendingReplyPR = pr;
6392 XtSetArg(args[i], XtNresizable, True); i++;
6393 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
6394 askQuestionShell = popup =
6395 XtCreatePopupShell(title, transientShellWidgetClass,
6396 shellWidget, args, i);
6399 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
6400 layoutArgs, XtNumber(layoutArgs));
6403 XtSetArg(args[i], XtNlabel, question); i++;
6404 XtSetArg(args[i], XtNvalue, ""); i++;
6405 XtSetArg(args[i], XtNborderWidth, 0); i++;
6406 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
6409 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
6410 (XtPointer) dialog);
6411 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
6412 (XtPointer) dialog);
6414 XtRealizeWidget(popup);
6415 CatchDeleteWindow(popup, "AskQuestionPopDown");
6417 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
6418 &x, &y, &win_x, &win_y, &mask);
6420 XtSetArg(args[0], XtNx, x - 10);
6421 XtSetArg(args[1], XtNy, y - 30);
6422 XtSetValues(popup, args, 2);
6424 XtPopup(popup, XtGrabExclusive);
6425 askQuestionUp = True;
6427 edit = XtNameToWidget(dialog, "*value");
6428 XtSetKeyboardFocus(popup, edit);
6433 PlaySound (char *name)
6435 if (*name == NULLCHAR) {
6437 } else if (strcmp(name, "$") == 0) {
6438 putc(BELLCHAR, stderr);
6441 char *prefix = "", *sep = "";
6442 if(appData.soundProgram[0] == NULLCHAR) return;
6443 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
6444 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
6452 PlaySound(appData.soundMove);
6458 PlaySound(appData.soundIcsWin);
6464 PlaySound(appData.soundIcsLoss);
6470 PlaySound(appData.soundIcsDraw);
6474 PlayIcsUnfinishedSound ()
6476 PlaySound(appData.soundIcsUnfinished);
6482 PlaySound(appData.soundIcsAlarm);
6488 PlaySound(appData.soundTell);
6494 system("stty echo");
6501 system("stty -echo");
6506 RunCommand (char *buf)
6512 Colorize (ColorClass cc, int continuation)
6515 int count, outCount, error;
6517 if (textColors[(int)cc].bg > 0) {
6518 if (textColors[(int)cc].fg > 0) {
6519 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
6520 textColors[(int)cc].fg, textColors[(int)cc].bg);
6522 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
6523 textColors[(int)cc].bg);
6526 if (textColors[(int)cc].fg > 0) {
6527 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
6528 textColors[(int)cc].fg);
6530 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
6533 count = strlen(buf);
6534 outCount = OutputToProcess(NoProc, buf, count, &error);
6535 if (outCount < count) {
6536 DisplayFatalError(_("Error writing to display"), error, 1);
6539 if (continuation) return;
6542 PlaySound(appData.soundShout);
6545 PlaySound(appData.soundSShout);
6548 PlaySound(appData.soundChannel1);
6551 PlaySound(appData.soundChannel);
6554 PlaySound(appData.soundKibitz);
6557 PlaySound(appData.soundTell);
6559 case ColorChallenge:
6560 PlaySound(appData.soundChallenge);
6563 PlaySound(appData.soundRequest);
6566 PlaySound(appData.soundSeek);
6578 return getpwuid(getuid())->pw_name;
6582 ExpandPathName (char *path)
6584 static char static_buf[4*MSG_SIZ];
6585 char *d, *s, buf[4*MSG_SIZ];
6591 while (*s && isspace(*s))
6600 if (*(s+1) == '/') {
6601 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
6605 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
6606 { char *p; if(p = strchr(buf, '/')) *p = 0; }
6607 pwd = getpwnam(buf);
6610 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
6614 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
6615 strcat(d, strchr(s+1, '/'));
6619 safeStrCpy(d, s, 4*MSG_SIZ );
6627 static char host_name[MSG_SIZ];
6629 #if HAVE_GETHOSTNAME
6630 gethostname(host_name, MSG_SIZ);
6632 #else /* not HAVE_GETHOSTNAME */
6633 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
6634 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
6636 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
6638 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
6639 #endif /* not HAVE_GETHOSTNAME */
6642 XtIntervalId delayedEventTimerXID = 0;
6643 DelayedEventCallback delayedEventCallback = 0;
6648 delayedEventTimerXID = 0;
6649 delayedEventCallback();
6653 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
6655 if(delayedEventTimerXID && delayedEventCallback == cb)
6656 // [HGM] alive: replace, rather than add or flush identical event
6657 XtRemoveTimeOut(delayedEventTimerXID);
6658 delayedEventCallback = cb;
6659 delayedEventTimerXID =
6660 XtAppAddTimeOut(appContext, millisec,
6661 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
6664 DelayedEventCallback
6667 if (delayedEventTimerXID) {
6668 return delayedEventCallback;
6675 CancelDelayedEvent ()
6677 if (delayedEventTimerXID) {
6678 XtRemoveTimeOut(delayedEventTimerXID);
6679 delayedEventTimerXID = 0;
6683 XtIntervalId loadGameTimerXID = 0;
6686 LoadGameTimerRunning ()
6688 return loadGameTimerXID != 0;
6692 StopLoadGameTimer ()
6694 if (loadGameTimerXID != 0) {
6695 XtRemoveTimeOut(loadGameTimerXID);
6696 loadGameTimerXID = 0;
6704 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
6706 loadGameTimerXID = 0;
6711 StartLoadGameTimer (long millisec)
6714 XtAppAddTimeOut(appContext, millisec,
6715 (XtTimerCallbackProc) LoadGameTimerCallback,
6719 XtIntervalId analysisClockXID = 0;
6722 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
6724 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
6725 || appData.icsEngineAnalyze) { // [DM]
6726 AnalysisPeriodicEvent(0);
6727 StartAnalysisClock();
6732 StartAnalysisClock ()
6735 XtAppAddTimeOut(appContext, 2000,
6736 (XtTimerCallbackProc) AnalysisClockCallback,
6740 XtIntervalId clockTimerXID = 0;
6743 ClockTimerRunning ()
6745 return clockTimerXID != 0;
6751 if (clockTimerXID != 0) {
6752 XtRemoveTimeOut(clockTimerXID);
6761 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
6768 StartClockTimer (long millisec)
6771 XtAppAddTimeOut(appContext, millisec,
6772 (XtTimerCallbackProc) ClockTimerCallback,
6777 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
6782 /* check for low time warning */
6783 Pixel foregroundOrWarningColor = timerForegroundPixel;
6786 appData.lowTimeWarning &&
6787 (timer / 1000) < appData.icsAlarmTime)
6788 foregroundOrWarningColor = lowTimeWarningColor;
6790 if (appData.clockMode) {
6791 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
6792 XtSetArg(args[0], XtNlabel, buf);
6794 snprintf(buf, MSG_SIZ, "%s ", color);
6795 XtSetArg(args[0], XtNlabel, buf);
6800 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
6801 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
6803 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
6804 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
6807 XtSetValues(w, args, 3);
6811 DisplayWhiteClock (long timeRemaining, int highlight)
6815 if(appData.noGUI) return;
6816 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
6817 if (highlight && iconPixmap == bIconPixmap) {
6818 iconPixmap = wIconPixmap;
6819 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
6820 XtSetValues(shellWidget, args, 1);
6825 DisplayBlackClock (long timeRemaining, int highlight)
6829 if(appData.noGUI) return;
6830 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
6831 if (highlight && iconPixmap == wIconPixmap) {
6832 iconPixmap = bIconPixmap;
6833 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
6834 XtSetValues(shellWidget, args, 1);
6853 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
6857 int to_prog[2], from_prog[2];
6861 if (appData.debugMode) {
6862 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
6865 /* We do NOT feed the cmdLine to the shell; we just
6866 parse it into blank-separated arguments in the
6867 most simple-minded way possible.
6870 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
6873 while(*p == ' ') p++;
6875 if(*p == '"' || *p == '\'')
6876 p = strchr(++argv[i-1], *p);
6877 else p = strchr(p, ' ');
6878 if (p == NULL) break;
6883 SetUpChildIO(to_prog, from_prog);
6885 if ((pid = fork()) == 0) {
6887 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
6888 close(to_prog[1]); // first close the unused pipe ends
6889 close(from_prog[0]);
6890 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
6891 dup2(from_prog[1], 1);
6892 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
6893 close(from_prog[1]); // and closing again loses one of the pipes!
6894 if(fileno(stderr) >= 2) // better safe than sorry...
6895 dup2(1, fileno(stderr)); /* force stderr to the pipe */
6897 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
6902 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
6904 execvp(argv[0], argv);
6906 /* If we get here, exec failed */
6911 /* Parent process */
6913 close(from_prog[1]);
6915 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
6918 cp->fdFrom = from_prog[0];
6919 cp->fdTo = to_prog[1];
6924 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
6926 AlarmCallBack (int n)
6932 DestroyChildProcess (ProcRef pr, int signalType)
6934 ChildProc *cp = (ChildProc *) pr;
6936 if (cp->kind != CPReal) return;
6938 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
6939 signal(SIGALRM, AlarmCallBack);
6941 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
6942 kill(cp->pid, SIGKILL); // kill it forcefully
6943 wait((int *) 0); // and wait again
6947 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
6949 /* Process is exiting either because of the kill or because of
6950 a quit command sent by the backend; either way, wait for it to die.
6959 InterruptChildProcess (ProcRef pr)
6961 ChildProc *cp = (ChildProc *) pr;
6963 if (cp->kind != CPReal) return;
6964 (void) kill(cp->pid, SIGINT); /* stop it thinking */
6968 OpenTelnet (char *host, char *port, ProcRef *pr)
6970 char cmdLine[MSG_SIZ];
6972 if (port[0] == NULLCHAR) {
6973 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
6975 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
6977 return StartChildProcess(cmdLine, "", pr);
6981 OpenTCP (char *host, char *port, ProcRef *pr)
6984 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
6985 #else /* !OMIT_SOCKETS */
6986 struct addrinfo hints;
6987 struct addrinfo *ais, *ai;
6992 memset(&hints, 0, sizeof(hints));
6993 hints.ai_family = AF_UNSPEC;
6994 hints.ai_socktype = SOCK_STREAM;
6996 error = getaddrinfo(host, port, &hints, &ais);
6998 /* a getaddrinfo error is not an errno, so can't return it */
6999 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7000 host, port, gai_strerror(error));
7004 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7005 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7009 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7022 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7028 #endif /* !OMIT_SOCKETS */
7034 OpenCommPort (char *name, ProcRef *pr)
7039 fd = open(name, 2, 0);
7040 if (fd < 0) return errno;
7042 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7053 OpenLoopback (ProcRef *pr)
7058 SetUpChildIO(to, from);
7060 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7063 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7071 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
7073 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7077 #define INPUT_SOURCE_BUF_SIZE 8192
7086 char buf[INPUT_SOURCE_BUF_SIZE];
7091 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
7093 InputSource *is = (InputSource *) closure;
7098 if (is->lineByLine) {
7099 count = read(is->fd, is->unused,
7100 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7102 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7105 is->unused += count;
7107 while (p < is->unused) {
7108 q = memchr(p, '\n', is->unused - p);
7109 if (q == NULL) break;
7111 (is->func)(is, is->closure, p, q - p, 0);
7115 while (p < is->unused) {
7120 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7125 (is->func)(is, is->closure, is->buf, count, error);
7130 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
7133 ChildProc *cp = (ChildProc *) pr;
7135 is = (InputSource *) calloc(1, sizeof(InputSource));
7136 is->lineByLine = lineByLine;
7140 is->fd = fileno(stdin);
7142 is->kind = cp->kind;
7143 is->fd = cp->fdFrom;
7146 is->unused = is->buf;
7149 is->xid = XtAppAddInput(appContext, is->fd,
7150 (XtPointer) (XtInputReadMask),
7151 (XtInputCallbackProc) DoInputCallback,
7153 is->closure = closure;
7154 return (InputSourceRef) is;
7158 RemoveInputSource (InputSourceRef isr)
7160 InputSource *is = (InputSource *) isr;
7162 if (is->xid == 0) return;
7163 XtRemoveInput(is->xid);
7168 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
7170 static int line = 0;
7171 ChildProc *cp = (ChildProc *) pr;
7176 if (appData.noJoin || !appData.useInternalWrap)
7177 outCount = fwrite(message, 1, count, stdout);
7180 int width = get_term_width();
7181 int len = wrap(NULL, message, count, width, &line);
7182 char *msg = malloc(len);
7186 outCount = fwrite(message, 1, count, stdout);
7189 dbgchk = wrap(msg, message, count, width, &line);
7190 if (dbgchk != len && appData.debugMode)
7191 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7192 outCount = fwrite(msg, 1, dbgchk, stdout);
7198 outCount = write(cp->fdTo, message, count);
7208 /* Output message to process, with "ms" milliseconds of delay
7209 between each character. This is needed when sending the logon
7210 script to ICC, which for some reason doesn't like the
7211 instantaneous send. */
7213 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
7215 ChildProc *cp = (ChildProc *) pr;
7220 r = write(cp->fdTo, message++, 1);
7233 /**** Animation code by Hugh Fisher, DCS, ANU.
7235 Known problem: if a window overlapping the board is
7236 moved away while a piece is being animated underneath,
7237 the newly exposed area won't be updated properly.
7238 I can live with this.
7240 Known problem: if you look carefully at the animation
7241 of pieces in mono mode, they are being drawn as solid
7242 shapes without interior detail while moving. Fixing
7243 this would be a major complication for minimal return.
7246 /* Masks for XPM pieces. Black and white pieces can have
7247 different shapes, but in the interest of retaining my
7248 sanity pieces must have the same outline on both light
7249 and dark squares, and all pieces must use the same
7250 background square colors/images. */
7252 static int xpmDone = 0;
7255 CreateAnimMasks (int pieceDepth)
7261 unsigned long plane;
7264 /* Need a bitmap just to get a GC with right depth */
7265 buf = XCreatePixmap(xDisplay, xBoardWindow,
7267 values.foreground = 1;
7268 values.background = 0;
7269 /* Don't use XtGetGC, not read only */
7270 maskGC = XCreateGC(xDisplay, buf,
7271 GCForeground | GCBackground, &values);
7272 XFreePixmap(xDisplay, buf);
7274 buf = XCreatePixmap(xDisplay, xBoardWindow,
7275 squareSize, squareSize, pieceDepth);
7276 values.foreground = XBlackPixel(xDisplay, xScreen);
7277 values.background = XWhitePixel(xDisplay, xScreen);
7278 bufGC = XCreateGC(xDisplay, buf,
7279 GCForeground | GCBackground, &values);
7281 for (piece = WhitePawn; piece <= BlackKing; piece++) {
7282 /* Begin with empty mask */
7283 if(!xpmDone) // [HGM] pieces: keep using existing
7284 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7285 squareSize, squareSize, 1);
7286 XSetFunction(xDisplay, maskGC, GXclear);
7287 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7288 0, 0, squareSize, squareSize);
7290 /* Take a copy of the piece */
7295 XSetFunction(xDisplay, bufGC, GXcopy);
7296 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
7298 0, 0, squareSize, squareSize, 0, 0);
7300 /* XOR the background (light) over the piece */
7301 XSetFunction(xDisplay, bufGC, GXxor);
7303 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7304 0, 0, squareSize, squareSize, 0, 0);
7306 XSetForeground(xDisplay, bufGC, lightSquareColor);
7307 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7310 /* We now have an inverted piece image with the background
7311 erased. Construct mask by just selecting all the non-zero
7312 pixels - no need to reconstruct the original image. */
7313 XSetFunction(xDisplay, maskGC, GXor);
7315 /* Might be quicker to download an XImage and create bitmap
7316 data from it rather than this N copies per piece, but it
7317 only takes a fraction of a second and there is a much
7318 longer delay for loading the pieces. */
7319 for (n = 0; n < pieceDepth; n ++) {
7320 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7321 0, 0, squareSize, squareSize,
7327 XFreePixmap(xDisplay, buf);
7328 XFreeGC(xDisplay, bufGC);
7329 XFreeGC(xDisplay, maskGC);
7333 InitAnimState (AnimState *anim, XWindowAttributes *info)
7338 /* Each buffer is square size, same depth as window */
7339 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
7340 squareSize, squareSize, info->depth);
7341 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
7342 squareSize, squareSize, info->depth);
7344 /* Create a plain GC for blitting */
7345 mask = GCForeground | GCBackground | GCFunction |
7346 GCPlaneMask | GCGraphicsExposures;
7347 values.foreground = XBlackPixel(xDisplay, xScreen);
7348 values.background = XWhitePixel(xDisplay, xScreen);
7349 values.function = GXcopy;
7350 values.plane_mask = AllPlanes;
7351 values.graphics_exposures = False;
7352 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
7354 /* Piece will be copied from an existing context at
7355 the start of each new animation/drag. */
7356 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
7358 /* Outline will be a read-only copy of an existing */
7359 anim->outlineGC = None;
7365 XWindowAttributes info;
7367 if (xpmDone && gameInfo.variant == oldVariant) return;
7368 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
7369 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
7371 InitAnimState(&game, &info);
7372 InitAnimState(&player, &info);
7374 /* For XPM pieces, we need bitmaps to use as masks. */
7376 CreateAnimMasks(info.depth), xpmDone = 1;
7381 static Boolean frameWaiting;
7384 FrameAlarm (int sig)
7386 frameWaiting = False;
7387 /* In case System-V style signals. Needed?? */
7388 signal(SIGALRM, FrameAlarm);
7392 FrameDelay (int time)
7394 struct itimerval delay;
7396 XSync(xDisplay, False);
7399 frameWaiting = True;
7400 signal(SIGALRM, FrameAlarm);
7401 delay.it_interval.tv_sec =
7402 delay.it_value.tv_sec = time / 1000;
7403 delay.it_interval.tv_usec =
7404 delay.it_value.tv_usec = (time % 1000) * 1000;
7405 setitimer(ITIMER_REAL, &delay, NULL);
7406 while (frameWaiting) pause();
7407 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
7408 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
7409 setitimer(ITIMER_REAL, &delay, NULL);
7416 FrameDelay (int time)
7418 XSync(xDisplay, False);
7420 usleep(time * 1000);
7431 /* Convert board position to corner of screen rect and color */
7434 ScreenSquare (int column, int row, XPoint *pt, int *color)
7437 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
7438 pt->y = lineGap + row * (squareSize + lineGap);
7440 pt->x = lineGap + column * (squareSize + lineGap);
7441 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
7443 *color = SquareColor(row, column);
7446 /* Convert window coords to square */
7449 BoardSquare (int x, int y, int *column, int *row)
7451 *column = EventToSquare(x, BOARD_WIDTH);
7452 if (flipView && *column >= 0)
7453 *column = BOARD_WIDTH - 1 - *column;
7454 *row = EventToSquare(y, BOARD_HEIGHT);
7455 if (!flipView && *row >= 0)
7456 *row = BOARD_HEIGHT - 1 - *row;
7461 #undef Max /* just in case */
7463 #define Max(a, b) ((a) > (b) ? (a) : (b))
7464 #define Min(a, b) ((a) < (b) ? (a) : (b))
7467 SetRect (XRectangle *rect, int x, int y, int width, int height)
7471 rect->width = width;
7472 rect->height = height;
7475 /* Test if two frames overlap. If they do, return
7476 intersection rect within old and location of
7477 that rect within new. */
7480 Intersect ( XPoint *old, XPoint *new, int size, XRectangle *area, XPoint *pt)
7482 if (old->x > new->x + size || new->x > old->x + size ||
7483 old->y > new->y + size || new->y > old->y + size) {
7486 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
7487 size - abs(old->x - new->x), size - abs(old->y - new->y));
7488 pt->x = Max(old->x - new->x, 0);
7489 pt->y = Max(old->y - new->y, 0);
7494 /* For two overlapping frames, return the rect(s)
7495 in the old that do not intersect with the new. */
7498 CalcUpdateRects (XPoint *old, XPoint *new, int size, XRectangle update[], int *nUpdates)
7502 /* If old = new (shouldn't happen) then nothing to draw */
7503 if (old->x == new->x && old->y == new->y) {
7507 /* Work out what bits overlap. Since we know the rects
7508 are the same size we don't need a full intersect calc. */
7510 /* Top or bottom edge? */
7511 if (new->y > old->y) {
7512 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
7514 } else if (old->y > new->y) {
7515 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
7516 size, old->y - new->y);
7519 /* Left or right edge - don't overlap any update calculated above. */
7520 if (new->x > old->x) {
7521 SetRect(&(update[count]), old->x, Max(new->y, old->y),
7522 new->x - old->x, size - abs(new->y - old->y));
7524 } else if (old->x > new->x) {
7525 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
7526 old->x - new->x, size - abs(new->y - old->y));
7533 /* Generate a series of frame coords from start->mid->finish.
7534 The movement rate doubles until the half way point is
7535 reached, then halves back down to the final destination,
7536 which gives a nice slow in/out effect. The algorithmn
7537 may seem to generate too many intermediates for short
7538 moves, but remember that the purpose is to attract the
7539 viewers attention to the piece about to be moved and
7540 then to where it ends up. Too few frames would be less
7544 Tween (XPoint *start, XPoint *mid, XPoint *finish, int factor, XPoint frames[], int *nFrames)
7546 int fraction, n, count;
7550 /* Slow in, stepping 1/16th, then 1/8th, ... */
7552 for (n = 0; n < factor; n++)
7554 for (n = 0; n < factor; n++) {
7555 frames[count].x = start->x + (mid->x - start->x) / fraction;
7556 frames[count].y = start->y + (mid->y - start->y) / fraction;
7558 fraction = fraction / 2;
7562 frames[count] = *mid;
7565 /* Slow out, stepping 1/2, then 1/4, ... */
7567 for (n = 0; n < factor; n++) {
7568 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
7569 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
7571 fraction = fraction * 2;
7576 /* Draw a piece on the screen without disturbing what's there */
7579 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
7583 /* Bitmap for piece being moved. */
7584 if (appData.monoMode) {
7585 *mask = *pieceToSolid(piece);
7586 } else if (useImages) {
7588 *mask = xpmMask[piece];
7590 *mask = ximMaskPm[piece];
7593 *mask = *pieceToSolid(piece);
7596 /* GC for piece being moved. Square color doesn't matter, but
7597 since it gets modified we make a copy of the original. */
7599 if (appData.monoMode)
7604 if (appData.monoMode)
7609 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
7611 /* Outline only used in mono mode and is not modified */
7613 *outline = bwPieceGC;
7615 *outline = wbPieceGC;
7619 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
7624 /* Draw solid rectangle which will be clipped to shape of piece */
7625 XFillRectangle(xDisplay, dest, clip,
7626 0, 0, squareSize, squareSize);
7627 if (appData.monoMode)
7628 /* Also draw outline in contrasting color for black
7629 on black / white on white cases */
7630 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
7631 0, 0, squareSize, squareSize, 0, 0, 1);
7633 /* Copy the piece */
7638 if(appData.upsideDown && flipView) kind ^= 2;
7639 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
7641 0, 0, squareSize, squareSize,
7646 /* Animate the movement of a single piece */
7649 BeginAnimation (AnimState *anim, ChessSquare piece, int startColor, XPoint *start)
7653 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
7654 /* The old buffer is initialised with the start square (empty) */
7655 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
7656 anim->prevFrame = *start;
7658 /* The piece will be drawn using its own bitmap as a matte */
7659 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
7660 XSetClipMask(xDisplay, anim->pieceGC, mask);
7664 AnimationFrame (AnimState *anim, XPoint *frame, ChessSquare piece)
7666 XRectangle updates[4];
7671 /* Save what we are about to draw into the new buffer */
7672 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
7673 frame->x, frame->y, squareSize, squareSize,
7676 /* Erase bits of the previous frame */
7677 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
7678 /* Where the new frame overlapped the previous,
7679 the contents in newBuf are wrong. */
7680 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
7681 overlap.x, overlap.y,
7682 overlap.width, overlap.height,
7684 /* Repaint the areas in the old that don't overlap new */
7685 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
7686 for (i = 0; i < count; i++)
7687 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
7688 updates[i].x - anim->prevFrame.x,
7689 updates[i].y - anim->prevFrame.y,
7690 updates[i].width, updates[i].height,
7691 updates[i].x, updates[i].y);
7693 /* Easy when no overlap */
7694 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
7695 0, 0, squareSize, squareSize,
7696 anim->prevFrame.x, anim->prevFrame.y);
7699 /* Save this frame for next time round */
7700 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
7701 0, 0, squareSize, squareSize,
7703 anim->prevFrame = *frame;
7705 /* Draw piece over original screen contents, not current,
7706 and copy entire rect. Wipes out overlapping piece images. */
7707 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
7708 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
7709 0, 0, squareSize, squareSize,
7710 frame->x, frame->y);
7714 EndAnimation (AnimState *anim, XPoint *finish)
7716 XRectangle updates[4];
7721 /* The main code will redraw the final square, so we
7722 only need to erase the bits that don't overlap. */
7723 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
7724 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
7725 for (i = 0; i < count; i++)
7726 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
7727 updates[i].x - anim->prevFrame.x,
7728 updates[i].y - anim->prevFrame.y,
7729 updates[i].width, updates[i].height,
7730 updates[i].x, updates[i].y);
7732 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
7733 0, 0, squareSize, squareSize,
7734 anim->prevFrame.x, anim->prevFrame.y);
7739 FrameSequence (AnimState *anim, ChessSquare piece, int startColor, XPoint *start, XPoint *finish, XPoint frames[], int nFrames)
7743 BeginAnimation(anim, piece, startColor, start);
7744 for (n = 0; n < nFrames; n++) {
7745 AnimationFrame(anim, &(frames[n]), piece);
7746 FrameDelay(appData.animSpeed);
7748 EndAnimation(anim, finish);
7752 AnimateAtomicCapture (Board board, int fromX, int fromY, int toX, int toY)
7755 ChessSquare piece = board[fromY][toY];
7756 board[fromY][toY] = EmptySquare;
7757 DrawPosition(FALSE, board);
7759 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
7760 y = lineGap + toY * (squareSize + lineGap);
7762 x = lineGap + toX * (squareSize + lineGap);
7763 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
7765 for(i=1; i<4*kFactor; i++) {
7766 int r = squareSize * 9 * i/(20*kFactor - 5);
7767 XFillArc(xDisplay, xBoardWindow, highlineGC,
7768 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
7769 FrameDelay(appData.animSpeed);
7771 board[fromY][toY] = piece;
7774 /* Main control logic for deciding what to animate and how */
7777 AnimateMove (Board board, int fromX, int fromY, int toX, int toY)
7781 XPoint start, finish, mid;
7782 XPoint frames[kFactor * 2 + 1];
7783 int nFrames, startColor, endColor;
7785 /* Are we animating? */
7786 if (!appData.animate || appData.blindfold)
7789 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
7790 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
7791 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
7793 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
7794 piece = board[fromY][fromX];
7795 if (piece >= EmptySquare) return;
7800 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
7803 ScreenSquare(fromX, fromY, &start, &startColor);
7804 ScreenSquare(toX, toY, &finish, &endColor);
7807 /* Knight: make straight movement then diagonal */
7808 if (abs(toY - fromY) < abs(toX - fromX)) {
7809 mid.x = start.x + (finish.x - start.x) / 2;
7813 mid.y = start.y + (finish.y - start.y) / 2;
7816 mid.x = start.x + (finish.x - start.x) / 2;
7817 mid.y = start.y + (finish.y - start.y) / 2;
7820 /* Don't use as many frames for very short moves */
7821 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
7822 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
7824 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
7825 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
7826 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
7828 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
7829 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
7832 /* Be sure end square is redrawn */
7833 damage[0][toY][toX] = True;
7837 DragPieceBegin (int x, int y, Boolean instantly)
7839 int boardX, boardY, color;
7842 /* Are we animating? */
7843 if (!appData.animateDragging || appData.blindfold)
7846 /* Figure out which square we start in and the
7847 mouse position relative to top left corner. */
7848 BoardSquare(x, y, &boardX, &boardY);
7849 player.startBoardX = boardX;
7850 player.startBoardY = boardY;
7851 ScreenSquare(boardX, boardY, &corner, &color);
7852 player.startSquare = corner;
7853 player.startColor = color;
7854 /* As soon as we start dragging, the piece will jump slightly to
7855 be centered over the mouse pointer. */
7856 player.mouseDelta.x = squareSize/2;
7857 player.mouseDelta.y = squareSize/2;
7858 /* Initialise animation */
7859 player.dragPiece = PieceForSquare(boardX, boardY);
7861 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
7862 player.dragActive = True;
7863 BeginAnimation(&player, player.dragPiece, color, &corner);
7864 /* Mark this square as needing to be redrawn. Note that
7865 we don't remove the piece though, since logically (ie
7866 as seen by opponent) the move hasn't been made yet. */
7867 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
7868 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
7869 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
7870 corner.x, corner.y, squareSize, squareSize,
7871 0, 0); // [HGM] zh: unstack in stead of grab
7872 if(gatingPiece != EmptySquare) {
7873 /* Kludge alert: When gating we want the introduced
7874 piece to appear on the from square. To generate an
7875 image of it, we draw it on the board, copy the image,
7876 and draw the original piece again. */
7877 ChessSquare piece = boards[currentMove][boardY][boardX];
7878 DrawSquare(boardY, boardX, gatingPiece, 0);
7879 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
7880 corner.x, corner.y, squareSize, squareSize, 0, 0);
7881 DrawSquare(boardY, boardX, piece, 0);
7883 damage[0][boardY][boardX] = True;
7885 player.dragActive = False;
7890 ChangeDragPiece (ChessSquare piece)
7893 player.dragPiece = piece;
7894 /* The piece will be drawn using its own bitmap as a matte */
7895 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
7896 XSetClipMask(xDisplay, player.pieceGC, mask);
7900 DragPieceMove (int x, int y)
7904 /* Are we animating? */
7905 if (!appData.animateDragging || appData.blindfold)
7909 if (! player.dragActive)
7911 /* Move piece, maintaining same relative position
7912 of mouse within square */
7913 corner.x = x - player.mouseDelta.x;
7914 corner.y = y - player.mouseDelta.y;
7915 AnimationFrame(&player, &corner, player.dragPiece);
7917 if (appData.highlightDragging) {
7919 BoardSquare(x, y, &boardX, &boardY);
7920 SetHighlights(fromX, fromY, boardX, boardY);
7926 DragPieceEnd (int x, int y)
7928 int boardX, boardY, color;
7931 /* Are we animating? */
7932 if (!appData.animateDragging || appData.blindfold)
7936 if (! player.dragActive)
7938 /* Last frame in sequence is square piece is
7939 placed on, which may not match mouse exactly. */
7940 BoardSquare(x, y, &boardX, &boardY);
7941 ScreenSquare(boardX, boardY, &corner, &color);
7942 EndAnimation(&player, &corner);
7944 /* Be sure end square is redrawn */
7945 damage[0][boardY][boardX] = True;
7947 /* This prevents weird things happening with fast successive
7948 clicks which on my Sun at least can cause motion events
7949 without corresponding press/release. */
7950 player.dragActive = False;
7953 /* Handle expose event while piece being dragged */
7958 if (!player.dragActive || appData.blindfold)
7961 /* What we're doing: logically, the move hasn't been made yet,
7962 so the piece is still in it's original square. But visually
7963 it's being dragged around the board. So we erase the square
7964 that the piece is on and draw it at the last known drag point. */
7965 BlankSquare(player.startSquare.x, player.startSquare.y,
7966 player.startColor, EmptySquare, xBoardWindow, 1);
7967 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
7968 damage[0][player.startBoardY][player.startBoardX] = TRUE;
7971 #include <sys/ioctl.h>
7975 int fd, default_width;
7978 default_width = 79; // this is FICS default anyway...
7980 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
7982 if (!ioctl(fd, TIOCGSIZE, &win))
7983 default_width = win.ts_cols;
7984 #elif defined(TIOCGWINSZ)
7986 if (!ioctl(fd, TIOCGWINSZ, &win))
7987 default_width = win.ws_col;
7989 return default_width;
7995 static int old_width = 0;
7996 int new_width = get_term_width();
7998 if (old_width != new_width)
7999 ics_printf("set width %d\n", new_width);
8000 old_width = new_width;
8004 NotifyFrontendLogin ()
8009 /* [AS] Arrow highlighting support */
8011 static double A_WIDTH = 5; /* Width of arrow body */
8013 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8014 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8025 return (int) (x + 0.5);
8029 SquareToPos (int rank, int file, int *x, int *y)
8032 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8033 *y = lineGap + rank * (squareSize + lineGap);
8035 *x = lineGap + file * (squareSize + lineGap);
8036 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8040 /* Draw an arrow between two points using current settings */
8042 DrawArrowBetweenPoints (int s_x, int s_y, int d_x, int d_y)
8045 double dx, dy, j, k, x, y;
8048 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8050 arrow[0].x = s_x + A_WIDTH + 0.5;
8053 arrow[1].x = s_x + A_WIDTH + 0.5;
8054 arrow[1].y = d_y - h;
8056 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8057 arrow[2].y = d_y - h;
8062 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8063 arrow[5].y = d_y - h;
8065 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8066 arrow[4].y = d_y - h;
8068 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8071 else if( d_y == s_y ) {
8072 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8075 arrow[0].y = s_y + A_WIDTH + 0.5;
8077 arrow[1].x = d_x - w;
8078 arrow[1].y = s_y + A_WIDTH + 0.5;
8080 arrow[2].x = d_x - w;
8081 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8086 arrow[5].x = d_x - w;
8087 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8089 arrow[4].x = d_x - w;
8090 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8093 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8096 /* [AS] Needed a lot of paper for this! :-) */
8097 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8098 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8100 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8102 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8107 arrow[0].x = Round(x - j);
8108 arrow[0].y = Round(y + j*dx);
8110 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8111 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8114 x = (double) d_x - k;
8115 y = (double) d_y - k*dy;
8118 x = (double) d_x + k;
8119 y = (double) d_y + k*dy;
8122 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8124 arrow[6].x = Round(x - j);
8125 arrow[6].y = Round(y + j*dx);
8127 arrow[2].x = Round(arrow[6].x + 2*j);
8128 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8130 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8131 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8136 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8137 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8140 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8141 if(appData.monoMode) arrow[7] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, arrow, 8, CoordModeOrigin);
8142 // Polygon( hdc, arrow, 7 );
8146 ArrowDamage (int s_col, int s_row, int d_col, int d_row)
8149 hor = 64*s_col + 32; vert = 64*s_row + 32;
8150 for(i=0; i<= 64; i++) {
8151 damage[0][vert+6>>6][hor+6>>6] = True;
8152 damage[0][vert-6>>6][hor+6>>6] = True;
8153 damage[0][vert+6>>6][hor-6>>6] = True;
8154 damage[0][vert-6>>6][hor-6>>6] = True;
8155 hor += d_col - s_col; vert += d_row - s_row;
8159 /* [AS] Draw an arrow between two squares */
8161 DrawArrowBetweenSquares (int s_col, int s_row, int d_col, int d_row)
8163 int s_x, s_y, d_x, d_y;
8165 if( s_col == d_col && s_row == d_row ) {
8169 /* Get source and destination points */
8170 SquareToPos( s_row, s_col, &s_x, &s_y);
8171 SquareToPos( d_row, d_col, &d_x, &d_y);
8174 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8176 else if( d_y < s_y ) {
8177 d_y += squareSize / 2 + squareSize / 4;
8180 d_y += squareSize / 2;
8184 d_x += squareSize / 2 - squareSize / 4;
8186 else if( d_x < s_x ) {
8187 d_x += squareSize / 2 + squareSize / 4;
8190 d_x += squareSize / 2;
8193 s_x += squareSize / 2;
8194 s_y += squareSize / 2;
8197 A_WIDTH = squareSize / 14.; //[HGM] make float
8199 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8200 ArrowDamage(s_col, s_row, d_col, d_row);
8204 IsDrawArrowEnabled ()
8206 return appData.highlightMoveWithArrow && squareSize >= 32;
8210 DrawArrowHighlight (int fromX, int fromY, int toX,int toY)
8212 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8213 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
8217 UpdateLogos (int displ)
8219 return; // no logos in XBoard yet