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"
216 #define usleep(t) _sleep2(((t)+500)/1000)
220 # define _(s) gettext (s)
221 # define N_(s) gettext_noop (s)
227 int main P((int argc, char **argv));
228 RETSIGTYPE CmailSigHandler P((int sig));
229 RETSIGTYPE IntSigHandler P((int sig));
230 RETSIGTYPE TermSizeSigHandler P((int sig));
231 static void CreateGCs P((int redo));
232 static 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 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
254 void DelayedDrag P((void));
255 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
256 void HandleUserMove P((Widget w, XEvent *event,
257 String *prms, Cardinal *nprms));
258 void AnimateUserMove P((Widget w, XEvent * event,
259 String * params, Cardinal * nParams));
260 void HandlePV P((Widget w, XEvent * event,
261 String * params, Cardinal * nParams));
262 void SelectPV P((Widget w, XEvent * event,
263 String * params, Cardinal * nParams));
264 void StopPV P((Widget w, XEvent * event,
265 String * params, Cardinal * nParams));
266 void WhiteClock P((Widget w, XEvent *event,
267 String *prms, Cardinal *nprms));
268 void BlackClock P((Widget w, XEvent *event,
269 String *prms, Cardinal *nprms));
270 void DrawPositionProc P((Widget w, XEvent *event,
271 String *prms, Cardinal *nprms));
272 void CommentClick P((Widget w, XEvent * event,
273 String * params, Cardinal * nParams));
274 void ICSInputBoxPopUp P((void));
275 void ICSInputBoxPopDown P((void));
276 void FileNamePopUp P((char *label, char *def, char *filter,
277 FileProc proc, char *openMode));
278 void AskQuestionReplyAction P((Widget w, XEvent *event,
279 String *prms, Cardinal *nprms));
280 void AskQuestionPopDown P((void));
281 void PromotionPopDown P((void));
282 void PromotionCallback P((Widget w, XtPointer client_data,
283 XtPointer call_data));
284 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
285 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
286 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
287 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
288 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
289 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
290 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
291 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
292 Boolean TempBackwardActive = False;
293 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
294 void DisplayMove P((int moveNumber));
295 void ICSInitScript P((void));
296 static char *ExpandPathName P((char *path));
297 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
298 void update_ics_width P(());
299 int get_term_width P(());
300 int CopyMemoProc P(());
303 * XBoard depends on Xt R4 or higher
305 int xtVersion = XtSpecificationRelease;
310 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
311 highlightSquareColor, premoveHighlightColor;
312 Pixel lowTimeWarningColor;
313 GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
314 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
316 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
317 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
318 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
319 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
320 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
321 ICSInputShell, fileNameShell, askQuestionShell;
322 Widget historyShell, evalGraphShell, gameListShell;
323 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
324 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
326 XFontSet fontSet, clockFontSet;
329 XFontStruct *clockFontStruct;
331 Font coordFontID, countFontID;
332 XFontStruct *coordFontStruct, *countFontStruct;
333 XtAppContext appContext;
335 char *oldICSInteractionTitle;
339 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
341 Position commentX = -1, commentY = -1;
342 Dimension commentW, commentH;
343 typedef unsigned int BoardSize;
345 Boolean chessProgram;
347 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
348 int smallLayout = 0, tinyLayout = 0,
349 marginW, marginH, // [HGM] for run-time resizing
350 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
351 ICSInputBoxUp = False, askQuestionUp = False,
352 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
353 errorUp = False, errorExitStatus = -1, defaultLineGap;
354 Dimension textHeight;
355 Pixel timerForegroundPixel, timerBackgroundPixel;
356 Pixel buttonForegroundPixel, buttonBackgroundPixel;
357 char *chessDir, *programName, *programVersion;
358 Boolean alwaysOnTop = False;
359 char *icsTextMenuString;
361 char *firstChessProgramNames;
362 char *secondChessProgramNames;
364 WindowPlacement wpMain;
365 WindowPlacement wpConsole;
366 WindowPlacement wpComment;
367 WindowPlacement wpMoveHistory;
368 WindowPlacement wpEvalGraph;
369 WindowPlacement wpEngineOutput;
370 WindowPlacement wpGameList;
371 WindowPlacement wpTags;
376 Pixmap pieceBitmap[2][(int)BlackPawn];
377 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
378 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
379 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
380 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
381 Pixmap xpmBoardBitmap[2];
382 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
383 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
384 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
385 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
386 XImage *ximLightSquare, *ximDarkSquare;
389 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
390 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
392 #define White(piece) ((int)(piece) < (int)BlackPawn)
394 /* Bitmaps for use as masks when drawing XPM pieces.
395 Need one for each black and white piece. */
396 static Pixmap xpmMask[BlackKing + 1];
398 /* This magic number is the number of intermediate frames used
399 in each half of the animation. For short moves it's reduced
400 by 1. The total number of frames will be factor * 2 + 1. */
403 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
405 #define PAUSE_BUTTON "P"
406 MenuItem buttonBar[] = {
407 {"<<", "<<", ToStartEvent},
408 {"<", "<", BackwardEvent},
409 {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseEvent},
410 {">", ">", ForwardEvent},
411 {">>", ">>", ToEndEvent},
415 #define PIECE_MENU_SIZE 18
416 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
417 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
418 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
419 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
420 N_("Empty square"), N_("Clear board") },
421 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
422 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
423 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
424 N_("Empty square"), N_("Clear board") }
426 /* must be in same order as pieceMenuStrings! */
427 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
428 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
429 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
430 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
431 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
432 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
433 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
434 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
435 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
438 #define DROP_MENU_SIZE 6
439 String dropMenuStrings[DROP_MENU_SIZE] = {
440 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
442 /* must be in same order as dropMenuStrings! */
443 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
444 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
445 WhiteRook, WhiteQueen
453 DropMenuEnables dmEnables[] = {
471 { XtNborderWidth, 0 },
472 { XtNdefaultDistance, 0 },
476 { XtNborderWidth, 0 },
477 { XtNresizable, (XtArgVal) True },
481 { XtNborderWidth, 0 },
487 { XtNjustify, (XtArgVal) XtJustifyRight },
488 { XtNlabel, (XtArgVal) "..." },
489 { XtNresizable, (XtArgVal) True },
490 { XtNresize, (XtArgVal) False }
493 Arg messageArgs[] = {
494 { XtNjustify, (XtArgVal) XtJustifyLeft },
495 { XtNlabel, (XtArgVal) "..." },
496 { XtNresizable, (XtArgVal) True },
497 { XtNresize, (XtArgVal) False }
501 { XtNborderWidth, 0 },
502 { XtNjustify, (XtArgVal) XtJustifyLeft }
505 XtResource clientResources[] = {
506 { "flashCount", "flashCount", XtRInt, sizeof(int),
507 XtOffset(AppDataPtr, flashCount), XtRImmediate,
508 (XtPointer) FLASH_COUNT },
511 XrmOptionDescRec shellOptions[] = {
512 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
513 { "-flash", "flashCount", XrmoptionNoArg, "3" },
514 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
517 XtActionsRec boardActions[] = {
518 { "DrawPosition", DrawPositionProc },
519 { "HandleUserMove", HandleUserMove },
520 { "AnimateUserMove", AnimateUserMove },
521 { "HandlePV", HandlePV },
522 { "SelectPV", SelectPV },
523 { "StopPV", StopPV },
524 { "AskQuestionReplyAction", AskQuestionReplyAction },
525 { "PieceMenuPopup", PieceMenuPopup },
526 { "WhiteClock", WhiteClock },
527 { "BlackClock", BlackClock },
528 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
529 { "QuitProc", QuitWrapper },
530 { "ManProc", ManInner },
531 { "TempBackwardProc", TempBackwardProc },
532 { "TempForwardProc", TempForwardProc },
533 { "CommentClick", (XtActionProc) CommentClick },
534 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
535 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
536 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
537 { "GameListPopDown", (XtActionProc) GameListPopDown },
538 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
539 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
540 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
541 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
542 { "GenericPopDown", (XtActionProc) GenericPopDown },
543 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
544 { "SelectMove", (XtActionProc) SelectMove },
545 { "LoadSelectedProc", LoadSelectedProc },
546 { "SetFilterProc", SetFilterProc },
547 { "TypeInProc", TypeInProc },
548 { "EnterKeyProc", EnterKeyProc },
549 { "UpKeyProc", UpKeyProc },
550 { "DownKeyProc", DownKeyProc },
553 char globalTranslations[] =
554 ":<Key>F9: MenuItem(ResignProc) \n \
555 :Ctrl<Key>n: MenuItem(NewGame) \n \
556 :Meta<Key>V: MenuItem(NewVariant) \n \
557 :Ctrl<Key>o: MenuItem(LoadGame) \n \
558 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
559 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
560 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
561 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
562 :Ctrl<Key>s: MenuItem(SaveGame) \n \
563 :Ctrl<Key>c: MenuItem(CopyGame) \n \
564 :Ctrl<Key>v: MenuItem(PasteGame) \n \
565 :Ctrl<Key>O: MenuItem(LoadPosition) \n \
566 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
567 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
568 :Ctrl<Key>S: MenuItem(SavePosition) \n \
569 :Ctrl<Key>C: MenuItem(CopyPosition) \n \
570 :Ctrl<Key>V: MenuItem(PastePosition) \n \
571 :Ctrl<Key>q: MenuItem(Exit) \n \
572 :Ctrl<Key>w: MenuItem(MachineWhite) \n \
573 :Ctrl<Key>b: MenuItem(MachineBlack) \n \
574 :Ctrl<Key>t: MenuItem(TwoMachines) \n \
575 :Ctrl<Key>a: MenuItem(AnalysisMode) \n \
576 :Ctrl<Key>g: MenuItem(AnalyzeFile) \n \
577 :Ctrl<Key>e: MenuItem(EditGame) \n \
578 :Ctrl<Key>E: MenuItem(EditPosition) \n \
579 :Meta<Key>O: MenuItem(ShowEngineOutput) \n \
580 :Meta<Key>E: MenuItem(ShowEvaluationGraph) \n \
581 :Meta<Key>G: MenuItem(ShowGameList) \n \
582 :Meta<Key>H: MenuItem(ShowMoveHistory) \n \
583 :<Key>Pause: MenuItem(Pause) \n \
584 :<Key>F3: MenuItem(Accept) \n \
585 :<Key>F4: MenuItem(Decline) \n \
586 :<Key>F12: MenuItem(Rematch) \n \
587 :<Key>F5: MenuItem(CallFlag) \n \
588 :<Key>F6: MenuItem(Draw) \n \
589 :<Key>F7: MenuItem(Adjourn) \n \
590 :<Key>F8: MenuItem(Abort) \n \
591 :<Key>F10: MenuItem(StopObserving) \n \
592 :<Key>F11: MenuItem(StopExamining) \n \
593 :Ctrl<Key>d: MenuItem(DebugProc) \n \
594 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
595 :Meta<Key>End: MenuItem(ToEnd) \n \
596 :Meta<Key>Right: MenuItem(Forward) \n \
597 :Meta<Key>Home: MenuItem(ToStart) \n \
598 :Meta<Key>Left: MenuItem(Backward) \n \
599 :<Key>Left: MenuItem(Backward) \n \
600 :<Key>Right: MenuItem(Forward) \n \
601 :<Key>Home: MenuItem(Revert) \n \
602 :<Key>End: MenuItem(TruncateGame) \n \
603 :Ctrl<Key>m: MenuItem(MoveNow) \n \
604 :Ctrl<Key>x: MenuItem(RetractMove) \n \
605 :Meta<Key>J: MenuItem(Adjudications) \n \
606 :Meta<Key>U: MenuItem(CommonEngine) \n \
607 :Meta<Key>T: MenuItem(TimeControl) \n \
608 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
609 #ifndef OPTIONSDIALOG
611 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
612 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
613 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
614 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
615 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
618 :<Key>F1: MenuItem(Manual) \n \
619 :<Key>F2: MenuItem(FlipView) \n \
620 :<KeyDown>Return: TempBackwardProc() \n \
621 :<KeyUp>Return: TempForwardProc() \n";
623 char boardTranslations[] =
624 "<Btn1Down>: HandleUserMove(0) \n \
625 Shift<Btn1Up>: HandleUserMove(1) \n \
626 <Btn1Up>: HandleUserMove(0) \n \
627 <Btn1Motion>: AnimateUserMove() \n \
628 <Btn3Motion>: HandlePV() \n \
629 <Btn2Motion>: HandlePV() \n \
630 <Btn3Up>: PieceMenuPopup(menuB) \n \
631 <Btn2Up>: PieceMenuPopup(menuB) \n \
632 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
633 PieceMenuPopup(menuB) \n \
634 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
635 PieceMenuPopup(menuW) \n \
636 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
637 PieceMenuPopup(menuW) \n \
638 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
639 PieceMenuPopup(menuB) \n";
641 char whiteTranslations[] =
642 "Shift<BtnDown>: WhiteClock(1)\n \
643 <BtnDown>: WhiteClock(0)\n";
644 char blackTranslations[] =
645 "Shift<BtnDown>: BlackClock(1)\n \
646 <BtnDown>: BlackClock(0)\n";
648 char ICSInputTranslations[] =
649 "<Key>Up: UpKeyProc() \n "
650 "<Key>Down: DownKeyProc() \n "
651 "<Key>Return: EnterKeyProc() \n";
653 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
654 // as the widget is destroyed before the up-click can call extend-end
655 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
657 String xboardResources[] = {
658 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
659 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
664 /* Max possible square size */
665 #define MAXSQSIZE 256
667 static int xpm_avail[MAXSQSIZE];
669 #ifdef HAVE_DIR_STRUCT
671 /* Extract piece size from filename */
673 xpm_getsize (char *name, int len, char *ext)
681 if ((p=strchr(name, '.')) == NULL ||
682 StrCaseCmp(p+1, ext) != 0)
688 while (*p && isdigit(*p))
695 /* Setup xpm_avail */
697 xpm_getavail (char *dirname, char *ext)
703 for (i=0; i<MAXSQSIZE; ++i)
706 if (appData.debugMode)
707 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
709 dir = opendir(dirname);
712 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
713 programName, dirname);
717 while ((ent=readdir(dir)) != NULL) {
718 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
719 if (i > 0 && i < MAXSQSIZE)
729 xpm_print_avail (FILE *fp, char *ext)
733 fprintf(fp, _("Available `%s' sizes:\n"), ext);
734 for (i=1; i<MAXSQSIZE; ++i) {
740 /* Return XPM piecesize closest to size */
742 xpm_closest_to (char *dirname, int size, char *ext)
745 int sm_diff = MAXSQSIZE;
749 xpm_getavail(dirname, ext);
751 if (appData.debugMode)
752 xpm_print_avail(stderr, ext);
754 for (i=1; i<MAXSQSIZE; ++i) {
757 diff = (diff<0) ? -diff : diff;
758 if (diff < sm_diff) {
766 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
772 #else /* !HAVE_DIR_STRUCT */
773 /* If we are on a system without a DIR struct, we can't
774 read the directory, so we can't collect a list of
775 filenames, etc., so we can't do any size-fitting. */
777 xpm_closest_to (char *dirname, int size, char *ext)
780 Warning: No DIR structure found on this system --\n\
781 Unable to autosize for XPM/XIM pieces.\n\
782 Please report this error to %s.\n\
783 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
786 #endif /* HAVE_DIR_STRUCT */
788 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
789 "magenta", "cyan", "white" };
793 TextColors textColors[(int)NColorClasses];
795 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
797 parse_color (char *str, int which)
799 char *p, buf[100], *d;
802 if (strlen(str) > 99) /* watch bounds on buf */
807 for (i=0; i<which; ++i) {
814 /* Could be looking at something like:
816 .. in which case we want to stop on a comma also */
817 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
821 return -1; /* Use default for empty field */
824 if (which == 2 || isdigit(*p))
827 while (*p && isalpha(*p))
832 for (i=0; i<8; ++i) {
833 if (!StrCaseCmp(buf, cnames[i]))
834 return which? (i+40) : (i+30);
836 if (!StrCaseCmp(buf, "default")) return -1;
838 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
843 parse_cpair (ColorClass cc, char *str)
845 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
846 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
851 /* bg and attr are optional */
852 textColors[(int)cc].bg = parse_color(str, 1);
853 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
854 textColors[(int)cc].attr = 0;
860 /* Arrange to catch delete-window events */
861 Atom wm_delete_window;
863 CatchDeleteWindow (Widget w, String procname)
866 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
867 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
868 XtAugmentTranslations(w, XtParseTranslationTable(buf));
875 XtSetArg(args[0], XtNiconic, False);
876 XtSetValues(shellWidget, args, 1);
878 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
881 //---------------------------------------------------------------------------------------------------------
882 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
885 #define CW_USEDEFAULT (1<<31)
886 #define ICS_TEXT_MENU_SIZE 90
887 #define DEBUG_FILE "xboard.debug"
888 #define SetCurrentDirectory chdir
889 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
893 // these two must some day move to frontend.h, when they are implemented
894 Boolean GameListIsUp();
896 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
899 // front-end part of option handling
901 // [HGM] This platform-dependent table provides the location for storing the color info
902 extern char *crWhite, * crBlack;
906 &appData.whitePieceColor,
907 &appData.blackPieceColor,
908 &appData.lightSquareColor,
909 &appData.darkSquareColor,
910 &appData.highlightSquareColor,
911 &appData.premoveHighlightColor,
912 &appData.lowTimeWarningColor,
923 // [HGM] font: keep a font for each square size, even non-stndard ones
926 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
927 char *fontTable[NUM_FONTS][MAX_SIZE];
930 ParseFont (char *name, int number)
931 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
933 if(sscanf(name, "size%d:", &size)) {
934 // [HGM] font: font is meant for specific boardSize (likely from settings file);
935 // defer processing it until we know if it matches our board size
936 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
937 fontTable[number][size] = strdup(strchr(name, ':')+1);
938 fontValid[number][size] = True;
943 case 0: // CLOCK_FONT
944 appData.clockFont = strdup(name);
946 case 1: // MESSAGE_FONT
947 appData.font = strdup(name);
949 case 2: // COORD_FONT
950 appData.coordFont = strdup(name);
955 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
960 { // only 2 fonts currently
961 appData.clockFont = CLOCK_FONT_NAME;
962 appData.coordFont = COORD_FONT_NAME;
963 appData.font = DEFAULT_FONT_NAME;
968 { // no-op, until we identify the code for this already in XBoard and move it here
972 ParseColor (int n, char *name)
973 { // in XBoard, just copy the color-name string
974 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
978 ParseTextAttribs (ColorClass cc, char *s)
980 (&appData.colorShout)[cc] = strdup(s);
984 ParseBoardSize (void *addr, char *name)
986 appData.boardSize = strdup(name);
991 { // In XBoard the sound-playing program takes care of obtaining the actual sound
995 SetCommPortDefaults ()
996 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
999 // [HGM] args: these three cases taken out to stay in front-end
1001 SaveFontArg (FILE *f, ArgDescriptor *ad)
1004 int i, n = (int)(intptr_t)ad->argLoc;
1006 case 0: // CLOCK_FONT
1007 name = appData.clockFont;
1009 case 1: // MESSAGE_FONT
1010 name = appData.font;
1012 case 2: // COORD_FONT
1013 name = appData.coordFont;
1018 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1019 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1020 fontTable[n][squareSize] = strdup(name);
1021 fontValid[n][squareSize] = True;
1024 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1025 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1030 { // nothing to do, as the sounds are at all times represented by their text-string names already
1034 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
1035 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1036 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1040 SaveColor (FILE *f, ArgDescriptor *ad)
1041 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1042 if(colorVariable[(int)(intptr_t)ad->argLoc])
1043 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1047 SaveBoardSize (FILE *f, char *name, void *addr)
1048 { // wrapper to shield back-end from BoardSize & sizeInfo
1049 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1053 ParseCommPortSettings (char *s)
1054 { // no such option in XBoard (yet)
1057 extern Widget engineOutputShell;
1061 GetActualPlacement (Widget wg, WindowPlacement *wp)
1066 XWindowAttributes winAt;
1073 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
1074 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
1075 wp->x = rx - winAt.x;
1076 wp->y = ry - winAt.y;
1077 wp->height = winAt.height;
1078 wp->width = winAt.width;
1079 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
1084 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1085 // In XBoard this will have to wait until awareness of window parameters is implemented
1086 GetActualPlacement(shellWidget, &wpMain);
1087 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1088 if(MoveHistoryIsUp()) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
1089 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1090 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1091 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
1092 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
1096 PrintCommPortSettings (FILE *f, char *name)
1097 { // This option does not exist in XBoard
1101 MySearchPath (char *installDir, char *name, char *fullname)
1102 { // just append installDir and name. Perhaps ExpandPath should be used here?
1103 name = ExpandPathName(name);
1104 if(name && name[0] == '/')
1105 safeStrCpy(fullname, name, MSG_SIZ );
1107 sprintf(fullname, "%s%c%s", installDir, '/', name);
1113 MyGetFullPathName (char *name, char *fullname)
1114 { // should use ExpandPath?
1115 name = ExpandPathName(name);
1116 safeStrCpy(fullname, name, MSG_SIZ );
1121 EnsureOnScreen (int *x, int *y, int minX, int minY)
1128 { // [HGM] args: allows testing if main window is realized from back-end
1129 return xBoardWindow != 0;
1133 PopUpStartupDialog ()
1134 { // start menu not implemented in XBoard
1138 ConvertToLine (int argc, char **argv)
1140 static char line[128*1024], buf[1024];
1144 for(i=1; i<argc; i++)
1146 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1147 && argv[i][0] != '{' )
1148 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1150 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1151 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1154 line[strlen(line)-1] = NULLCHAR;
1158 //--------------------------------------------------------------------------------------------
1161 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1163 #define BoardSize int
1165 InitDrawingSizes (BoardSize boardSize, int flags)
1166 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1167 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1169 XtGeometryResult gres;
1171 static Dimension oldWidth, oldHeight;
1172 static VariantClass oldVariant;
1173 static int oldDual = -1, oldMono = -1;
1175 if(!formWidget) return;
1177 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1178 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1179 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1181 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1183 * Enable shell resizing.
1185 shellArgs[0].value = (XtArgVal) &w;
1186 shellArgs[1].value = (XtArgVal) &h;
1187 XtGetValues(shellWidget, shellArgs, 2);
1189 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1190 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1191 XtSetValues(shellWidget, &shellArgs[2], 4);
1193 XtSetArg(args[0], XtNdefaultDistance, &sep);
1194 XtGetValues(formWidget, args, 1);
1196 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1198 hOffset = boardWidth + 10;
1199 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1200 secondSegments[i] = gridSegments[i];
1201 secondSegments[i].x1 += hOffset;
1202 secondSegments[i].x2 += hOffset;
1205 XtSetArg(args[0], XtNwidth, boardWidth);
1206 XtSetArg(args[1], XtNheight, boardHeight);
1207 XtSetValues(boardWidget, args, 2);
1209 timerWidth = (boardWidth - sep) / 2;
1210 XtSetArg(args[0], XtNwidth, timerWidth);
1211 XtSetValues(whiteTimerWidget, args, 1);
1212 XtSetValues(blackTimerWidget, args, 1);
1214 XawFormDoLayout(formWidget, False);
1216 if (appData.titleInWindow) {
1218 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1219 XtSetArg(args[i], XtNheight, &h); i++;
1220 XtGetValues(titleWidget, args, i);
1222 w = boardWidth - 2*bor;
1224 XtSetArg(args[0], XtNwidth, &w);
1225 XtGetValues(menuBarWidget, args, 1);
1226 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1229 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1230 if (gres != XtGeometryYes && appData.debugMode) {
1232 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1233 programName, gres, w, h, wr, hr);
1237 XawFormDoLayout(formWidget, True);
1240 * Inhibit shell resizing.
1242 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1243 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1244 shellArgs[4].value = shellArgs[2].value = w;
1245 shellArgs[5].value = shellArgs[3].value = h;
1246 XtSetValues(shellWidget, &shellArgs[0], 6);
1248 XSync(xDisplay, False);
1252 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1255 if(gameInfo.variant != oldVariant) { // and only if variant changed
1258 for(i=0; i<4; i++) {
1260 for(p=0; p<=(int)WhiteKing; p++)
1261 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1262 if(gameInfo.variant == VariantShogi) {
1263 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1264 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1265 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1266 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1267 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1270 if(gameInfo.variant == VariantGothic) {
1271 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1274 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1275 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1276 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1279 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1280 for(p=0; p<=(int)WhiteKing; p++)
1281 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1282 if(gameInfo.variant == VariantShogi) {
1283 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1284 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1285 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1286 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1287 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1290 if(gameInfo.variant == VariantGothic) {
1291 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1294 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1295 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1296 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1301 for(i=0; i<2; i++) {
1303 for(p=0; p<=(int)WhiteKing; p++)
1304 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1305 if(gameInfo.variant == VariantShogi) {
1306 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1307 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1308 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1309 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1310 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1313 if(gameInfo.variant == VariantGothic) {
1314 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1317 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1318 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1319 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1323 oldMono = -10; // kludge to force recreation of animation masks
1324 oldVariant = gameInfo.variant;
1327 if(appData.monoMode != oldMono)
1330 oldMono = appData.monoMode;
1335 ParseIcsTextColors ()
1336 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1337 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1338 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1339 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1340 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1341 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1342 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1343 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1344 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1345 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1346 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1348 if (appData.colorize) {
1350 _("%s: can't parse color names; disabling colorization\n"),
1353 appData.colorize = FALSE;
1358 MakeOneColor (char *name, Pixel *color)
1360 XrmValue vFrom, vTo;
1361 if (!appData.monoMode) {
1362 vFrom.addr = (caddr_t) name;
1363 vFrom.size = strlen(name);
1364 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1365 if (vTo.addr == NULL) {
1366 appData.monoMode = True;
1369 *color = *(Pixel *) vTo.addr;
1377 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1378 int forceMono = False;
1380 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1381 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1382 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1383 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1384 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1385 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1392 { // [HGM] taken out of main
1394 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1395 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1396 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1398 if (appData.bitmapDirectory[0] != NULLCHAR) {
1402 CreateXPMBoard(appData.liteBackTextureFile, 1);
1403 CreateXPMBoard(appData.darkBackTextureFile, 0);
1407 /* Create regular pieces */
1408 if (!useImages) CreatePieces();
1413 InitDrawingParams ()
1415 MakeColors(); CreateGCs(True);
1420 main (int argc, char **argv)
1422 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1423 XSetWindowAttributes window_attributes;
1425 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1426 XrmValue vFrom, vTo;
1427 XtGeometryResult gres;
1430 int forceMono = False;
1432 srandom(time(0)); // [HGM] book: make random truly random
1434 setbuf(stdout, NULL);
1435 setbuf(stderr, NULL);
1438 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1439 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1443 programName = strrchr(argv[0], '/');
1444 if (programName == NULL)
1445 programName = argv[0];
1450 XtSetLanguageProc(NULL, NULL, NULL);
1451 bindtextdomain(PACKAGE, LOCALEDIR);
1452 textdomain(PACKAGE);
1456 XtAppInitialize(&appContext, "XBoard", shellOptions,
1457 XtNumber(shellOptions),
1458 &argc, argv, xboardResources, NULL, 0);
1459 appData.boardSize = "";
1460 InitAppData(ConvertToLine(argc, argv));
1462 if (p == NULL) p = "/tmp";
1463 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1464 gameCopyFilename = (char*) malloc(i);
1465 gamePasteFilename = (char*) malloc(i);
1466 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1467 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1469 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1470 clientResources, XtNumber(clientResources),
1473 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1474 static char buf[MSG_SIZ];
1475 EscapeExpand(buf, appData.firstInitString);
1476 appData.firstInitString = strdup(buf);
1477 EscapeExpand(buf, appData.secondInitString);
1478 appData.secondInitString = strdup(buf);
1479 EscapeExpand(buf, appData.firstComputerString);
1480 appData.firstComputerString = strdup(buf);
1481 EscapeExpand(buf, appData.secondComputerString);
1482 appData.secondComputerString = strdup(buf);
1485 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1488 if (chdir(chessDir) != 0) {
1489 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1495 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1496 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1497 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1498 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1501 setbuf(debugFP, NULL);
1505 if (appData.debugMode) {
1506 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1510 /* [HGM,HR] make sure board size is acceptable */
1511 if(appData.NrFiles > BOARD_FILES ||
1512 appData.NrRanks > BOARD_RANKS )
1513 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1516 /* This feature does not work; animation needs a rewrite */
1517 appData.highlightDragging = FALSE;
1521 xDisplay = XtDisplay(shellWidget);
1522 xScreen = DefaultScreen(xDisplay);
1523 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1525 gameInfo.variant = StringToVariant(appData.variant);
1526 InitPosition(FALSE);
1529 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1531 if (isdigit(appData.boardSize[0])) {
1532 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1533 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1534 &fontPxlSize, &smallLayout, &tinyLayout);
1536 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1537 programName, appData.boardSize);
1541 /* Find some defaults; use the nearest known size */
1542 SizeDefaults *szd, *nearest;
1543 int distance = 99999;
1544 nearest = szd = sizeDefaults;
1545 while (szd->name != NULL) {
1546 if (abs(szd->squareSize - squareSize) < distance) {
1548 distance = abs(szd->squareSize - squareSize);
1549 if (distance == 0) break;
1553 if (i < 2) lineGap = nearest->lineGap;
1554 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1555 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1556 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1557 if (i < 6) smallLayout = nearest->smallLayout;
1558 if (i < 7) tinyLayout = nearest->tinyLayout;
1561 SizeDefaults *szd = sizeDefaults;
1562 if (*appData.boardSize == NULLCHAR) {
1563 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1564 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1567 if (szd->name == NULL) szd--;
1568 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1570 while (szd->name != NULL &&
1571 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1572 if (szd->name == NULL) {
1573 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1574 programName, appData.boardSize);
1578 squareSize = szd->squareSize;
1579 lineGap = szd->lineGap;
1580 clockFontPxlSize = szd->clockFontPxlSize;
1581 coordFontPxlSize = szd->coordFontPxlSize;
1582 fontPxlSize = szd->fontPxlSize;
1583 smallLayout = szd->smallLayout;
1584 tinyLayout = szd->tinyLayout;
1585 // [HGM] font: use defaults from settings file if available and not overruled
1587 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1588 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1589 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1590 appData.font = fontTable[MESSAGE_FONT][squareSize];
1591 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1592 appData.coordFont = fontTable[COORD_FONT][squareSize];
1594 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1595 if (strlen(appData.pixmapDirectory) > 0) {
1596 p = ExpandPathName(appData.pixmapDirectory);
1598 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1599 appData.pixmapDirectory);
1602 if (appData.debugMode) {
1603 fprintf(stderr, _("\
1604 XBoard square size (hint): %d\n\
1605 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1607 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1608 if (appData.debugMode) {
1609 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1612 defaultLineGap = lineGap;
1613 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1615 /* [HR] height treated separately (hacked) */
1616 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1617 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1618 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1619 XtSetArg(boardArgs[2], XtNheight, boardHeight);
1622 * Determine what fonts to use.
1625 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1626 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1627 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1628 fontSet = CreateFontSet(appData.font);
1629 clockFontSet = CreateFontSet(appData.clockFont);
1631 /* For the coordFont, use the 0th font of the fontset. */
1632 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1633 XFontStruct **font_struct_list;
1634 XFontSetExtents *fontSize;
1635 char **font_name_list;
1636 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1637 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1638 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1639 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1640 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1643 appData.font = FindFont(appData.font, fontPxlSize);
1644 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1645 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1646 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1647 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1648 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1649 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1651 countFontID = coordFontID; // [HGM] holdings
1652 countFontStruct = coordFontStruct;
1654 xdb = XtDatabase(xDisplay);
1656 XrmPutLineResource(&xdb, "*international: True");
1657 vTo.size = sizeof(XFontSet);
1658 vTo.addr = (XtPointer) &fontSet;
1659 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1661 XrmPutStringResource(&xdb, "*font", appData.font);
1665 * Detect if there are not enough colors available and adapt.
1667 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1668 appData.monoMode = True;
1671 forceMono = MakeColors();
1674 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1676 appData.monoMode = True;
1679 if (appData.lowTimeWarning && !appData.monoMode) {
1680 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1681 vFrom.size = strlen(appData.lowTimeWarningColor);
1682 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1683 if (vTo.addr == NULL)
1684 appData.monoMode = True;
1686 lowTimeWarningColor = *(Pixel *) vTo.addr;
1689 if (appData.monoMode && appData.debugMode) {
1690 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1691 (unsigned long) XWhitePixel(xDisplay, xScreen),
1692 (unsigned long) XBlackPixel(xDisplay, xScreen));
1695 ParseIcsTextColors();
1696 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
1697 textColors[ColorNone].attr = 0;
1699 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1705 layoutName = "tinyLayout";
1706 } else if (smallLayout) {
1707 layoutName = "smallLayout";
1709 layoutName = "normalLayout";
1711 /* Outer layoutWidget is there only to provide a name for use in
1712 resources that depend on the layout style */
1714 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
1715 layoutArgs, XtNumber(layoutArgs));
1717 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
1718 formArgs, XtNumber(formArgs));
1719 XtSetArg(args[0], XtNdefaultDistance, &sep);
1720 XtGetValues(formWidget, args, 1);
1723 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
1724 XtSetArg(args[0], XtNtop, XtChainTop);
1725 XtSetArg(args[1], XtNbottom, XtChainTop);
1726 XtSetArg(args[2], XtNright, XtChainLeft);
1727 XtSetValues(menuBarWidget, args, 3);
1729 widgetList[j++] = whiteTimerWidget =
1730 XtCreateWidget("whiteTime", labelWidgetClass,
1731 formWidget, timerArgs, XtNumber(timerArgs));
1733 XtSetArg(args[0], XtNfontSet, clockFontSet);
1735 XtSetArg(args[0], XtNfont, clockFontStruct);
1737 XtSetArg(args[1], XtNtop, XtChainTop);
1738 XtSetArg(args[2], XtNbottom, XtChainTop);
1739 XtSetValues(whiteTimerWidget, args, 3);
1741 widgetList[j++] = blackTimerWidget =
1742 XtCreateWidget("blackTime", labelWidgetClass,
1743 formWidget, timerArgs, XtNumber(timerArgs));
1745 XtSetArg(args[0], XtNfontSet, clockFontSet);
1747 XtSetArg(args[0], XtNfont, clockFontStruct);
1749 XtSetArg(args[1], XtNtop, XtChainTop);
1750 XtSetArg(args[2], XtNbottom, XtChainTop);
1751 XtSetValues(blackTimerWidget, args, 3);
1753 if (appData.titleInWindow) {
1754 widgetList[j++] = titleWidget =
1755 XtCreateWidget("title", labelWidgetClass, formWidget,
1756 titleArgs, XtNumber(titleArgs));
1757 XtSetArg(args[0], XtNtop, XtChainTop);
1758 XtSetArg(args[1], XtNbottom, XtChainTop);
1759 XtSetValues(titleWidget, args, 2);
1762 if (appData.showButtonBar) {
1763 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
1764 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
1765 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
1766 XtSetArg(args[2], XtNtop, XtChainTop);
1767 XtSetArg(args[3], XtNbottom, XtChainTop);
1768 XtSetValues(buttonBarWidget, args, 4);
1771 widgetList[j++] = messageWidget =
1772 XtCreateWidget("message", labelWidgetClass, formWidget,
1773 messageArgs, XtNumber(messageArgs));
1774 XtSetArg(args[0], XtNtop, XtChainTop);
1775 XtSetArg(args[1], XtNbottom, XtChainTop);
1776 XtSetValues(messageWidget, args, 2);
1778 widgetList[j++] = boardWidget =
1779 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
1780 XtNumber(boardArgs));
1782 XtManageChildren(widgetList, j);
1784 timerWidth = (boardWidth - sep) / 2;
1785 XtSetArg(args[0], XtNwidth, timerWidth);
1786 XtSetValues(whiteTimerWidget, args, 1);
1787 XtSetValues(blackTimerWidget, args, 1);
1789 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1790 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1791 XtGetValues(whiteTimerWidget, args, 2);
1793 if (appData.showButtonBar) {
1794 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1795 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1796 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
1800 * formWidget uses these constraints but they are stored
1804 XtSetArg(args[i], XtNfromHoriz, 0); i++;
1805 XtSetValues(menuBarWidget, args, i);
1806 if (appData.titleInWindow) {
1809 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1810 XtSetValues(whiteTimerWidget, args, i);
1812 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1813 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1814 XtSetValues(blackTimerWidget, args, i);
1816 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1817 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
1818 XtSetValues(titleWidget, args, i);
1820 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1821 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1822 XtSetValues(messageWidget, args, i);
1823 if (appData.showButtonBar) {
1825 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1826 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1827 XtSetValues(buttonBarWidget, args, i);
1831 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1832 XtSetValues(whiteTimerWidget, args, i);
1834 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1835 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1836 XtSetValues(blackTimerWidget, args, i);
1838 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
1839 XtSetValues(titleWidget, args, i);
1841 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1842 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1843 XtSetValues(messageWidget, args, i);
1844 if (appData.showButtonBar) {
1846 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1847 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1848 XtSetValues(buttonBarWidget, args, i);
1853 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1854 XtSetValues(whiteTimerWidget, args, i);
1856 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1857 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1858 XtSetValues(blackTimerWidget, args, i);
1860 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1861 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1862 XtSetValues(messageWidget, args, i);
1863 if (appData.showButtonBar) {
1865 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1866 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1867 XtSetValues(buttonBarWidget, args, i);
1871 XtSetArg(args[0], XtNfromVert, messageWidget);
1872 XtSetArg(args[1], XtNtop, XtChainTop);
1873 XtSetArg(args[2], XtNbottom, XtChainBottom);
1874 XtSetArg(args[3], XtNleft, XtChainLeft);
1875 XtSetArg(args[4], XtNright, XtChainRight);
1876 XtSetValues(boardWidget, args, 5);
1878 XtRealizeWidget(shellWidget);
1881 XtSetArg(args[0], XtNx, wpMain.x);
1882 XtSetArg(args[1], XtNy, wpMain.y);
1883 XtSetValues(shellWidget, args, 2);
1887 * Correct the width of the message and title widgets.
1888 * It is not known why some systems need the extra fudge term.
1889 * The value "2" is probably larger than needed.
1891 XawFormDoLayout(formWidget, False);
1893 #define WIDTH_FUDGE 2
1895 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1896 XtSetArg(args[i], XtNheight, &h); i++;
1897 XtGetValues(messageWidget, args, i);
1898 if (appData.showButtonBar) {
1900 XtSetArg(args[i], XtNwidth, &w); i++;
1901 XtGetValues(buttonBarWidget, args, i);
1902 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1904 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
1907 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1908 if (gres != XtGeometryYes && appData.debugMode) {
1909 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1910 programName, gres, w, h, wr, hr);
1913 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
1914 /* The size used for the child widget in layout lags one resize behind
1915 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
1917 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1918 if (gres != XtGeometryYes && appData.debugMode) {
1919 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1920 programName, gres, w, h, wr, hr);
1923 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
1924 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
1925 XtSetArg(args[1], XtNright, XtChainRight);
1926 XtSetValues(messageWidget, args, 2);
1928 if (appData.titleInWindow) {
1930 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1931 XtSetArg(args[i], XtNheight, &h); i++;
1932 XtGetValues(titleWidget, args, i);
1934 w = boardWidth - 2*bor;
1936 XtSetArg(args[0], XtNwidth, &w);
1937 XtGetValues(menuBarWidget, args, 1);
1938 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1941 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1942 if (gres != XtGeometryYes && appData.debugMode) {
1944 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1945 programName, gres, w, h, wr, hr);
1948 XawFormDoLayout(formWidget, True);
1950 xBoardWindow = XtWindow(boardWidget);
1952 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1953 // not need to go into InitDrawingSizes().
1957 * Create X checkmark bitmap and initialize option menu checks.
1959 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1960 checkmark_bits, checkmark_width, checkmark_height);
1966 ReadBitmap(&wIconPixmap, "icon_white.bm",
1967 icon_white_bits, icon_white_width, icon_white_height);
1968 ReadBitmap(&bIconPixmap, "icon_black.bm",
1969 icon_black_bits, icon_black_width, icon_black_height);
1970 iconPixmap = wIconPixmap;
1972 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1973 XtSetValues(shellWidget, args, i);
1976 * Create a cursor for the board widget.
1978 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1979 XChangeWindowAttributes(xDisplay, xBoardWindow,
1980 CWCursor, &window_attributes);
1983 * Inhibit shell resizing.
1985 shellArgs[0].value = (XtArgVal) &w;
1986 shellArgs[1].value = (XtArgVal) &h;
1987 XtGetValues(shellWidget, shellArgs, 2);
1988 shellArgs[4].value = shellArgs[2].value = w;
1989 shellArgs[5].value = shellArgs[3].value = h;
1990 XtSetValues(shellWidget, &shellArgs[2], 4);
1991 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1992 marginH = h - boardHeight;
1994 CatchDeleteWindow(shellWidget, "QuitProc");
2002 if (appData.animate || appData.animateDragging)
2005 XtAugmentTranslations(formWidget,
2006 XtParseTranslationTable(globalTranslations));
2007 XtAugmentTranslations(boardWidget,
2008 XtParseTranslationTable(boardTranslations));
2009 XtAugmentTranslations(whiteTimerWidget,
2010 XtParseTranslationTable(whiteTranslations));
2011 XtAugmentTranslations(blackTimerWidget,
2012 XtParseTranslationTable(blackTranslations));
2014 /* Why is the following needed on some versions of X instead
2015 * of a translation? */
2016 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2017 (XtEventHandler) EventProc, NULL);
2019 XtAddEventHandler(formWidget, KeyPressMask, False,
2020 (XtEventHandler) MoveTypeInProc, NULL);
2021 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
2022 (XtEventHandler) EventProc, NULL);
2024 /* [AS] Restore layout */
2025 if( wpMoveHistory.visible ) {
2029 if( wpEvalGraph.visible )
2034 if( wpEngineOutput.visible ) {
2035 EngineOutputPopUp();
2040 if (errorExitStatus == -1) {
2041 if (appData.icsActive) {
2042 /* We now wait until we see "login:" from the ICS before
2043 sending the logon script (problems with timestamp otherwise) */
2044 /*ICSInitScript();*/
2045 if (appData.icsInputBox) ICSInputBoxPopUp();
2049 signal(SIGWINCH, TermSizeSigHandler);
2051 signal(SIGINT, IntSigHandler);
2052 signal(SIGTERM, IntSigHandler);
2053 if (*appData.cmailGameName != NULLCHAR) {
2054 signal(SIGUSR1, CmailSigHandler);
2058 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2060 // XtSetKeyboardFocus(shellWidget, formWidget);
2061 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2063 XtAppMainLoop(appContext);
2064 if (appData.debugMode) fclose(debugFP); // [DM] debug
2068 static Boolean noEcho;
2073 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2074 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2076 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2077 unlink(gameCopyFilename);
2078 unlink(gamePasteFilename);
2079 if(noEcho) EchoOn();
2083 TermSizeSigHandler (int sig)
2089 IntSigHandler (int sig)
2095 CmailSigHandler (int sig)
2100 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2102 /* Activate call-back function CmailSigHandlerCallBack() */
2103 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2105 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2109 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
2112 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2114 /**** end signal code ****/
2120 /* try to open the icsLogon script, either in the location given
2121 * or in the users HOME directory
2128 f = fopen(appData.icsLogon, "r");
2131 homedir = getenv("HOME");
2132 if (homedir != NULL)
2134 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2135 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2136 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2137 f = fopen(buf, "r");
2142 ProcessICSInitScript(f);
2144 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2157 #define Abs(n) ((n)<0 ? -(n) : (n))
2161 InsertPxlSize (char *pattern, int targetPxlSize)
2163 char *base_fnt_lst, strInt[12], *p, *q;
2164 int alternatives, i, len, strIntLen;
2167 * Replace the "*" (if present) in the pixel-size slot of each
2168 * alternative with the targetPxlSize.
2172 while ((p = strchr(p, ',')) != NULL) {
2176 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
2177 strIntLen = strlen(strInt);
2178 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
2182 while (alternatives--) {
2183 char *comma = strchr(p, ',');
2184 for (i=0; i<14; i++) {
2185 char *hyphen = strchr(p, '-');
2187 if (comma && hyphen > comma) break;
2188 len = hyphen + 1 - p;
2189 if (i == 7 && *p == '*' && len == 2) {
2191 memcpy(q, strInt, strIntLen);
2201 len = comma + 1 - p;
2208 return base_fnt_lst;
2212 CreateFontSet (char *base_fnt_lst)
2215 char **missing_list;
2219 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
2220 &missing_list, &missing_count, &def_string);
2221 if (appData.debugMode) {
2223 XFontStruct **font_struct_list;
2224 char **font_name_list;
2225 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
2227 fprintf(debugFP, " got list %s, locale %s\n",
2228 XBaseFontNameListOfFontSet(fntSet),
2229 XLocaleOfFontSet(fntSet));
2230 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
2231 for (i = 0; i < count; i++) {
2232 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
2235 for (i = 0; i < missing_count; i++) {
2236 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
2239 if (fntSet == NULL) {
2240 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
2245 #else // not ENABLE_NLS
2247 * Find a font that matches "pattern" that is as close as
2248 * possible to the targetPxlSize. Prefer fonts that are k
2249 * pixels smaller to fonts that are k pixels larger. The
2250 * pattern must be in the X Consortium standard format,
2251 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2252 * The return value should be freed with XtFree when no
2256 FindFont (char *pattern, int targetPxlSize)
2258 char **fonts, *p, *best, *scalable, *scalableTail;
2259 int i, j, nfonts, minerr, err, pxlSize;
2261 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2263 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2264 programName, pattern);
2271 for (i=0; i<nfonts; i++) {
2274 if (*p != '-') continue;
2276 if (*p == NULLCHAR) break;
2277 if (*p++ == '-') j++;
2279 if (j < 7) continue;
2282 scalable = fonts[i];
2285 err = pxlSize - targetPxlSize;
2286 if (Abs(err) < Abs(minerr) ||
2287 (minerr > 0 && err < 0 && -err == minerr)) {
2293 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2294 /* If the error is too big and there is a scalable font,
2295 use the scalable font. */
2296 int headlen = scalableTail - scalable;
2297 p = (char *) XtMalloc(strlen(scalable) + 10);
2298 while (isdigit(*scalableTail)) scalableTail++;
2299 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2301 p = (char *) XtMalloc(strlen(best) + 2);
2302 safeStrCpy(p, best, strlen(best)+1 );
2304 if (appData.debugMode) {
2305 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2306 pattern, targetPxlSize, p);
2308 XFreeFontNames(fonts);
2315 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
2316 // must be called before all non-first callse to CreateGCs()
2317 XtReleaseGC(shellWidget, highlineGC);
2318 XtReleaseGC(shellWidget, lightSquareGC);
2319 XtReleaseGC(shellWidget, darkSquareGC);
2320 XtReleaseGC(shellWidget, lineGC);
2321 if (appData.monoMode) {
2322 if (DefaultDepth(xDisplay, xScreen) == 1) {
2323 XtReleaseGC(shellWidget, wbPieceGC);
2325 XtReleaseGC(shellWidget, bwPieceGC);
2328 XtReleaseGC(shellWidget, prelineGC);
2329 XtReleaseGC(shellWidget, wdPieceGC);
2330 XtReleaseGC(shellWidget, wlPieceGC);
2331 XtReleaseGC(shellWidget, bdPieceGC);
2332 XtReleaseGC(shellWidget, blPieceGC);
2337 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
2339 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2340 | GCBackground | GCFunction | GCPlaneMask;
2341 gc_values->foreground = foreground;
2342 gc_values->background = background;
2343 return XtGetGC(shellWidget, value_mask, gc_values);
2347 CreateGCs (int redo)
2349 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2350 | GCBackground | GCFunction | GCPlaneMask;
2351 XGCValues gc_values;
2353 Pixel white = XWhitePixel(xDisplay, xScreen);
2354 Pixel black = XBlackPixel(xDisplay, xScreen);
2356 gc_values.plane_mask = AllPlanes;
2357 gc_values.line_width = lineGap;
2358 gc_values.line_style = LineSolid;
2359 gc_values.function = GXcopy;
2362 DeleteGCs(); // called a second time; clean up old GCs first
2363 } else { // [HGM] grid and font GCs created on first call only
2364 coordGC = CreateOneGC(&gc_values, black, white);
2365 XSetFont(xDisplay, coordGC, coordFontID);
2367 // [HGM] make font for holdings counts (white on black)
2368 countGC = CreateOneGC(&gc_values, white, black);
2369 XSetFont(xDisplay, countGC, countFontID);
2371 lineGC = CreateOneGC(&gc_values, black, black);
2373 if (appData.monoMode) {
2375 highlineGC = CreateOneGC(&gc_values, white, white);
2376 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
2377 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
2379 if (DefaultDepth(xDisplay, xScreen) == 1) {
2380 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2381 gc_values.function = GXcopyInverted;
2382 copyInvertedGC = CreateOneGC(&gc_values, black, white);
2383 gc_values.function = GXcopy;
2384 if (XBlackPixel(xDisplay, xScreen) == 1) {
2385 bwPieceGC = darkSquareGC;
2386 wbPieceGC = copyInvertedGC;
2388 bwPieceGC = copyInvertedGC;
2389 wbPieceGC = lightSquareGC;
2394 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
2395 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
2396 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
2397 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
2398 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
2399 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
2400 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
2401 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
2406 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
2414 fp = fopen(filename, "rb");
2416 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
2423 for (y=0; y<h; ++y) {
2424 for (x=0; x<h; ++x) {
2429 XPutPixel(xim, x, y, blackPieceColor);
2431 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2434 XPutPixel(xim, x, y, darkSquareColor);
2436 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2439 XPutPixel(xim, x, y, whitePieceColor);
2441 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2444 XPutPixel(xim, x, y, lightSquareColor);
2446 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2454 /* create Pixmap of piece */
2455 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2457 XPutImage(xDisplay, *dest, lightSquareGC, xim,
2460 /* create Pixmap of clipmask
2461 Note: We assume the white/black pieces have the same
2462 outline, so we make only 6 masks. This is okay
2463 since the XPM clipmask routines do the same. */
2465 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2467 XPutImage(xDisplay, temp, lightSquareGC, xmask,
2470 /* now create the 1-bit version */
2471 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2474 values.foreground = 1;
2475 values.background = 0;
2477 /* Don't use XtGetGC, not read only */
2478 maskGC = XCreateGC(xDisplay, *mask,
2479 GCForeground | GCBackground, &values);
2480 XCopyPlane(xDisplay, temp, *mask, maskGC,
2481 0, 0, squareSize, squareSize, 0, 0, 1);
2482 XFreePixmap(xDisplay, temp);
2487 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
2495 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
2500 /* The XSynchronize calls were copied from CreatePieces.
2501 Not sure if needed, but can't hurt */
2502 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2505 /* temp needed by loadXIM() */
2506 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2507 0, 0, ss, ss, AllPlanes, XYPixmap);
2509 if (strlen(appData.pixmapDirectory) == 0) {
2513 if (appData.monoMode) {
2514 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
2518 fprintf(stderr, _("\nLoading XIMs...\n"));
2520 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2521 fprintf(stderr, "%d", piece+1);
2522 for (kind=0; kind<4; kind++) {
2523 fprintf(stderr, ".");
2524 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2525 ExpandPathName(appData.pixmapDirectory),
2526 piece <= (int) WhiteKing ? "" : "w",
2527 pieceBitmapNames[piece],
2529 ximPieceBitmap[kind][piece] =
2530 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2531 0, 0, ss, ss, AllPlanes, XYPixmap);
2532 if (appData.debugMode)
2533 fprintf(stderr, _("(File:%s:) "), buf);
2534 loadXIM(ximPieceBitmap[kind][piece],
2536 &(xpmPieceBitmap2[kind][piece]),
2537 &(ximMaskPm2[piece]));
2538 if(piece <= (int)WhiteKing)
2539 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2541 fprintf(stderr," ");
2543 /* Load light and dark squares */
2544 /* If the LSQ and DSQ pieces don't exist, we will
2545 draw them with solid squares. */
2546 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2547 if (access(buf, 0) != 0) {
2551 fprintf(stderr, _("light square "));
2553 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2554 0, 0, ss, ss, AllPlanes, XYPixmap);
2555 if (appData.debugMode)
2556 fprintf(stderr, _("(File:%s:) "), buf);
2558 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2559 fprintf(stderr, _("dark square "));
2560 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2561 ExpandPathName(appData.pixmapDirectory), ss);
2562 if (appData.debugMode)
2563 fprintf(stderr, _("(File:%s:) "), buf);
2565 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2566 0, 0, ss, ss, AllPlanes, XYPixmap);
2567 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2568 xpmJailSquare = xpmLightSquare;
2570 fprintf(stderr, _("Done.\n"));
2572 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2575 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2579 CreateXPMBoard (char *s, int kind)
2583 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2584 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2585 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2591 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2592 // thisroutine has to be called t free the old piece pixmaps
2594 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2595 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2597 XFreePixmap(xDisplay, xpmLightSquare);
2598 XFreePixmap(xDisplay, xpmDarkSquare);
2607 u_int ss = squareSize;
2609 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2610 XpmColorSymbol symbols[4];
2611 static int redo = False;
2613 if(redo) FreeXPMPieces(); else redo = 1;
2615 /* The XSynchronize calls were copied from CreatePieces.
2616 Not sure if needed, but can't hurt */
2617 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2619 /* Setup translations so piece colors match square colors */
2620 symbols[0].name = "light_piece";
2621 symbols[0].value = appData.whitePieceColor;
2622 symbols[1].name = "dark_piece";
2623 symbols[1].value = appData.blackPieceColor;
2624 symbols[2].name = "light_square";
2625 symbols[2].value = appData.lightSquareColor;
2626 symbols[3].name = "dark_square";
2627 symbols[3].value = appData.darkSquareColor;
2629 attr.valuemask = XpmColorSymbols;
2630 attr.colorsymbols = symbols;
2631 attr.numsymbols = 4;
2633 if (appData.monoMode) {
2634 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2638 if (strlen(appData.pixmapDirectory) == 0) {
2639 XpmPieces* pieces = builtInXpms;
2642 while (pieces->size != squareSize && pieces->size) pieces++;
2643 if (!pieces->size) {
2644 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2647 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2648 for (kind=0; kind<4; kind++) {
2650 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2651 pieces->xpm[piece][kind],
2652 &(xpmPieceBitmap2[kind][piece]),
2653 NULL, &attr)) != 0) {
2654 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2658 if(piece <= (int) WhiteKing)
2659 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2663 xpmJailSquare = xpmLightSquare;
2667 fprintf(stderr, _("\nLoading XPMs...\n"));
2670 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2671 fprintf(stderr, "%d ", piece+1);
2672 for (kind=0; kind<4; kind++) {
2673 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2674 ExpandPathName(appData.pixmapDirectory),
2675 piece > (int) WhiteKing ? "w" : "",
2676 pieceBitmapNames[piece],
2678 if (appData.debugMode) {
2679 fprintf(stderr, _("(File:%s:) "), buf);
2681 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2682 &(xpmPieceBitmap2[kind][piece]),
2683 NULL, &attr)) != 0) {
2684 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2685 // [HGM] missing: read of unorthodox piece failed; substitute King.
2686 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2687 ExpandPathName(appData.pixmapDirectory),
2689 if (appData.debugMode) {
2690 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2692 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2693 &(xpmPieceBitmap2[kind][piece]),
2697 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2702 if(piece <= (int) WhiteKing)
2703 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2706 /* Load light and dark squares */
2707 /* If the LSQ and DSQ pieces don't exist, we will
2708 draw them with solid squares. */
2709 fprintf(stderr, _("light square "));
2710 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2711 if (access(buf, 0) != 0) {
2715 if (appData.debugMode)
2716 fprintf(stderr, _("(File:%s:) "), buf);
2718 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2719 &xpmLightSquare, NULL, &attr)) != 0) {
2720 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2723 fprintf(stderr, _("dark square "));
2724 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2725 ExpandPathName(appData.pixmapDirectory), ss);
2726 if (appData.debugMode) {
2727 fprintf(stderr, _("(File:%s:) "), buf);
2729 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2730 &xpmDarkSquare, NULL, &attr)) != 0) {
2731 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2735 xpmJailSquare = xpmLightSquare;
2736 fprintf(stderr, _("Done.\n"));
2738 oldVariant = -1; // kludge to force re-makig of animation masks
2739 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2742 #endif /* HAVE_LIBXPM */
2745 /* No built-in bitmaps */
2750 u_int ss = squareSize;
2752 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2755 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2756 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2757 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2758 pieceBitmapNames[piece],
2759 ss, kind == SOLID ? 's' : 'o');
2760 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2761 if(piece <= (int)WhiteKing)
2762 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2766 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2770 /* With built-in bitmaps */
2774 BuiltInBits* bib = builtInBits;
2777 u_int ss = squareSize;
2779 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2782 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2784 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2785 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2786 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2787 pieceBitmapNames[piece],
2788 ss, kind == SOLID ? 's' : 'o');
2789 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2790 bib->bits[kind][piece], ss, ss);
2791 if(piece <= (int)WhiteKing)
2792 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2796 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2802 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2807 char msg[MSG_SIZ], fullname[MSG_SIZ];
2809 if (*appData.bitmapDirectory != NULLCHAR) {
2810 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2811 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2812 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2813 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2814 &w, &h, pm, &x_hot, &y_hot);
2815 fprintf(stderr, "load %s\n", name);
2816 if (errcode != BitmapSuccess) {
2818 case BitmapOpenFailed:
2819 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2821 case BitmapFileInvalid:
2822 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2824 case BitmapNoMemory:
2825 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2829 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2833 fprintf(stderr, _("%s: %s...using built-in\n"),
2835 } else if (w != wreq || h != hreq) {
2837 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2838 programName, fullname, w, h, wreq, hreq);
2844 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2854 if (lineGap == 0) return;
2856 /* [HR] Split this into 2 loops for non-square boards. */
2858 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2859 gridSegments[i].x1 = 0;
2860 gridSegments[i].x2 =
2861 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2862 gridSegments[i].y1 = gridSegments[i].y2
2863 = lineGap / 2 + (i * (squareSize + lineGap));
2866 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2867 gridSegments[j + i].y1 = 0;
2868 gridSegments[j + i].y2 =
2869 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2870 gridSegments[j + i].x1 = gridSegments[j + i].x2
2871 = lineGap / 2 + (j * (squareSize + lineGap));
2875 int nrOfMenuItems = 7;
2876 Widget menuWidget[150];
2877 MenuListItem menuItemList[150] = {
2878 { "LoadNextGameProc", LoadNextGameProc },
2879 { "LoadPrevGameProc", LoadPrevGameProc },
2880 { "ReloadGameProc", ReloadGameProc },
2881 { "ReloadPositionProc", ReloadPositionProc },
2882 #ifndef OPTIONSDIALOG
2883 { "AlwaysQueenProc", AlwaysQueenProc },
2884 { "AnimateDraggingProc", AnimateDraggingProc },
2885 { "AnimateMovingProc", AnimateMovingProc },
2886 { "AutoflagProc", AutoflagProc },
2887 { "AutoflipProc", AutoflipProc },
2888 { "BlindfoldProc", BlindfoldProc },
2889 { "FlashMovesProc", FlashMovesProc },
2891 { "HighlightDraggingProc", HighlightDraggingProc },
2893 { "HighlightLastMoveProc", HighlightLastMoveProc },
2894 // { "IcsAlarmProc", IcsAlarmProc },
2895 { "MoveSoundProc", MoveSoundProc },
2896 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
2897 { "PopupExitMessageProc", PopupExitMessageProc },
2898 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
2899 // { "PremoveProc", PremoveProc },
2900 { "ShowCoordsProc", ShowCoordsProc },
2901 { "ShowThinkingProc", ShowThinkingProc },
2902 { "HideThinkingProc", HideThinkingProc },
2903 { "TestLegalityProc", TestLegalityProc },
2905 { "AboutGameProc", AboutGameEvent },
2906 { "DebugProc", DebugProc },
2907 { "NothingProc", NothingProc },
2912 MarkMenuItem (char *menuRef, int state)
2914 int nr = MenuToNumber(menuRef);
2917 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2918 XtSetValues(menuWidget[nr], args, 1);
2923 EnableMenuItem (char *menuRef, int state)
2925 int nr = MenuToNumber(menuRef);
2926 if(nr >= 0) XtSetSensitive(menuWidget[nr], state);
2930 EnableButtonBar (int state)
2932 XtSetSensitive(buttonBarWidget, state);
2937 SetMenuEnables (Enables *enab)
2939 while (enab->name != NULL) {
2940 EnableMenuItem(enab->name, enab->value);
2946 Equal(char *p, char *s)
2947 { // compare strings skipping spaces in second
2949 if(*s == ' ') { s++; continue; }
2950 if(*s++ != *p++) return 0;
2956 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2957 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2959 if(*nprms == 0) return;
2960 for(i=0; menuItemList[i].name; i++) {
2961 if(Equal(prms[0], menuItemList[i].name)) {
2962 (menuItemList[i].proc) ();
2969 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
2971 MenuProc *proc = (MenuProc *) addr;
2977 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2979 RecentEngineEvent((int) (intptr_t) addr);
2982 // some stuff that must remain in front-end
2983 static Widget mainBar, currentMenu;
2984 static int wtot, nr = 0, widths[10];
2987 AppendMenuItem (char *text, char *name, MenuProc *action)
2994 XtSetArg(args[j], XtNleftMargin, 20); j++;
2995 XtSetArg(args[j], XtNrightMargin, 20); j++;
2997 if (strcmp(text, "----") == 0) {
2998 entry = XtCreateManagedWidget(text, smeLineObjectClass,
2999 currentMenu, args, j);
3001 XtSetArg(args[j], XtNlabel, XtNewString(_(text)));
3002 entry = XtCreateManagedWidget(name, smeBSBObjectClass,
3003 currentMenu, args, j+1);
3004 XtAddCallback(entry, XtNcallback,
3005 (XtCallbackProc) (strcmp(name, "recent") ? MenuBarSelect : MenuEngineSelect),
3007 menuWidget[nrOfMenuItems] = entry;
3012 CreateMenuButton (char *name, Menu *mb)
3013 { // create menu button on main bar, and shell for pull-down list
3019 XtSetArg(args[j], XtNmenuName, XtNewString(name)); j++;
3020 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3021 XtSetArg(args[j], XtNborderWidth, 0); j++;
3022 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3024 currentMenu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3027 XtSetArg(args[j], XtNwidth, &w); j++;
3028 XtGetValues(mb->subMenu, args, j);
3029 wtot += mb->textWidth = widths[nr++] = w;
3033 CreateMenuBar (Menu *mb, int boardWidth)
3037 char menuName[MSG_SIZ];
3041 // create bar itself
3043 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3044 XtSetArg(args[j], XtNvSpace, 0); j++;
3045 XtSetArg(args[j], XtNborderWidth, 0); j++;
3046 mainBar = XtCreateWidget("menuBar", boxWidgetClass,
3047 formWidget, args, j);
3049 CreateMainMenus(mb); // put menus in bar according to description in back-end
3051 // size buttons to make menu bar fit, clipping menu names where necessary
3052 while(wtot > boardWidth - 40) {
3054 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
3058 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
3060 XtSetArg(args[j], XtNwidth, widths[i]); j++;
3061 XtSetValues(ma[i].subMenu, args, j);
3068 CreateButtonBar (MenuItem *mi)
3071 Widget button, buttonBar;
3075 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3077 XtSetArg(args[j], XtNhSpace, 0); j++;
3079 XtSetArg(args[j], XtNborderWidth, 0); j++;
3080 XtSetArg(args[j], XtNvSpace, 0); j++;
3081 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3082 formWidget, args, j);
3084 while (mi->string != NULL) {
3087 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3088 XtSetArg(args[j], XtNborderWidth, 0); j++;
3090 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3091 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3092 buttonBar, args, j);
3093 XtAddCallback(button, XtNcallback,
3094 (XtCallbackProc) MenuBarSelect,
3095 (caddr_t) mi->proc);
3102 CreatePieceMenu (char *name, int color)
3107 ChessSquare selection;
3109 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3110 boardWidget, args, 0);
3112 for (i = 0; i < PIECE_MENU_SIZE; i++) {
3113 String item = pieceMenuStrings[color][i];
3115 if (strcmp(item, "----") == 0) {
3116 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3119 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3120 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3122 selection = pieceMenuTranslation[color][i];
3123 XtAddCallback(entry, XtNcallback,
3124 (XtCallbackProc) PieceMenuSelect,
3125 (caddr_t) selection);
3126 if (selection == WhitePawn || selection == BlackPawn) {
3127 XtSetArg(args[0], XtNpopupOnEntry, entry);
3128 XtSetValues(menu, args, 1);
3141 ChessSquare selection;
3143 whitePieceMenu = CreatePieceMenu("menuW", 0);
3144 blackPieceMenu = CreatePieceMenu("menuB", 1);
3146 if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
3147 XtRegisterGrabAction(PieceMenuPopup, True,
3148 (unsigned)(ButtonPressMask|ButtonReleaseMask),
3149 GrabModeAsync, GrabModeAsync);
3151 XtSetArg(args[0], XtNlabel, _("Drop"));
3152 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3153 boardWidget, args, 1);
3154 for (i = 0; i < DROP_MENU_SIZE; i++) {
3155 String item = dropMenuStrings[i];
3157 if (strcmp(item, "----") == 0) {
3158 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3161 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3162 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3164 selection = dropMenuTranslation[i];
3165 XtAddCallback(entry, XtNcallback,
3166 (XtCallbackProc) DropMenuSelect,
3167 (caddr_t) selection);
3181 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3182 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3183 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3184 dmEnables[i].piece);
3185 XtSetSensitive(entry, p != NULL || !appData.testLegality
3186 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3187 && !appData.icsActive));
3189 while (p && *p++ == dmEnables[i].piece) count++;
3190 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3192 XtSetArg(args[j], XtNlabel, label); j++;
3193 XtSetValues(entry, args, j);
3198 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
3200 String whichMenu; int menuNr = -2;
3201 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3202 if (event->type == ButtonRelease)
3203 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3204 else if (event->type == ButtonPress)
3205 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3207 case 0: whichMenu = params[0]; break;
3208 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3210 case -1: if (errorUp) ErrorPopDown();
3213 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3217 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3219 if (pmFromX < 0 || pmFromY < 0) return;
3220 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3224 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3226 if (pmFromX < 0 || pmFromY < 0) return;
3227 DropMenuEvent(piece, pmFromX, pmFromY);
3231 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3233 shiftKey = prms[0][0] & 1;
3238 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3240 shiftKey = prms[0][0] & 1;
3246 do_flash_delay (unsigned long msec)
3252 DrawBorder (int x, int y, int type)
3256 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
3258 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3259 squareSize+lineGap, squareSize+lineGap);
3263 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
3265 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
3266 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
3268 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
3269 if(textureW[kind] < W*squareSize)
3270 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
3272 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
3273 if(textureH[kind] < H*squareSize)
3274 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
3276 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
3281 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
3282 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
3284 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
3285 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
3286 squareSize, squareSize, x*fac, y*fac);
3288 if (useImages && useImageSqs) {
3292 pm = xpmLightSquare;
3297 case 2: /* neutral */
3299 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
3302 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3303 squareSize, squareSize, x*fac, y*fac);
3313 case 2: /* neutral */
3318 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
3323 I split out the routines to draw a piece so that I could
3324 make a generic flash routine.
3327 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3329 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3330 switch (square_color) {
3332 case 2: /* neutral */
3334 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3335 ? *pieceToOutline(piece)
3336 : *pieceToSolid(piece),
3337 dest, bwPieceGC, 0, 0,
3338 squareSize, squareSize, x, y);
3341 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3342 ? *pieceToSolid(piece)
3343 : *pieceToOutline(piece),
3344 dest, wbPieceGC, 0, 0,
3345 squareSize, squareSize, x, y);
3351 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3353 switch (square_color) {
3355 case 2: /* neutral */
3357 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3358 ? *pieceToOutline(piece)
3359 : *pieceToSolid(piece),
3360 dest, bwPieceGC, 0, 0,
3361 squareSize, squareSize, x, y, 1);
3364 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3365 ? *pieceToSolid(piece)
3366 : *pieceToOutline(piece),
3367 dest, wbPieceGC, 0, 0,
3368 squareSize, squareSize, x, y, 1);
3374 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3376 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
3377 switch (square_color) {
3379 XCopyPlane(xDisplay, *pieceToSolid(piece),
3380 dest, (int) piece < (int) BlackPawn
3381 ? wlPieceGC : blPieceGC, 0, 0,
3382 squareSize, squareSize, x, y, 1);
3385 XCopyPlane(xDisplay, *pieceToSolid(piece),
3386 dest, (int) piece < (int) BlackPawn
3387 ? wdPieceGC : bdPieceGC, 0, 0,
3388 squareSize, squareSize, x, y, 1);
3390 case 2: /* neutral */
3392 break; // should never contain pieces
3397 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3399 int kind, p = piece;
3401 switch (square_color) {
3403 case 2: /* neutral */
3405 if ((int)piece < (int) BlackPawn) {
3413 if ((int)piece < (int) BlackPawn) {
3421 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
3422 if(useTexture & square_color+1) {
3423 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
3424 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
3425 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
3426 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
3427 XSetClipMask(xDisplay, wlPieceGC, None);
3428 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
3430 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3431 dest, wlPieceGC, 0, 0,
3432 squareSize, squareSize, x, y);
3435 typedef void (*DrawFunc)();
3440 if (appData.monoMode) {
3441 if (DefaultDepth(xDisplay, xScreen) == 1) {
3442 return monoDrawPiece_1bit;
3444 return monoDrawPiece;
3448 return colorDrawPieceImage;
3450 return colorDrawPiece;
3455 DrawDot (int marker, int x, int y, int r)
3457 if(appData.monoMode) {
3458 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
3459 x, y, r, r, 0, 64*360);
3460 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
3461 x, y, r, r, 0, 64*360);
3463 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
3464 x, y, r, r, 0, 64*360);
3468 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
3469 { // basic front-end board-draw function: takes care of everything that can be in square:
3470 // piece, background, coordinate/count, marker dot
3471 int direction, font_ascent, font_descent;
3472 XCharStruct overall;
3475 if (piece == EmptySquare) {
3476 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
3478 drawfunc = ChooseDrawFunc();
3479 drawfunc(piece, square_color, x, y, xBoardWindow);
3482 if(align) { // square carries inscription (coord or piece count)
3484 GC hGC = align < 3 ? coordGC : countGC;
3485 // first calculate where it goes
3486 XTextExtents(countFontStruct, string, 1, &direction,
3487 &font_ascent, &font_descent, &overall);
3489 xx += squareSize - overall.width - 2;
3490 yy += squareSize - font_descent - 1;
3491 } else if (align == 2) {
3492 xx += 2, yy += font_ascent + 1;
3493 } else if (align == 3) {
3494 xx += squareSize - overall.width - 2;
3495 yy += font_ascent + 1;
3496 } else if (align == 4) {
3497 xx += 2, yy += font_ascent + 1;
3500 if (appData.monoMode) {
3501 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3503 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3507 if(marker) { // print fat marker dot, if requested
3508 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
3513 FlashDelay (int flash_delay)
3515 XSync(xDisplay, False);
3516 if(flash_delay) do_flash_delay(flash_delay);
3520 Fraction (int x, int start, int stop)
3522 double f = ((double) x - start)/(stop - start);
3523 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
3527 static WindowPlacement wpNew;
3530 CoDrag (Widget sh, WindowPlacement *wp)
3533 int j=0, touch=0, fudge = 2;
3534 GetActualPlacement(sh, wp);
3535 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
3536 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
3537 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
3538 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
3539 if(!touch ) return; // only windows that touch co-move
3540 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
3541 int heightInc = wpNew.height - wpMain.height;
3542 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3543 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3544 wp->y += fracTop * heightInc;
3545 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
3546 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
3547 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
3548 int widthInc = wpNew.width - wpMain.width;
3549 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3550 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3551 wp->y += fracLeft * widthInc;
3552 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
3553 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
3555 wp->x += wpNew.x - wpMain.x;
3556 wp->y += wpNew.y - wpMain.y;
3557 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
3558 if(touch == 3) wp->y += wpNew.height - wpMain.height;
3559 XtSetArg(args[j], XtNx, wp->x); j++;
3560 XtSetArg(args[j], XtNy, wp->y); j++;
3561 XtSetValues(sh, args, j);
3564 static XtIntervalId delayedDragID = 0;
3569 GetActualPlacement(shellWidget, &wpNew);
3570 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
3571 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
3572 return; // false alarm
3573 if(EngineOutputIsUp()) CoDrag(engineOutputShell, &wpEngineOutput);
3574 if(MoveHistoryIsUp()) CoDrag(shells[HistoryDlg], &wpMoveHistory);
3575 if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
3576 if(GameListIsUp()) CoDrag(gameListShell, &wpGameList);
3578 DrawPosition(True, NULL);
3579 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
3586 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
3588 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
3591 /* Why is this needed on some versions of X? */
3593 EventProc (Widget widget, caddr_t unused, XEvent *event)
3595 if (!XtIsRealized(widget))
3597 switch (event->type) {
3598 case ConfigureNotify: // main window is being dragged: drag attached windows with it
3599 if(appData.useStickyWindows)
3600 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
3603 if (event->xexpose.count > 0) return; /* no clipping is done */
3604 DrawPosition(True, NULL);
3605 if(twoBoards) { // [HGM] dual: draw other board in other orientation
3606 flipView = !flipView; partnerUp = !partnerUp;
3607 DrawPosition(True, NULL);
3608 flipView = !flipView; partnerUp = !partnerUp;
3612 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
3619 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
3621 DrawSeekAxis (int x, int y, int xTo, int yTo)
3623 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
3627 DrawSeekBackground (int left, int top, int right, int bottom)
3629 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
3633 DrawSeekText (char *buf, int x, int y)
3635 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
3639 DrawSeekDot (int x, int y, int colorNr)
3641 int square = colorNr & 0x80;
3644 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
3646 XFillRectangle(xDisplay, xBoardWindow, color,
3647 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3649 XFillArc(xDisplay, xBoardWindow, color,
3650 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
3654 DrawGrid (int second)
3656 XDrawSegments(xDisplay, xBoardWindow, lineGC,
3657 second ? secondSegments : // [HGM] dual
3658 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
3663 * event handler for redrawing the board
3666 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3668 DrawPosition(True, NULL);
3673 * event handler for parsing user moves
3675 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
3676 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
3677 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
3678 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
3679 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
3680 // and at the end FinishMove() to perform the move after optional promotion popups.
3681 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
3683 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3685 if (w != boardWidget || errorExitStatus != -1) return;
3686 if(nprms) shiftKey = !strcmp(prms[0], "1");
3689 if (event->type == ButtonPress) {
3690 XtPopdown(promotionShell);
3691 XtDestroyWidget(promotionShell);
3692 promotionUp = False;
3700 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
3701 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
3702 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
3706 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
3708 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
3709 DragPieceMove(event->xmotion.x, event->xmotion.y);
3713 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3714 { // [HGM] pv: walk PV
3715 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3718 static int savedIndex; /* gross that this is global */
3721 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3724 XawTextPosition index, dummy;
3727 XawTextGetSelectionPos(w, &index, &dummy);
3728 XtSetArg(arg, XtNstring, &val);
3729 XtGetValues(w, &arg, 1);
3730 ReplaceComment(savedIndex, val);
3731 if(savedIndex != currentMove) ToNrEvent(savedIndex);
3732 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3736 EditCommentPopUp (int index, char *title, char *text)
3739 if (text == NULL) text = "";
3740 NewCommentPopup(title, text, index);
3751 ICSInputBoxPopDown ()
3753 PopDown(InputBoxDlg);
3757 CommentPopUp (char *title, char *text)
3759 savedIndex = currentMove; // [HGM] vari
3760 NewCommentPopup(title, text, currentMove);
3766 PopDown(CommentDlg);
3769 static char *openName;
3775 (void) (*fileProc)(openFP, 0, openName);
3779 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
3781 fileProc = proc; /* I can't see a way not */
3782 fileOpenMode = openMode; /* to use globals here */
3783 { // [HGM] use file-selector dialog stolen from Ghostview
3784 int index; // this is not supported yet
3785 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
3786 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
3787 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
3788 ScheduleDelayedEvent(&DelayedLoad, 50);
3796 Widget dialog, layout;
3798 Dimension bw_width, pw_width;
3800 char *PromoChars = "wglcqrbnkac+=\0";
3803 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3804 XtGetValues(boardWidget, args, j);
3807 XtSetArg(args[j], XtNresizable, True); j++;
3808 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
3810 XtCreatePopupShell("Promotion", transientShellWidgetClass,
3811 shellWidget, args, j);
3813 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
3814 layoutArgs, XtNumber(layoutArgs));
3817 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
3818 XtSetArg(args[j], XtNborderWidth, 0); j++;
3819 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
3822 if(gameInfo.variant != VariantShogi) {
3823 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
3824 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
3825 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
3826 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
3827 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
3829 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
3830 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
3831 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
3832 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
3834 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
3835 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
3836 gameInfo.variant == VariantGiveaway) {
3837 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
3839 if(gameInfo.variant == VariantCapablanca ||
3840 gameInfo.variant == VariantGothic ||
3841 gameInfo.variant == VariantCapaRandom) {
3842 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
3843 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
3845 } else // [HGM] shogi
3847 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
3848 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
3850 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
3852 XtRealizeWidget(promotionShell);
3853 CatchDeleteWindow(promotionShell, "PromotionPopDown");
3856 XtSetArg(args[j], XtNwidth, &pw_width); j++;
3857 XtGetValues(promotionShell, args, j);
3859 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3860 lineGap + squareSize/3 +
3861 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
3862 0 : 6*(squareSize + lineGap)), &x, &y);
3865 XtSetArg(args[j], XtNx, x); j++;
3866 XtSetArg(args[j], XtNy, y); j++;
3867 XtSetValues(promotionShell, args, j);
3869 XtPopup(promotionShell, XtGrabNone);
3877 if (!promotionUp) return;
3878 XtPopdown(promotionShell);
3879 XtDestroyWidget(promotionShell);
3880 promotionUp = False;
3884 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
3886 int promoChar = * (const char *) client_data;
3890 if (fromX == -1) return;
3897 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
3899 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
3900 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3906 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
3908 dialogError = errorUp = False;
3909 XtPopdown(w = XtParent(XtParent(XtParent(w))));
3911 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3918 if (!errorUp) return;
3919 dialogError = errorUp = False;
3920 XtPopdown(errorShell);
3921 XtDestroyWidget(errorShell);
3922 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3926 ErrorPopUp (char *title, char *label, int modal)
3929 Widget dialog, layout;
3933 Dimension bw_width, pw_width;
3934 Dimension pw_height;
3938 XtSetArg(args[i], XtNresizable, True); i++;
3939 XtSetArg(args[i], XtNtitle, title); i++;
3941 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
3942 shellUp[TransientDlg] ? (dialogError = modal = TRUE, shells[TransientDlg]) : shellWidget, args, i);
3944 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
3945 layoutArgs, XtNumber(layoutArgs));
3948 XtSetArg(args[i], XtNlabel, label); i++;
3949 XtSetArg(args[i], XtNborderWidth, 0); i++;
3950 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
3953 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
3955 XtRealizeWidget(errorShell);
3956 CatchDeleteWindow(errorShell, "ErrorPopDown");
3959 XtSetArg(args[i], XtNwidth, &bw_width); i++;
3960 XtGetValues(boardWidget, args, i);
3962 XtSetArg(args[i], XtNwidth, &pw_width); i++;
3963 XtSetArg(args[i], XtNheight, &pw_height); i++;
3964 XtGetValues(errorShell, args, i);
3967 /* This code seems to tickle an X bug if it is executed too soon
3968 after xboard starts up. The coordinates get transformed as if
3969 the main window was positioned at (0, 0).
3971 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3972 0 - pw_height + squareSize / 3, &x, &y);
3974 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
3975 RootWindowOfScreen(XtScreen(boardWidget)),
3976 (bw_width - pw_width) / 2,
3977 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
3981 if (y < 0) y = 0; /*avoid positioning top offscreen*/
3984 XtSetArg(args[i], XtNx, x); i++;
3985 XtSetArg(args[i], XtNy, y); i++;
3986 XtSetValues(errorShell, args, i);
3989 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
3992 /* Disable all user input other than deleting the window */
3993 static int frozen = 0;
3999 /* Grab by a widget that doesn't accept input */
4000 XtAddGrab(messageWidget, TRUE, FALSE);
4004 /* Undo a FreezeUI */
4008 if (!frozen) return;
4009 XtRemoveGrab(messageWidget);
4017 static int oldPausing = FALSE;
4018 static GameMode oldmode = (GameMode) -1;
4021 if (!boardWidget || !XtIsRealized(boardWidget)) return;
4023 if (pausing != oldPausing) {
4024 oldPausing = pausing;
4025 MarkMenuItem("Pause", pausing);
4027 if (appData.showButtonBar) {
4028 /* Always toggle, don't set. Previous code messes up when
4029 invoked while the button is pressed, as releasing it
4030 toggles the state again. */
4033 XtSetArg(args[0], XtNbackground, &oldbg);
4034 XtSetArg(args[1], XtNforeground, &oldfg);
4035 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
4037 XtSetArg(args[0], XtNbackground, oldfg);
4038 XtSetArg(args[1], XtNforeground, oldbg);
4040 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
4044 wname = ModeToWidgetName(oldmode);
4045 if (wname != NULL) {
4046 MarkMenuItem(wname, False);
4048 wname = ModeToWidgetName(gameMode);
4049 if (wname != NULL) {
4050 MarkMenuItem(wname, True);
4053 MarkMenuItem("Machine Match", matchMode && matchGame < appData.matchGames);
4055 /* Maybe all the enables should be handled here, not just this one */
4056 EnableMenuItem("Training", gameMode == Training || gameMode == PlayFromGameFile);
4061 * Button/menu procedures
4064 LoadGamePopUp (FILE *f, int gameNumber, char *title)
4066 cmailMsgLoaded = FALSE;
4067 if (gameNumber == 0) {
4068 int error = GameListBuild(f);
4070 DisplayError(_("Cannot build game list"), error);
4071 } else if (!ListEmpty(&gameList) &&
4072 ((ListGame *) gameList.tailPred)->number > 1) {
4073 GameListPopUp(f, title);
4079 return LoadGame(f, gameNumber, title, FALSE);
4082 /* this variable is shared between CopyPositionProc and SendPositionSelection */
4083 char *selected_fen_position=NULL;
4086 SendPositionSelection (Widget w, Atom *selection, Atom *target,
4087 Atom *type_return, XtPointer *value_return,
4088 unsigned long *length_return, int *format_return)
4090 char *selection_tmp;
4092 // if (!selected_fen_position) return False; /* should never happen */
4093 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4094 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
4095 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
4098 if (f == NULL) return False;
4102 selection_tmp = XtMalloc(len + 1);
4103 count = fread(selection_tmp, 1, len, f);
4106 XtFree(selection_tmp);
4109 selection_tmp[len] = NULLCHAR;
4111 /* note: since no XtSelectionDoneProc was registered, Xt will
4112 * automatically call XtFree on the value returned. So have to
4113 * make a copy of it allocated with XtMalloc */
4114 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
4115 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
4118 *value_return=selection_tmp;
4119 *length_return=strlen(selection_tmp);
4120 *type_return=*target;
4121 *format_return = 8; /* bits per byte */
4123 } else if (*target == XA_TARGETS(xDisplay)) {
4124 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4125 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4126 targets_tmp[1] = XA_STRING;
4127 *value_return = targets_tmp;
4128 *type_return = XA_ATOM;
4131 // This code leads to a read of value_return out of bounds on 64-bit systems.
4132 // Other code which I have seen always sets *format_return to 32 independent of
4133 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
4134 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
4135 *format_return = 8 * sizeof(Atom);
4136 if (*format_return > 32) {
4137 *length_return *= *format_return / 32;
4138 *format_return = 32;
4141 *format_return = 32;
4149 /* note: when called from menu all parameters are NULL, so no clue what the
4150 * Widget which was clicked on was, or what the click event was
4153 CopySomething (char *src)
4155 selected_fen_position = src;
4157 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4158 * have a notion of a position that is selected but not copied.
4159 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4161 XtOwnSelection(menuBarWidget, XA_PRIMARY,
4163 SendPositionSelection,
4164 NULL/* lose_ownership_proc */ ,
4165 NULL/* transfer_done_proc */);
4166 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4168 SendPositionSelection,
4169 NULL/* lose_ownership_proc */ ,
4170 NULL/* transfer_done_proc */);
4173 /* function called when the data to Paste is ready */
4175 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
4176 Atom *type, XtPointer value, unsigned long *len, int *format)
4179 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
4180 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
4181 EditPositionPasteFEN(fenstr);
4185 /* called when Paste Position button is pressed,
4186 * all parameters will be NULL */
4188 PastePositionProc ()
4190 XtGetSelectionValue(menuBarWidget,
4191 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4192 /* (XtSelectionCallbackProc) */ PastePositionCB,
4193 NULL, /* client_data passed to PastePositionCB */
4195 /* better to use the time field from the event that triggered the
4196 * call to this function, but that isn't trivial to get
4203 /* note: when called from menu all parameters are NULL, so no clue what the
4204 * Widget which was clicked on was, or what the click event was
4206 /* function called when the data to Paste is ready */
4208 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
4209 Atom *type, XtPointer value, unsigned long *len, int *format)
4212 if (value == NULL || *len == 0) {
4213 return; /* nothing had been selected to copy */
4215 f = fopen(gamePasteFilename, "w");
4217 DisplayError(_("Can't open temp file"), errno);
4220 fwrite(value, 1, *len, f);
4223 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
4226 /* called when Paste Game button is pressed,
4227 * all parameters will be NULL */
4231 XtGetSelectionValue(menuBarWidget,
4232 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4233 /* (XtSelectionCallbackProc) */ PasteGameCB,
4234 NULL, /* client_data passed to PasteGameCB */
4236 /* better to use the time field from the event that triggered the
4237 * call to this function, but that isn't trivial to get
4246 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4252 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
4254 char buf[10], keys[32];
4256 KeyCode metaL, metaR; //, ctrlL, ctrlR;
4257 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
4258 XQueryKeymap(xDisplay,keys);
4259 metaL = XKeysymToKeycode(xDisplay, XK_Meta_L);
4260 metaR = XKeysymToKeycode(xDisplay, XK_Meta_R);
4261 // ctrlL = XKeysymToKeycode(xDisplay, XK_Control_L);
4262 // ctrlR = XKeysymToKeycode(xDisplay, XK_Control_R);
4263 if ( n == 1 && *buf >= 32 // printable
4264 && !(keys[metaL>>3]&1<<(metaL&7)) && !(keys[metaR>>3]&1<<(metaR&7)) // no alt key pressed
4265 // && !(keys[ctrlL>>3]&1<<(ctrlL&7)) && !(keys[ctrlR>>3]&1<<(ctrlR&7)) // no ctrl key pressed
4266 ) BoxAutoPopUp (buf);
4270 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4271 { // [HGM] input: let up-arrow recall previous line from history
4276 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4277 { // [HGM] input: let down-arrow recall next line from history
4282 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4288 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4290 if (!TempBackwardActive) {
4291 TempBackwardActive = True;
4297 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4299 /* Check to see if triggered by a key release event for a repeating key.
4300 * If so the next queued event will be a key press of the same key at the same time */
4301 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
4303 XPeekEvent(xDisplay, &next);
4304 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
4305 next.xkey.keycode == event->xkey.keycode)
4309 TempBackwardActive = False;
4313 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4314 { // called as key binding
4317 if (nprms && *nprms > 0)
4321 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
4326 DisplayMessage (char *message, char *extMessage)
4328 /* display a message in the message widget */
4337 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
4342 message = extMessage;
4346 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
4348 /* need to test if messageWidget already exists, since this function
4349 can also be called during the startup, if for example a Xresource
4350 is not set up correctly */
4353 XtSetArg(arg, XtNlabel, message);
4354 XtSetValues(messageWidget, &arg, 1);
4361 SetWindowTitle (char *text, char *title, char *icon)
4365 if (appData.titleInWindow) {
4367 XtSetArg(args[i], XtNlabel, text); i++;
4368 XtSetValues(titleWidget, args, i);
4371 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
4372 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
4373 XtSetValues(shellWidget, args, i);
4374 XSync(xDisplay, False);
4379 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
4385 DisplayIcsInteractionTitle (String message)
4387 if (oldICSInteractionTitle == NULL) {
4388 /* Magic to find the old window title, adapted from vim */
4389 char *wina = getenv("WINDOWID");
4391 Window win = (Window) atoi(wina);
4392 Window root, parent, *children;
4393 unsigned int nchildren;
4394 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4396 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4397 if (!XQueryTree(xDisplay, win, &root, &parent,
4398 &children, &nchildren)) break;
4399 if (children) XFree((void *)children);
4400 if (parent == root || parent == 0) break;
4403 XSetErrorHandler(oldHandler);
4405 if (oldICSInteractionTitle == NULL) {
4406 oldICSInteractionTitle = "xterm";
4409 printf("\033]0;%s\007", message);
4413 char pendingReplyPrefix[MSG_SIZ];
4414 ProcRef pendingReplyPR;
4417 AskQuestionPopDown ()
4419 if (!askQuestionUp) return;
4420 XtPopdown(askQuestionShell);
4421 XtDestroyWidget(askQuestionShell);
4422 askQuestionUp = False;
4426 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4432 reply = XawDialogGetValueString(w = XtParent(w));
4433 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
4434 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
4435 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
4436 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
4437 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
4438 AskQuestionPopDown();
4440 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
4444 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
4449 XtSetArg(args[0], XtNlabel, &name);
4450 XtGetValues(w, args, 1);
4452 if (strcmp(name, _("cancel")) == 0) {
4453 AskQuestionPopDown();
4455 AskQuestionReplyAction(w, NULL, NULL, NULL);
4460 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
4463 Widget popup, layout, dialog, edit;
4469 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
4470 pendingReplyPR = pr;
4473 XtSetArg(args[i], XtNresizable, True); i++;
4474 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
4475 askQuestionShell = popup =
4476 XtCreatePopupShell(title, transientShellWidgetClass,
4477 shellWidget, args, i);
4480 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
4481 layoutArgs, XtNumber(layoutArgs));
4484 XtSetArg(args[i], XtNlabel, question); i++;
4485 XtSetArg(args[i], XtNvalue, ""); i++;
4486 XtSetArg(args[i], XtNborderWidth, 0); i++;
4487 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
4490 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
4491 (XtPointer) dialog);
4492 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
4493 (XtPointer) dialog);
4495 XtRealizeWidget(popup);
4496 CatchDeleteWindow(popup, "AskQuestionPopDown");
4498 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
4499 &x, &y, &win_x, &win_y, &mask);
4501 XtSetArg(args[0], XtNx, x - 10);
4502 XtSetArg(args[1], XtNy, y - 30);
4503 XtSetValues(popup, args, 2);
4505 XtPopup(popup, XtGrabExclusive);
4506 askQuestionUp = True;
4508 edit = XtNameToWidget(dialog, "*value");
4509 XtSetKeyboardFocus(popup, edit);
4514 PlaySound (char *name)
4516 if (*name == NULLCHAR) {
4518 } else if (strcmp(name, "$") == 0) {
4519 putc(BELLCHAR, stderr);
4522 char *prefix = "", *sep = "";
4523 if(appData.soundProgram[0] == NULLCHAR) return;
4524 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
4525 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
4533 PlaySound(appData.soundMove);
4539 PlaySound(appData.soundIcsWin);
4545 PlaySound(appData.soundIcsLoss);
4551 PlaySound(appData.soundIcsDraw);
4555 PlayIcsUnfinishedSound ()
4557 PlaySound(appData.soundIcsUnfinished);
4563 PlaySound(appData.soundIcsAlarm);
4569 PlaySound(appData.soundTell);
4575 system("stty echo");
4582 system("stty -echo");
4587 RunCommand (char *buf)
4593 Colorize (ColorClass cc, int continuation)
4596 int count, outCount, error;
4598 if (textColors[(int)cc].bg > 0) {
4599 if (textColors[(int)cc].fg > 0) {
4600 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
4601 textColors[(int)cc].fg, textColors[(int)cc].bg);
4603 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
4604 textColors[(int)cc].bg);
4607 if (textColors[(int)cc].fg > 0) {
4608 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
4609 textColors[(int)cc].fg);
4611 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
4614 count = strlen(buf);
4615 outCount = OutputToProcess(NoProc, buf, count, &error);
4616 if (outCount < count) {
4617 DisplayFatalError(_("Error writing to display"), error, 1);
4620 if (continuation) return;
4623 PlaySound(appData.soundShout);
4626 PlaySound(appData.soundSShout);
4629 PlaySound(appData.soundChannel1);
4632 PlaySound(appData.soundChannel);
4635 PlaySound(appData.soundKibitz);
4638 PlaySound(appData.soundTell);
4640 case ColorChallenge:
4641 PlaySound(appData.soundChallenge);
4644 PlaySound(appData.soundRequest);
4647 PlaySound(appData.soundSeek);
4659 return getpwuid(getuid())->pw_name;
4663 ExpandPathName (char *path)
4665 static char static_buf[4*MSG_SIZ];
4666 char *d, *s, buf[4*MSG_SIZ];
4672 while (*s && isspace(*s))
4681 if (*(s+1) == '/') {
4682 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
4686 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
4687 { char *p; if(p = strchr(buf, '/')) *p = 0; }
4688 pwd = getpwnam(buf);
4691 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
4695 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
4696 strcat(d, strchr(s+1, '/'));
4700 safeStrCpy(d, s, 4*MSG_SIZ );
4708 static char host_name[MSG_SIZ];
4710 #if HAVE_GETHOSTNAME
4711 gethostname(host_name, MSG_SIZ);
4713 #else /* not HAVE_GETHOSTNAME */
4714 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
4715 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
4717 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4719 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4720 #endif /* not HAVE_GETHOSTNAME */
4723 XtIntervalId delayedEventTimerXID = 0;
4724 DelayedEventCallback delayedEventCallback = 0;
4729 delayedEventTimerXID = 0;
4730 delayedEventCallback();
4734 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
4736 if(delayedEventTimerXID && delayedEventCallback == cb)
4737 // [HGM] alive: replace, rather than add or flush identical event
4738 XtRemoveTimeOut(delayedEventTimerXID);
4739 delayedEventCallback = cb;
4740 delayedEventTimerXID =
4741 XtAppAddTimeOut(appContext, millisec,
4742 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
4745 DelayedEventCallback
4748 if (delayedEventTimerXID) {
4749 return delayedEventCallback;
4756 CancelDelayedEvent ()
4758 if (delayedEventTimerXID) {
4759 XtRemoveTimeOut(delayedEventTimerXID);
4760 delayedEventTimerXID = 0;
4764 XtIntervalId loadGameTimerXID = 0;
4767 LoadGameTimerRunning ()
4769 return loadGameTimerXID != 0;
4773 StopLoadGameTimer ()
4775 if (loadGameTimerXID != 0) {
4776 XtRemoveTimeOut(loadGameTimerXID);
4777 loadGameTimerXID = 0;
4785 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
4787 loadGameTimerXID = 0;
4792 StartLoadGameTimer (long millisec)
4795 XtAppAddTimeOut(appContext, millisec,
4796 (XtTimerCallbackProc) LoadGameTimerCallback,
4800 XtIntervalId analysisClockXID = 0;
4803 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
4805 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
4806 || appData.icsEngineAnalyze) { // [DM]
4807 AnalysisPeriodicEvent(0);
4808 StartAnalysisClock();
4813 StartAnalysisClock ()
4816 XtAppAddTimeOut(appContext, 2000,
4817 (XtTimerCallbackProc) AnalysisClockCallback,
4821 XtIntervalId clockTimerXID = 0;
4824 ClockTimerRunning ()
4826 return clockTimerXID != 0;
4832 if (clockTimerXID != 0) {
4833 XtRemoveTimeOut(clockTimerXID);
4842 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
4849 StartClockTimer (long millisec)
4852 XtAppAddTimeOut(appContext, millisec,
4853 (XtTimerCallbackProc) ClockTimerCallback,
4858 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
4863 /* check for low time warning */
4864 Pixel foregroundOrWarningColor = timerForegroundPixel;
4867 appData.lowTimeWarning &&
4868 (timer / 1000) < appData.icsAlarmTime)
4869 foregroundOrWarningColor = lowTimeWarningColor;
4871 if (appData.clockMode) {
4872 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
4873 XtSetArg(args[0], XtNlabel, buf);
4875 snprintf(buf, MSG_SIZ, "%s ", color);
4876 XtSetArg(args[0], XtNlabel, buf);
4881 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
4882 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
4884 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
4885 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
4888 XtSetValues(w, args, 3);
4892 DisplayWhiteClock (long timeRemaining, int highlight)
4896 if(appData.noGUI) return;
4897 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
4898 if (highlight && iconPixmap == bIconPixmap) {
4899 iconPixmap = wIconPixmap;
4900 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
4901 XtSetValues(shellWidget, args, 1);
4906 DisplayBlackClock (long timeRemaining, int highlight)
4910 if(appData.noGUI) return;
4911 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
4912 if (highlight && iconPixmap == wIconPixmap) {
4913 iconPixmap = bIconPixmap;
4914 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
4915 XtSetValues(shellWidget, args, 1);
4934 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
4938 int to_prog[2], from_prog[2];
4942 if (appData.debugMode) {
4943 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
4946 /* We do NOT feed the cmdLine to the shell; we just
4947 parse it into blank-separated arguments in the
4948 most simple-minded way possible.
4951 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
4954 while(*p == ' ') p++;
4956 if(*p == '"' || *p == '\'')
4957 p = strchr(++argv[i-1], *p);
4958 else p = strchr(p, ' ');
4959 if (p == NULL) break;
4964 SetUpChildIO(to_prog, from_prog);
4966 if ((pid = fork()) == 0) {
4968 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
4969 close(to_prog[1]); // first close the unused pipe ends
4970 close(from_prog[0]);
4971 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
4972 dup2(from_prog[1], 1);
4973 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
4974 close(from_prog[1]); // and closing again loses one of the pipes!
4975 if(fileno(stderr) >= 2) // better safe than sorry...
4976 dup2(1, fileno(stderr)); /* force stderr to the pipe */
4978 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
4983 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
4985 execvp(argv[0], argv);
4987 /* If we get here, exec failed */
4992 /* Parent process */
4994 close(from_prog[1]);
4996 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
4999 cp->fdFrom = from_prog[0];
5000 cp->fdTo = to_prog[1];
5005 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
5007 AlarmCallBack (int n)
5013 DestroyChildProcess (ProcRef pr, int signalType)
5015 ChildProc *cp = (ChildProc *) pr;
5017 if (cp->kind != CPReal) return;
5019 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
5020 signal(SIGALRM, AlarmCallBack);
5022 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
5023 kill(cp->pid, SIGKILL); // kill it forcefully
5024 wait((int *) 0); // and wait again
5028 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
5030 /* Process is exiting either because of the kill or because of
5031 a quit command sent by the backend; either way, wait for it to die.
5040 InterruptChildProcess (ProcRef pr)
5042 ChildProc *cp = (ChildProc *) pr;
5044 if (cp->kind != CPReal) return;
5045 (void) kill(cp->pid, SIGINT); /* stop it thinking */
5049 OpenTelnet (char *host, char *port, ProcRef *pr)
5051 char cmdLine[MSG_SIZ];
5053 if (port[0] == NULLCHAR) {
5054 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
5056 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
5058 return StartChildProcess(cmdLine, "", pr);
5062 OpenTCP (char *host, char *port, ProcRef *pr)
5065 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
5066 #else /* !OMIT_SOCKETS */
5067 struct addrinfo hints;
5068 struct addrinfo *ais, *ai;
5073 memset(&hints, 0, sizeof(hints));
5074 hints.ai_family = AF_UNSPEC;
5075 hints.ai_socktype = SOCK_STREAM;
5077 error = getaddrinfo(host, port, &hints, &ais);
5079 /* a getaddrinfo error is not an errno, so can't return it */
5080 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
5081 host, port, gai_strerror(error));
5085 for (ai = ais; ai != NULL; ai = ai->ai_next) {
5086 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
5090 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
5103 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5109 #endif /* !OMIT_SOCKETS */
5115 OpenCommPort (char *name, ProcRef *pr)
5120 fd = open(name, 2, 0);
5121 if (fd < 0) return errno;
5123 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5134 OpenLoopback (ProcRef *pr)
5139 SetUpChildIO(to, from);
5141 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5144 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
5152 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
5154 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
5158 #define INPUT_SOURCE_BUF_SIZE 8192
5167 char buf[INPUT_SOURCE_BUF_SIZE];
5172 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
5174 InputSource *is = (InputSource *) closure;
5179 if (is->lineByLine) {
5180 count = read(is->fd, is->unused,
5181 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
5183 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
5186 is->unused += count;
5188 while (p < is->unused) {
5189 q = memchr(p, '\n', is->unused - p);
5190 if (q == NULL) break;
5192 (is->func)(is, is->closure, p, q - p, 0);
5196 while (p < is->unused) {
5201 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
5206 (is->func)(is, is->closure, is->buf, count, error);
5211 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
5214 ChildProc *cp = (ChildProc *) pr;
5216 is = (InputSource *) calloc(1, sizeof(InputSource));
5217 is->lineByLine = lineByLine;
5221 is->fd = fileno(stdin);
5223 is->kind = cp->kind;
5224 is->fd = cp->fdFrom;
5227 is->unused = is->buf;
5230 is->xid = XtAppAddInput(appContext, is->fd,
5231 (XtPointer) (XtInputReadMask),
5232 (XtInputCallbackProc) DoInputCallback,
5234 is->closure = closure;
5235 return (InputSourceRef) is;
5239 RemoveInputSource (InputSourceRef isr)
5241 InputSource *is = (InputSource *) isr;
5243 if (is->xid == 0) return;
5244 XtRemoveInput(is->xid);
5249 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
5251 static int line = 0;
5252 ChildProc *cp = (ChildProc *) pr;
5257 if (appData.noJoin || !appData.useInternalWrap)
5258 outCount = fwrite(message, 1, count, stdout);
5261 int width = get_term_width();
5262 int len = wrap(NULL, message, count, width, &line);
5263 char *msg = malloc(len);
5267 outCount = fwrite(message, 1, count, stdout);
5270 dbgchk = wrap(msg, message, count, width, &line);
5271 if (dbgchk != len && appData.debugMode)
5272 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
5273 outCount = fwrite(msg, 1, dbgchk, stdout);
5279 outCount = write(cp->fdTo, message, count);
5289 /* Output message to process, with "ms" milliseconds of delay
5290 between each character. This is needed when sending the logon
5291 script to ICC, which for some reason doesn't like the
5292 instantaneous send. */
5294 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
5296 ChildProc *cp = (ChildProc *) pr;
5301 r = write(cp->fdTo, message++, 1);
5314 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
5316 /* Masks for XPM pieces. Black and white pieces can have
5317 different shapes, but in the interest of retaining my
5318 sanity pieces must have the same outline on both light
5319 and dark squares, and all pieces must use the same
5320 background square colors/images. */
5322 static int xpmDone = 0;
5323 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
5324 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
5327 CreateAnimMasks (int pieceDepth)
5333 unsigned long plane;
5336 /* Need a bitmap just to get a GC with right depth */
5337 buf = XCreatePixmap(xDisplay, xBoardWindow,
5339 values.foreground = 1;
5340 values.background = 0;
5341 /* Don't use XtGetGC, not read only */
5342 maskGC = XCreateGC(xDisplay, buf,
5343 GCForeground | GCBackground, &values);
5344 XFreePixmap(xDisplay, buf);
5346 buf = XCreatePixmap(xDisplay, xBoardWindow,
5347 squareSize, squareSize, pieceDepth);
5348 values.foreground = XBlackPixel(xDisplay, xScreen);
5349 values.background = XWhitePixel(xDisplay, xScreen);
5350 bufGC = XCreateGC(xDisplay, buf,
5351 GCForeground | GCBackground, &values);
5353 for (piece = WhitePawn; piece <= BlackKing; piece++) {
5354 /* Begin with empty mask */
5355 if(!xpmDone) // [HGM] pieces: keep using existing
5356 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
5357 squareSize, squareSize, 1);
5358 XSetFunction(xDisplay, maskGC, GXclear);
5359 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
5360 0, 0, squareSize, squareSize);
5362 /* Take a copy of the piece */
5367 XSetFunction(xDisplay, bufGC, GXcopy);
5368 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
5370 0, 0, squareSize, squareSize, 0, 0);
5372 /* XOR the background (light) over the piece */
5373 XSetFunction(xDisplay, bufGC, GXxor);
5375 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
5376 0, 0, squareSize, squareSize, 0, 0);
5378 XSetForeground(xDisplay, bufGC, lightSquareColor);
5379 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
5382 /* We now have an inverted piece image with the background
5383 erased. Construct mask by just selecting all the non-zero
5384 pixels - no need to reconstruct the original image. */
5385 XSetFunction(xDisplay, maskGC, GXor);
5387 /* Might be quicker to download an XImage and create bitmap
5388 data from it rather than this N copies per piece, but it
5389 only takes a fraction of a second and there is a much
5390 longer delay for loading the pieces. */
5391 for (n = 0; n < pieceDepth; n ++) {
5392 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
5393 0, 0, squareSize, squareSize,
5399 XFreePixmap(xDisplay, buf);
5400 XFreeGC(xDisplay, bufGC);
5401 XFreeGC(xDisplay, maskGC);
5405 InitAnimState (AnimNr anr, XWindowAttributes *info)
5410 /* Each buffer is square size, same depth as window */
5411 animBufs[anr+4] = xBoardWindow;
5412 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
5413 squareSize, squareSize, info->depth);
5414 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
5415 squareSize, squareSize, info->depth);
5417 /* Create a plain GC for blitting */
5418 mask = GCForeground | GCBackground | GCFunction |
5419 GCPlaneMask | GCGraphicsExposures;
5420 values.foreground = XBlackPixel(xDisplay, xScreen);
5421 values.background = XWhitePixel(xDisplay, xScreen);
5422 values.function = GXcopy;
5423 values.plane_mask = AllPlanes;
5424 values.graphics_exposures = False;
5425 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
5427 /* Piece will be copied from an existing context at
5428 the start of each new animation/drag. */
5429 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
5431 /* Outline will be a read-only copy of an existing */
5432 animGCs[anr+4] = None;
5438 XWindowAttributes info;
5440 if (xpmDone && gameInfo.variant == oldVariant) return;
5441 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
5442 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
5444 InitAnimState(Game, &info);
5445 InitAnimState(Player, &info);
5447 /* For XPM pieces, we need bitmaps to use as masks. */
5449 CreateAnimMasks(info.depth), xpmDone = 1;
5454 static Boolean frameWaiting;
5457 FrameAlarm (int sig)
5459 frameWaiting = False;
5460 /* In case System-V style signals. Needed?? */
5461 signal(SIGALRM, FrameAlarm);
5465 FrameDelay (int time)
5467 struct itimerval delay;
5469 XSync(xDisplay, False);
5472 frameWaiting = True;
5473 signal(SIGALRM, FrameAlarm);
5474 delay.it_interval.tv_sec =
5475 delay.it_value.tv_sec = time / 1000;
5476 delay.it_interval.tv_usec =
5477 delay.it_value.tv_usec = (time % 1000) * 1000;
5478 setitimer(ITIMER_REAL, &delay, NULL);
5479 while (frameWaiting) pause();
5480 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
5481 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
5482 setitimer(ITIMER_REAL, &delay, NULL);
5489 FrameDelay (int time)
5491 XSync(xDisplay, False);
5493 usleep(time * 1000);
5499 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
5503 /* Bitmap for piece being moved. */
5504 if (appData.monoMode) {
5505 *mask = *pieceToSolid(piece);
5506 } else if (useImages) {
5508 *mask = xpmMask[piece];
5510 *mask = ximMaskPm[piece];
5513 *mask = *pieceToSolid(piece);
5516 /* GC for piece being moved. Square color doesn't matter, but
5517 since it gets modified we make a copy of the original. */
5519 if (appData.monoMode)
5524 if (appData.monoMode)
5529 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
5531 /* Outline only used in mono mode and is not modified */
5533 *outline = bwPieceGC;
5535 *outline = wbPieceGC;
5539 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
5544 /* Draw solid rectangle which will be clipped to shape of piece */
5545 XFillRectangle(xDisplay, dest, clip,
5546 0, 0, squareSize, squareSize);
5547 if (appData.monoMode)
5548 /* Also draw outline in contrasting color for black
5549 on black / white on white cases */
5550 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
5551 0, 0, squareSize, squareSize, 0, 0, 1);
5553 /* Copy the piece */
5558 if(appData.upsideDown && flipView) kind ^= 2;
5559 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
5561 0, 0, squareSize, squareSize,
5567 InsertPiece (AnimNr anr, ChessSquare piece)
5569 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
5573 DrawBlank (AnimNr anr, int x, int y, int startColor)
5575 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
5578 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
5579 int srcX, int srcY, int width, int height, int destX, int destY)
5581 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
5582 srcX, srcY, width, height, destX, destY);
5586 SetDragPiece (AnimNr anr, ChessSquare piece)
5589 /* The piece will be drawn using its own bitmap as a matte */
5590 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
5591 XSetClipMask(xDisplay, animGCs[anr+2], mask);
5594 #include <sys/ioctl.h>
5598 int fd, default_width;
5601 default_width = 79; // this is FICS default anyway...
5603 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
5605 if (!ioctl(fd, TIOCGSIZE, &win))
5606 default_width = win.ts_cols;
5607 #elif defined(TIOCGWINSZ)
5609 if (!ioctl(fd, TIOCGWINSZ, &win))
5610 default_width = win.ws_col;
5612 return default_width;
5618 static int old_width = 0;
5619 int new_width = get_term_width();
5621 if (old_width != new_width)
5622 ics_printf("set width %d\n", new_width);
5623 old_width = new_width;
5627 NotifyFrontendLogin ()
5632 /* [AS] Arrow highlighting support */
5635 DrawPolygon (Pnt arrow[], int nr)
5639 for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
5640 XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
5641 if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
5645 UpdateLogos (int displ)
5647 return; // no logos in XBoard yet