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, "menuOptions.Always Queen"),
2078 if (appData.animateDragging) {
2079 XtSetValues(XtNameToWidget(menuBarWidget,
2080 "menuOptions.Animate Dragging"),
2083 if (appData.animate) {
2084 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2087 if (appData.autoCallFlag) {
2088 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2091 if (appData.autoFlipView) {
2092 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2095 if (appData.blindfold) {
2096 XtSetValues(XtNameToWidget(menuBarWidget,
2097 "menuOptions.Blindfold"), args, 1);
2099 if (appData.flashCount > 0) {
2100 XtSetValues(XtNameToWidget(menuBarWidget,
2101 "menuOptions.Flash Moves"),
2105 if (appData.highlightDragging) {
2106 XtSetValues(XtNameToWidget(menuBarWidget,
2107 "menuOptions.Highlight Dragging"),
2111 if (appData.highlightLastMove) {
2112 XtSetValues(XtNameToWidget(menuBarWidget,
2113 "menuOptions.Highlight Last Move"),
2116 if (appData.highlightMoveWithArrow) {
2117 XtSetValues(XtNameToWidget(menuBarWidget,
2118 "menuOptions.Arrow"),
2121 // if (appData.icsAlarm) {
2122 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2125 if (appData.ringBellAfterMoves) {
2126 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2129 if (appData.oneClick) {
2130 XtSetValues(XtNameToWidget(menuBarWidget,
2131 "menuOptions.OneClick"), args, 1);
2133 if (appData.periodicUpdates) {
2134 XtSetValues(XtNameToWidget(menuBarWidget,
2135 "menuOptions.Periodic Updates"), args, 1);
2137 if (appData.ponderNextMove) {
2138 XtSetValues(XtNameToWidget(menuBarWidget,
2139 "menuOptions.Ponder Next Move"), args, 1);
2141 if (appData.popupExitMessage) {
2142 XtSetValues(XtNameToWidget(menuBarWidget,
2143 "menuOptions.Popup Exit Message"), args, 1);
2145 if (appData.popupMoveErrors) {
2146 XtSetValues(XtNameToWidget(menuBarWidget,
2147 "menuOptions.Popup Move Errors"), args, 1);
2149 // if (appData.premove) {
2150 // XtSetValues(XtNameToWidget(menuBarWidget,
2151 // "menuOptions.Premove"), args, 1);
2153 if (appData.showCoords) {
2154 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2157 if (appData.hideThinkingFromHuman) {
2158 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2161 if (appData.testLegality) {
2162 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2166 if (saveSettingsOnExit) {
2167 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2174 ReadBitmap(&wIconPixmap, "icon_white.bm",
2175 icon_white_bits, icon_white_width, icon_white_height);
2176 ReadBitmap(&bIconPixmap, "icon_black.bm",
2177 icon_black_bits, icon_black_width, icon_black_height);
2178 iconPixmap = wIconPixmap;
2180 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2181 XtSetValues(shellWidget, args, i);
2184 * Create a cursor for the board widget.
2186 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2187 XChangeWindowAttributes(xDisplay, xBoardWindow,
2188 CWCursor, &window_attributes);
2191 * Inhibit shell resizing.
2193 shellArgs[0].value = (XtArgVal) &w;
2194 shellArgs[1].value = (XtArgVal) &h;
2195 XtGetValues(shellWidget, shellArgs, 2);
2196 shellArgs[4].value = shellArgs[2].value = w;
2197 shellArgs[5].value = shellArgs[3].value = h;
2198 XtSetValues(shellWidget, &shellArgs[2], 4);
2199 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2200 marginH = h - boardHeight;
2202 CatchDeleteWindow(shellWidget, "QuitProc");
2210 if (appData.animate || appData.animateDragging)
2213 XtAugmentTranslations(formWidget,
2214 XtParseTranslationTable(globalTranslations));
2215 XtAugmentTranslations(boardWidget,
2216 XtParseTranslationTable(boardTranslations));
2217 XtAugmentTranslations(whiteTimerWidget,
2218 XtParseTranslationTable(whiteTranslations));
2219 XtAugmentTranslations(blackTimerWidget,
2220 XtParseTranslationTable(blackTranslations));
2222 /* Why is the following needed on some versions of X instead
2223 * of a translation? */
2224 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2225 (XtEventHandler) EventProc, NULL);
2227 XtAddEventHandler(formWidget, KeyPressMask, False,
2228 (XtEventHandler) MoveTypeInProc, NULL);
2229 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
2230 (XtEventHandler) EventProc, NULL);
2232 /* [AS] Restore layout */
2233 if( wpMoveHistory.visible ) {
2237 if( wpEvalGraph.visible )
2242 if( wpEngineOutput.visible ) {
2243 EngineOutputPopUp();
2248 if (errorExitStatus == -1) {
2249 if (appData.icsActive) {
2250 /* We now wait until we see "login:" from the ICS before
2251 sending the logon script (problems with timestamp otherwise) */
2252 /*ICSInitScript();*/
2253 if (appData.icsInputBox) ICSInputBoxPopUp();
2257 signal(SIGWINCH, TermSizeSigHandler);
2259 signal(SIGINT, IntSigHandler);
2260 signal(SIGTERM, IntSigHandler);
2261 if (*appData.cmailGameName != NULLCHAR) {
2262 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);
2370 GreyRevert (Boolean grey)
2373 if (!menuBarWidget) return;
2374 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2376 DisplayError("menuEdit.Revert", 0);
2378 XtSetSensitive(w, !grey);
2380 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2382 DisplayError("menuEdit.Annotate", 0);
2384 XtSetSensitive(w, !grey);
2389 SetMenuEnables (Enables *enab)
2392 if (!menuBarWidget) return;
2393 while (enab->name != NULL) {
2394 w = XtNameToWidget(menuBarWidget, enab->name);
2396 DisplayError(enab->name, 0);
2398 XtSetSensitive(w, enab->value);
2404 Enables icsEnables[] = {
2405 { "menuFile.Mail Move", False },
2406 { "menuFile.Reload CMail Message", False },
2407 { "menuMode.Machine Black", False },
2408 { "menuMode.Machine White", False },
2409 { "menuMode.Analysis Mode", False },
2410 { "menuMode.Analyze File", False },
2411 { "menuMode.Two Machines", False },
2412 { "menuMode.Machine Match", False },
2414 { "menuEngine.Hint", False },
2415 { "menuEngine.Book", False },
2416 { "menuEngine.Move Now", False },
2417 #ifndef OPTIONSDIALOG
2418 { "menuOptions.Periodic Updates", False },
2419 { "menuOptions.Hide Thinking", False },
2420 { "menuOptions.Ponder Next Move", False },
2423 { "menuEngine.Engine #1 Settings", False },
2424 { "menuEngine.Engine #2 Settings", False },
2425 { "menuEngine.Load Engine", False },
2426 { "menuEdit.Annotate", False },
2427 { "menuOptions.Match", False },
2431 Enables ncpEnables[] = {
2432 { "menuFile.Mail Move", False },
2433 { "menuFile.Reload CMail Message", False },
2434 { "menuMode.Machine White", False },
2435 { "menuMode.Machine Black", False },
2436 { "menuMode.Analysis Mode", False },
2437 { "menuMode.Analyze File", False },
2438 { "menuMode.Two Machines", False },
2439 { "menuMode.Machine Match", False },
2440 { "menuMode.ICS Client", False },
2441 { "menuView.ICStex", False },
2442 { "menuView.ICS Input Box", False },
2443 { "Action", False },
2444 { "menuEdit.Revert", False },
2445 { "menuEdit.Annotate", False },
2446 { "menuEngine.Engine #1 Settings", False },
2447 { "menuEngine.Engine #2 Settings", False },
2448 { "menuEngine.Move Now", False },
2449 { "menuEngine.Retract Move", False },
2450 { "menuOptions.ICS", False },
2451 #ifndef OPTIONSDIALOG
2452 { "menuOptions.Auto Flag", False },
2453 { "menuOptions.Auto Flip View", False },
2454 // { "menuOptions.ICS Alarm", False },
2455 { "menuOptions.Move Sound", False },
2456 { "menuOptions.Hide Thinking", False },
2457 { "menuOptions.Periodic Updates", False },
2458 { "menuOptions.Ponder Next Move", False },
2460 { "menuEngine.Hint", False },
2461 { "menuEngine.Book", False },
2465 Enables gnuEnables[] = {
2466 { "menuMode.ICS Client", False },
2467 { "menuView.ICStex", False },
2468 { "menuView.ICS Input Box", False },
2469 { "menuAction.Accept", False },
2470 { "menuAction.Decline", False },
2471 { "menuAction.Rematch", False },
2472 { "menuAction.Adjourn", False },
2473 { "menuAction.Stop Examining", False },
2474 { "menuAction.Stop Observing", False },
2475 { "menuAction.Upload to Examine", False },
2476 { "menuEdit.Revert", False },
2477 { "menuEdit.Annotate", False },
2478 { "menuOptions.ICS", False },
2480 /* The next two options rely on SetCmailMode being called *after* */
2481 /* SetGNUMode so that when GNU is being used to give hints these */
2482 /* menu options are still available */
2484 { "menuFile.Mail Move", False },
2485 { "menuFile.Reload CMail Message", False },
2486 // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2487 { "menuMode.Machine White", True },
2488 { "menuMode.Machine Black", True },
2489 { "menuMode.Analysis Mode", True },
2490 { "menuMode.Analyze File", True },
2491 { "menuMode.Two Machines", True },
2492 { "menuMode.Machine Match", True },
2493 { "menuEngine.Engine #1 Settings", True },
2494 { "menuEngine.Engine #2 Settings", True },
2495 { "menuEngine.Hint", True },
2496 { "menuEngine.Book", True },
2497 { "menuEngine.Move Now", True },
2498 { "menuEngine.Retract Move", True },
2503 Enables cmailEnables[] = {
2505 { "menuAction.Call Flag", False },
2506 { "menuAction.Draw", True },
2507 { "menuAction.Adjourn", False },
2508 { "menuAction.Abort", False },
2509 { "menuAction.Stop Observing", False },
2510 { "menuAction.Stop Examining", False },
2511 { "menuFile.Mail Move", True },
2512 { "menuFile.Reload CMail Message", True },
2516 Enables trainingOnEnables[] = {
2517 { "menuMode.Edit Comment", False },
2518 { "menuMode.Pause", False },
2519 { "menuEdit.Forward", False },
2520 { "menuEdit.Backward", False },
2521 { "menuEdit.Forward to End", False },
2522 { "menuEdit.Back to Start", False },
2523 { "menuEngine.Move Now", False },
2524 { "menuEdit.Truncate Game", False },
2528 Enables trainingOffEnables[] = {
2529 { "menuMode.Edit Comment", True },
2530 { "menuMode.Pause", True },
2531 { "menuEdit.Forward", True },
2532 { "menuEdit.Backward", True },
2533 { "menuEdit.Forward to End", True },
2534 { "menuEdit.Back to Start", True },
2535 { "menuEngine.Move Now", True },
2536 { "menuEdit.Truncate Game", True },
2540 Enables machineThinkingEnables[] = {
2541 { "menuFile.Load Game", False },
2542 // { "menuFile.Load Next Game", False },
2543 // { "menuFile.Load Previous Game", False },
2544 // { "menuFile.Reload Same Game", False },
2545 { "menuEdit.Paste Game", False },
2546 { "menuFile.Load Position", False },
2547 // { "menuFile.Load Next Position", False },
2548 // { "menuFile.Load Previous Position", False },
2549 // { "menuFile.Reload Same Position", False },
2550 { "menuEdit.Paste Position", False },
2551 { "menuMode.Machine White", False },
2552 { "menuMode.Machine Black", False },
2553 { "menuMode.Two Machines", False },
2554 // { "menuMode.Machine Match", False },
2555 { "menuEngine.Retract Move", False },
2559 Enables userThinkingEnables[] = {
2560 { "menuFile.Load Game", True },
2561 // { "menuFile.Load Next Game", True },
2562 // { "menuFile.Load Previous Game", True },
2563 // { "menuFile.Reload Same Game", True },
2564 { "menuEdit.Paste Game", True },
2565 { "menuFile.Load Position", True },
2566 // { "menuFile.Load Next Position", True },
2567 // { "menuFile.Load Previous Position", True },
2568 // { "menuFile.Reload Same Position", True },
2569 { "menuEdit.Paste Position", True },
2570 { "menuMode.Machine White", True },
2571 { "menuMode.Machine Black", True },
2572 { "menuMode.Two Machines", True },
2573 // { "menuMode.Machine Match", True },
2574 { "menuEngine.Retract Move", True },
2581 SetMenuEnables(icsEnables);
2584 if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
2585 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2586 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
2594 SetMenuEnables(ncpEnables);
2600 SetMenuEnables(gnuEnables);
2606 SetMenuEnables(cmailEnables);
2610 SetTrainingModeOn ()
2612 SetMenuEnables(trainingOnEnables);
2613 if (appData.showButtonBar) {
2614 XtSetSensitive(buttonBarWidget, False);
2620 SetTrainingModeOff ()
2622 SetMenuEnables(trainingOffEnables);
2623 if (appData.showButtonBar) {
2624 XtSetSensitive(buttonBarWidget, True);
2629 SetUserThinkingEnables ()
2631 if (appData.noChessProgram) return;
2632 SetMenuEnables(userThinkingEnables);
2636 SetMachineThinkingEnables ()
2638 if (appData.noChessProgram) return;
2639 SetMenuEnables(machineThinkingEnables);
2641 case MachinePlaysBlack:
2642 case MachinePlaysWhite:
2643 case TwoMachinesPlay:
2644 XtSetSensitive(XtNameToWidget(menuBarWidget,
2645 ModeToWidgetName(gameMode)), True);
2652 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
2653 #define HISTORY_SIZE 64
2654 static char *history[HISTORY_SIZE];
2655 int histIn = 0, histP = 0;
2658 SaveInHistory (char *cmd)
2660 if (history[histIn] != NULL) {
2661 free(history[histIn]);
2662 history[histIn] = NULL;
2664 if (*cmd == NULLCHAR) return;
2665 history[histIn] = StrSave(cmd);
2666 histIn = (histIn + 1) % HISTORY_SIZE;
2667 if (history[histIn] != NULL) {
2668 free(history[histIn]);
2669 history[histIn] = NULL;
2675 PrevInHistory (char *cmd)
2678 if (histP == histIn) {
2679 if (history[histIn] != NULL) free(history[histIn]);
2680 history[histIn] = StrSave(cmd);
2682 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
2683 if (newhp == histIn || history[newhp] == NULL) return NULL;
2685 return history[histP];
2691 if (histP == histIn) return NULL;
2692 histP = (histP + 1) % HISTORY_SIZE;
2693 return history[histP];
2695 // end of borrowed code
2697 #define Abs(n) ((n)<0 ? -(n) : (n))
2701 InsertPxlSize (char *pattern, int targetPxlSize)
2703 char *base_fnt_lst, strInt[12], *p, *q;
2704 int alternatives, i, len, strIntLen;
2707 * Replace the "*" (if present) in the pixel-size slot of each
2708 * alternative with the targetPxlSize.
2712 while ((p = strchr(p, ',')) != NULL) {
2716 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
2717 strIntLen = strlen(strInt);
2718 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
2722 while (alternatives--) {
2723 char *comma = strchr(p, ',');
2724 for (i=0; i<14; i++) {
2725 char *hyphen = strchr(p, '-');
2727 if (comma && hyphen > comma) break;
2728 len = hyphen + 1 - p;
2729 if (i == 7 && *p == '*' && len == 2) {
2731 memcpy(q, strInt, strIntLen);
2741 len = comma + 1 - p;
2748 return base_fnt_lst;
2752 CreateFontSet (char *base_fnt_lst)
2755 char **missing_list;
2759 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
2760 &missing_list, &missing_count, &def_string);
2761 if (appData.debugMode) {
2763 XFontStruct **font_struct_list;
2764 char **font_name_list;
2765 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
2767 fprintf(debugFP, " got list %s, locale %s\n",
2768 XBaseFontNameListOfFontSet(fntSet),
2769 XLocaleOfFontSet(fntSet));
2770 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
2771 for (i = 0; i < count; i++) {
2772 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
2775 for (i = 0; i < missing_count; i++) {
2776 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
2779 if (fntSet == NULL) {
2780 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
2785 #else // not ENABLE_NLS
2787 * Find a font that matches "pattern" that is as close as
2788 * possible to the targetPxlSize. Prefer fonts that are k
2789 * pixels smaller to fonts that are k pixels larger. The
2790 * pattern must be in the X Consortium standard format,
2791 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2792 * The return value should be freed with XtFree when no
2796 FindFont (char *pattern, int targetPxlSize)
2798 char **fonts, *p, *best, *scalable, *scalableTail;
2799 int i, j, nfonts, minerr, err, pxlSize;
2801 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2803 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2804 programName, pattern);
2811 for (i=0; i<nfonts; i++) {
2814 if (*p != '-') continue;
2816 if (*p == NULLCHAR) break;
2817 if (*p++ == '-') j++;
2819 if (j < 7) continue;
2822 scalable = fonts[i];
2825 err = pxlSize - targetPxlSize;
2826 if (Abs(err) < Abs(minerr) ||
2827 (minerr > 0 && err < 0 && -err == minerr)) {
2833 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2834 /* If the error is too big and there is a scalable font,
2835 use the scalable font. */
2836 int headlen = scalableTail - scalable;
2837 p = (char *) XtMalloc(strlen(scalable) + 10);
2838 while (isdigit(*scalableTail)) scalableTail++;
2839 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2841 p = (char *) XtMalloc(strlen(best) + 2);
2842 safeStrCpy(p, best, strlen(best)+1 );
2844 if (appData.debugMode) {
2845 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2846 pattern, targetPxlSize, p);
2848 XFreeFontNames(fonts);
2855 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
2856 // must be called before all non-first callse to CreateGCs()
2857 XtReleaseGC(shellWidget, highlineGC);
2858 XtReleaseGC(shellWidget, lightSquareGC);
2859 XtReleaseGC(shellWidget, darkSquareGC);
2860 XtReleaseGC(shellWidget, lineGC);
2861 if (appData.monoMode) {
2862 if (DefaultDepth(xDisplay, xScreen) == 1) {
2863 XtReleaseGC(shellWidget, wbPieceGC);
2865 XtReleaseGC(shellWidget, bwPieceGC);
2868 XtReleaseGC(shellWidget, prelineGC);
2869 XtReleaseGC(shellWidget, jailSquareGC);
2870 XtReleaseGC(shellWidget, wdPieceGC);
2871 XtReleaseGC(shellWidget, wlPieceGC);
2872 XtReleaseGC(shellWidget, wjPieceGC);
2873 XtReleaseGC(shellWidget, bdPieceGC);
2874 XtReleaseGC(shellWidget, blPieceGC);
2875 XtReleaseGC(shellWidget, bjPieceGC);
2880 CreateGCs (int redo)
2882 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2883 | GCBackground | GCFunction | GCPlaneMask;
2884 XGCValues gc_values;
2887 gc_values.plane_mask = AllPlanes;
2888 gc_values.line_width = lineGap;
2889 gc_values.line_style = LineSolid;
2890 gc_values.function = GXcopy;
2893 DeleteGCs(); // called a second time; clean up old GCs first
2894 } else { // [HGM] grid and font GCs created on first call only
2895 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
2896 gc_values.background = XWhitePixel(xDisplay, xScreen);
2897 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
2898 XSetFont(xDisplay, coordGC, coordFontID);
2900 // [HGM] make font for holdings counts (white on black)
2901 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
2902 gc_values.background = XBlackPixel(xDisplay, xScreen);
2903 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
2904 XSetFont(xDisplay, countGC, countFontID);
2906 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
2907 gc_values.background = XBlackPixel(xDisplay, xScreen);
2908 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
2910 if (appData.monoMode) {
2911 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
2912 gc_values.background = XWhitePixel(xDisplay, xScreen);
2913 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
2915 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
2916 gc_values.background = XBlackPixel(xDisplay, xScreen);
2917 lightSquareGC = wbPieceGC
2918 = XtGetGC(shellWidget, value_mask, &gc_values);
2920 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
2921 gc_values.background = XWhitePixel(xDisplay, xScreen);
2922 darkSquareGC = bwPieceGC
2923 = XtGetGC(shellWidget, value_mask, &gc_values);
2925 if (DefaultDepth(xDisplay, xScreen) == 1) {
2926 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2927 gc_values.function = GXcopyInverted;
2928 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
2929 gc_values.function = GXcopy;
2930 if (XBlackPixel(xDisplay, xScreen) == 1) {
2931 bwPieceGC = darkSquareGC;
2932 wbPieceGC = copyInvertedGC;
2934 bwPieceGC = copyInvertedGC;
2935 wbPieceGC = lightSquareGC;
2939 gc_values.foreground = highlightSquareColor;
2940 gc_values.background = highlightSquareColor;
2941 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
2943 gc_values.foreground = premoveHighlightColor;
2944 gc_values.background = premoveHighlightColor;
2945 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
2947 gc_values.foreground = lightSquareColor;
2948 gc_values.background = darkSquareColor;
2949 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
2951 gc_values.foreground = darkSquareColor;
2952 gc_values.background = lightSquareColor;
2953 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
2955 gc_values.foreground = jailSquareColor;
2956 gc_values.background = jailSquareColor;
2957 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
2959 gc_values.foreground = whitePieceColor;
2960 gc_values.background = darkSquareColor;
2961 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2963 gc_values.foreground = whitePieceColor;
2964 gc_values.background = lightSquareColor;
2965 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2967 gc_values.foreground = whitePieceColor;
2968 gc_values.background = jailSquareColor;
2969 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2971 gc_values.foreground = blackPieceColor;
2972 gc_values.background = darkSquareColor;
2973 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2975 gc_values.foreground = blackPieceColor;
2976 gc_values.background = lightSquareColor;
2977 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2979 gc_values.foreground = blackPieceColor;
2980 gc_values.background = jailSquareColor;
2981 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2986 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
2994 fp = fopen(filename, "rb");
2996 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3003 for (y=0; y<h; ++y) {
3004 for (x=0; x<h; ++x) {
3009 XPutPixel(xim, x, y, blackPieceColor);
3011 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3014 XPutPixel(xim, x, y, darkSquareColor);
3016 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3019 XPutPixel(xim, x, y, whitePieceColor);
3021 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3024 XPutPixel(xim, x, y, lightSquareColor);
3026 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3034 /* create Pixmap of piece */
3035 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3037 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3040 /* create Pixmap of clipmask
3041 Note: We assume the white/black pieces have the same
3042 outline, so we make only 6 masks. This is okay
3043 since the XPM clipmask routines do the same. */
3045 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3047 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3050 /* now create the 1-bit version */
3051 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3054 values.foreground = 1;
3055 values.background = 0;
3057 /* Don't use XtGetGC, not read only */
3058 maskGC = XCreateGC(xDisplay, *mask,
3059 GCForeground | GCBackground, &values);
3060 XCopyPlane(xDisplay, temp, *mask, maskGC,
3061 0, 0, squareSize, squareSize, 0, 0, 1);
3062 XFreePixmap(xDisplay, temp);
3067 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3075 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3080 /* The XSynchronize calls were copied from CreatePieces.
3081 Not sure if needed, but can't hurt */
3082 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3085 /* temp needed by loadXIM() */
3086 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3087 0, 0, ss, ss, AllPlanes, XYPixmap);
3089 if (strlen(appData.pixmapDirectory) == 0) {
3093 if (appData.monoMode) {
3094 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3098 fprintf(stderr, _("\nLoading XIMs...\n"));
3100 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3101 fprintf(stderr, "%d", piece+1);
3102 for (kind=0; kind<4; kind++) {
3103 fprintf(stderr, ".");
3104 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3105 ExpandPathName(appData.pixmapDirectory),
3106 piece <= (int) WhiteKing ? "" : "w",
3107 pieceBitmapNames[piece],
3109 ximPieceBitmap[kind][piece] =
3110 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3111 0, 0, ss, ss, AllPlanes, XYPixmap);
3112 if (appData.debugMode)
3113 fprintf(stderr, _("(File:%s:) "), buf);
3114 loadXIM(ximPieceBitmap[kind][piece],
3116 &(xpmPieceBitmap2[kind][piece]),
3117 &(ximMaskPm2[piece]));
3118 if(piece <= (int)WhiteKing)
3119 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3121 fprintf(stderr," ");
3123 /* Load light and dark squares */
3124 /* If the LSQ and DSQ pieces don't exist, we will
3125 draw them with solid squares. */
3126 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3127 if (access(buf, 0) != 0) {
3131 fprintf(stderr, _("light square "));
3133 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3134 0, 0, ss, ss, AllPlanes, XYPixmap);
3135 if (appData.debugMode)
3136 fprintf(stderr, _("(File:%s:) "), buf);
3138 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3139 fprintf(stderr, _("dark square "));
3140 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3141 ExpandPathName(appData.pixmapDirectory), ss);
3142 if (appData.debugMode)
3143 fprintf(stderr, _("(File:%s:) "), buf);
3145 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3146 0, 0, ss, ss, AllPlanes, XYPixmap);
3147 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3148 xpmJailSquare = xpmLightSquare;
3150 fprintf(stderr, _("Done.\n"));
3152 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3155 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3159 CreateXPMBoard (char *s, int kind)
3163 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3164 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3165 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3171 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3172 // thisroutine has to be called t free the old piece pixmaps
3174 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3175 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3177 XFreePixmap(xDisplay, xpmLightSquare);
3178 XFreePixmap(xDisplay, xpmDarkSquare);
3187 u_int ss = squareSize;
3189 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3190 XpmColorSymbol symbols[4];
3191 static int redo = False;
3193 if(redo) FreeXPMPieces(); else redo = 1;
3195 /* The XSynchronize calls were copied from CreatePieces.
3196 Not sure if needed, but can't hurt */
3197 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3199 /* Setup translations so piece colors match square colors */
3200 symbols[0].name = "light_piece";
3201 symbols[0].value = appData.whitePieceColor;
3202 symbols[1].name = "dark_piece";
3203 symbols[1].value = appData.blackPieceColor;
3204 symbols[2].name = "light_square";
3205 symbols[2].value = appData.lightSquareColor;
3206 symbols[3].name = "dark_square";
3207 symbols[3].value = appData.darkSquareColor;
3209 attr.valuemask = XpmColorSymbols;
3210 attr.colorsymbols = symbols;
3211 attr.numsymbols = 4;
3213 if (appData.monoMode) {
3214 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3218 if (strlen(appData.pixmapDirectory) == 0) {
3219 XpmPieces* pieces = builtInXpms;
3222 while (pieces->size != squareSize && pieces->size) pieces++;
3223 if (!pieces->size) {
3224 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3227 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3228 for (kind=0; kind<4; kind++) {
3230 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3231 pieces->xpm[piece][kind],
3232 &(xpmPieceBitmap2[kind][piece]),
3233 NULL, &attr)) != 0) {
3234 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3238 if(piece <= (int) WhiteKing)
3239 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3243 xpmJailSquare = xpmLightSquare;
3247 fprintf(stderr, _("\nLoading XPMs...\n"));
3250 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3251 fprintf(stderr, "%d ", piece+1);
3252 for (kind=0; kind<4; kind++) {
3253 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3254 ExpandPathName(appData.pixmapDirectory),
3255 piece > (int) WhiteKing ? "w" : "",
3256 pieceBitmapNames[piece],
3258 if (appData.debugMode) {
3259 fprintf(stderr, _("(File:%s:) "), buf);
3261 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3262 &(xpmPieceBitmap2[kind][piece]),
3263 NULL, &attr)) != 0) {
3264 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3265 // [HGM] missing: read of unorthodox piece failed; substitute King.
3266 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3267 ExpandPathName(appData.pixmapDirectory),
3269 if (appData.debugMode) {
3270 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3272 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3273 &(xpmPieceBitmap2[kind][piece]),
3277 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3282 if(piece <= (int) WhiteKing)
3283 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3286 /* Load light and dark squares */
3287 /* If the LSQ and DSQ pieces don't exist, we will
3288 draw them with solid squares. */
3289 fprintf(stderr, _("light square "));
3290 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3291 if (access(buf, 0) != 0) {
3295 if (appData.debugMode)
3296 fprintf(stderr, _("(File:%s:) "), buf);
3298 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3299 &xpmLightSquare, NULL, &attr)) != 0) {
3300 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3303 fprintf(stderr, _("dark square "));
3304 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3305 ExpandPathName(appData.pixmapDirectory), ss);
3306 if (appData.debugMode) {
3307 fprintf(stderr, _("(File:%s:) "), buf);
3309 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3310 &xpmDarkSquare, NULL, &attr)) != 0) {
3311 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3315 xpmJailSquare = xpmLightSquare;
3316 fprintf(stderr, _("Done.\n"));
3318 oldVariant = -1; // kludge to force re-makig of animation masks
3319 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3322 #endif /* HAVE_LIBXPM */
3325 /* No built-in bitmaps */
3330 u_int ss = squareSize;
3332 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3335 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3336 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3337 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3338 pieceBitmapNames[piece],
3339 ss, kind == SOLID ? 's' : 'o');
3340 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3341 if(piece <= (int)WhiteKing)
3342 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3346 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3350 /* With built-in bitmaps */
3354 BuiltInBits* bib = builtInBits;
3357 u_int ss = squareSize;
3359 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3362 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3364 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3365 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3366 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3367 pieceBitmapNames[piece],
3368 ss, kind == SOLID ? 's' : 'o');
3369 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3370 bib->bits[kind][piece], ss, ss);
3371 if(piece <= (int)WhiteKing)
3372 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3376 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3382 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
3387 char msg[MSG_SIZ], fullname[MSG_SIZ];
3389 if (*appData.bitmapDirectory != NULLCHAR) {
3390 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3391 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3392 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3393 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3394 &w, &h, pm, &x_hot, &y_hot);
3395 fprintf(stderr, "load %s\n", name);
3396 if (errcode != BitmapSuccess) {
3398 case BitmapOpenFailed:
3399 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3401 case BitmapFileInvalid:
3402 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3404 case BitmapNoMemory:
3405 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3409 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3413 fprintf(stderr, _("%s: %s...using built-in\n"),
3415 } else if (w != wreq || h != hreq) {
3417 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3418 programName, fullname, w, h, wreq, hreq);
3424 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3434 if (lineGap == 0) return;
3436 /* [HR] Split this into 2 loops for non-square boards. */
3438 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3439 gridSegments[i].x1 = 0;
3440 gridSegments[i].x2 =
3441 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3442 gridSegments[i].y1 = gridSegments[i].y2
3443 = lineGap / 2 + (i * (squareSize + lineGap));
3446 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3447 gridSegments[j + i].y1 = 0;
3448 gridSegments[j + i].y2 =
3449 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3450 gridSegments[j + i].x1 = gridSegments[j + i].x2
3451 = lineGap / 2 + (j * (squareSize + lineGap));
3455 int nrOfMenuItems = 7;
3456 MenuListItem menuItemList[150] = {
3457 { "LoadNextGameProc", LoadNextGameProc },
3458 { "LoadPrevGameProc", LoadPrevGameProc },
3459 { "ReloadGameProc", ReloadGameProc },
3460 { "ReloadPositionProc", ReloadPositionProc },
3461 #ifndef OPTIONSDIALOG
3462 { "AlwaysQueenProc", AlwaysQueenProc },
3463 { "AnimateDraggingProc", AnimateDraggingProc },
3464 { "AnimateMovingProc", AnimateMovingProc },
3465 { "AutoflagProc", AutoflagProc },
3466 { "AutoflipProc", AutoflipProc },
3467 { "BlindfoldProc", BlindfoldProc },
3468 { "FlashMovesProc", FlashMovesProc },
3470 { "HighlightDraggingProc", HighlightDraggingProc },
3472 { "HighlightLastMoveProc", HighlightLastMoveProc },
3473 // { "IcsAlarmProc", IcsAlarmProc },
3474 { "MoveSoundProc", MoveSoundProc },
3475 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
3476 { "PopupExitMessageProc", PopupExitMessageProc },
3477 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
3478 // { "PremoveProc", PremoveProc },
3479 { "ShowCoordsProc", ShowCoordsProc },
3480 { "ShowThinkingProc", ShowThinkingProc },
3481 { "HideThinkingProc", HideThinkingProc },
3482 { "TestLegalityProc", TestLegalityProc },
3484 { "AboutGameProc", AboutGameEvent },
3485 { "DebugProc", DebugProc },
3486 { "NothingProc", NothingProc },
3491 Equal(char *p, char *s)
3492 { // compare strings skipping spaces in second
3494 if(*s == ' ') { s++; continue; }
3495 if(*s++ != *p++) return 0;
3501 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3502 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
3504 if(*nprms == 0) return;
3505 for(i=0; menuItemList[i].name; i++) {
3506 if(Equal(prms[0], menuItemList[i].name)) {
3507 (menuItemList[i].proc) ();
3514 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
3516 MenuProc *proc = (MenuProc *) addr;
3522 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
3524 RecentEngineEvent((int) (intptr_t) addr);
3527 // some stuff that must remain in front-end
3528 static Widget mainBar, currentMenu;
3529 static int wtot, nr = 0, widths[10];
3532 AppendMenuItem (char *text, char *name, MenuProc *action)
3539 XtSetArg(args[j], XtNleftMargin, 20); j++;
3540 XtSetArg(args[j], XtNrightMargin, 20); j++;
3542 if (strcmp(text, "----") == 0) {
3543 entry = XtCreateManagedWidget(text, smeLineObjectClass,
3544 currentMenu, args, j);
3546 XtSetArg(args[j], XtNlabel, XtNewString(_(text)));
3547 entry = XtCreateManagedWidget(name, smeBSBObjectClass,
3548 currentMenu, args, j+1);
3549 XtAddCallback(entry, XtNcallback,
3550 (XtCallbackProc) (strcmp(name, "recent") ? MenuBarSelect : MenuEngineSelect),
3556 CreateMenuButton (char *name, Menu *mb)
3557 { // create menu button on main bar, and shell for pull-down list
3563 XtSetArg(args[j], XtNmenuName, XtNewString(name)); j++;
3564 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3565 XtSetArg(args[j], XtNborderWidth, 0); j++;
3566 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3568 currentMenu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3571 XtSetArg(args[j], XtNwidth, &w); j++;
3572 XtGetValues(mb->subMenu, args, j);
3573 wtot += mb->textWidth = widths[nr++] = w;
3577 CreateMenuBar (Menu *mb, int boardWidth)
3581 char menuName[MSG_SIZ];
3585 // create bar itself
3587 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3588 XtSetArg(args[j], XtNvSpace, 0); j++;
3589 XtSetArg(args[j], XtNborderWidth, 0); j++;
3590 mainBar = XtCreateWidget("menuBar", boxWidgetClass,
3591 formWidget, args, j);
3593 CreateMainMenus(mb); // put menus in bar according to description in back-end
3595 // size buttons to make menu bar fit, clipping menu names where necessary
3596 while(wtot > boardWidth - 40) {
3598 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
3602 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
3604 XtSetArg(args[j], XtNwidth, widths[i]); j++;
3605 XtSetValues(ma[i].subMenu, args, j);
3612 CreateButtonBar (MenuItem *mi)
3615 Widget button, buttonBar;
3619 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3621 XtSetArg(args[j], XtNhSpace, 0); j++;
3623 XtSetArg(args[j], XtNborderWidth, 0); j++;
3624 XtSetArg(args[j], XtNvSpace, 0); j++;
3625 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3626 formWidget, args, j);
3628 while (mi->string != NULL) {
3631 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3632 XtSetArg(args[j], XtNborderWidth, 0); j++;
3634 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3635 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3636 buttonBar, args, j);
3637 XtAddCallback(button, XtNcallback,
3638 (XtCallbackProc) MenuBarSelect,
3639 (caddr_t) mi->proc);
3646 CreatePieceMenu (char *name, int color)
3651 ChessSquare selection;
3653 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3654 boardWidget, args, 0);
3656 for (i = 0; i < PIECE_MENU_SIZE; i++) {
3657 String item = pieceMenuStrings[color][i];
3659 if (strcmp(item, "----") == 0) {
3660 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3663 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3664 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3666 selection = pieceMenuTranslation[color][i];
3667 XtAddCallback(entry, XtNcallback,
3668 (XtCallbackProc) PieceMenuSelect,
3669 (caddr_t) selection);
3670 if (selection == WhitePawn || selection == BlackPawn) {
3671 XtSetArg(args[0], XtNpopupOnEntry, entry);
3672 XtSetValues(menu, args, 1);
3685 ChessSquare selection;
3687 whitePieceMenu = CreatePieceMenu("menuW", 0);
3688 blackPieceMenu = CreatePieceMenu("menuB", 1);
3690 if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
3691 XtRegisterGrabAction(PieceMenuPopup, True,
3692 (unsigned)(ButtonPressMask|ButtonReleaseMask),
3693 GrabModeAsync, GrabModeAsync);
3695 XtSetArg(args[0], XtNlabel, _("Drop"));
3696 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3697 boardWidget, args, 1);
3698 for (i = 0; i < DROP_MENU_SIZE; i++) {
3699 String item = dropMenuStrings[i];
3701 if (strcmp(item, "----") == 0) {
3702 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3705 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3706 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3708 selection = dropMenuTranslation[i];
3709 XtAddCallback(entry, XtNcallback,
3710 (XtCallbackProc) DropMenuSelect,
3711 (caddr_t) selection);
3725 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3726 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3727 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3728 dmEnables[i].piece);
3729 XtSetSensitive(entry, p != NULL || !appData.testLegality
3730 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3731 && !appData.icsActive));
3733 while (p && *p++ == dmEnables[i].piece) count++;
3734 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3736 XtSetArg(args[j], XtNlabel, label); j++;
3737 XtSetValues(entry, args, j);
3742 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
3744 String whichMenu; int menuNr = -2;
3745 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3746 if (event->type == ButtonRelease)
3747 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3748 else if (event->type == ButtonPress)
3749 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3751 case 0: whichMenu = params[0]; break;
3752 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3754 case -1: if (errorUp) ErrorPopDown();
3757 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3761 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3763 if (pmFromX < 0 || pmFromY < 0) return;
3764 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3768 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3770 if (pmFromX < 0 || pmFromY < 0) return;
3771 DropMenuEvent(piece, pmFromX, pmFromY);
3775 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3777 shiftKey = prms[0][0] & 1;
3782 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3784 shiftKey = prms[0][0] & 1;
3790 * If the user selects on a border boundary, return -1; if off the board,
3791 * return -2. Otherwise map the event coordinate to the square.
3794 EventToSquare (int x, int limit)
3801 if ((x % (squareSize + lineGap)) >= squareSize)
3803 x /= (squareSize + lineGap);
3810 do_flash_delay (unsigned long msec)
3816 drawHighlight (int file, int rank, GC gc)
3820 if (lineGap == 0) return;
3823 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
3824 (squareSize + lineGap);
3825 y = lineGap/2 + rank * (squareSize + lineGap);
3827 x = lineGap/2 + file * (squareSize + lineGap);
3828 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
3829 (squareSize + lineGap);
3832 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3833 squareSize+lineGap, squareSize+lineGap);
3836 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
3837 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
3840 SetHighlights (int fromX, int fromY, int toX, int toY)
3842 if (hi1X != fromX || hi1Y != fromY) {
3843 if (hi1X >= 0 && hi1Y >= 0) {
3844 drawHighlight(hi1X, hi1Y, lineGC);
3846 } // [HGM] first erase both, then draw new!
3848 if (hi2X != toX || hi2Y != toY) {
3849 if (hi2X >= 0 && hi2Y >= 0) {
3850 drawHighlight(hi2X, hi2Y, lineGC);
3853 if (hi1X != fromX || hi1Y != fromY) {
3854 if (fromX >= 0 && fromY >= 0) {
3855 drawHighlight(fromX, fromY, highlineGC);
3858 if (hi2X != toX || hi2Y != toY) {
3859 if (toX >= 0 && toY >= 0) {
3860 drawHighlight(toX, toY, highlineGC);
3864 if(toX<0) // clearing the highlights must have damaged arrow
3865 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y); // for now, redraw it (should really be cleared!)
3876 SetHighlights(-1, -1, -1, -1);
3881 SetPremoveHighlights (int fromX, int fromY, int toX, int toY)
3883 if (pm1X != fromX || pm1Y != fromY) {
3884 if (pm1X >= 0 && pm1Y >= 0) {
3885 drawHighlight(pm1X, pm1Y, lineGC);
3887 if (fromX >= 0 && fromY >= 0) {
3888 drawHighlight(fromX, fromY, prelineGC);
3891 if (pm2X != toX || pm2Y != toY) {
3892 if (pm2X >= 0 && pm2Y >= 0) {
3893 drawHighlight(pm2X, pm2Y, lineGC);
3895 if (toX >= 0 && toY >= 0) {
3896 drawHighlight(toX, toY, prelineGC);
3906 ClearPremoveHighlights ()
3908 SetPremoveHighlights(-1, -1, -1, -1);
3912 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
3914 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
3915 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
3917 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
3918 if(textureW[kind] < W*squareSize)
3919 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
3921 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
3922 if(textureH[kind] < H*squareSize)
3923 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
3925 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
3930 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
3931 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
3933 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
3934 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
3935 squareSize, squareSize, x*fac, y*fac);
3937 if (useImages && useImageSqs) {
3941 pm = xpmLightSquare;
3946 case 2: /* neutral */
3951 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3952 squareSize, squareSize, x*fac, y*fac);
3962 case 2: /* neutral */
3967 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
3972 I split out the routines to draw a piece so that I could
3973 make a generic flash routine.
3976 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3978 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3979 switch (square_color) {
3981 case 2: /* neutral */
3983 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3984 ? *pieceToOutline(piece)
3985 : *pieceToSolid(piece),
3986 dest, bwPieceGC, 0, 0,
3987 squareSize, squareSize, x, y);
3990 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3991 ? *pieceToSolid(piece)
3992 : *pieceToOutline(piece),
3993 dest, wbPieceGC, 0, 0,
3994 squareSize, squareSize, x, y);
4000 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4002 switch (square_color) {
4004 case 2: /* neutral */
4006 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4007 ? *pieceToOutline(piece)
4008 : *pieceToSolid(piece),
4009 dest, bwPieceGC, 0, 0,
4010 squareSize, squareSize, x, y, 1);
4013 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4014 ? *pieceToSolid(piece)
4015 : *pieceToOutline(piece),
4016 dest, wbPieceGC, 0, 0,
4017 squareSize, squareSize, x, y, 1);
4023 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4025 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4026 switch (square_color) {
4028 XCopyPlane(xDisplay, *pieceToSolid(piece),
4029 dest, (int) piece < (int) BlackPawn
4030 ? wlPieceGC : blPieceGC, 0, 0,
4031 squareSize, squareSize, x, y, 1);
4034 XCopyPlane(xDisplay, *pieceToSolid(piece),
4035 dest, (int) piece < (int) BlackPawn
4036 ? wdPieceGC : bdPieceGC, 0, 0,
4037 squareSize, squareSize, x, y, 1);
4039 case 2: /* neutral */
4041 XCopyPlane(xDisplay, *pieceToSolid(piece),
4042 dest, (int) piece < (int) BlackPawn
4043 ? wjPieceGC : bjPieceGC, 0, 0,
4044 squareSize, squareSize, x, y, 1);
4050 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4052 int kind, p = piece;
4054 switch (square_color) {
4056 case 2: /* neutral */
4058 if ((int)piece < (int) BlackPawn) {
4066 if ((int)piece < (int) BlackPawn) {
4074 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4075 if(useTexture & square_color+1) {
4076 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4077 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4078 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4079 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4080 XSetClipMask(xDisplay, wlPieceGC, None);
4081 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4083 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4084 dest, wlPieceGC, 0, 0,
4085 squareSize, squareSize, x, y);
4088 typedef void (*DrawFunc)();
4093 if (appData.monoMode) {
4094 if (DefaultDepth(xDisplay, xScreen) == 1) {
4095 return monoDrawPiece_1bit;
4097 return monoDrawPiece;
4101 return colorDrawPieceImage;
4103 return colorDrawPiece;
4107 /* [HR] determine square color depending on chess variant. */
4109 SquareColor (int row, int column)
4113 if (gameInfo.variant == VariantXiangqi) {
4114 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4116 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4118 } else if (row <= 4) {
4124 square_color = ((column + row) % 2) == 1;
4127 /* [hgm] holdings: next line makes all holdings squares light */
4128 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4130 return square_color;
4134 DrawSquare (int row, int column, ChessSquare piece, int do_flash)
4136 int square_color, x, y, direction, font_ascent, font_descent;
4139 XCharStruct overall;
4143 /* Calculate delay in milliseconds (2-delays per complete flash) */
4144 flash_delay = 500 / appData.flashRate;
4147 x = lineGap + ((BOARD_WIDTH-1)-column) *
4148 (squareSize + lineGap);
4149 y = lineGap + row * (squareSize + lineGap);
4151 x = lineGap + column * (squareSize + lineGap);
4152 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4153 (squareSize + lineGap);
4156 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4158 square_color = SquareColor(row, column);
4160 if ( // [HGM] holdings: blank out area between board and holdings
4161 column == BOARD_LEFT-1 || column == BOARD_RGHT
4162 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4163 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4164 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4166 // [HGM] print piece counts next to holdings
4167 string[1] = NULLCHAR;
4168 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4169 string[0] = '0' + piece;
4170 XTextExtents(countFontStruct, string, 1, &direction,
4171 &font_ascent, &font_descent, &overall);
4172 if (appData.monoMode) {
4173 XDrawImageString(xDisplay, xBoardWindow, countGC,
4174 x + squareSize - overall.width - 2,
4175 y + font_ascent + 1, string, 1);
4177 XDrawString(xDisplay, xBoardWindow, countGC,
4178 x + squareSize - overall.width - 2,
4179 y + font_ascent + 1, string, 1);
4182 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4183 string[0] = '0' + piece;
4184 XTextExtents(countFontStruct, string, 1, &direction,
4185 &font_ascent, &font_descent, &overall);
4186 if (appData.monoMode) {
4187 XDrawImageString(xDisplay, xBoardWindow, countGC,
4188 x + 2, y + font_ascent + 1, string, 1);
4190 XDrawString(xDisplay, xBoardWindow, countGC,
4191 x + 2, y + font_ascent + 1, string, 1);
4195 if (piece == EmptySquare || appData.blindfold) {
4196 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4198 drawfunc = ChooseDrawFunc();
4200 if (do_flash && appData.flashCount > 0) {
4201 for (i=0; i<appData.flashCount; ++i) {
4202 drawfunc(piece, square_color, x, y, xBoardWindow);
4203 XSync(xDisplay, False);
4204 do_flash_delay(flash_delay);
4206 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4207 XSync(xDisplay, False);
4208 do_flash_delay(flash_delay);
4211 drawfunc(piece, square_color, x, y, xBoardWindow);
4215 string[1] = NULLCHAR;
4216 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4217 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4218 string[0] = 'a' + column - BOARD_LEFT;
4219 XTextExtents(coordFontStruct, string, 1, &direction,
4220 &font_ascent, &font_descent, &overall);
4221 if (appData.monoMode) {
4222 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4223 x + squareSize - overall.width - 2,
4224 y + squareSize - font_descent - 1, string, 1);
4226 XDrawString(xDisplay, xBoardWindow, coordGC,
4227 x + squareSize - overall.width - 2,
4228 y + squareSize - font_descent - 1, string, 1);
4231 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4232 string[0] = ONE + row;
4233 XTextExtents(coordFontStruct, string, 1, &direction,
4234 &font_ascent, &font_descent, &overall);
4235 if (appData.monoMode) {
4236 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4237 x + 2, y + font_ascent + 1, string, 1);
4239 XDrawString(xDisplay, xBoardWindow, coordGC,
4240 x + 2, y + font_ascent + 1, string, 1);
4243 if(!partnerUp && marker[row][column]) {
4244 if(appData.monoMode) {
4245 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? darkSquareGC : lightSquareGC,
4246 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4247 XDrawArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? lightSquareGC : darkSquareGC,
4248 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4250 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4251 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4256 Fraction (int x, int start, int stop)
4258 double f = ((double) x - start)/(stop - start);
4259 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
4263 static WindowPlacement wpNew;
4266 CoDrag (Widget sh, WindowPlacement *wp)
4269 int j=0, touch=0, fudge = 2;
4270 GetActualPlacement(sh, wp);
4271 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
4272 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
4273 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
4274 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
4275 if(!touch ) return; // only windows that touch co-move
4276 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
4277 int heightInc = wpNew.height - wpMain.height;
4278 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
4279 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
4280 wp->y += fracTop * heightInc;
4281 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
4282 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
4283 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
4284 int widthInc = wpNew.width - wpMain.width;
4285 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
4286 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
4287 wp->y += fracLeft * widthInc;
4288 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
4289 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
4291 wp->x += wpNew.x - wpMain.x;
4292 wp->y += wpNew.y - wpMain.y;
4293 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
4294 if(touch == 3) wp->y += wpNew.height - wpMain.height;
4295 XtSetArg(args[j], XtNx, wp->x); j++;
4296 XtSetArg(args[j], XtNy, wp->y); j++;
4297 XtSetValues(sh, args, j);
4300 static XtIntervalId delayedDragID = 0;
4305 GetActualPlacement(shellWidget, &wpNew);
4306 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
4307 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
4308 return; // false alarm
4309 if(EngineOutputIsUp()) CoDrag(engineOutputShell, &wpEngineOutput);
4310 if(MoveHistoryIsUp()) CoDrag(shells[7], &wpMoveHistory);
4311 if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
4312 if(GameListIsUp()) CoDrag(gameListShell, &wpGameList);
4314 XDrawPosition(boardWidget, True, NULL);
4315 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
4322 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
4324 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
4327 /* Why is this needed on some versions of X? */
4329 EventProc (Widget widget, caddr_t unused, XEvent *event)
4331 if (!XtIsRealized(widget))
4333 switch (event->type) {
4334 case ConfigureNotify: // main window is being dragged: drag attached windows with it
4335 if(appData.useStickyWindows)
4336 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
4339 if (event->xexpose.count > 0) return; /* no clipping is done */
4340 XDrawPosition(widget, True, NULL);
4341 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4342 flipView = !flipView; partnerUp = !partnerUp;
4343 XDrawPosition(widget, True, NULL);
4344 flipView = !flipView; partnerUp = !partnerUp;
4348 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4356 DrawPosition (int fullRedraw, Board board)
4358 XDrawPosition(boardWidget, fullRedraw, board);
4361 /* Returns 1 if there are "too many" differences between b1 and b2
4362 (i.e. more than 1 move was made) */
4364 too_many_diffs (Board b1, Board b2)
4369 for (i=0; i<BOARD_HEIGHT; ++i) {
4370 for (j=0; j<BOARD_WIDTH; ++j) {
4371 if (b1[i][j] != b2[i][j]) {
4372 if (++c > 4) /* Castling causes 4 diffs */
4380 /* Matrix describing castling maneuvers */
4381 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4382 static int castling_matrix[4][5] = {
4383 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4384 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4385 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4386 { 7, 7, 4, 5, 6 } /* 0-0, black */
4389 /* Checks whether castling occurred. If it did, *rrow and *rcol
4390 are set to the destination (row,col) of the rook that moved.
4392 Returns 1 if castling occurred, 0 if not.
4394 Note: Only handles a max of 1 castling move, so be sure
4395 to call too_many_diffs() first.
4398 check_castle_draw (Board newb, Board oldb, int *rrow, int *rcol)
4403 /* For each type of castling... */
4404 for (i=0; i<4; ++i) {
4405 r = castling_matrix[i];
4407 /* Check the 4 squares involved in the castling move */
4409 for (j=1; j<=4; ++j) {
4410 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4417 /* All 4 changed, so it must be a castling move */
4426 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4428 DrawSeekAxis (int x, int y, int xTo, int yTo)
4430 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4434 DrawSeekBackground (int left, int top, int right, int bottom)
4436 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4440 DrawSeekText (char *buf, int x, int y)
4442 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4446 DrawSeekDot (int x, int y, int colorNr)
4448 int square = colorNr & 0x80;
4451 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4453 XFillRectangle(xDisplay, xBoardWindow, color,
4454 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4456 XFillArc(xDisplay, xBoardWindow, color,
4457 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4460 static int damage[2][BOARD_RANKS][BOARD_FILES];
4463 * event handler for redrawing the board
4466 XDrawPosition (Widget w, int repaint, Board board)
4469 static int lastFlipView = 0;
4470 static int lastBoardValid[2] = {0, 0};
4471 static Board lastBoard[2];
4474 int nr = twoBoards*partnerUp;
4476 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4478 if (board == NULL) {
4479 if (!lastBoardValid[nr]) return;
4480 board = lastBoard[nr];
4482 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4483 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4484 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4489 * It would be simpler to clear the window with XClearWindow()
4490 * but this causes a very distracting flicker.
4493 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4495 if ( lineGap && IsDrawArrowEnabled())
4496 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4497 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4499 /* If too much changes (begin observing new game, etc.), don't
4501 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4503 /* Special check for castling so we don't flash both the king
4504 and the rook (just flash the king). */
4506 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4507 /* Draw rook with NO flashing. King will be drawn flashing later */
4508 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4509 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4513 /* First pass -- Draw (newly) empty squares and repair damage.
4514 This prevents you from having a piece show up twice while it
4515 is flashing on its new square */
4516 for (i = 0; i < BOARD_HEIGHT; i++)
4517 for (j = 0; j < BOARD_WIDTH; j++)
4518 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4519 || damage[nr][i][j]) {
4520 DrawSquare(i, j, board[i][j], 0);
4521 damage[nr][i][j] = False;
4524 /* Second pass -- Draw piece(s) in new position and flash them */
4525 for (i = 0; i < BOARD_HEIGHT; i++)
4526 for (j = 0; j < BOARD_WIDTH; j++)
4527 if (board[i][j] != lastBoard[nr][i][j]) {
4528 DrawSquare(i, j, board[i][j], do_flash);
4532 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4533 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4534 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4536 for (i = 0; i < BOARD_HEIGHT; i++)
4537 for (j = 0; j < BOARD_WIDTH; j++) {
4538 DrawSquare(i, j, board[i][j], 0);
4539 damage[nr][i][j] = False;
4543 CopyBoard(lastBoard[nr], board);
4544 lastBoardValid[nr] = 1;
4545 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4546 lastFlipView = flipView;
4548 /* Draw highlights */
4549 if (pm1X >= 0 && pm1Y >= 0) {
4550 drawHighlight(pm1X, pm1Y, prelineGC);
4552 if (pm2X >= 0 && pm2Y >= 0) {
4553 drawHighlight(pm2X, pm2Y, prelineGC);
4555 if (hi1X >= 0 && hi1Y >= 0) {
4556 drawHighlight(hi1X, hi1Y, highlineGC);
4558 if (hi2X >= 0 && hi2Y >= 0) {
4559 drawHighlight(hi2X, hi2Y, highlineGC);
4561 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4563 /* If piece being dragged around board, must redraw that too */
4566 XSync(xDisplay, False);
4571 * event handler for redrawing the board
4574 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4576 XDrawPosition(w, True, NULL);
4581 * event handler for parsing user moves
4583 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4584 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4585 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4586 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4587 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4588 // and at the end FinishMove() to perform the move after optional promotion popups.
4589 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4591 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4593 if (w != boardWidget || errorExitStatus != -1) return;
4594 if(nprms) shiftKey = !strcmp(prms[0], "1");
4597 if (event->type == ButtonPress) {
4598 XtPopdown(promotionShell);
4599 XtDestroyWidget(promotionShell);
4600 promotionUp = False;
4608 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4609 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4610 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4614 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
4616 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4617 DragPieceMove(event->xmotion.x, event->xmotion.y);
4621 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
4622 { // [HGM] pv: walk PV
4623 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4626 static int savedIndex; /* gross that this is global */
4629 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4632 XawTextPosition index, dummy;
4635 XawTextGetSelectionPos(w, &index, &dummy);
4636 XtSetArg(arg, XtNstring, &val);
4637 XtGetValues(w, &arg, 1);
4638 ReplaceComment(savedIndex, val);
4639 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4640 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4644 EditCommentPopUp (int index, char *title, char *text)
4647 if (text == NULL) text = "";
4648 NewCommentPopup(title, text, index);
4657 extern Option boxOptions[];
4667 edit = boxOptions[0].handle;
4669 XtSetArg(args[j], XtNstring, &val); j++;
4670 XtGetValues(edit, args, j);
4672 SendMultiLineToICS(val);
4673 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4674 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4678 ICSInputBoxPopDown ()
4684 CommentPopUp (char *title, char *text)
4686 savedIndex = currentMove; // [HGM] vari
4687 NewCommentPopup(title, text, currentMove);
4696 static char *openName;
4702 (void) (*fileProc)(openFP, 0, openName);
4706 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
4708 fileProc = proc; /* I can't see a way not */
4709 fileOpenMode = openMode; /* to use globals here */
4710 { // [HGM] use file-selector dialog stolen from Ghostview
4711 int index; // this is not supported yet
4712 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
4713 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
4714 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
4715 ScheduleDelayedEvent(&DelayedLoad, 50);
4722 if (!filenameUp) return;
4723 XtPopdown(fileNameShell);
4724 XtDestroyWidget(fileNameShell);
4730 FileNameCallback (Widget w, XtPointer client_data, XtPointer call_data)
4735 XtSetArg(args[0], XtNlabel, &name);
4736 XtGetValues(w, args, 1);
4738 if (strcmp(name, _("cancel")) == 0) {
4743 FileNameAction(w, NULL, NULL, NULL);
4747 FileNameAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4755 name = XawDialogGetValueString(w = XtParent(w));
4757 if ((name != NULL) && (*name != NULLCHAR)) {
4758 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
4759 XtPopdown(w = XtParent(XtParent(w)));
4763 p = strrchr(buf, ' ');
4770 fullname = ExpandPathName(buf);
4772 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
4775 f = fopen(fullname, fileOpenMode);
4777 DisplayError(_("Failed to open file"), errno);
4779 (void) (*fileProc)(f, index, buf);
4786 XtPopdown(w = XtParent(XtParent(w)));
4796 Widget dialog, layout;
4798 Dimension bw_width, pw_width;
4800 char *PromoChars = "wglcqrbnkac+=\0";
4803 XtSetArg(args[j], XtNwidth, &bw_width); j++;
4804 XtGetValues(boardWidget, args, j);
4807 XtSetArg(args[j], XtNresizable, True); j++;
4808 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
4810 XtCreatePopupShell("Promotion", transientShellWidgetClass,
4811 shellWidget, args, j);
4813 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
4814 layoutArgs, XtNumber(layoutArgs));
4817 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
4818 XtSetArg(args[j], XtNborderWidth, 0); j++;
4819 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
4822 if(gameInfo.variant != VariantShogi) {
4823 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
4824 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
4825 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
4826 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
4827 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
4829 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
4830 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
4831 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
4832 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
4834 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
4835 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
4836 gameInfo.variant == VariantGiveaway) {
4837 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
4839 if(gameInfo.variant == VariantCapablanca ||
4840 gameInfo.variant == VariantGothic ||
4841 gameInfo.variant == VariantCapaRandom) {
4842 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
4843 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
4845 } else // [HGM] shogi
4847 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
4848 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
4850 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
4852 XtRealizeWidget(promotionShell);
4853 CatchDeleteWindow(promotionShell, "PromotionPopDown");
4856 XtSetArg(args[j], XtNwidth, &pw_width); j++;
4857 XtGetValues(promotionShell, args, j);
4859 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
4860 lineGap + squareSize/3 +
4861 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
4862 0 : 6*(squareSize + lineGap)), &x, &y);
4865 XtSetArg(args[j], XtNx, x); j++;
4866 XtSetArg(args[j], XtNy, y); j++;
4867 XtSetValues(promotionShell, args, j);
4869 XtPopup(promotionShell, XtGrabNone);
4877 if (!promotionUp) return;
4878 XtPopdown(promotionShell);
4879 XtDestroyWidget(promotionShell);
4880 promotionUp = False;
4884 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
4886 int promoChar = * (const char *) client_data;
4890 if (fromX == -1) return;
4897 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
4899 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
4900 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
4906 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
4908 dialogError = errorUp = False;
4909 XtPopdown(w = XtParent(XtParent(XtParent(w))));
4911 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
4918 if (!errorUp) return;
4919 dialogError = errorUp = False;
4920 XtPopdown(errorShell);
4921 XtDestroyWidget(errorShell);
4922 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
4926 ErrorPopUp (char *title, char *label, int modal)
4929 Widget dialog, layout;
4933 Dimension bw_width, pw_width;
4934 Dimension pw_height;
4938 XtSetArg(args[i], XtNresizable, True); i++;
4939 XtSetArg(args[i], XtNtitle, title); i++;
4941 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
4942 shellUp[0] ? (dialogError = modal = TRUE, shells[0]) : shellWidget, args, i);
4944 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
4945 layoutArgs, XtNumber(layoutArgs));
4948 XtSetArg(args[i], XtNlabel, label); i++;
4949 XtSetArg(args[i], XtNborderWidth, 0); i++;
4950 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
4953 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
4955 XtRealizeWidget(errorShell);
4956 CatchDeleteWindow(errorShell, "ErrorPopDown");
4959 XtSetArg(args[i], XtNwidth, &bw_width); i++;
4960 XtGetValues(boardWidget, args, i);
4962 XtSetArg(args[i], XtNwidth, &pw_width); i++;
4963 XtSetArg(args[i], XtNheight, &pw_height); i++;
4964 XtGetValues(errorShell, args, i);
4967 /* This code seems to tickle an X bug if it is executed too soon
4968 after xboard starts up. The coordinates get transformed as if
4969 the main window was positioned at (0, 0).
4971 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
4972 0 - pw_height + squareSize / 3, &x, &y);
4974 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
4975 RootWindowOfScreen(XtScreen(boardWidget)),
4976 (bw_width - pw_width) / 2,
4977 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
4981 if (y < 0) y = 0; /*avoid positioning top offscreen*/
4984 XtSetArg(args[i], XtNx, x); i++;
4985 XtSetArg(args[i], XtNy, y); i++;
4986 XtSetValues(errorShell, args, i);
4989 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
4992 /* Disable all user input other than deleting the window */
4993 static int frozen = 0;
4999 /* Grab by a widget that doesn't accept input */
5000 XtAddGrab(messageWidget, TRUE, FALSE);
5004 /* Undo a FreezeUI */
5008 if (!frozen) return;
5009 XtRemoveGrab(messageWidget);
5014 ModeToWidgetName (GameMode mode)
5017 case BeginningOfGame:
5018 if (appData.icsActive)
5019 return "menuMode.ICS Client";
5020 else if (appData.noChessProgram ||
5021 *appData.cmailGameName != NULLCHAR)
5022 return "menuMode.Edit Game";
5024 return "menuMode.Machine Black";
5025 case MachinePlaysBlack:
5026 return "menuMode.Machine Black";
5027 case MachinePlaysWhite:
5028 return "menuMode.Machine White";
5030 return "menuMode.Analysis Mode";
5032 return "menuMode.Analyze File";
5033 case TwoMachinesPlay:
5034 return "menuMode.Two Machines";
5036 return "menuMode.Edit Game";
5037 case PlayFromGameFile:
5038 return "menuFile.Load Game";
5040 return "menuMode.Edit Position";
5042 return "menuMode.Training";
5043 case IcsPlayingWhite:
5044 case IcsPlayingBlack:
5048 return "menuMode.ICS Client";
5059 static int oldPausing = FALSE;
5060 static GameMode oldmode = (GameMode) -1;
5063 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5065 if (pausing != oldPausing) {
5066 oldPausing = pausing;
5068 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5070 XtSetArg(args[0], XtNleftBitmap, None);
5072 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5075 if (appData.showButtonBar) {
5076 /* Always toggle, don't set. Previous code messes up when
5077 invoked while the button is pressed, as releasing it
5078 toggles the state again. */
5081 XtSetArg(args[0], XtNbackground, &oldbg);
5082 XtSetArg(args[1], XtNforeground, &oldfg);
5083 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5085 XtSetArg(args[0], XtNbackground, oldfg);
5086 XtSetArg(args[1], XtNforeground, oldbg);
5088 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5092 wname = ModeToWidgetName(oldmode);
5093 if (wname != NULL) {
5094 XtSetArg(args[0], XtNleftBitmap, None);
5095 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5097 wname = ModeToWidgetName(gameMode);
5098 if (wname != NULL) {
5099 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5100 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5103 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5104 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5106 /* Maybe all the enables should be handled here, not just this one */
5107 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5108 gameMode == Training || gameMode == PlayFromGameFile);
5113 * Button/menu procedures
5116 LoadGamePopUp (FILE *f, int gameNumber, char *title)
5118 cmailMsgLoaded = FALSE;
5119 if (gameNumber == 0) {
5120 int error = GameListBuild(f);
5122 DisplayError(_("Cannot build game list"), error);
5123 } else if (!ListEmpty(&gameList) &&
5124 ((ListGame *) gameList.tailPred)->number > 1) {
5125 GameListPopUp(f, title);
5131 return LoadGame(f, gameNumber, title, FALSE);
5137 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5140 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5162 LoadNextPositionProc ()
5168 LoadPrevPositionProc ()
5174 ReloadPositionProc ()
5182 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5185 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5191 FileNamePopUp(_("Save game file name?"),
5192 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5193 appData.oldSaveStyle ? ".game" : ".pgn",
5200 FileNamePopUp(_("Save position file name?"),
5201 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5202 appData.oldSaveStyle ? ".pos" : ".fen",
5207 ReloadCmailMsgProc ()
5209 ReloadCmailMsgEvent(FALSE);
5212 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5213 char *selected_fen_position=NULL;
5216 SendPositionSelection (Widget w, Atom *selection, Atom *target,
5217 Atom *type_return, XtPointer *value_return,
5218 unsigned long *length_return, int *format_return)
5220 char *selection_tmp;
5222 if (!selected_fen_position) return False; /* should never happen */
5223 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5224 /* note: since no XtSelectionDoneProc was registered, Xt will
5225 * automatically call XtFree on the value returned. So have to
5226 * make a copy of it allocated with XtMalloc */
5227 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5228 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5230 *value_return=selection_tmp;
5231 *length_return=strlen(selection_tmp);
5232 *type_return=*target;
5233 *format_return = 8; /* bits per byte */
5235 } else if (*target == XA_TARGETS(xDisplay)) {
5236 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5237 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5238 targets_tmp[1] = XA_STRING;
5239 *value_return = targets_tmp;
5240 *type_return = XA_ATOM;
5243 // This code leads to a read of value_return out of bounds on 64-bit systems.
5244 // Other code which I have seen always sets *format_return to 32 independent of
5245 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5246 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5247 *format_return = 8 * sizeof(Atom);
5248 if (*format_return > 32) {
5249 *length_return *= *format_return / 32;
5250 *format_return = 32;
5253 *format_return = 32;
5261 /* note: when called from menu all parameters are NULL, so no clue what the
5262 * Widget which was clicked on was, or what the click event was
5268 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5269 * have a notion of a position that is selected but not copied.
5270 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5272 if(gameMode == EditPosition) EditPositionDone(TRUE);
5273 if (selected_fen_position) free(selected_fen_position);
5274 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5275 if (!selected_fen_position) return;
5276 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5278 SendPositionSelection,
5279 NULL/* lose_ownership_proc */ ,
5280 NULL/* transfer_done_proc */);
5281 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5283 SendPositionSelection,
5284 NULL/* lose_ownership_proc */ ,
5285 NULL/* transfer_done_proc */);
5289 CopyFENToClipboard ()
5290 { // wrapper to make call from back-end possible
5294 /* function called when the data to Paste is ready */
5296 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
5297 Atom *type, XtPointer value, unsigned long *len, int *format)
5300 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5301 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5302 EditPositionPasteFEN(fenstr);
5306 /* called when Paste Position button is pressed,
5307 * all parameters will be NULL */
5309 PastePositionProc ()
5311 XtGetSelectionValue(menuBarWidget,
5312 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5313 /* (XtSelectionCallbackProc) */ PastePositionCB,
5314 NULL, /* client_data passed to PastePositionCB */
5316 /* better to use the time field from the event that triggered the
5317 * call to this function, but that isn't trivial to get
5325 SendGameSelection (Widget w, Atom *selection, Atom *target,
5326 Atom *type_return, XtPointer *value_return,
5327 unsigned long *length_return, int *format_return)
5329 char *selection_tmp;
5331 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5332 FILE* f = fopen(gameCopyFilename, "r");
5335 if (f == NULL) return False;
5339 selection_tmp = XtMalloc(len + 1);
5340 count = fread(selection_tmp, 1, len, f);
5343 XtFree(selection_tmp);
5346 selection_tmp[len] = NULLCHAR;
5347 *value_return = selection_tmp;
5348 *length_return = len;
5349 *type_return = *target;
5350 *format_return = 8; /* bits per byte */
5352 } else if (*target == XA_TARGETS(xDisplay)) {
5353 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5354 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5355 targets_tmp[1] = XA_STRING;
5356 *value_return = targets_tmp;
5357 *type_return = XA_ATOM;
5360 // This code leads to a read of value_return out of bounds on 64-bit systems.
5361 // Other code which I have seen always sets *format_return to 32 independent of
5362 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5363 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5364 *format_return = 8 * sizeof(Atom);
5365 if (*format_return > 32) {
5366 *length_return *= *format_return / 32;
5367 *format_return = 32;
5370 *format_return = 32;
5382 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5383 * have a notion of a game that is selected but not copied.
5384 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5386 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5389 NULL/* lose_ownership_proc */ ,
5390 NULL/* transfer_done_proc */);
5391 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5394 NULL/* lose_ownership_proc */ ,
5395 NULL/* transfer_done_proc */);
5398 /* note: when called from menu all parameters are NULL, so no clue what the
5399 * Widget which was clicked on was, or what the click event was
5406 ret = SaveGameToFile(gameCopyFilename, FALSE);
5415 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5419 /* function called when the data to Paste is ready */
5421 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
5422 Atom *type, XtPointer value, unsigned long *len, int *format)
5425 if (value == NULL || *len == 0) {
5426 return; /* nothing had been selected to copy */
5428 f = fopen(gamePasteFilename, "w");
5430 DisplayError(_("Can't open temp file"), errno);
5433 fwrite(value, 1, *len, f);
5436 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5439 /* called when Paste Game button is pressed,
5440 * all parameters will be NULL */
5444 XtGetSelectionValue(menuBarWidget,
5445 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5446 /* (XtSelectionCallbackProc) */ PasteGameCB,
5447 NULL, /* client_data passed to PasteGameCB */
5449 /* better to use the time field from the event that triggered the
5450 * call to this function, but that isn't trivial to get
5472 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5482 if (!first.analysisSupport) {
5483 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5484 DisplayError(buf, 0);
5487 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5488 if (appData.icsActive) {
5489 if (gameMode != IcsObserving) {
5490 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5491 DisplayError(buf, 0);
5493 if (appData.icsEngineAnalyze) {
5494 if (appData.debugMode)
5495 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5501 /* if enable, use want disable icsEngineAnalyze */
5502 if (appData.icsEngineAnalyze) {
5507 appData.icsEngineAnalyze = TRUE;
5508 if (appData.debugMode)
5509 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5511 #ifndef OPTIONSDIALOG
5512 if (!appData.showThinking)
5522 if (!first.analysisSupport) {
5524 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5525 DisplayError(buf, 0);
5528 // Reset(FALSE, TRUE);
5529 #ifndef OPTIONSDIALOG
5530 if (!appData.showThinking)
5534 // FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5535 AnalysisPeriodicEvent(1);
5549 if (PopDown(1)) { // popdown succesful
5551 XtSetArg(args[j], XtNleftBitmap, None); j++;
5552 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
5553 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
5554 } else // was not up
5561 if (!PopDown(4)) ICSInputBoxPopUp();
5567 UserAdjudicationEvent(+1);
5573 UserAdjudicationEvent(-1);
5579 UserAdjudicationEvent(0);
5583 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5585 if (shellUp[4] == True)
5590 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5591 { // [HGM] input: let up-arrow recall previous line from history
5598 if (!shellUp[4]) return;
5599 edit = boxOptions[0].handle;
5601 XtSetArg(args[j], XtNstring, &val); j++;
5602 XtGetValues(edit, args, j);
5603 val = PrevInHistory(val);
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 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5615 { // [HGM] input: let down-arrow recall next line from history
5620 if (!shellUp[4]) return;
5621 edit = boxOptions[0].handle;
5622 val = NextInHistory();
5623 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5624 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5626 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
5627 XawTextReplace(edit, 0, 0, &t);
5628 XawTextSetInsertionPoint(edit, 9999);
5633 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5635 if (!TempBackwardActive) {
5636 TempBackwardActive = True;
5642 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5644 /* Check to see if triggered by a key release event for a repeating key.
5645 * If so the next queued event will be a key press of the same key at the same time */
5646 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
5648 XPeekEvent(xDisplay, &next);
5649 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
5650 next.xkey.keycode == event->xkey.keycode)
5654 TempBackwardActive = False;
5672 flipView = !flipView;
5673 DrawPosition(True, NULL);
5677 PonderNextMoveProc ()
5681 PonderNextMoveEvent(!appData.ponderNextMove);
5682 #ifndef OPTIONSDIALOG
5683 if (appData.ponderNextMove) {
5684 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5686 XtSetArg(args[0], XtNleftBitmap, None);
5688 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
5693 #ifndef OPTIONSDIALOG
5699 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
5701 if (appData.alwaysPromoteToQueen) {
5702 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5704 XtSetArg(args[0], XtNleftBitmap, None);
5706 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
5711 AnimateDraggingProc ()
5715 appData.animateDragging = !appData.animateDragging;
5717 if (appData.animateDragging) {
5718 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5721 XtSetArg(args[0], XtNleftBitmap, None);
5723 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
5728 AnimateMovingProc ()
5732 appData.animate = !appData.animate;
5734 if (appData.animate) {
5735 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5738 XtSetArg(args[0], XtNleftBitmap, None);
5740 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
5749 appData.autoCallFlag = !appData.autoCallFlag;
5751 if (appData.autoCallFlag) {
5752 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5754 XtSetArg(args[0], XtNleftBitmap, None);
5756 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
5765 appData.autoFlipView = !appData.autoFlipView;
5767 if (appData.autoFlipView) {
5768 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5770 XtSetArg(args[0], XtNleftBitmap, None);
5772 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
5781 appData.blindfold = !appData.blindfold;
5783 if (appData.blindfold) {
5784 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5786 XtSetArg(args[0], XtNleftBitmap, None);
5788 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
5791 DrawPosition(True, NULL);
5799 appData.testLegality = !appData.testLegality;
5801 if (appData.testLegality) {
5802 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5804 XtSetArg(args[0], XtNleftBitmap, None);
5806 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
5816 if (appData.flashCount == 0) {
5817 appData.flashCount = 3;
5819 appData.flashCount = -appData.flashCount;
5822 if (appData.flashCount > 0) {
5823 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5825 XtSetArg(args[0], XtNleftBitmap, None);
5827 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
5833 HighlightDraggingProc ()
5837 appData.highlightDragging = !appData.highlightDragging;
5839 if (appData.highlightDragging) {
5840 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5842 XtSetArg(args[0], XtNleftBitmap, None);
5844 XtSetValues(XtNameToWidget(menuBarWidget,
5845 "menuOptions.Highlight Dragging"), args, 1);
5850 HighlightLastMoveProc ()
5854 appData.highlightLastMove = !appData.highlightLastMove;
5856 if (appData.highlightLastMove) {
5857 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5859 XtSetArg(args[0], XtNleftBitmap, None);
5861 XtSetValues(XtNameToWidget(menuBarWidget,
5862 "menuOptions.Highlight Last Move"), args, 1);
5866 HighlightArrowProc ()
5870 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
5872 if (appData.highlightMoveWithArrow) {
5873 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5875 XtSetArg(args[0], XtNleftBitmap, None);
5877 XtSetValues(XtNameToWidget(menuBarWidget,
5878 "menuOptions.Arrow"), args, 1);
5887 appData.icsAlarm = !appData.icsAlarm;
5889 if (appData.icsAlarm) {
5890 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5892 XtSetArg(args[0], XtNleftBitmap, None);
5894 XtSetValues(XtNameToWidget(menuBarWidget,
5895 "menuOptions.ICS Alarm"), args, 1);
5904 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
5906 if (appData.ringBellAfterMoves) {
5907 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5909 XtSetArg(args[0], XtNleftBitmap, None);
5911 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
5920 appData.oneClick = !appData.oneClick;
5922 if (appData.oneClick) {
5923 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5925 XtSetArg(args[0], XtNleftBitmap, None);
5927 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
5932 PeriodicUpdatesProc ()
5936 PeriodicUpdatesEvent(!appData.periodicUpdates);
5938 if (appData.periodicUpdates) {
5939 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5941 XtSetArg(args[0], XtNleftBitmap, None);
5943 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
5948 PopupExitMessageProc ()
5952 appData.popupExitMessage = !appData.popupExitMessage;
5954 if (appData.popupExitMessage) {
5955 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5957 XtSetArg(args[0], XtNleftBitmap, None);
5959 XtSetValues(XtNameToWidget(menuBarWidget,
5960 "menuOptions.Popup Exit Message"), args, 1);
5964 PopupMoveErrorsProc ()
5968 appData.popupMoveErrors = !appData.popupMoveErrors;
5970 if (appData.popupMoveErrors) {
5971 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5973 XtSetArg(args[0], XtNleftBitmap, None);
5975 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
5985 appData.premove = !appData.premove;
5987 if (appData.premove) {
5988 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5990 XtSetArg(args[0], XtNleftBitmap, None);
5992 XtSetValues(XtNameToWidget(menuBarWidget,
5993 "menuOptions.Premove"), args, 1);
6002 appData.showCoords = !appData.showCoords;
6004 if (appData.showCoords) {
6005 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6007 XtSetArg(args[0], XtNleftBitmap, None);
6009 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6012 DrawPosition(True, NULL);
6018 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6019 ShowThinkingEvent();
6027 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6028 ShowThinkingEvent();
6030 if (appData.hideThinkingFromHuman) {
6031 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6033 XtSetArg(args[0], XtNleftBitmap, None);
6035 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6045 saveSettingsOnExit = !saveSettingsOnExit;
6047 if (saveSettingsOnExit) {
6048 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6050 XtSetArg(args[0], XtNleftBitmap, None);
6052 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6059 SaveSettings(settingsFileName);
6066 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6072 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6073 { // called as key binding
6076 if (nprms && *nprms > 0)
6080 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6086 { // called from menu
6087 ManInner(NULL, NULL, NULL, NULL);
6094 snprintf(buf, MSG_SIZ, "%s mailto:bug-xboard@gnu.org", appData.sysOpen);
6102 snprintf(buf, MSG_SIZ, "%s http://www.gnu.org/software/xboard/user_guide/UserGuide.html", appData.sysOpen);
6110 snprintf(buf, MSG_SIZ, "%s http://www.gnu.org/software/xboard/", appData.sysOpen);
6118 snprintf(buf, MSG_SIZ, "%s http://www.gnu.org/software/xboard/whats_new/portal.html", appData.sysOpen);
6125 char buf[2 * MSG_SIZ];
6127 char *zippy = _(" (with Zippy code)");
6131 snprintf(buf, sizeof(buf),
6133 "Copyright 1991 Digital Equipment Corporation\n"
6134 "Enhancements Copyright 1992-2012 Free Software Foundation\n"
6135 "Enhancements Copyright 2005 Alessandro Scotti\n\n"
6136 "%s is free software and carries NO WARRANTY;"
6137 "see the file COPYING for more information.\n\n"
6138 "Visit XBoard on the web at: http://www.gnu.org/software/xboard/\n"
6139 "Check out the newest features at: http://www.gnu.org/software/xboard/whats_new.html\n\n"
6140 "Report bugs via email at: <bug-xboard@gnu.org>\n\n"
6142 programVersion, zippy, PACKAGE);
6143 ErrorPopUp(_("About XBoard"), buf, FALSE);
6149 appData.debugMode = !appData.debugMode;
6159 DisplayMessage (char *message, char *extMessage)
6161 /* display a message in the message widget */
6170 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6175 message = extMessage;
6179 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6181 /* need to test if messageWidget already exists, since this function
6182 can also be called during the startup, if for example a Xresource
6183 is not set up correctly */
6186 XtSetArg(arg, XtNlabel, message);
6187 XtSetValues(messageWidget, &arg, 1);
6194 DisplayTitle (char *text)
6198 char title[MSG_SIZ];
6201 if (text == NULL) text = "";
6203 if (appData.titleInWindow) {
6205 XtSetArg(args[i], XtNlabel, text); i++;
6206 XtSetValues(titleWidget, args, i);
6209 if (*text != NULLCHAR) {
6210 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6211 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6212 } else if (appData.icsActive) {
6213 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6214 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6215 } else if (appData.cmailGameName[0] != NULLCHAR) {
6216 snprintf(icon, sizeof(icon), "%s", "CMail");
6217 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6219 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6220 } else if (gameInfo.variant == VariantGothic) {
6221 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6222 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6225 } else if (gameInfo.variant == VariantFalcon) {
6226 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6227 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6229 } else if (appData.noChessProgram) {
6230 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6231 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6233 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6234 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6237 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6238 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6239 XtSetValues(shellWidget, args, i);
6240 XSync(xDisplay, False);
6245 DisplayError (String message, int error)
6250 if (appData.debugMode || appData.matchMode) {
6251 fprintf(stderr, "%s: %s\n", programName, message);
6254 if (appData.debugMode || appData.matchMode) {
6255 fprintf(stderr, "%s: %s: %s\n",
6256 programName, message, strerror(error));
6258 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6261 ErrorPopUp(_("Error"), message, FALSE);
6266 DisplayMoveError (String message)
6270 DrawPosition(FALSE, NULL);
6271 if (appData.debugMode || appData.matchMode) {
6272 fprintf(stderr, "%s: %s\n", programName, message);
6274 if (appData.popupMoveErrors) {
6275 ErrorPopUp(_("Error"), message, FALSE);
6277 DisplayMessage(message, "");
6283 DisplayFatalError (String message, int error, int status)
6287 errorExitStatus = status;
6289 fprintf(stderr, "%s: %s\n", programName, message);
6291 fprintf(stderr, "%s: %s: %s\n",
6292 programName, message, strerror(error));
6293 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6296 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6297 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6304 DisplayInformation (String message)
6307 ErrorPopUp(_("Information"), message, TRUE);
6311 DisplayNote (String message)
6314 ErrorPopUp(_("Note"), message, FALSE);
6318 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
6324 DisplayIcsInteractionTitle (String message)
6326 if (oldICSInteractionTitle == NULL) {
6327 /* Magic to find the old window title, adapted from vim */
6328 char *wina = getenv("WINDOWID");
6330 Window win = (Window) atoi(wina);
6331 Window root, parent, *children;
6332 unsigned int nchildren;
6333 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6335 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6336 if (!XQueryTree(xDisplay, win, &root, &parent,
6337 &children, &nchildren)) break;
6338 if (children) XFree((void *)children);
6339 if (parent == root || parent == 0) break;
6342 XSetErrorHandler(oldHandler);
6344 if (oldICSInteractionTitle == NULL) {
6345 oldICSInteractionTitle = "xterm";
6348 printf("\033]0;%s\007", message);
6352 char pendingReplyPrefix[MSG_SIZ];
6353 ProcRef pendingReplyPR;
6356 AskQuestionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6359 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
6363 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6367 AskQuestionPopDown ()
6369 if (!askQuestionUp) return;
6370 XtPopdown(askQuestionShell);
6371 XtDestroyWidget(askQuestionShell);
6372 askQuestionUp = False;
6376 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6382 reply = XawDialogGetValueString(w = XtParent(w));
6383 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
6384 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
6385 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
6386 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
6387 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6388 AskQuestionPopDown();
6390 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
6394 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
6399 XtSetArg(args[0], XtNlabel, &name);
6400 XtGetValues(w, args, 1);
6402 if (strcmp(name, _("cancel")) == 0) {
6403 AskQuestionPopDown();
6405 AskQuestionReplyAction(w, NULL, NULL, NULL);
6410 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
6413 Widget popup, layout, dialog, edit;
6419 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
6420 pendingReplyPR = pr;
6423 XtSetArg(args[i], XtNresizable, True); i++;
6424 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
6425 askQuestionShell = popup =
6426 XtCreatePopupShell(title, transientShellWidgetClass,
6427 shellWidget, args, i);
6430 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
6431 layoutArgs, XtNumber(layoutArgs));
6434 XtSetArg(args[i], XtNlabel, question); i++;
6435 XtSetArg(args[i], XtNvalue, ""); i++;
6436 XtSetArg(args[i], XtNborderWidth, 0); i++;
6437 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
6440 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
6441 (XtPointer) dialog);
6442 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
6443 (XtPointer) dialog);
6445 XtRealizeWidget(popup);
6446 CatchDeleteWindow(popup, "AskQuestionPopDown");
6448 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
6449 &x, &y, &win_x, &win_y, &mask);
6451 XtSetArg(args[0], XtNx, x - 10);
6452 XtSetArg(args[1], XtNy, y - 30);
6453 XtSetValues(popup, args, 2);
6455 XtPopup(popup, XtGrabExclusive);
6456 askQuestionUp = True;
6458 edit = XtNameToWidget(dialog, "*value");
6459 XtSetKeyboardFocus(popup, edit);
6464 PlaySound (char *name)
6466 if (*name == NULLCHAR) {
6468 } else if (strcmp(name, "$") == 0) {
6469 putc(BELLCHAR, stderr);
6472 char *prefix = "", *sep = "";
6473 if(appData.soundProgram[0] == NULLCHAR) return;
6474 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
6475 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
6483 PlaySound(appData.soundMove);
6489 PlaySound(appData.soundIcsWin);
6495 PlaySound(appData.soundIcsLoss);
6501 PlaySound(appData.soundIcsDraw);
6505 PlayIcsUnfinishedSound ()
6507 PlaySound(appData.soundIcsUnfinished);
6513 PlaySound(appData.soundIcsAlarm);
6519 PlaySound(appData.soundTell);
6525 system("stty echo");
6532 system("stty -echo");
6537 RunCommand (char *buf)
6543 Colorize (ColorClass cc, int continuation)
6546 int count, outCount, error;
6548 if (textColors[(int)cc].bg > 0) {
6549 if (textColors[(int)cc].fg > 0) {
6550 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
6551 textColors[(int)cc].fg, textColors[(int)cc].bg);
6553 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
6554 textColors[(int)cc].bg);
6557 if (textColors[(int)cc].fg > 0) {
6558 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
6559 textColors[(int)cc].fg);
6561 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
6564 count = strlen(buf);
6565 outCount = OutputToProcess(NoProc, buf, count, &error);
6566 if (outCount < count) {
6567 DisplayFatalError(_("Error writing to display"), error, 1);
6570 if (continuation) return;
6573 PlaySound(appData.soundShout);
6576 PlaySound(appData.soundSShout);
6579 PlaySound(appData.soundChannel1);
6582 PlaySound(appData.soundChannel);
6585 PlaySound(appData.soundKibitz);
6588 PlaySound(appData.soundTell);
6590 case ColorChallenge:
6591 PlaySound(appData.soundChallenge);
6594 PlaySound(appData.soundRequest);
6597 PlaySound(appData.soundSeek);
6609 return getpwuid(getuid())->pw_name;
6613 ExpandPathName (char *path)
6615 static char static_buf[4*MSG_SIZ];
6616 char *d, *s, buf[4*MSG_SIZ];
6622 while (*s && isspace(*s))
6631 if (*(s+1) == '/') {
6632 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
6636 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
6637 { char *p; if(p = strchr(buf, '/')) *p = 0; }
6638 pwd = getpwnam(buf);
6641 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
6645 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
6646 strcat(d, strchr(s+1, '/'));
6650 safeStrCpy(d, s, 4*MSG_SIZ );
6658 static char host_name[MSG_SIZ];
6660 #if HAVE_GETHOSTNAME
6661 gethostname(host_name, MSG_SIZ);
6663 #else /* not HAVE_GETHOSTNAME */
6664 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
6665 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
6667 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
6669 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
6670 #endif /* not HAVE_GETHOSTNAME */
6673 XtIntervalId delayedEventTimerXID = 0;
6674 DelayedEventCallback delayedEventCallback = 0;
6679 delayedEventTimerXID = 0;
6680 delayedEventCallback();
6684 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
6686 if(delayedEventTimerXID && delayedEventCallback == cb)
6687 // [HGM] alive: replace, rather than add or flush identical event
6688 XtRemoveTimeOut(delayedEventTimerXID);
6689 delayedEventCallback = cb;
6690 delayedEventTimerXID =
6691 XtAppAddTimeOut(appContext, millisec,
6692 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
6695 DelayedEventCallback
6698 if (delayedEventTimerXID) {
6699 return delayedEventCallback;
6706 CancelDelayedEvent ()
6708 if (delayedEventTimerXID) {
6709 XtRemoveTimeOut(delayedEventTimerXID);
6710 delayedEventTimerXID = 0;
6714 XtIntervalId loadGameTimerXID = 0;
6717 LoadGameTimerRunning ()
6719 return loadGameTimerXID != 0;
6723 StopLoadGameTimer ()
6725 if (loadGameTimerXID != 0) {
6726 XtRemoveTimeOut(loadGameTimerXID);
6727 loadGameTimerXID = 0;
6735 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
6737 loadGameTimerXID = 0;
6742 StartLoadGameTimer (long millisec)
6745 XtAppAddTimeOut(appContext, millisec,
6746 (XtTimerCallbackProc) LoadGameTimerCallback,
6750 XtIntervalId analysisClockXID = 0;
6753 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
6755 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
6756 || appData.icsEngineAnalyze) { // [DM]
6757 AnalysisPeriodicEvent(0);
6758 StartAnalysisClock();
6763 StartAnalysisClock ()
6766 XtAppAddTimeOut(appContext, 2000,
6767 (XtTimerCallbackProc) AnalysisClockCallback,
6771 XtIntervalId clockTimerXID = 0;
6774 ClockTimerRunning ()
6776 return clockTimerXID != 0;
6782 if (clockTimerXID != 0) {
6783 XtRemoveTimeOut(clockTimerXID);
6792 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
6799 StartClockTimer (long millisec)
6802 XtAppAddTimeOut(appContext, millisec,
6803 (XtTimerCallbackProc) ClockTimerCallback,
6808 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
6813 /* check for low time warning */
6814 Pixel foregroundOrWarningColor = timerForegroundPixel;
6817 appData.lowTimeWarning &&
6818 (timer / 1000) < appData.icsAlarmTime)
6819 foregroundOrWarningColor = lowTimeWarningColor;
6821 if (appData.clockMode) {
6822 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
6823 XtSetArg(args[0], XtNlabel, buf);
6825 snprintf(buf, MSG_SIZ, "%s ", color);
6826 XtSetArg(args[0], XtNlabel, buf);
6831 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
6832 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
6834 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
6835 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
6838 XtSetValues(w, args, 3);
6842 DisplayWhiteClock (long timeRemaining, int highlight)
6846 if(appData.noGUI) return;
6847 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
6848 if (highlight && iconPixmap == bIconPixmap) {
6849 iconPixmap = wIconPixmap;
6850 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
6851 XtSetValues(shellWidget, args, 1);
6856 DisplayBlackClock (long timeRemaining, int highlight)
6860 if(appData.noGUI) return;
6861 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
6862 if (highlight && iconPixmap == wIconPixmap) {
6863 iconPixmap = bIconPixmap;
6864 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
6865 XtSetValues(shellWidget, args, 1);
6884 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
6888 int to_prog[2], from_prog[2];
6892 if (appData.debugMode) {
6893 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
6896 /* We do NOT feed the cmdLine to the shell; we just
6897 parse it into blank-separated arguments in the
6898 most simple-minded way possible.
6901 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
6904 while(*p == ' ') p++;
6906 if(*p == '"' || *p == '\'')
6907 p = strchr(++argv[i-1], *p);
6908 else p = strchr(p, ' ');
6909 if (p == NULL) break;
6914 SetUpChildIO(to_prog, from_prog);
6916 if ((pid = fork()) == 0) {
6918 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
6919 close(to_prog[1]); // first close the unused pipe ends
6920 close(from_prog[0]);
6921 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
6922 dup2(from_prog[1], 1);
6923 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
6924 close(from_prog[1]); // and closing again loses one of the pipes!
6925 if(fileno(stderr) >= 2) // better safe than sorry...
6926 dup2(1, fileno(stderr)); /* force stderr to the pipe */
6928 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
6933 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
6935 execvp(argv[0], argv);
6937 /* If we get here, exec failed */
6942 /* Parent process */
6944 close(from_prog[1]);
6946 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
6949 cp->fdFrom = from_prog[0];
6950 cp->fdTo = to_prog[1];
6955 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
6957 AlarmCallBack (int n)
6963 DestroyChildProcess (ProcRef pr, int signalType)
6965 ChildProc *cp = (ChildProc *) pr;
6967 if (cp->kind != CPReal) return;
6969 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
6970 signal(SIGALRM, AlarmCallBack);
6972 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
6973 kill(cp->pid, SIGKILL); // kill it forcefully
6974 wait((int *) 0); // and wait again
6978 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
6980 /* Process is exiting either because of the kill or because of
6981 a quit command sent by the backend; either way, wait for it to die.
6990 InterruptChildProcess (ProcRef pr)
6992 ChildProc *cp = (ChildProc *) pr;
6994 if (cp->kind != CPReal) return;
6995 (void) kill(cp->pid, SIGINT); /* stop it thinking */
6999 OpenTelnet (char *host, char *port, ProcRef *pr)
7001 char cmdLine[MSG_SIZ];
7003 if (port[0] == NULLCHAR) {
7004 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7006 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7008 return StartChildProcess(cmdLine, "", pr);
7012 OpenTCP (char *host, char *port, ProcRef *pr)
7015 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7016 #else /* !OMIT_SOCKETS */
7017 struct addrinfo hints;
7018 struct addrinfo *ais, *ai;
7023 memset(&hints, 0, sizeof(hints));
7024 hints.ai_family = AF_UNSPEC;
7025 hints.ai_socktype = SOCK_STREAM;
7027 error = getaddrinfo(host, port, &hints, &ais);
7029 /* a getaddrinfo error is not an errno, so can't return it */
7030 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7031 host, port, gai_strerror(error));
7035 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7036 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7040 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7053 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7059 #endif /* !OMIT_SOCKETS */
7065 OpenCommPort (char *name, ProcRef *pr)
7070 fd = open(name, 2, 0);
7071 if (fd < 0) return errno;
7073 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7084 OpenLoopback (ProcRef *pr)
7089 SetUpChildIO(to, from);
7091 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7094 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7102 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
7104 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7108 #define INPUT_SOURCE_BUF_SIZE 8192
7117 char buf[INPUT_SOURCE_BUF_SIZE];
7122 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
7124 InputSource *is = (InputSource *) closure;
7129 if (is->lineByLine) {
7130 count = read(is->fd, is->unused,
7131 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7133 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7136 is->unused += count;
7138 while (p < is->unused) {
7139 q = memchr(p, '\n', is->unused - p);
7140 if (q == NULL) break;
7142 (is->func)(is, is->closure, p, q - p, 0);
7146 while (p < is->unused) {
7151 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7156 (is->func)(is, is->closure, is->buf, count, error);
7161 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
7164 ChildProc *cp = (ChildProc *) pr;
7166 is = (InputSource *) calloc(1, sizeof(InputSource));
7167 is->lineByLine = lineByLine;
7171 is->fd = fileno(stdin);
7173 is->kind = cp->kind;
7174 is->fd = cp->fdFrom;
7177 is->unused = is->buf;
7180 is->xid = XtAppAddInput(appContext, is->fd,
7181 (XtPointer) (XtInputReadMask),
7182 (XtInputCallbackProc) DoInputCallback,
7184 is->closure = closure;
7185 return (InputSourceRef) is;
7189 RemoveInputSource (InputSourceRef isr)
7191 InputSource *is = (InputSource *) isr;
7193 if (is->xid == 0) return;
7194 XtRemoveInput(is->xid);
7199 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
7201 static int line = 0;
7202 ChildProc *cp = (ChildProc *) pr;
7207 if (appData.noJoin || !appData.useInternalWrap)
7208 outCount = fwrite(message, 1, count, stdout);
7211 int width = get_term_width();
7212 int len = wrap(NULL, message, count, width, &line);
7213 char *msg = malloc(len);
7217 outCount = fwrite(message, 1, count, stdout);
7220 dbgchk = wrap(msg, message, count, width, &line);
7221 if (dbgchk != len && appData.debugMode)
7222 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7223 outCount = fwrite(msg, 1, dbgchk, stdout);
7229 outCount = write(cp->fdTo, message, count);
7239 /* Output message to process, with "ms" milliseconds of delay
7240 between each character. This is needed when sending the logon
7241 script to ICC, which for some reason doesn't like the
7242 instantaneous send. */
7244 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
7246 ChildProc *cp = (ChildProc *) pr;
7251 r = write(cp->fdTo, message++, 1);
7264 /**** Animation code by Hugh Fisher, DCS, ANU.
7266 Known problem: if a window overlapping the board is
7267 moved away while a piece is being animated underneath,
7268 the newly exposed area won't be updated properly.
7269 I can live with this.
7271 Known problem: if you look carefully at the animation
7272 of pieces in mono mode, they are being drawn as solid
7273 shapes without interior detail while moving. Fixing
7274 this would be a major complication for minimal return.
7277 /* Masks for XPM pieces. Black and white pieces can have
7278 different shapes, but in the interest of retaining my
7279 sanity pieces must have the same outline on both light
7280 and dark squares, and all pieces must use the same
7281 background square colors/images. */
7283 static int xpmDone = 0;
7286 CreateAnimMasks (int pieceDepth)
7292 unsigned long plane;
7295 /* Need a bitmap just to get a GC with right depth */
7296 buf = XCreatePixmap(xDisplay, xBoardWindow,
7298 values.foreground = 1;
7299 values.background = 0;
7300 /* Don't use XtGetGC, not read only */
7301 maskGC = XCreateGC(xDisplay, buf,
7302 GCForeground | GCBackground, &values);
7303 XFreePixmap(xDisplay, buf);
7305 buf = XCreatePixmap(xDisplay, xBoardWindow,
7306 squareSize, squareSize, pieceDepth);
7307 values.foreground = XBlackPixel(xDisplay, xScreen);
7308 values.background = XWhitePixel(xDisplay, xScreen);
7309 bufGC = XCreateGC(xDisplay, buf,
7310 GCForeground | GCBackground, &values);
7312 for (piece = WhitePawn; piece <= BlackKing; piece++) {
7313 /* Begin with empty mask */
7314 if(!xpmDone) // [HGM] pieces: keep using existing
7315 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7316 squareSize, squareSize, 1);
7317 XSetFunction(xDisplay, maskGC, GXclear);
7318 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7319 0, 0, squareSize, squareSize);
7321 /* Take a copy of the piece */
7326 XSetFunction(xDisplay, bufGC, GXcopy);
7327 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
7329 0, 0, squareSize, squareSize, 0, 0);
7331 /* XOR the background (light) over the piece */
7332 XSetFunction(xDisplay, bufGC, GXxor);
7334 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7335 0, 0, squareSize, squareSize, 0, 0);
7337 XSetForeground(xDisplay, bufGC, lightSquareColor);
7338 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7341 /* We now have an inverted piece image with the background
7342 erased. Construct mask by just selecting all the non-zero
7343 pixels - no need to reconstruct the original image. */
7344 XSetFunction(xDisplay, maskGC, GXor);
7346 /* Might be quicker to download an XImage and create bitmap
7347 data from it rather than this N copies per piece, but it
7348 only takes a fraction of a second and there is a much
7349 longer delay for loading the pieces. */
7350 for (n = 0; n < pieceDepth; n ++) {
7351 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7352 0, 0, squareSize, squareSize,
7358 XFreePixmap(xDisplay, buf);
7359 XFreeGC(xDisplay, bufGC);
7360 XFreeGC(xDisplay, maskGC);
7364 InitAnimState (AnimState *anim, XWindowAttributes *info)
7369 /* Each buffer is square size, same depth as window */
7370 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
7371 squareSize, squareSize, info->depth);
7372 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
7373 squareSize, squareSize, info->depth);
7375 /* Create a plain GC for blitting */
7376 mask = GCForeground | GCBackground | GCFunction |
7377 GCPlaneMask | GCGraphicsExposures;
7378 values.foreground = XBlackPixel(xDisplay, xScreen);
7379 values.background = XWhitePixel(xDisplay, xScreen);
7380 values.function = GXcopy;
7381 values.plane_mask = AllPlanes;
7382 values.graphics_exposures = False;
7383 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
7385 /* Piece will be copied from an existing context at
7386 the start of each new animation/drag. */
7387 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
7389 /* Outline will be a read-only copy of an existing */
7390 anim->outlineGC = None;
7396 XWindowAttributes info;
7398 if (xpmDone && gameInfo.variant == oldVariant) return;
7399 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
7400 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
7402 InitAnimState(&game, &info);
7403 InitAnimState(&player, &info);
7405 /* For XPM pieces, we need bitmaps to use as masks. */
7407 CreateAnimMasks(info.depth), xpmDone = 1;
7412 static Boolean frameWaiting;
7415 FrameAlarm (int sig)
7417 frameWaiting = False;
7418 /* In case System-V style signals. Needed?? */
7419 signal(SIGALRM, FrameAlarm);
7423 FrameDelay (int time)
7425 struct itimerval delay;
7427 XSync(xDisplay, False);
7430 frameWaiting = True;
7431 signal(SIGALRM, FrameAlarm);
7432 delay.it_interval.tv_sec =
7433 delay.it_value.tv_sec = time / 1000;
7434 delay.it_interval.tv_usec =
7435 delay.it_value.tv_usec = (time % 1000) * 1000;
7436 setitimer(ITIMER_REAL, &delay, NULL);
7437 while (frameWaiting) pause();
7438 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
7439 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
7440 setitimer(ITIMER_REAL, &delay, NULL);
7447 FrameDelay (int time)
7449 XSync(xDisplay, False);
7451 usleep(time * 1000);
7462 /* Convert board position to corner of screen rect and color */
7465 ScreenSquare (int column, int row, XPoint *pt, int *color)
7468 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
7469 pt->y = lineGap + row * (squareSize + lineGap);
7471 pt->x = lineGap + column * (squareSize + lineGap);
7472 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
7474 *color = SquareColor(row, column);
7477 /* Convert window coords to square */
7480 BoardSquare (int x, int y, int *column, int *row)
7482 *column = EventToSquare(x, BOARD_WIDTH);
7483 if (flipView && *column >= 0)
7484 *column = BOARD_WIDTH - 1 - *column;
7485 *row = EventToSquare(y, BOARD_HEIGHT);
7486 if (!flipView && *row >= 0)
7487 *row = BOARD_HEIGHT - 1 - *row;
7492 #undef Max /* just in case */
7494 #define Max(a, b) ((a) > (b) ? (a) : (b))
7495 #define Min(a, b) ((a) < (b) ? (a) : (b))
7498 SetRect (XRectangle *rect, int x, int y, int width, int height)
7502 rect->width = width;
7503 rect->height = height;
7506 /* Test if two frames overlap. If they do, return
7507 intersection rect within old and location of
7508 that rect within new. */
7511 Intersect ( XPoint *old, XPoint *new, int size, XRectangle *area, XPoint *pt)
7513 if (old->x > new->x + size || new->x > old->x + size ||
7514 old->y > new->y + size || new->y > old->y + size) {
7517 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
7518 size - abs(old->x - new->x), size - abs(old->y - new->y));
7519 pt->x = Max(old->x - new->x, 0);
7520 pt->y = Max(old->y - new->y, 0);
7525 /* For two overlapping frames, return the rect(s)
7526 in the old that do not intersect with the new. */
7529 CalcUpdateRects (XPoint *old, XPoint *new, int size, XRectangle update[], int *nUpdates)
7533 /* If old = new (shouldn't happen) then nothing to draw */
7534 if (old->x == new->x && old->y == new->y) {
7538 /* Work out what bits overlap. Since we know the rects
7539 are the same size we don't need a full intersect calc. */
7541 /* Top or bottom edge? */
7542 if (new->y > old->y) {
7543 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
7545 } else if (old->y > new->y) {
7546 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
7547 size, old->y - new->y);
7550 /* Left or right edge - don't overlap any update calculated above. */
7551 if (new->x > old->x) {
7552 SetRect(&(update[count]), old->x, Max(new->y, old->y),
7553 new->x - old->x, size - abs(new->y - old->y));
7555 } else if (old->x > new->x) {
7556 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
7557 old->x - new->x, size - abs(new->y - old->y));
7564 /* Generate a series of frame coords from start->mid->finish.
7565 The movement rate doubles until the half way point is
7566 reached, then halves back down to the final destination,
7567 which gives a nice slow in/out effect. The algorithmn
7568 may seem to generate too many intermediates for short
7569 moves, but remember that the purpose is to attract the
7570 viewers attention to the piece about to be moved and
7571 then to where it ends up. Too few frames would be less
7575 Tween (XPoint *start, XPoint *mid, XPoint *finish, int factor, XPoint frames[], int *nFrames)
7577 int fraction, n, count;
7581 /* Slow in, stepping 1/16th, then 1/8th, ... */
7583 for (n = 0; n < factor; n++)
7585 for (n = 0; n < factor; n++) {
7586 frames[count].x = start->x + (mid->x - start->x) / fraction;
7587 frames[count].y = start->y + (mid->y - start->y) / fraction;
7589 fraction = fraction / 2;
7593 frames[count] = *mid;
7596 /* Slow out, stepping 1/2, then 1/4, ... */
7598 for (n = 0; n < factor; n++) {
7599 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
7600 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
7602 fraction = fraction * 2;
7607 /* Draw a piece on the screen without disturbing what's there */
7610 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
7614 /* Bitmap for piece being moved. */
7615 if (appData.monoMode) {
7616 *mask = *pieceToSolid(piece);
7617 } else if (useImages) {
7619 *mask = xpmMask[piece];
7621 *mask = ximMaskPm[piece];
7624 *mask = *pieceToSolid(piece);
7627 /* GC for piece being moved. Square color doesn't matter, but
7628 since it gets modified we make a copy of the original. */
7630 if (appData.monoMode)
7635 if (appData.monoMode)
7640 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
7642 /* Outline only used in mono mode and is not modified */
7644 *outline = bwPieceGC;
7646 *outline = wbPieceGC;
7650 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
7655 /* Draw solid rectangle which will be clipped to shape of piece */
7656 XFillRectangle(xDisplay, dest, clip,
7657 0, 0, squareSize, squareSize);
7658 if (appData.monoMode)
7659 /* Also draw outline in contrasting color for black
7660 on black / white on white cases */
7661 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
7662 0, 0, squareSize, squareSize, 0, 0, 1);
7664 /* Copy the piece */
7669 if(appData.upsideDown && flipView) kind ^= 2;
7670 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
7672 0, 0, squareSize, squareSize,
7677 /* Animate the movement of a single piece */
7680 BeginAnimation (AnimState *anim, ChessSquare piece, int startColor, XPoint *start)
7684 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
7685 /* The old buffer is initialised with the start square (empty) */
7686 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
7687 anim->prevFrame = *start;
7689 /* The piece will be drawn using its own bitmap as a matte */
7690 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
7691 XSetClipMask(xDisplay, anim->pieceGC, mask);
7695 AnimationFrame (AnimState *anim, XPoint *frame, ChessSquare piece)
7697 XRectangle updates[4];
7702 /* Save what we are about to draw into the new buffer */
7703 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
7704 frame->x, frame->y, squareSize, squareSize,
7707 /* Erase bits of the previous frame */
7708 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
7709 /* Where the new frame overlapped the previous,
7710 the contents in newBuf are wrong. */
7711 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
7712 overlap.x, overlap.y,
7713 overlap.width, overlap.height,
7715 /* Repaint the areas in the old that don't overlap new */
7716 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
7717 for (i = 0; i < count; i++)
7718 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
7719 updates[i].x - anim->prevFrame.x,
7720 updates[i].y - anim->prevFrame.y,
7721 updates[i].width, updates[i].height,
7722 updates[i].x, updates[i].y);
7724 /* Easy when no overlap */
7725 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
7726 0, 0, squareSize, squareSize,
7727 anim->prevFrame.x, anim->prevFrame.y);
7730 /* Save this frame for next time round */
7731 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
7732 0, 0, squareSize, squareSize,
7734 anim->prevFrame = *frame;
7736 /* Draw piece over original screen contents, not current,
7737 and copy entire rect. Wipes out overlapping piece images. */
7738 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
7739 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
7740 0, 0, squareSize, squareSize,
7741 frame->x, frame->y);
7745 EndAnimation (AnimState *anim, XPoint *finish)
7747 XRectangle updates[4];
7752 /* The main code will redraw the final square, so we
7753 only need to erase the bits that don't overlap. */
7754 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
7755 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
7756 for (i = 0; i < count; i++)
7757 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
7758 updates[i].x - anim->prevFrame.x,
7759 updates[i].y - anim->prevFrame.y,
7760 updates[i].width, updates[i].height,
7761 updates[i].x, updates[i].y);
7763 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
7764 0, 0, squareSize, squareSize,
7765 anim->prevFrame.x, anim->prevFrame.y);
7770 FrameSequence (AnimState *anim, ChessSquare piece, int startColor, XPoint *start, XPoint *finish, XPoint frames[], int nFrames)
7774 BeginAnimation(anim, piece, startColor, start);
7775 for (n = 0; n < nFrames; n++) {
7776 AnimationFrame(anim, &(frames[n]), piece);
7777 FrameDelay(appData.animSpeed);
7779 EndAnimation(anim, finish);
7783 AnimateAtomicCapture (Board board, int fromX, int fromY, int toX, int toY)
7786 ChessSquare piece = board[fromY][toY];
7787 board[fromY][toY] = EmptySquare;
7788 DrawPosition(FALSE, board);
7790 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
7791 y = lineGap + toY * (squareSize + lineGap);
7793 x = lineGap + toX * (squareSize + lineGap);
7794 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
7796 for(i=1; i<4*kFactor; i++) {
7797 int r = squareSize * 9 * i/(20*kFactor - 5);
7798 XFillArc(xDisplay, xBoardWindow, highlineGC,
7799 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
7800 FrameDelay(appData.animSpeed);
7802 board[fromY][toY] = piece;
7805 /* Main control logic for deciding what to animate and how */
7808 AnimateMove (Board board, int fromX, int fromY, int toX, int toY)
7812 XPoint start, finish, mid;
7813 XPoint frames[kFactor * 2 + 1];
7814 int nFrames, startColor, endColor;
7816 /* Are we animating? */
7817 if (!appData.animate || appData.blindfold)
7820 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
7821 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
7822 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
7824 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
7825 piece = board[fromY][fromX];
7826 if (piece >= EmptySquare) return;
7831 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
7834 ScreenSquare(fromX, fromY, &start, &startColor);
7835 ScreenSquare(toX, toY, &finish, &endColor);
7838 /* Knight: make straight movement then diagonal */
7839 if (abs(toY - fromY) < abs(toX - fromX)) {
7840 mid.x = start.x + (finish.x - start.x) / 2;
7844 mid.y = start.y + (finish.y - start.y) / 2;
7847 mid.x = start.x + (finish.x - start.x) / 2;
7848 mid.y = start.y + (finish.y - start.y) / 2;
7851 /* Don't use as many frames for very short moves */
7852 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
7853 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
7855 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
7856 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
7857 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
7859 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
7860 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
7863 /* Be sure end square is redrawn */
7864 damage[0][toY][toX] = True;
7868 DragPieceBegin (int x, int y, Boolean instantly)
7870 int boardX, boardY, color;
7873 /* Are we animating? */
7874 if (!appData.animateDragging || appData.blindfold)
7877 /* Figure out which square we start in and the
7878 mouse position relative to top left corner. */
7879 BoardSquare(x, y, &boardX, &boardY);
7880 player.startBoardX = boardX;
7881 player.startBoardY = boardY;
7882 ScreenSquare(boardX, boardY, &corner, &color);
7883 player.startSquare = corner;
7884 player.startColor = color;
7885 /* As soon as we start dragging, the piece will jump slightly to
7886 be centered over the mouse pointer. */
7887 player.mouseDelta.x = squareSize/2;
7888 player.mouseDelta.y = squareSize/2;
7889 /* Initialise animation */
7890 player.dragPiece = PieceForSquare(boardX, boardY);
7892 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
7893 player.dragActive = True;
7894 BeginAnimation(&player, player.dragPiece, color, &corner);
7895 /* Mark this square as needing to be redrawn. Note that
7896 we don't remove the piece though, since logically (ie
7897 as seen by opponent) the move hasn't been made yet. */
7898 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
7899 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
7900 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
7901 corner.x, corner.y, squareSize, squareSize,
7902 0, 0); // [HGM] zh: unstack in stead of grab
7903 if(gatingPiece != EmptySquare) {
7904 /* Kludge alert: When gating we want the introduced
7905 piece to appear on the from square. To generate an
7906 image of it, we draw it on the board, copy the image,
7907 and draw the original piece again. */
7908 ChessSquare piece = boards[currentMove][boardY][boardX];
7909 DrawSquare(boardY, boardX, gatingPiece, 0);
7910 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
7911 corner.x, corner.y, squareSize, squareSize, 0, 0);
7912 DrawSquare(boardY, boardX, piece, 0);
7914 damage[0][boardY][boardX] = True;
7916 player.dragActive = False;
7921 ChangeDragPiece (ChessSquare piece)
7924 player.dragPiece = piece;
7925 /* The piece will be drawn using its own bitmap as a matte */
7926 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
7927 XSetClipMask(xDisplay, player.pieceGC, mask);
7931 DragPieceMove (int x, int y)
7935 /* Are we animating? */
7936 if (!appData.animateDragging || appData.blindfold)
7940 if (! player.dragActive)
7942 /* Move piece, maintaining same relative position
7943 of mouse within square */
7944 corner.x = x - player.mouseDelta.x;
7945 corner.y = y - player.mouseDelta.y;
7946 AnimationFrame(&player, &corner, player.dragPiece);
7948 if (appData.highlightDragging) {
7950 BoardSquare(x, y, &boardX, &boardY);
7951 SetHighlights(fromX, fromY, boardX, boardY);
7957 DragPieceEnd (int x, int y)
7959 int boardX, boardY, color;
7962 /* Are we animating? */
7963 if (!appData.animateDragging || appData.blindfold)
7967 if (! player.dragActive)
7969 /* Last frame in sequence is square piece is
7970 placed on, which may not match mouse exactly. */
7971 BoardSquare(x, y, &boardX, &boardY);
7972 ScreenSquare(boardX, boardY, &corner, &color);
7973 EndAnimation(&player, &corner);
7975 /* Be sure end square is redrawn */
7976 damage[0][boardY][boardX] = True;
7978 /* This prevents weird things happening with fast successive
7979 clicks which on my Sun at least can cause motion events
7980 without corresponding press/release. */
7981 player.dragActive = False;
7984 /* Handle expose event while piece being dragged */
7989 if (!player.dragActive || appData.blindfold)
7992 /* What we're doing: logically, the move hasn't been made yet,
7993 so the piece is still in it's original square. But visually
7994 it's being dragged around the board. So we erase the square
7995 that the piece is on and draw it at the last known drag point. */
7996 BlankSquare(player.startSquare.x, player.startSquare.y,
7997 player.startColor, EmptySquare, xBoardWindow, 1);
7998 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
7999 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8002 #include <sys/ioctl.h>
8006 int fd, default_width;
8009 default_width = 79; // this is FICS default anyway...
8011 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8013 if (!ioctl(fd, TIOCGSIZE, &win))
8014 default_width = win.ts_cols;
8015 #elif defined(TIOCGWINSZ)
8017 if (!ioctl(fd, TIOCGWINSZ, &win))
8018 default_width = win.ws_col;
8020 return default_width;
8026 static int old_width = 0;
8027 int new_width = get_term_width();
8029 if (old_width != new_width)
8030 ics_printf("set width %d\n", new_width);
8031 old_width = new_width;
8035 NotifyFrontendLogin ()
8040 /* [AS] Arrow highlighting support */
8042 static double A_WIDTH = 5; /* Width of arrow body */
8044 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8045 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8056 return (int) (x + 0.5);
8060 SquareToPos (int rank, int file, int *x, int *y)
8063 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8064 *y = lineGap + rank * (squareSize + lineGap);
8066 *x = lineGap + file * (squareSize + lineGap);
8067 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8071 /* Draw an arrow between two points using current settings */
8073 DrawArrowBetweenPoints (int s_x, int s_y, int d_x, int d_y)
8076 double dx, dy, j, k, x, y;
8079 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8081 arrow[0].x = s_x + A_WIDTH + 0.5;
8084 arrow[1].x = s_x + A_WIDTH + 0.5;
8085 arrow[1].y = d_y - h;
8087 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8088 arrow[2].y = d_y - h;
8093 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8094 arrow[5].y = d_y - h;
8096 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8097 arrow[4].y = d_y - h;
8099 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8102 else if( d_y == s_y ) {
8103 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8106 arrow[0].y = s_y + A_WIDTH + 0.5;
8108 arrow[1].x = d_x - w;
8109 arrow[1].y = s_y + A_WIDTH + 0.5;
8111 arrow[2].x = d_x - w;
8112 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8117 arrow[5].x = d_x - w;
8118 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8120 arrow[4].x = d_x - w;
8121 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8124 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8127 /* [AS] Needed a lot of paper for this! :-) */
8128 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8129 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8131 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8133 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8138 arrow[0].x = Round(x - j);
8139 arrow[0].y = Round(y + j*dx);
8141 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8142 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8145 x = (double) d_x - k;
8146 y = (double) d_y - k*dy;
8149 x = (double) d_x + k;
8150 y = (double) d_y + k*dy;
8153 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8155 arrow[6].x = Round(x - j);
8156 arrow[6].y = Round(y + j*dx);
8158 arrow[2].x = Round(arrow[6].x + 2*j);
8159 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8161 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8162 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8167 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8168 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8171 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8172 if(appData.monoMode) arrow[7] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, arrow, 8, CoordModeOrigin);
8173 // Polygon( hdc, arrow, 7 );
8177 ArrowDamage (int s_col, int s_row, int d_col, int d_row)
8180 hor = 64*s_col + 32; vert = 64*s_row + 32;
8181 for(i=0; i<= 64; i++) {
8182 damage[0][vert+6>>6][hor+6>>6] = True;
8183 damage[0][vert-6>>6][hor+6>>6] = True;
8184 damage[0][vert+6>>6][hor-6>>6] = True;
8185 damage[0][vert-6>>6][hor-6>>6] = True;
8186 hor += d_col - s_col; vert += d_row - s_row;
8190 /* [AS] Draw an arrow between two squares */
8192 DrawArrowBetweenSquares (int s_col, int s_row, int d_col, int d_row)
8194 int s_x, s_y, d_x, d_y;
8196 if( s_col == d_col && s_row == d_row ) {
8200 /* Get source and destination points */
8201 SquareToPos( s_row, s_col, &s_x, &s_y);
8202 SquareToPos( d_row, d_col, &d_x, &d_y);
8205 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8207 else if( d_y < s_y ) {
8208 d_y += squareSize / 2 + squareSize / 4;
8211 d_y += squareSize / 2;
8215 d_x += squareSize / 2 - squareSize / 4;
8217 else if( d_x < s_x ) {
8218 d_x += squareSize / 2 + squareSize / 4;
8221 d_x += squareSize / 2;
8224 s_x += squareSize / 2;
8225 s_y += squareSize / 2;
8228 A_WIDTH = squareSize / 14.; //[HGM] make float
8230 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8231 ArrowDamage(s_col, s_row, d_col, d_row);
8235 IsDrawArrowEnabled ()
8237 return appData.highlightMoveWithArrow && squareSize >= 32;
8241 DrawArrowHighlight (int fromX, int fromY, int toX,int toY)
8243 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8244 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
8248 UpdateLogos (int displ)
8250 return; // no logos in XBoard yet