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 FileNamePopUp P((char *label, char *def, char *filter,
276 FileProc proc, char *openMode));
277 void AskQuestionReplyAction P((Widget w, XEvent *event,
278 String *prms, Cardinal *nprms));
279 void AskQuestionPopDown P((void));
280 void PromotionPopDown P((void));
281 void PromotionCallback P((Widget w, XtPointer client_data,
282 XtPointer call_data));
283 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
284 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
285 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
286 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
287 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
288 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
289 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
290 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
291 Boolean TempBackwardActive = False;
292 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
293 void DisplayMove P((int moveNumber));
294 void ICSInitScript P((void));
295 static char *ExpandPathName P((char *path));
296 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
297 void update_ics_width P(());
298 int get_term_width P(());
299 int CopyMemoProc P(());
302 * XBoard depends on Xt R4 or higher
304 int xtVersion = XtSpecificationRelease;
309 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
310 highlightSquareColor, premoveHighlightColor;
311 Pixel lowTimeWarningColor;
312 GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
313 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
315 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
316 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
317 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
318 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
319 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
320 ICSInputShell, fileNameShell, askQuestionShell;
321 Widget historyShell, evalGraphShell, gameListShell;
322 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
323 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
325 XFontSet fontSet, clockFontSet;
328 XFontStruct *clockFontStruct;
330 Font coordFontID, countFontID;
331 XFontStruct *coordFontStruct, *countFontStruct;
332 XtAppContext appContext;
334 char *oldICSInteractionTitle;
338 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
340 Position commentX = -1, commentY = -1;
341 Dimension commentW, commentH;
342 typedef unsigned int BoardSize;
344 Boolean chessProgram;
346 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
347 int smallLayout = 0, tinyLayout = 0,
348 marginW, marginH, // [HGM] for run-time resizing
349 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
350 ICSInputBoxUp = False, askQuestionUp = False,
351 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
352 errorUp = False, errorExitStatus = -1, defaultLineGap;
353 Dimension textHeight;
354 Pixel timerForegroundPixel, timerBackgroundPixel;
355 Pixel buttonForegroundPixel, buttonBackgroundPixel;
356 char *chessDir, *programName, *programVersion;
357 Boolean alwaysOnTop = False;
358 char *icsTextMenuString;
360 char *firstChessProgramNames;
361 char *secondChessProgramNames;
363 WindowPlacement wpMain;
364 WindowPlacement wpConsole;
365 WindowPlacement wpComment;
366 WindowPlacement wpMoveHistory;
367 WindowPlacement wpEvalGraph;
368 WindowPlacement wpEngineOutput;
369 WindowPlacement wpGameList;
370 WindowPlacement wpTags;
375 Pixmap pieceBitmap[2][(int)BlackPawn];
376 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
377 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
378 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
379 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
380 Pixmap xpmBoardBitmap[2];
381 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
382 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
383 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
384 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
385 XImage *ximLightSquare, *ximDarkSquare;
388 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
389 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
391 #define White(piece) ((int)(piece) < (int)BlackPawn)
393 /* Bitmaps for use as masks when drawing XPM pieces.
394 Need one for each black and white piece. */
395 static Pixmap xpmMask[BlackKing + 1];
397 /* This magic number is the number of intermediate frames used
398 in each half of the animation. For short moves it's reduced
399 by 1. The total number of frames will be factor * 2 + 1. */
402 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
404 #define PAUSE_BUTTON "P"
405 MenuItem buttonBar[] = {
406 {"<<", "<<", ToStartEvent},
407 {"<", "<", BackwardEvent},
408 {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseEvent},
409 {">", ">", ForwardEvent},
410 {">>", ">>", ToEndEvent},
414 #define PIECE_MENU_SIZE 18
415 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
416 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
417 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
418 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
419 N_("Empty square"), N_("Clear board") },
420 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
421 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
422 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
423 N_("Empty square"), N_("Clear board") }
425 /* must be in same order as pieceMenuStrings! */
426 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
427 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
428 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
429 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
430 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
431 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
432 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
433 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
434 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
437 #define DROP_MENU_SIZE 6
438 String dropMenuStrings[DROP_MENU_SIZE] = {
439 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
441 /* must be in same order as dropMenuStrings! */
442 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
443 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
444 WhiteRook, WhiteQueen
452 DropMenuEnables dmEnables[] = {
470 { XtNborderWidth, 0 },
471 { XtNdefaultDistance, 0 },
475 { XtNborderWidth, 0 },
476 { XtNresizable, (XtArgVal) True },
480 { XtNborderWidth, 0 },
486 { XtNjustify, (XtArgVal) XtJustifyRight },
487 { XtNlabel, (XtArgVal) "..." },
488 { XtNresizable, (XtArgVal) True },
489 { XtNresize, (XtArgVal) False }
492 Arg messageArgs[] = {
493 { XtNjustify, (XtArgVal) XtJustifyLeft },
494 { XtNlabel, (XtArgVal) "..." },
495 { XtNresizable, (XtArgVal) True },
496 { XtNresize, (XtArgVal) False }
500 { XtNborderWidth, 0 },
501 { XtNjustify, (XtArgVal) XtJustifyLeft }
504 XtResource clientResources[] = {
505 { "flashCount", "flashCount", XtRInt, sizeof(int),
506 XtOffset(AppDataPtr, flashCount), XtRImmediate,
507 (XtPointer) FLASH_COUNT },
510 XrmOptionDescRec shellOptions[] = {
511 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
512 { "-flash", "flashCount", XrmoptionNoArg, "3" },
513 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
516 XtActionsRec boardActions[] = {
517 { "DrawPosition", DrawPositionProc },
518 { "HandleUserMove", HandleUserMove },
519 { "AnimateUserMove", AnimateUserMove },
520 { "HandlePV", HandlePV },
521 { "SelectPV", SelectPV },
522 { "StopPV", StopPV },
523 { "AskQuestionReplyAction", AskQuestionReplyAction },
524 { "PieceMenuPopup", PieceMenuPopup },
525 { "WhiteClock", WhiteClock },
526 { "BlackClock", BlackClock },
527 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
528 { "QuitProc", QuitWrapper },
529 { "ManProc", ManInner },
530 { "TempBackwardProc", TempBackwardProc },
531 { "TempForwardProc", TempForwardProc },
532 { "CommentClick", (XtActionProc) CommentClick },
533 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
534 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
535 { "GameListPopDown", (XtActionProc) GameListPopDown },
536 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
537 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
538 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
539 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
540 { "GenericPopDown", (XtActionProc) GenericPopDown },
541 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
542 { "SelectMove", (XtActionProc) SelectMove },
543 { "LoadSelectedProc", LoadSelectedProc },
544 { "SetFilterProc", SetFilterProc },
545 { "TypeInProc", TypeInProc },
546 { "EnterKeyProc", EnterKeyProc },
547 { "UpKeyProc", UpKeyProc },
548 { "DownKeyProc", DownKeyProc },
551 char globalTranslations[] =
552 ":<Key>F9: MenuItem(ResignProc) \n \
553 :Ctrl<Key>n: MenuItem(NewGame) \n \
554 :Meta<Key>V: MenuItem(NewVariant) \n \
555 :Ctrl<Key>o: MenuItem(LoadGame) \n \
556 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
557 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
558 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
559 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
560 :Ctrl<Key>s: MenuItem(SaveGame) \n \
561 :Ctrl<Key>c: MenuItem(CopyGame) \n \
562 :Ctrl<Key>v: MenuItem(PasteGame) \n \
563 :Ctrl<Key>O: MenuItem(LoadPosition) \n \
564 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
565 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
566 :Ctrl<Key>S: MenuItem(SavePosition) \n \
567 :Ctrl<Key>C: MenuItem(CopyPosition) \n \
568 :Ctrl<Key>V: MenuItem(PastePosition) \n \
569 :Ctrl<Key>q: MenuItem(Exit) \n \
570 :Ctrl<Key>w: MenuItem(MachineWhite) \n \
571 :Ctrl<Key>b: MenuItem(MachineBlack) \n \
572 :Ctrl<Key>t: MenuItem(TwoMachines) \n \
573 :Ctrl<Key>a: MenuItem(AnalysisMode) \n \
574 :Ctrl<Key>g: MenuItem(AnalyzeFile) \n \
575 :Ctrl<Key>e: MenuItem(EditGame) \n \
576 :Ctrl<Key>E: MenuItem(EditPosition) \n \
577 :Meta<Key>O: MenuItem(ShowEngineOutput) \n \
578 :Meta<Key>E: MenuItem(ShowEvaluationGraph) \n \
579 :Meta<Key>G: MenuItem(ShowGameList) \n \
580 :Meta<Key>H: MenuItem(ShowMoveHistory) \n \
581 :<Key>Pause: MenuItem(Pause) \n \
582 :<Key>F3: MenuItem(Accept) \n \
583 :<Key>F4: MenuItem(Decline) \n \
584 :<Key>F12: MenuItem(Rematch) \n \
585 :<Key>F5: MenuItem(CallFlag) \n \
586 :<Key>F6: MenuItem(Draw) \n \
587 :<Key>F7: MenuItem(Adjourn) \n \
588 :<Key>F8: MenuItem(Abort) \n \
589 :<Key>F10: MenuItem(StopObserving) \n \
590 :<Key>F11: MenuItem(StopExamining) \n \
591 :Ctrl<Key>d: MenuItem(DebugProc) \n \
592 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
593 :Meta<Key>End: MenuItem(ToEnd) \n \
594 :Meta<Key>Right: MenuItem(Forward) \n \
595 :Meta<Key>Home: MenuItem(ToStart) \n \
596 :Meta<Key>Left: MenuItem(Backward) \n \
597 :<Key>Left: MenuItem(Backward) \n \
598 :<Key>Right: MenuItem(Forward) \n \
599 :<Key>Home: MenuItem(Revert) \n \
600 :<Key>End: MenuItem(TruncateGame) \n \
601 :Ctrl<Key>m: MenuItem(MoveNow) \n \
602 :Ctrl<Key>x: MenuItem(RetractMove) \n \
603 :Meta<Key>J: MenuItem(Adjudications) \n \
604 :Meta<Key>U: MenuItem(CommonEngine) \n \
605 :Meta<Key>T: MenuItem(TimeControl) \n \
606 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
607 #ifndef OPTIONSDIALOG
609 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
610 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
611 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
612 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
613 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
616 :<Key>F1: MenuItem(Manual) \n \
617 :<Key>F2: MenuItem(FlipView) \n \
618 :<KeyDown>Return: TempBackwardProc() \n \
619 :<KeyUp>Return: TempForwardProc() \n";
621 char boardTranslations[] =
622 "<Btn1Down>: HandleUserMove(0) \n \
623 Shift<Btn1Up>: HandleUserMove(1) \n \
624 <Btn1Up>: HandleUserMove(0) \n \
625 <Btn1Motion>: AnimateUserMove() \n \
626 <Btn3Motion>: HandlePV() \n \
627 <Btn2Motion>: HandlePV() \n \
628 <Btn3Up>: PieceMenuPopup(menuB) \n \
629 <Btn2Up>: PieceMenuPopup(menuB) \n \
630 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
631 PieceMenuPopup(menuB) \n \
632 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
633 PieceMenuPopup(menuW) \n \
634 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
635 PieceMenuPopup(menuW) \n \
636 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
637 PieceMenuPopup(menuB) \n";
639 char whiteTranslations[] =
640 "Shift<BtnDown>: WhiteClock(1)\n \
641 <BtnDown>: WhiteClock(0)\n";
642 char blackTranslations[] =
643 "Shift<BtnDown>: BlackClock(1)\n \
644 <BtnDown>: BlackClock(0)\n";
646 char ICSInputTranslations[] =
647 "<Key>Up: UpKeyProc() \n "
648 "<Key>Down: DownKeyProc() \n "
649 "<Key>Return: EnterKeyProc() \n";
651 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
652 // as the widget is destroyed before the up-click can call extend-end
653 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
655 String xboardResources[] = {
656 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
657 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
662 /* Max possible square size */
663 #define MAXSQSIZE 256
665 static int xpm_avail[MAXSQSIZE];
667 #ifdef HAVE_DIR_STRUCT
669 /* Extract piece size from filename */
671 xpm_getsize (char *name, int len, char *ext)
679 if ((p=strchr(name, '.')) == NULL ||
680 StrCaseCmp(p+1, ext) != 0)
686 while (*p && isdigit(*p))
693 /* Setup xpm_avail */
695 xpm_getavail (char *dirname, char *ext)
701 for (i=0; i<MAXSQSIZE; ++i)
704 if (appData.debugMode)
705 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
707 dir = opendir(dirname);
710 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
711 programName, dirname);
715 while ((ent=readdir(dir)) != NULL) {
716 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
717 if (i > 0 && i < MAXSQSIZE)
727 xpm_print_avail (FILE *fp, char *ext)
731 fprintf(fp, _("Available `%s' sizes:\n"), ext);
732 for (i=1; i<MAXSQSIZE; ++i) {
738 /* Return XPM piecesize closest to size */
740 xpm_closest_to (char *dirname, int size, char *ext)
743 int sm_diff = MAXSQSIZE;
747 xpm_getavail(dirname, ext);
749 if (appData.debugMode)
750 xpm_print_avail(stderr, ext);
752 for (i=1; i<MAXSQSIZE; ++i) {
755 diff = (diff<0) ? -diff : diff;
756 if (diff < sm_diff) {
764 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
770 #else /* !HAVE_DIR_STRUCT */
771 /* If we are on a system without a DIR struct, we can't
772 read the directory, so we can't collect a list of
773 filenames, etc., so we can't do any size-fitting. */
775 xpm_closest_to (char *dirname, int size, char *ext)
778 Warning: No DIR structure found on this system --\n\
779 Unable to autosize for XPM/XIM pieces.\n\
780 Please report this error to %s.\n\
781 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
784 #endif /* HAVE_DIR_STRUCT */
786 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
787 "magenta", "cyan", "white" };
791 TextColors textColors[(int)NColorClasses];
793 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
795 parse_color (char *str, int which)
797 char *p, buf[100], *d;
800 if (strlen(str) > 99) /* watch bounds on buf */
805 for (i=0; i<which; ++i) {
812 /* Could be looking at something like:
814 .. in which case we want to stop on a comma also */
815 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
819 return -1; /* Use default for empty field */
822 if (which == 2 || isdigit(*p))
825 while (*p && isalpha(*p))
830 for (i=0; i<8; ++i) {
831 if (!StrCaseCmp(buf, cnames[i]))
832 return which? (i+40) : (i+30);
834 if (!StrCaseCmp(buf, "default")) return -1;
836 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
841 parse_cpair (ColorClass cc, char *str)
843 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
844 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
849 /* bg and attr are optional */
850 textColors[(int)cc].bg = parse_color(str, 1);
851 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
852 textColors[(int)cc].attr = 0;
858 /* Arrange to catch delete-window events */
859 Atom wm_delete_window;
861 CatchDeleteWindow (Widget w, String procname)
864 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
865 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
866 XtAugmentTranslations(w, XtParseTranslationTable(buf));
873 XtSetArg(args[0], XtNiconic, False);
874 XtSetValues(shellWidget, args, 1);
876 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
879 //---------------------------------------------------------------------------------------------------------
880 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
883 #define CW_USEDEFAULT (1<<31)
884 #define ICS_TEXT_MENU_SIZE 90
885 #define DEBUG_FILE "xboard.debug"
886 #define SetCurrentDirectory chdir
887 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
891 // these two must some day move to frontend.h, when they are implemented
892 Boolean GameListIsUp();
894 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
897 // front-end part of option handling
899 // [HGM] This platform-dependent table provides the location for storing the color info
900 extern char *crWhite, * crBlack;
904 &appData.whitePieceColor,
905 &appData.blackPieceColor,
906 &appData.lightSquareColor,
907 &appData.darkSquareColor,
908 &appData.highlightSquareColor,
909 &appData.premoveHighlightColor,
910 &appData.lowTimeWarningColor,
921 // [HGM] font: keep a font for each square size, even non-stndard ones
924 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
925 char *fontTable[NUM_FONTS][MAX_SIZE];
928 ParseFont (char *name, int number)
929 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
931 if(sscanf(name, "size%d:", &size)) {
932 // [HGM] font: font is meant for specific boardSize (likely from settings file);
933 // defer processing it until we know if it matches our board size
934 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
935 fontTable[number][size] = strdup(strchr(name, ':')+1);
936 fontValid[number][size] = True;
941 case 0: // CLOCK_FONT
942 appData.clockFont = strdup(name);
944 case 1: // MESSAGE_FONT
945 appData.font = strdup(name);
947 case 2: // COORD_FONT
948 appData.coordFont = strdup(name);
953 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
958 { // only 2 fonts currently
959 appData.clockFont = CLOCK_FONT_NAME;
960 appData.coordFont = COORD_FONT_NAME;
961 appData.font = DEFAULT_FONT_NAME;
966 { // no-op, until we identify the code for this already in XBoard and move it here
970 ParseColor (int n, char *name)
971 { // in XBoard, just copy the color-name string
972 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
976 ParseTextAttribs (ColorClass cc, char *s)
978 (&appData.colorShout)[cc] = strdup(s);
982 ParseBoardSize (void *addr, char *name)
984 appData.boardSize = strdup(name);
989 { // In XBoard the sound-playing program takes care of obtaining the actual sound
993 SetCommPortDefaults ()
994 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
997 // [HGM] args: these three cases taken out to stay in front-end
999 SaveFontArg (FILE *f, ArgDescriptor *ad)
1002 int i, n = (int)(intptr_t)ad->argLoc;
1004 case 0: // CLOCK_FONT
1005 name = appData.clockFont;
1007 case 1: // MESSAGE_FONT
1008 name = appData.font;
1010 case 2: // COORD_FONT
1011 name = appData.coordFont;
1016 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1017 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1018 fontTable[n][squareSize] = strdup(name);
1019 fontValid[n][squareSize] = True;
1022 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1023 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1028 { // nothing to do, as the sounds are at all times represented by their text-string names already
1032 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
1033 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1034 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1038 SaveColor (FILE *f, ArgDescriptor *ad)
1039 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1040 if(colorVariable[(int)(intptr_t)ad->argLoc])
1041 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1045 SaveBoardSize (FILE *f, char *name, void *addr)
1046 { // wrapper to shield back-end from BoardSize & sizeInfo
1047 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1051 ParseCommPortSettings (char *s)
1052 { // no such option in XBoard (yet)
1055 extern Widget engineOutputShell;
1059 GetActualPlacement (Widget wg, WindowPlacement *wp)
1064 XWindowAttributes winAt;
1071 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
1072 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
1073 wp->x = rx - winAt.x;
1074 wp->y = ry - winAt.y;
1075 wp->height = winAt.height;
1076 wp->width = winAt.width;
1077 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
1082 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1083 // In XBoard this will have to wait until awareness of window parameters is implemented
1084 GetActualPlacement(shellWidget, &wpMain);
1085 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1086 if(MoveHistoryIsUp()) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
1087 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1088 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1089 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
1090 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
1094 PrintCommPortSettings (FILE *f, char *name)
1095 { // This option does not exist in XBoard
1099 MySearchPath (char *installDir, char *name, char *fullname)
1100 { // just append installDir and name. Perhaps ExpandPath should be used here?
1101 name = ExpandPathName(name);
1102 if(name && name[0] == '/')
1103 safeStrCpy(fullname, name, MSG_SIZ );
1105 sprintf(fullname, "%s%c%s", installDir, '/', name);
1111 MyGetFullPathName (char *name, char *fullname)
1112 { // should use ExpandPath?
1113 name = ExpandPathName(name);
1114 safeStrCpy(fullname, name, MSG_SIZ );
1119 EnsureOnScreen (int *x, int *y, int minX, int minY)
1126 { // [HGM] args: allows testing if main window is realized from back-end
1127 return xBoardWindow != 0;
1131 PopUpStartupDialog ()
1132 { // start menu not implemented in XBoard
1136 ConvertToLine (int argc, char **argv)
1138 static char line[128*1024], buf[1024];
1142 for(i=1; i<argc; i++)
1144 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1145 && argv[i][0] != '{' )
1146 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1148 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1149 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1152 line[strlen(line)-1] = NULLCHAR;
1156 //--------------------------------------------------------------------------------------------
1159 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1161 #define BoardSize int
1163 InitDrawingSizes (BoardSize boardSize, int flags)
1164 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1165 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1167 XtGeometryResult gres;
1169 static Dimension oldWidth, oldHeight;
1170 static VariantClass oldVariant;
1171 static int oldDual = -1, oldMono = -1;
1173 if(!formWidget) return;
1175 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1176 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1177 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1179 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1181 * Enable shell resizing.
1183 shellArgs[0].value = (XtArgVal) &w;
1184 shellArgs[1].value = (XtArgVal) &h;
1185 XtGetValues(shellWidget, shellArgs, 2);
1187 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1188 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1189 XtSetValues(shellWidget, &shellArgs[2], 4);
1191 XtSetArg(args[0], XtNdefaultDistance, &sep);
1192 XtGetValues(formWidget, args, 1);
1194 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1196 hOffset = boardWidth + 10;
1197 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1198 secondSegments[i] = gridSegments[i];
1199 secondSegments[i].x1 += hOffset;
1200 secondSegments[i].x2 += hOffset;
1203 XtSetArg(args[0], XtNwidth, boardWidth);
1204 XtSetArg(args[1], XtNheight, boardHeight);
1205 XtSetValues(boardWidget, args, 2);
1207 timerWidth = (boardWidth - sep) / 2;
1208 XtSetArg(args[0], XtNwidth, timerWidth);
1209 XtSetValues(whiteTimerWidget, args, 1);
1210 XtSetValues(blackTimerWidget, args, 1);
1212 XawFormDoLayout(formWidget, False);
1214 if (appData.titleInWindow) {
1216 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1217 XtSetArg(args[i], XtNheight, &h); i++;
1218 XtGetValues(titleWidget, args, i);
1220 w = boardWidth - 2*bor;
1222 XtSetArg(args[0], XtNwidth, &w);
1223 XtGetValues(menuBarWidget, args, 1);
1224 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1227 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1228 if (gres != XtGeometryYes && appData.debugMode) {
1230 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1231 programName, gres, w, h, wr, hr);
1235 XawFormDoLayout(formWidget, True);
1238 * Inhibit shell resizing.
1240 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1241 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1242 shellArgs[4].value = shellArgs[2].value = w;
1243 shellArgs[5].value = shellArgs[3].value = h;
1244 XtSetValues(shellWidget, &shellArgs[0], 6);
1246 XSync(xDisplay, False);
1250 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1253 if(gameInfo.variant != oldVariant) { // and only if variant changed
1256 for(i=0; i<4; i++) {
1258 for(p=0; p<=(int)WhiteKing; p++)
1259 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1260 if(gameInfo.variant == VariantShogi) {
1261 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1262 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1263 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1264 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1265 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1268 if(gameInfo.variant == VariantGothic) {
1269 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1272 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1273 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1274 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1277 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1278 for(p=0; p<=(int)WhiteKing; p++)
1279 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1280 if(gameInfo.variant == VariantShogi) {
1281 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1282 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1283 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1284 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1285 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1288 if(gameInfo.variant == VariantGothic) {
1289 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1292 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1293 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1294 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1299 for(i=0; i<2; i++) {
1301 for(p=0; p<=(int)WhiteKing; p++)
1302 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1303 if(gameInfo.variant == VariantShogi) {
1304 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1305 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1306 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1307 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1308 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1311 if(gameInfo.variant == VariantGothic) {
1312 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1315 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1316 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1317 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1321 oldMono = -10; // kludge to force recreation of animation masks
1322 oldVariant = gameInfo.variant;
1325 if(appData.monoMode != oldMono)
1328 oldMono = appData.monoMode;
1333 ParseIcsTextColors ()
1334 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1335 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1336 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1337 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1338 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1339 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1340 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1341 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1342 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1343 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1344 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1346 if (appData.colorize) {
1348 _("%s: can't parse color names; disabling colorization\n"),
1351 appData.colorize = FALSE;
1356 MakeOneColor (char *name, Pixel *color)
1358 XrmValue vFrom, vTo;
1359 if (!appData.monoMode) {
1360 vFrom.addr = (caddr_t) name;
1361 vFrom.size = strlen(name);
1362 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1363 if (vTo.addr == NULL) {
1364 appData.monoMode = True;
1367 *color = *(Pixel *) vTo.addr;
1375 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1376 int forceMono = False;
1378 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1379 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1380 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1381 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1382 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1383 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1390 { // [HGM] taken out of main
1392 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1393 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1394 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1396 if (appData.bitmapDirectory[0] != NULLCHAR) {
1400 CreateXPMBoard(appData.liteBackTextureFile, 1);
1401 CreateXPMBoard(appData.darkBackTextureFile, 0);
1405 /* Create regular pieces */
1406 if (!useImages) CreatePieces();
1411 InitDrawingParams ()
1413 MakeColors(); CreateGCs(True);
1418 main (int argc, char **argv)
1420 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1421 XSetWindowAttributes window_attributes;
1423 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1424 XrmValue vFrom, vTo;
1425 XtGeometryResult gres;
1428 int forceMono = False;
1430 srandom(time(0)); // [HGM] book: make random truly random
1432 setbuf(stdout, NULL);
1433 setbuf(stderr, NULL);
1436 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1437 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1441 programName = strrchr(argv[0], '/');
1442 if (programName == NULL)
1443 programName = argv[0];
1448 XtSetLanguageProc(NULL, NULL, NULL);
1449 bindtextdomain(PACKAGE, LOCALEDIR);
1450 textdomain(PACKAGE);
1454 XtAppInitialize(&appContext, "XBoard", shellOptions,
1455 XtNumber(shellOptions),
1456 &argc, argv, xboardResources, NULL, 0);
1457 appData.boardSize = "";
1458 InitAppData(ConvertToLine(argc, argv));
1460 if (p == NULL) p = "/tmp";
1461 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1462 gameCopyFilename = (char*) malloc(i);
1463 gamePasteFilename = (char*) malloc(i);
1464 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1465 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1467 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1468 clientResources, XtNumber(clientResources),
1471 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1472 static char buf[MSG_SIZ];
1473 EscapeExpand(buf, appData.firstInitString);
1474 appData.firstInitString = strdup(buf);
1475 EscapeExpand(buf, appData.secondInitString);
1476 appData.secondInitString = strdup(buf);
1477 EscapeExpand(buf, appData.firstComputerString);
1478 appData.firstComputerString = strdup(buf);
1479 EscapeExpand(buf, appData.secondComputerString);
1480 appData.secondComputerString = strdup(buf);
1483 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1486 if (chdir(chessDir) != 0) {
1487 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1493 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1494 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1495 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1496 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1499 setbuf(debugFP, NULL);
1503 if (appData.debugMode) {
1504 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1508 /* [HGM,HR] make sure board size is acceptable */
1509 if(appData.NrFiles > BOARD_FILES ||
1510 appData.NrRanks > BOARD_RANKS )
1511 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1514 /* This feature does not work; animation needs a rewrite */
1515 appData.highlightDragging = FALSE;
1519 xDisplay = XtDisplay(shellWidget);
1520 xScreen = DefaultScreen(xDisplay);
1521 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1523 gameInfo.variant = StringToVariant(appData.variant);
1524 InitPosition(FALSE);
1527 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1529 if (isdigit(appData.boardSize[0])) {
1530 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1531 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1532 &fontPxlSize, &smallLayout, &tinyLayout);
1534 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1535 programName, appData.boardSize);
1539 /* Find some defaults; use the nearest known size */
1540 SizeDefaults *szd, *nearest;
1541 int distance = 99999;
1542 nearest = szd = sizeDefaults;
1543 while (szd->name != NULL) {
1544 if (abs(szd->squareSize - squareSize) < distance) {
1546 distance = abs(szd->squareSize - squareSize);
1547 if (distance == 0) break;
1551 if (i < 2) lineGap = nearest->lineGap;
1552 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1553 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1554 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1555 if (i < 6) smallLayout = nearest->smallLayout;
1556 if (i < 7) tinyLayout = nearest->tinyLayout;
1559 SizeDefaults *szd = sizeDefaults;
1560 if (*appData.boardSize == NULLCHAR) {
1561 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1562 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1565 if (szd->name == NULL) szd--;
1566 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1568 while (szd->name != NULL &&
1569 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1570 if (szd->name == NULL) {
1571 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1572 programName, appData.boardSize);
1576 squareSize = szd->squareSize;
1577 lineGap = szd->lineGap;
1578 clockFontPxlSize = szd->clockFontPxlSize;
1579 coordFontPxlSize = szd->coordFontPxlSize;
1580 fontPxlSize = szd->fontPxlSize;
1581 smallLayout = szd->smallLayout;
1582 tinyLayout = szd->tinyLayout;
1583 // [HGM] font: use defaults from settings file if available and not overruled
1585 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1586 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1587 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1588 appData.font = fontTable[MESSAGE_FONT][squareSize];
1589 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1590 appData.coordFont = fontTable[COORD_FONT][squareSize];
1592 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1593 if (strlen(appData.pixmapDirectory) > 0) {
1594 p = ExpandPathName(appData.pixmapDirectory);
1596 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1597 appData.pixmapDirectory);
1600 if (appData.debugMode) {
1601 fprintf(stderr, _("\
1602 XBoard square size (hint): %d\n\
1603 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1605 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1606 if (appData.debugMode) {
1607 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1610 defaultLineGap = lineGap;
1611 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1613 /* [HR] height treated separately (hacked) */
1614 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1615 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1616 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1617 XtSetArg(boardArgs[2], XtNheight, boardHeight);
1620 * Determine what fonts to use.
1623 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1624 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1625 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1626 fontSet = CreateFontSet(appData.font);
1627 clockFontSet = CreateFontSet(appData.clockFont);
1629 /* For the coordFont, use the 0th font of the fontset. */
1630 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1631 XFontStruct **font_struct_list;
1632 XFontSetExtents *fontSize;
1633 char **font_name_list;
1634 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1635 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1636 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1637 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1638 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1641 appData.font = FindFont(appData.font, fontPxlSize);
1642 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1643 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1644 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1645 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1646 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1647 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1649 countFontID = coordFontID; // [HGM] holdings
1650 countFontStruct = coordFontStruct;
1652 xdb = XtDatabase(xDisplay);
1654 XrmPutLineResource(&xdb, "*international: True");
1655 vTo.size = sizeof(XFontSet);
1656 vTo.addr = (XtPointer) &fontSet;
1657 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1659 XrmPutStringResource(&xdb, "*font", appData.font);
1663 * Detect if there are not enough colors available and adapt.
1665 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1666 appData.monoMode = True;
1669 forceMono = MakeColors();
1672 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1674 appData.monoMode = True;
1677 if (appData.lowTimeWarning && !appData.monoMode) {
1678 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1679 vFrom.size = strlen(appData.lowTimeWarningColor);
1680 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1681 if (vTo.addr == NULL)
1682 appData.monoMode = True;
1684 lowTimeWarningColor = *(Pixel *) vTo.addr;
1687 if (appData.monoMode && appData.debugMode) {
1688 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1689 (unsigned long) XWhitePixel(xDisplay, xScreen),
1690 (unsigned long) XBlackPixel(xDisplay, xScreen));
1693 ParseIcsTextColors();
1694 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
1695 textColors[ColorNone].attr = 0;
1697 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1703 layoutName = "tinyLayout";
1704 } else if (smallLayout) {
1705 layoutName = "smallLayout";
1707 layoutName = "normalLayout";
1709 /* Outer layoutWidget is there only to provide a name for use in
1710 resources that depend on the layout style */
1712 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
1713 layoutArgs, XtNumber(layoutArgs));
1715 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
1716 formArgs, XtNumber(formArgs));
1717 XtSetArg(args[0], XtNdefaultDistance, &sep);
1718 XtGetValues(formWidget, args, 1);
1721 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
1722 XtSetArg(args[0], XtNtop, XtChainTop);
1723 XtSetArg(args[1], XtNbottom, XtChainTop);
1724 XtSetArg(args[2], XtNright, XtChainLeft);
1725 XtSetValues(menuBarWidget, args, 3);
1727 widgetList[j++] = whiteTimerWidget =
1728 XtCreateWidget("whiteTime", labelWidgetClass,
1729 formWidget, timerArgs, XtNumber(timerArgs));
1731 XtSetArg(args[0], XtNfontSet, clockFontSet);
1733 XtSetArg(args[0], XtNfont, clockFontStruct);
1735 XtSetArg(args[1], XtNtop, XtChainTop);
1736 XtSetArg(args[2], XtNbottom, XtChainTop);
1737 XtSetValues(whiteTimerWidget, args, 3);
1739 widgetList[j++] = blackTimerWidget =
1740 XtCreateWidget("blackTime", labelWidgetClass,
1741 formWidget, timerArgs, XtNumber(timerArgs));
1743 XtSetArg(args[0], XtNfontSet, clockFontSet);
1745 XtSetArg(args[0], XtNfont, clockFontStruct);
1747 XtSetArg(args[1], XtNtop, XtChainTop);
1748 XtSetArg(args[2], XtNbottom, XtChainTop);
1749 XtSetValues(blackTimerWidget, args, 3);
1751 if (appData.titleInWindow) {
1752 widgetList[j++] = titleWidget =
1753 XtCreateWidget("title", labelWidgetClass, formWidget,
1754 titleArgs, XtNumber(titleArgs));
1755 XtSetArg(args[0], XtNtop, XtChainTop);
1756 XtSetArg(args[1], XtNbottom, XtChainTop);
1757 XtSetValues(titleWidget, args, 2);
1760 if (appData.showButtonBar) {
1761 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
1762 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
1763 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
1764 XtSetArg(args[2], XtNtop, XtChainTop);
1765 XtSetArg(args[3], XtNbottom, XtChainTop);
1766 XtSetValues(buttonBarWidget, args, 4);
1769 widgetList[j++] = messageWidget =
1770 XtCreateWidget("message", labelWidgetClass, formWidget,
1771 messageArgs, XtNumber(messageArgs));
1772 XtSetArg(args[0], XtNtop, XtChainTop);
1773 XtSetArg(args[1], XtNbottom, XtChainTop);
1774 XtSetValues(messageWidget, args, 2);
1776 widgetList[j++] = boardWidget =
1777 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
1778 XtNumber(boardArgs));
1780 XtManageChildren(widgetList, j);
1782 timerWidth = (boardWidth - sep) / 2;
1783 XtSetArg(args[0], XtNwidth, timerWidth);
1784 XtSetValues(whiteTimerWidget, args, 1);
1785 XtSetValues(blackTimerWidget, args, 1);
1787 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1788 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1789 XtGetValues(whiteTimerWidget, args, 2);
1791 if (appData.showButtonBar) {
1792 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1793 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1794 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
1798 * formWidget uses these constraints but they are stored
1802 XtSetArg(args[i], XtNfromHoriz, 0); i++;
1803 XtSetValues(menuBarWidget, args, i);
1804 if (appData.titleInWindow) {
1807 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1808 XtSetValues(whiteTimerWidget, args, i);
1810 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1811 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1812 XtSetValues(blackTimerWidget, args, i);
1814 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1815 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
1816 XtSetValues(titleWidget, args, i);
1818 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1819 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1820 XtSetValues(messageWidget, args, i);
1821 if (appData.showButtonBar) {
1823 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1824 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1825 XtSetValues(buttonBarWidget, args, i);
1829 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1830 XtSetValues(whiteTimerWidget, args, i);
1832 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1833 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1834 XtSetValues(blackTimerWidget, args, i);
1836 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
1837 XtSetValues(titleWidget, args, i);
1839 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1840 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1841 XtSetValues(messageWidget, args, i);
1842 if (appData.showButtonBar) {
1844 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1845 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1846 XtSetValues(buttonBarWidget, args, i);
1851 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1852 XtSetValues(whiteTimerWidget, args, i);
1854 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1855 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1856 XtSetValues(blackTimerWidget, args, i);
1858 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1859 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1860 XtSetValues(messageWidget, args, i);
1861 if (appData.showButtonBar) {
1863 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1864 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1865 XtSetValues(buttonBarWidget, args, i);
1869 XtSetArg(args[0], XtNfromVert, messageWidget);
1870 XtSetArg(args[1], XtNtop, XtChainTop);
1871 XtSetArg(args[2], XtNbottom, XtChainBottom);
1872 XtSetArg(args[3], XtNleft, XtChainLeft);
1873 XtSetArg(args[4], XtNright, XtChainRight);
1874 XtSetValues(boardWidget, args, 5);
1876 XtRealizeWidget(shellWidget);
1879 XtSetArg(args[0], XtNx, wpMain.x);
1880 XtSetArg(args[1], XtNy, wpMain.y);
1881 XtSetValues(shellWidget, args, 2);
1885 * Correct the width of the message and title widgets.
1886 * It is not known why some systems need the extra fudge term.
1887 * The value "2" is probably larger than needed.
1889 XawFormDoLayout(formWidget, False);
1891 #define WIDTH_FUDGE 2
1893 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1894 XtSetArg(args[i], XtNheight, &h); i++;
1895 XtGetValues(messageWidget, args, i);
1896 if (appData.showButtonBar) {
1898 XtSetArg(args[i], XtNwidth, &w); i++;
1899 XtGetValues(buttonBarWidget, args, i);
1900 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1902 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
1905 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1906 if (gres != XtGeometryYes && appData.debugMode) {
1907 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1908 programName, gres, w, h, wr, hr);
1911 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
1912 /* The size used for the child widget in layout lags one resize behind
1913 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
1915 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1916 if (gres != XtGeometryYes && appData.debugMode) {
1917 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1918 programName, gres, w, h, wr, hr);
1921 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
1922 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
1923 XtSetArg(args[1], XtNright, XtChainRight);
1924 XtSetValues(messageWidget, args, 2);
1926 if (appData.titleInWindow) {
1928 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1929 XtSetArg(args[i], XtNheight, &h); i++;
1930 XtGetValues(titleWidget, args, i);
1932 w = boardWidth - 2*bor;
1934 XtSetArg(args[0], XtNwidth, &w);
1935 XtGetValues(menuBarWidget, args, 1);
1936 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1939 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1940 if (gres != XtGeometryYes && appData.debugMode) {
1942 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1943 programName, gres, w, h, wr, hr);
1946 XawFormDoLayout(formWidget, True);
1948 xBoardWindow = XtWindow(boardWidget);
1950 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1951 // not need to go into InitDrawingSizes().
1955 * Create X checkmark bitmap and initialize option menu checks.
1957 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1958 checkmark_bits, checkmark_width, checkmark_height);
1964 ReadBitmap(&wIconPixmap, "icon_white.bm",
1965 icon_white_bits, icon_white_width, icon_white_height);
1966 ReadBitmap(&bIconPixmap, "icon_black.bm",
1967 icon_black_bits, icon_black_width, icon_black_height);
1968 iconPixmap = wIconPixmap;
1970 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1971 XtSetValues(shellWidget, args, i);
1974 * Create a cursor for the board widget.
1976 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1977 XChangeWindowAttributes(xDisplay, xBoardWindow,
1978 CWCursor, &window_attributes);
1981 * Inhibit shell resizing.
1983 shellArgs[0].value = (XtArgVal) &w;
1984 shellArgs[1].value = (XtArgVal) &h;
1985 XtGetValues(shellWidget, shellArgs, 2);
1986 shellArgs[4].value = shellArgs[2].value = w;
1987 shellArgs[5].value = shellArgs[3].value = h;
1988 XtSetValues(shellWidget, &shellArgs[2], 4);
1989 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1990 marginH = h - boardHeight;
1992 CatchDeleteWindow(shellWidget, "QuitProc");
2000 if (appData.animate || appData.animateDragging)
2003 XtAugmentTranslations(formWidget,
2004 XtParseTranslationTable(globalTranslations));
2005 XtAugmentTranslations(boardWidget,
2006 XtParseTranslationTable(boardTranslations));
2007 XtAugmentTranslations(whiteTimerWidget,
2008 XtParseTranslationTable(whiteTranslations));
2009 XtAugmentTranslations(blackTimerWidget,
2010 XtParseTranslationTable(blackTranslations));
2012 /* Why is the following needed on some versions of X instead
2013 * of a translation? */
2014 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2015 (XtEventHandler) EventProc, NULL);
2017 XtAddEventHandler(formWidget, KeyPressMask, False,
2018 (XtEventHandler) MoveTypeInProc, NULL);
2019 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
2020 (XtEventHandler) EventProc, NULL);
2022 /* [AS] Restore layout */
2023 if( wpMoveHistory.visible ) {
2027 if( wpEvalGraph.visible )
2032 if( wpEngineOutput.visible ) {
2033 EngineOutputPopUp();
2038 if (errorExitStatus == -1) {
2039 if (appData.icsActive) {
2040 /* We now wait until we see "login:" from the ICS before
2041 sending the logon script (problems with timestamp otherwise) */
2042 /*ICSInitScript();*/
2043 if (appData.icsInputBox) ICSInputBoxPopUp();
2047 signal(SIGWINCH, TermSizeSigHandler);
2049 signal(SIGINT, IntSigHandler);
2050 signal(SIGTERM, IntSigHandler);
2051 if (*appData.cmailGameName != NULLCHAR) {
2052 signal(SIGUSR1, CmailSigHandler);
2056 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2058 // XtSetKeyboardFocus(shellWidget, formWidget);
2059 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2061 XtAppMainLoop(appContext);
2062 if (appData.debugMode) fclose(debugFP); // [DM] debug
2066 static Boolean noEcho;
2071 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2072 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2074 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2075 unlink(gameCopyFilename);
2076 unlink(gamePasteFilename);
2077 if(noEcho) EchoOn();
2081 TermSizeSigHandler (int sig)
2087 IntSigHandler (int sig)
2093 CmailSigHandler (int sig)
2098 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2100 /* Activate call-back function CmailSigHandlerCallBack() */
2101 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2103 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2107 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
2110 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2112 /**** end signal code ****/
2118 /* try to open the icsLogon script, either in the location given
2119 * or in the users HOME directory
2126 f = fopen(appData.icsLogon, "r");
2129 homedir = getenv("HOME");
2130 if (homedir != NULL)
2132 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2133 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2134 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2135 f = fopen(buf, "r");
2140 ProcessICSInitScript(f);
2142 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2155 #define Abs(n) ((n)<0 ? -(n) : (n))
2159 InsertPxlSize (char *pattern, int targetPxlSize)
2161 char *base_fnt_lst, strInt[12], *p, *q;
2162 int alternatives, i, len, strIntLen;
2165 * Replace the "*" (if present) in the pixel-size slot of each
2166 * alternative with the targetPxlSize.
2170 while ((p = strchr(p, ',')) != NULL) {
2174 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
2175 strIntLen = strlen(strInt);
2176 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
2180 while (alternatives--) {
2181 char *comma = strchr(p, ',');
2182 for (i=0; i<14; i++) {
2183 char *hyphen = strchr(p, '-');
2185 if (comma && hyphen > comma) break;
2186 len = hyphen + 1 - p;
2187 if (i == 7 && *p == '*' && len == 2) {
2189 memcpy(q, strInt, strIntLen);
2199 len = comma + 1 - p;
2206 return base_fnt_lst;
2210 CreateFontSet (char *base_fnt_lst)
2213 char **missing_list;
2217 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
2218 &missing_list, &missing_count, &def_string);
2219 if (appData.debugMode) {
2221 XFontStruct **font_struct_list;
2222 char **font_name_list;
2223 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
2225 fprintf(debugFP, " got list %s, locale %s\n",
2226 XBaseFontNameListOfFontSet(fntSet),
2227 XLocaleOfFontSet(fntSet));
2228 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
2229 for (i = 0; i < count; i++) {
2230 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
2233 for (i = 0; i < missing_count; i++) {
2234 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
2237 if (fntSet == NULL) {
2238 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
2243 #else // not ENABLE_NLS
2245 * Find a font that matches "pattern" that is as close as
2246 * possible to the targetPxlSize. Prefer fonts that are k
2247 * pixels smaller to fonts that are k pixels larger. The
2248 * pattern must be in the X Consortium standard format,
2249 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2250 * The return value should be freed with XtFree when no
2254 FindFont (char *pattern, int targetPxlSize)
2256 char **fonts, *p, *best, *scalable, *scalableTail;
2257 int i, j, nfonts, minerr, err, pxlSize;
2259 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2261 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2262 programName, pattern);
2269 for (i=0; i<nfonts; i++) {
2272 if (*p != '-') continue;
2274 if (*p == NULLCHAR) break;
2275 if (*p++ == '-') j++;
2277 if (j < 7) continue;
2280 scalable = fonts[i];
2283 err = pxlSize - targetPxlSize;
2284 if (Abs(err) < Abs(minerr) ||
2285 (minerr > 0 && err < 0 && -err == minerr)) {
2291 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2292 /* If the error is too big and there is a scalable font,
2293 use the scalable font. */
2294 int headlen = scalableTail - scalable;
2295 p = (char *) XtMalloc(strlen(scalable) + 10);
2296 while (isdigit(*scalableTail)) scalableTail++;
2297 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2299 p = (char *) XtMalloc(strlen(best) + 2);
2300 safeStrCpy(p, best, strlen(best)+1 );
2302 if (appData.debugMode) {
2303 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2304 pattern, targetPxlSize, p);
2306 XFreeFontNames(fonts);
2313 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
2314 // must be called before all non-first callse to CreateGCs()
2315 XtReleaseGC(shellWidget, highlineGC);
2316 XtReleaseGC(shellWidget, lightSquareGC);
2317 XtReleaseGC(shellWidget, darkSquareGC);
2318 XtReleaseGC(shellWidget, lineGC);
2319 if (appData.monoMode) {
2320 if (DefaultDepth(xDisplay, xScreen) == 1) {
2321 XtReleaseGC(shellWidget, wbPieceGC);
2323 XtReleaseGC(shellWidget, bwPieceGC);
2326 XtReleaseGC(shellWidget, prelineGC);
2327 XtReleaseGC(shellWidget, wdPieceGC);
2328 XtReleaseGC(shellWidget, wlPieceGC);
2329 XtReleaseGC(shellWidget, bdPieceGC);
2330 XtReleaseGC(shellWidget, blPieceGC);
2335 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
2337 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2338 | GCBackground | GCFunction | GCPlaneMask;
2339 gc_values->foreground = foreground;
2340 gc_values->background = background;
2341 return XtGetGC(shellWidget, value_mask, gc_values);
2345 CreateGCs (int redo)
2347 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2348 | GCBackground | GCFunction | GCPlaneMask;
2349 XGCValues gc_values;
2351 Pixel white = XWhitePixel(xDisplay, xScreen);
2352 Pixel black = XBlackPixel(xDisplay, xScreen);
2354 gc_values.plane_mask = AllPlanes;
2355 gc_values.line_width = lineGap;
2356 gc_values.line_style = LineSolid;
2357 gc_values.function = GXcopy;
2360 DeleteGCs(); // called a second time; clean up old GCs first
2361 } else { // [HGM] grid and font GCs created on first call only
2362 coordGC = CreateOneGC(&gc_values, black, white);
2363 XSetFont(xDisplay, coordGC, coordFontID);
2365 // [HGM] make font for holdings counts (white on black)
2366 countGC = CreateOneGC(&gc_values, white, black);
2367 XSetFont(xDisplay, countGC, countFontID);
2369 lineGC = CreateOneGC(&gc_values, black, black);
2371 if (appData.monoMode) {
2373 highlineGC = CreateOneGC(&gc_values, white, white);
2374 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
2375 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
2377 if (DefaultDepth(xDisplay, xScreen) == 1) {
2378 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2379 gc_values.function = GXcopyInverted;
2380 copyInvertedGC = CreateOneGC(&gc_values, black, white);
2381 gc_values.function = GXcopy;
2382 if (XBlackPixel(xDisplay, xScreen) == 1) {
2383 bwPieceGC = darkSquareGC;
2384 wbPieceGC = copyInvertedGC;
2386 bwPieceGC = copyInvertedGC;
2387 wbPieceGC = lightSquareGC;
2392 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
2393 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
2394 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
2395 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
2396 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
2397 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
2398 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
2399 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
2404 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
2412 fp = fopen(filename, "rb");
2414 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
2421 for (y=0; y<h; ++y) {
2422 for (x=0; x<h; ++x) {
2427 XPutPixel(xim, x, y, blackPieceColor);
2429 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2432 XPutPixel(xim, x, y, darkSquareColor);
2434 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2437 XPutPixel(xim, x, y, whitePieceColor);
2439 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2442 XPutPixel(xim, x, y, lightSquareColor);
2444 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2452 /* create Pixmap of piece */
2453 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2455 XPutImage(xDisplay, *dest, lightSquareGC, xim,
2458 /* create Pixmap of clipmask
2459 Note: We assume the white/black pieces have the same
2460 outline, so we make only 6 masks. This is okay
2461 since the XPM clipmask routines do the same. */
2463 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2465 XPutImage(xDisplay, temp, lightSquareGC, xmask,
2468 /* now create the 1-bit version */
2469 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2472 values.foreground = 1;
2473 values.background = 0;
2475 /* Don't use XtGetGC, not read only */
2476 maskGC = XCreateGC(xDisplay, *mask,
2477 GCForeground | GCBackground, &values);
2478 XCopyPlane(xDisplay, temp, *mask, maskGC,
2479 0, 0, squareSize, squareSize, 0, 0, 1);
2480 XFreePixmap(xDisplay, temp);
2485 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
2493 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
2498 /* The XSynchronize calls were copied from CreatePieces.
2499 Not sure if needed, but can't hurt */
2500 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2503 /* temp needed by loadXIM() */
2504 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2505 0, 0, ss, ss, AllPlanes, XYPixmap);
2507 if (strlen(appData.pixmapDirectory) == 0) {
2511 if (appData.monoMode) {
2512 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
2516 fprintf(stderr, _("\nLoading XIMs...\n"));
2518 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2519 fprintf(stderr, "%d", piece+1);
2520 for (kind=0; kind<4; kind++) {
2521 fprintf(stderr, ".");
2522 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2523 ExpandPathName(appData.pixmapDirectory),
2524 piece <= (int) WhiteKing ? "" : "w",
2525 pieceBitmapNames[piece],
2527 ximPieceBitmap[kind][piece] =
2528 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2529 0, 0, ss, ss, AllPlanes, XYPixmap);
2530 if (appData.debugMode)
2531 fprintf(stderr, _("(File:%s:) "), buf);
2532 loadXIM(ximPieceBitmap[kind][piece],
2534 &(xpmPieceBitmap2[kind][piece]),
2535 &(ximMaskPm2[piece]));
2536 if(piece <= (int)WhiteKing)
2537 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2539 fprintf(stderr," ");
2541 /* Load light and dark squares */
2542 /* If the LSQ and DSQ pieces don't exist, we will
2543 draw them with solid squares. */
2544 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2545 if (access(buf, 0) != 0) {
2549 fprintf(stderr, _("light square "));
2551 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2552 0, 0, ss, ss, AllPlanes, XYPixmap);
2553 if (appData.debugMode)
2554 fprintf(stderr, _("(File:%s:) "), buf);
2556 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2557 fprintf(stderr, _("dark square "));
2558 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2559 ExpandPathName(appData.pixmapDirectory), ss);
2560 if (appData.debugMode)
2561 fprintf(stderr, _("(File:%s:) "), buf);
2563 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2564 0, 0, ss, ss, AllPlanes, XYPixmap);
2565 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2566 xpmJailSquare = xpmLightSquare;
2568 fprintf(stderr, _("Done.\n"));
2570 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2573 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2577 CreateXPMBoard (char *s, int kind)
2581 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2582 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2583 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2589 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2590 // thisroutine has to be called t free the old piece pixmaps
2592 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2593 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2595 XFreePixmap(xDisplay, xpmLightSquare);
2596 XFreePixmap(xDisplay, xpmDarkSquare);
2605 u_int ss = squareSize;
2607 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2608 XpmColorSymbol symbols[4];
2609 static int redo = False;
2611 if(redo) FreeXPMPieces(); else redo = 1;
2613 /* The XSynchronize calls were copied from CreatePieces.
2614 Not sure if needed, but can't hurt */
2615 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2617 /* Setup translations so piece colors match square colors */
2618 symbols[0].name = "light_piece";
2619 symbols[0].value = appData.whitePieceColor;
2620 symbols[1].name = "dark_piece";
2621 symbols[1].value = appData.blackPieceColor;
2622 symbols[2].name = "light_square";
2623 symbols[2].value = appData.lightSquareColor;
2624 symbols[3].name = "dark_square";
2625 symbols[3].value = appData.darkSquareColor;
2627 attr.valuemask = XpmColorSymbols;
2628 attr.colorsymbols = symbols;
2629 attr.numsymbols = 4;
2631 if (appData.monoMode) {
2632 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2636 if (strlen(appData.pixmapDirectory) == 0) {
2637 XpmPieces* pieces = builtInXpms;
2640 while (pieces->size != squareSize && pieces->size) pieces++;
2641 if (!pieces->size) {
2642 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2645 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2646 for (kind=0; kind<4; kind++) {
2648 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2649 pieces->xpm[piece][kind],
2650 &(xpmPieceBitmap2[kind][piece]),
2651 NULL, &attr)) != 0) {
2652 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2656 if(piece <= (int) WhiteKing)
2657 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2661 xpmJailSquare = xpmLightSquare;
2665 fprintf(stderr, _("\nLoading XPMs...\n"));
2668 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2669 fprintf(stderr, "%d ", piece+1);
2670 for (kind=0; kind<4; kind++) {
2671 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2672 ExpandPathName(appData.pixmapDirectory),
2673 piece > (int) WhiteKing ? "w" : "",
2674 pieceBitmapNames[piece],
2676 if (appData.debugMode) {
2677 fprintf(stderr, _("(File:%s:) "), buf);
2679 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2680 &(xpmPieceBitmap2[kind][piece]),
2681 NULL, &attr)) != 0) {
2682 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2683 // [HGM] missing: read of unorthodox piece failed; substitute King.
2684 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2685 ExpandPathName(appData.pixmapDirectory),
2687 if (appData.debugMode) {
2688 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2690 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2691 &(xpmPieceBitmap2[kind][piece]),
2695 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2700 if(piece <= (int) WhiteKing)
2701 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2704 /* Load light and dark squares */
2705 /* If the LSQ and DSQ pieces don't exist, we will
2706 draw them with solid squares. */
2707 fprintf(stderr, _("light square "));
2708 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2709 if (access(buf, 0) != 0) {
2713 if (appData.debugMode)
2714 fprintf(stderr, _("(File:%s:) "), buf);
2716 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2717 &xpmLightSquare, NULL, &attr)) != 0) {
2718 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2721 fprintf(stderr, _("dark square "));
2722 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2723 ExpandPathName(appData.pixmapDirectory), ss);
2724 if (appData.debugMode) {
2725 fprintf(stderr, _("(File:%s:) "), buf);
2727 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2728 &xpmDarkSquare, NULL, &attr)) != 0) {
2729 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2733 xpmJailSquare = xpmLightSquare;
2734 fprintf(stderr, _("Done.\n"));
2736 oldVariant = -1; // kludge to force re-makig of animation masks
2737 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2740 #endif /* HAVE_LIBXPM */
2743 /* No built-in bitmaps */
2748 u_int ss = squareSize;
2750 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2753 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2754 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2755 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2756 pieceBitmapNames[piece],
2757 ss, kind == SOLID ? 's' : 'o');
2758 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2759 if(piece <= (int)WhiteKing)
2760 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2764 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2768 /* With built-in bitmaps */
2772 BuiltInBits* bib = builtInBits;
2775 u_int ss = squareSize;
2777 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2780 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2782 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2783 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2784 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2785 pieceBitmapNames[piece],
2786 ss, kind == SOLID ? 's' : 'o');
2787 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2788 bib->bits[kind][piece], ss, ss);
2789 if(piece <= (int)WhiteKing)
2790 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2794 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2800 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2805 char msg[MSG_SIZ], fullname[MSG_SIZ];
2807 if (*appData.bitmapDirectory != NULLCHAR) {
2808 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2809 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2810 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2811 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2812 &w, &h, pm, &x_hot, &y_hot);
2813 fprintf(stderr, "load %s\n", name);
2814 if (errcode != BitmapSuccess) {
2816 case BitmapOpenFailed:
2817 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2819 case BitmapFileInvalid:
2820 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2822 case BitmapNoMemory:
2823 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2827 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2831 fprintf(stderr, _("%s: %s...using built-in\n"),
2833 } else if (w != wreq || h != hreq) {
2835 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2836 programName, fullname, w, h, wreq, hreq);
2842 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2852 if (lineGap == 0) return;
2854 /* [HR] Split this into 2 loops for non-square boards. */
2856 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2857 gridSegments[i].x1 = 0;
2858 gridSegments[i].x2 =
2859 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2860 gridSegments[i].y1 = gridSegments[i].y2
2861 = lineGap / 2 + (i * (squareSize + lineGap));
2864 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2865 gridSegments[j + i].y1 = 0;
2866 gridSegments[j + i].y2 =
2867 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2868 gridSegments[j + i].x1 = gridSegments[j + i].x2
2869 = lineGap / 2 + (j * (squareSize + lineGap));
2873 int nrOfMenuItems = 7;
2874 Widget menuWidget[150];
2875 MenuListItem menuItemList[150] = {
2876 { "LoadNextGameProc", LoadNextGameProc },
2877 { "LoadPrevGameProc", LoadPrevGameProc },
2878 { "ReloadGameProc", ReloadGameProc },
2879 { "ReloadPositionProc", ReloadPositionProc },
2880 #ifndef OPTIONSDIALOG
2881 { "AlwaysQueenProc", AlwaysQueenProc },
2882 { "AnimateDraggingProc", AnimateDraggingProc },
2883 { "AnimateMovingProc", AnimateMovingProc },
2884 { "AutoflagProc", AutoflagProc },
2885 { "AutoflipProc", AutoflipProc },
2886 { "BlindfoldProc", BlindfoldProc },
2887 { "FlashMovesProc", FlashMovesProc },
2889 { "HighlightDraggingProc", HighlightDraggingProc },
2891 { "HighlightLastMoveProc", HighlightLastMoveProc },
2892 // { "IcsAlarmProc", IcsAlarmProc },
2893 { "MoveSoundProc", MoveSoundProc },
2894 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
2895 { "PopupExitMessageProc", PopupExitMessageProc },
2896 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
2897 // { "PremoveProc", PremoveProc },
2898 { "ShowCoordsProc", ShowCoordsProc },
2899 { "ShowThinkingProc", ShowThinkingProc },
2900 { "HideThinkingProc", HideThinkingProc },
2901 { "TestLegalityProc", TestLegalityProc },
2903 { "AboutGameProc", AboutGameEvent },
2904 { "DebugProc", DebugProc },
2905 { "NothingProc", NothingProc },
2910 MarkMenuItem (char *menuRef, int state)
2912 int nr = MenuToNumber(menuRef);
2915 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2916 XtSetValues(menuWidget[nr], args, 1);
2921 EnableMenuItem (char *menuRef, int state)
2923 int nr = MenuToNumber(menuRef);
2924 if(nr >= 0) XtSetSensitive(menuWidget[nr], state);
2928 EnableButtonBar (int state)
2930 XtSetSensitive(buttonBarWidget, state);
2935 SetMenuEnables (Enables *enab)
2937 while (enab->name != NULL) {
2938 EnableMenuItem(enab->name, enab->value);
2944 Equal(char *p, char *s)
2945 { // compare strings skipping spaces in second
2947 if(*s == ' ') { s++; continue; }
2948 if(*s++ != *p++) return 0;
2954 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2955 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2957 if(*nprms == 0) return;
2958 for(i=0; menuItemList[i].name; i++) {
2959 if(Equal(prms[0], menuItemList[i].name)) {
2960 (menuItemList[i].proc) ();
2967 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
2969 MenuProc *proc = (MenuProc *) addr;
2975 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2977 RecentEngineEvent((int) (intptr_t) addr);
2980 // some stuff that must remain in front-end
2981 static Widget mainBar, currentMenu;
2982 static int wtot, nr = 0, widths[10];
2985 AppendMenuItem (char *text, char *name, MenuProc *action)
2992 XtSetArg(args[j], XtNleftMargin, 20); j++;
2993 XtSetArg(args[j], XtNrightMargin, 20); j++;
2995 if (strcmp(text, "----") == 0) {
2996 entry = XtCreateManagedWidget(text, smeLineObjectClass,
2997 currentMenu, args, j);
2999 XtSetArg(args[j], XtNlabel, XtNewString(_(text)));
3000 entry = XtCreateManagedWidget(name, smeBSBObjectClass,
3001 currentMenu, args, j+1);
3002 XtAddCallback(entry, XtNcallback,
3003 (XtCallbackProc) (strcmp(name, "recent") ? MenuBarSelect : MenuEngineSelect),
3005 menuWidget[nrOfMenuItems] = entry;
3010 CreateMenuButton (char *name, Menu *mb)
3011 { // create menu button on main bar, and shell for pull-down list
3017 XtSetArg(args[j], XtNmenuName, XtNewString(name)); j++;
3018 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3019 XtSetArg(args[j], XtNborderWidth, 0); j++;
3020 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3022 currentMenu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3025 XtSetArg(args[j], XtNwidth, &w); j++;
3026 XtGetValues(mb->subMenu, args, j);
3027 wtot += mb->textWidth = widths[nr++] = w;
3031 CreateMenuBar (Menu *mb, int boardWidth)
3035 char menuName[MSG_SIZ];
3039 // create bar itself
3041 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3042 XtSetArg(args[j], XtNvSpace, 0); j++;
3043 XtSetArg(args[j], XtNborderWidth, 0); j++;
3044 mainBar = XtCreateWidget("menuBar", boxWidgetClass,
3045 formWidget, args, j);
3047 CreateMainMenus(mb); // put menus in bar according to description in back-end
3049 // size buttons to make menu bar fit, clipping menu names where necessary
3050 while(wtot > boardWidth - 40) {
3052 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
3056 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
3058 XtSetArg(args[j], XtNwidth, widths[i]); j++;
3059 XtSetValues(ma[i].subMenu, args, j);
3066 CreateButtonBar (MenuItem *mi)
3069 Widget button, buttonBar;
3073 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3075 XtSetArg(args[j], XtNhSpace, 0); j++;
3077 XtSetArg(args[j], XtNborderWidth, 0); j++;
3078 XtSetArg(args[j], XtNvSpace, 0); j++;
3079 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3080 formWidget, args, j);
3082 while (mi->string != NULL) {
3085 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3086 XtSetArg(args[j], XtNborderWidth, 0); j++;
3088 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3089 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3090 buttonBar, args, j);
3091 XtAddCallback(button, XtNcallback,
3092 (XtCallbackProc) MenuBarSelect,
3093 (caddr_t) mi->proc);
3100 CreatePieceMenu (char *name, int color)
3105 ChessSquare selection;
3107 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3108 boardWidget, args, 0);
3110 for (i = 0; i < PIECE_MENU_SIZE; i++) {
3111 String item = pieceMenuStrings[color][i];
3113 if (strcmp(item, "----") == 0) {
3114 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3117 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3118 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3120 selection = pieceMenuTranslation[color][i];
3121 XtAddCallback(entry, XtNcallback,
3122 (XtCallbackProc) PieceMenuSelect,
3123 (caddr_t) selection);
3124 if (selection == WhitePawn || selection == BlackPawn) {
3125 XtSetArg(args[0], XtNpopupOnEntry, entry);
3126 XtSetValues(menu, args, 1);
3139 ChessSquare selection;
3141 whitePieceMenu = CreatePieceMenu("menuW", 0);
3142 blackPieceMenu = CreatePieceMenu("menuB", 1);
3144 if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
3145 XtRegisterGrabAction(PieceMenuPopup, True,
3146 (unsigned)(ButtonPressMask|ButtonReleaseMask),
3147 GrabModeAsync, GrabModeAsync);
3149 XtSetArg(args[0], XtNlabel, _("Drop"));
3150 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3151 boardWidget, args, 1);
3152 for (i = 0; i < DROP_MENU_SIZE; i++) {
3153 String item = dropMenuStrings[i];
3155 if (strcmp(item, "----") == 0) {
3156 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3159 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3160 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3162 selection = dropMenuTranslation[i];
3163 XtAddCallback(entry, XtNcallback,
3164 (XtCallbackProc) DropMenuSelect,
3165 (caddr_t) selection);
3179 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3180 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3181 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3182 dmEnables[i].piece);
3183 XtSetSensitive(entry, p != NULL || !appData.testLegality
3184 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3185 && !appData.icsActive));
3187 while (p && *p++ == dmEnables[i].piece) count++;
3188 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3190 XtSetArg(args[j], XtNlabel, label); j++;
3191 XtSetValues(entry, args, j);
3196 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
3198 String whichMenu; int menuNr = -2;
3199 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3200 if (event->type == ButtonRelease)
3201 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3202 else if (event->type == ButtonPress)
3203 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3205 case 0: whichMenu = params[0]; break;
3206 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3208 case -1: if (errorUp) ErrorPopDown();
3211 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3215 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3217 if (pmFromX < 0 || pmFromY < 0) return;
3218 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3222 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3224 if (pmFromX < 0 || pmFromY < 0) return;
3225 DropMenuEvent(piece, pmFromX, pmFromY);
3229 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3231 shiftKey = prms[0][0] & 1;
3236 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3238 shiftKey = prms[0][0] & 1;
3244 do_flash_delay (unsigned long msec)
3250 DrawBorder (int x, int y, int type)
3254 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
3256 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3257 squareSize+lineGap, squareSize+lineGap);
3261 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
3263 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
3264 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
3266 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
3267 if(textureW[kind] < W*squareSize)
3268 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
3270 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
3271 if(textureH[kind] < H*squareSize)
3272 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
3274 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
3279 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
3280 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
3282 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
3283 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
3284 squareSize, squareSize, x*fac, y*fac);
3286 if (useImages && useImageSqs) {
3290 pm = xpmLightSquare;
3295 case 2: /* neutral */
3297 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
3300 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3301 squareSize, squareSize, x*fac, y*fac);
3311 case 2: /* neutral */
3316 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
3321 I split out the routines to draw a piece so that I could
3322 make a generic flash routine.
3325 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3327 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3328 switch (square_color) {
3330 case 2: /* neutral */
3332 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3333 ? *pieceToOutline(piece)
3334 : *pieceToSolid(piece),
3335 dest, bwPieceGC, 0, 0,
3336 squareSize, squareSize, x, y);
3339 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3340 ? *pieceToSolid(piece)
3341 : *pieceToOutline(piece),
3342 dest, wbPieceGC, 0, 0,
3343 squareSize, squareSize, x, y);
3349 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3351 switch (square_color) {
3353 case 2: /* neutral */
3355 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3356 ? *pieceToOutline(piece)
3357 : *pieceToSolid(piece),
3358 dest, bwPieceGC, 0, 0,
3359 squareSize, squareSize, x, y, 1);
3362 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3363 ? *pieceToSolid(piece)
3364 : *pieceToOutline(piece),
3365 dest, wbPieceGC, 0, 0,
3366 squareSize, squareSize, x, y, 1);
3372 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3374 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
3375 switch (square_color) {
3377 XCopyPlane(xDisplay, *pieceToSolid(piece),
3378 dest, (int) piece < (int) BlackPawn
3379 ? wlPieceGC : blPieceGC, 0, 0,
3380 squareSize, squareSize, x, y, 1);
3383 XCopyPlane(xDisplay, *pieceToSolid(piece),
3384 dest, (int) piece < (int) BlackPawn
3385 ? wdPieceGC : bdPieceGC, 0, 0,
3386 squareSize, squareSize, x, y, 1);
3388 case 2: /* neutral */
3390 break; // should never contain pieces
3395 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3397 int kind, p = piece;
3399 switch (square_color) {
3401 case 2: /* neutral */
3403 if ((int)piece < (int) BlackPawn) {
3411 if ((int)piece < (int) BlackPawn) {
3419 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
3420 if(useTexture & square_color+1) {
3421 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
3422 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
3423 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
3424 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
3425 XSetClipMask(xDisplay, wlPieceGC, None);
3426 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
3428 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3429 dest, wlPieceGC, 0, 0,
3430 squareSize, squareSize, x, y);
3433 typedef void (*DrawFunc)();
3438 if (appData.monoMode) {
3439 if (DefaultDepth(xDisplay, xScreen) == 1) {
3440 return monoDrawPiece_1bit;
3442 return monoDrawPiece;
3446 return colorDrawPieceImage;
3448 return colorDrawPiece;
3453 DrawDot (int marker, int x, int y, int r)
3455 if(appData.monoMode) {
3456 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
3457 x, y, r, r, 0, 64*360);
3458 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
3459 x, y, r, r, 0, 64*360);
3461 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
3462 x, y, r, r, 0, 64*360);
3466 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
3467 { // basic front-end board-draw function: takes care of everything that can be in square:
3468 // piece, background, coordinate/count, marker dot
3469 int direction, font_ascent, font_descent;
3470 XCharStruct overall;
3473 if (piece == EmptySquare) {
3474 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
3476 drawfunc = ChooseDrawFunc();
3477 drawfunc(piece, square_color, x, y, xBoardWindow);
3480 if(align) { // square carries inscription (coord or piece count)
3482 GC hGC = align < 3 ? coordGC : countGC;
3483 // first calculate where it goes
3484 XTextExtents(countFontStruct, string, 1, &direction,
3485 &font_ascent, &font_descent, &overall);
3487 xx += squareSize - overall.width - 2;
3488 yy += squareSize - font_descent - 1;
3489 } else if (align == 2) {
3490 xx += 2, yy += font_ascent + 1;
3491 } else if (align == 3) {
3492 xx += squareSize - overall.width - 2;
3493 yy += font_ascent + 1;
3494 } else if (align == 4) {
3495 xx += 2, yy += font_ascent + 1;
3498 if (appData.monoMode) {
3499 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3501 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3505 if(marker) { // print fat marker dot, if requested
3506 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
3511 FlashDelay (int flash_delay)
3513 XSync(xDisplay, False);
3514 if(flash_delay) do_flash_delay(flash_delay);
3518 Fraction (int x, int start, int stop)
3520 double f = ((double) x - start)/(stop - start);
3521 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
3525 static WindowPlacement wpNew;
3528 CoDrag (Widget sh, WindowPlacement *wp)
3531 int j=0, touch=0, fudge = 2;
3532 GetActualPlacement(sh, wp);
3533 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
3534 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
3535 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
3536 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
3537 if(!touch ) return; // only windows that touch co-move
3538 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
3539 int heightInc = wpNew.height - wpMain.height;
3540 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3541 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3542 wp->y += fracTop * heightInc;
3543 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
3544 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
3545 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
3546 int widthInc = wpNew.width - wpMain.width;
3547 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3548 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3549 wp->y += fracLeft * widthInc;
3550 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
3551 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
3553 wp->x += wpNew.x - wpMain.x;
3554 wp->y += wpNew.y - wpMain.y;
3555 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
3556 if(touch == 3) wp->y += wpNew.height - wpMain.height;
3557 XtSetArg(args[j], XtNx, wp->x); j++;
3558 XtSetArg(args[j], XtNy, wp->y); j++;
3559 XtSetValues(sh, args, j);
3562 static XtIntervalId delayedDragID = 0;
3567 GetActualPlacement(shellWidget, &wpNew);
3568 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
3569 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
3570 return; // false alarm
3571 if(EngineOutputIsUp()) CoDrag(engineOutputShell, &wpEngineOutput);
3572 if(MoveHistoryIsUp()) CoDrag(shells[HistoryDlg], &wpMoveHistory);
3573 if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
3574 if(GameListIsUp()) CoDrag(gameListShell, &wpGameList);
3576 DrawPosition(True, NULL);
3577 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
3584 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
3586 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
3589 /* Why is this needed on some versions of X? */
3591 EventProc (Widget widget, caddr_t unused, XEvent *event)
3593 if (!XtIsRealized(widget))
3595 switch (event->type) {
3596 case ConfigureNotify: // main window is being dragged: drag attached windows with it
3597 if(appData.useStickyWindows)
3598 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
3601 if (event->xexpose.count > 0) return; /* no clipping is done */
3602 DrawPosition(True, NULL);
3603 if(twoBoards) { // [HGM] dual: draw other board in other orientation
3604 flipView = !flipView; partnerUp = !partnerUp;
3605 DrawPosition(True, NULL);
3606 flipView = !flipView; partnerUp = !partnerUp;
3610 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
3617 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
3619 DrawSeekAxis (int x, int y, int xTo, int yTo)
3621 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
3625 DrawSeekBackground (int left, int top, int right, int bottom)
3627 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
3631 DrawSeekText (char *buf, int x, int y)
3633 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
3637 DrawSeekDot (int x, int y, int colorNr)
3639 int square = colorNr & 0x80;
3642 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
3644 XFillRectangle(xDisplay, xBoardWindow, color,
3645 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3647 XFillArc(xDisplay, xBoardWindow, color,
3648 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
3652 DrawGrid (int second)
3654 XDrawSegments(xDisplay, xBoardWindow, lineGC,
3655 second ? secondSegments : // [HGM] dual
3656 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
3661 * event handler for redrawing the board
3664 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3666 DrawPosition(True, NULL);
3671 * event handler for parsing user moves
3673 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
3674 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
3675 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
3676 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
3677 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
3678 // and at the end FinishMove() to perform the move after optional promotion popups.
3679 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
3681 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3683 if (w != boardWidget || errorExitStatus != -1) return;
3684 if(nprms) shiftKey = !strcmp(prms[0], "1");
3687 if (event->type == ButtonPress) {
3688 XtPopdown(promotionShell);
3689 XtDestroyWidget(promotionShell);
3690 promotionUp = False;
3698 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
3699 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
3700 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
3704 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
3706 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
3707 DragPieceMove(event->xmotion.x, event->xmotion.y);
3711 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3712 { // [HGM] pv: walk PV
3713 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3716 static int savedIndex; /* gross that this is global */
3719 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3722 XawTextPosition index, dummy;
3725 XawTextGetSelectionPos(w, &index, &dummy);
3726 XtSetArg(arg, XtNstring, &val);
3727 XtGetValues(w, &arg, 1);
3728 ReplaceComment(savedIndex, val);
3729 if(savedIndex != currentMove) ToNrEvent(savedIndex);
3730 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3734 EditCommentPopUp (int index, char *title, char *text)
3737 if (text == NULL) text = "";
3738 NewCommentPopup(title, text, index);
3742 CommentPopUp (char *title, char *text)
3744 savedIndex = currentMove; // [HGM] vari
3745 NewCommentPopup(title, text, currentMove);
3751 PopDown(CommentDlg);
3754 static char *openName;
3760 (void) (*fileProc)(openFP, 0, openName);
3764 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
3766 fileProc = proc; /* I can't see a way not */
3767 fileOpenMode = openMode; /* to use globals here */
3768 { // [HGM] use file-selector dialog stolen from Ghostview
3769 int index; // this is not supported yet
3770 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
3771 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
3772 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
3773 ScheduleDelayedEvent(&DelayedLoad, 50);
3781 Widget dialog, layout;
3783 Dimension bw_width, pw_width;
3785 char *PromoChars = "wglcqrbnkac+=\0";
3788 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3789 XtGetValues(boardWidget, args, j);
3792 XtSetArg(args[j], XtNresizable, True); j++;
3793 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
3795 XtCreatePopupShell("Promotion", transientShellWidgetClass,
3796 shellWidget, args, j);
3798 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
3799 layoutArgs, XtNumber(layoutArgs));
3802 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
3803 XtSetArg(args[j], XtNborderWidth, 0); j++;
3804 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
3807 if(gameInfo.variant != VariantShogi) {
3808 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
3809 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
3810 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
3811 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
3812 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
3814 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
3815 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
3816 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
3817 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
3819 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
3820 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
3821 gameInfo.variant == VariantGiveaway) {
3822 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
3824 if(gameInfo.variant == VariantCapablanca ||
3825 gameInfo.variant == VariantGothic ||
3826 gameInfo.variant == VariantCapaRandom) {
3827 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
3828 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
3830 } else // [HGM] shogi
3832 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
3833 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
3835 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
3837 XtRealizeWidget(promotionShell);
3838 CatchDeleteWindow(promotionShell, "PromotionPopDown");
3841 XtSetArg(args[j], XtNwidth, &pw_width); j++;
3842 XtGetValues(promotionShell, args, j);
3844 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3845 lineGap + squareSize/3 +
3846 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
3847 0 : 6*(squareSize + lineGap)), &x, &y);
3850 XtSetArg(args[j], XtNx, x); j++;
3851 XtSetArg(args[j], XtNy, y); j++;
3852 XtSetValues(promotionShell, args, j);
3854 XtPopup(promotionShell, XtGrabNone);
3862 if (!promotionUp) return;
3863 XtPopdown(promotionShell);
3864 XtDestroyWidget(promotionShell);
3865 promotionUp = False;
3869 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
3871 int promoChar = * (const char *) client_data;
3875 if (fromX == -1) return;
3882 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
3884 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
3885 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3891 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
3893 dialogError = errorUp = False;
3894 XtPopdown(w = XtParent(XtParent(XtParent(w))));
3896 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3903 if (!errorUp) return;
3904 dialogError = errorUp = False;
3905 XtPopdown(errorShell);
3906 XtDestroyWidget(errorShell);
3907 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3911 ErrorPopUp (char *title, char *label, int modal)
3914 Widget dialog, layout;
3918 Dimension bw_width, pw_width;
3919 Dimension pw_height;
3923 XtSetArg(args[i], XtNresizable, True); i++;
3924 XtSetArg(args[i], XtNtitle, title); i++;
3926 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
3927 shellUp[TransientDlg] ? (dialogError = modal = TRUE, shells[TransientDlg]) : shellWidget, args, i);
3929 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
3930 layoutArgs, XtNumber(layoutArgs));
3933 XtSetArg(args[i], XtNlabel, label); i++;
3934 XtSetArg(args[i], XtNborderWidth, 0); i++;
3935 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
3938 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
3940 XtRealizeWidget(errorShell);
3941 CatchDeleteWindow(errorShell, "ErrorPopDown");
3944 XtSetArg(args[i], XtNwidth, &bw_width); i++;
3945 XtGetValues(boardWidget, args, i);
3947 XtSetArg(args[i], XtNwidth, &pw_width); i++;
3948 XtSetArg(args[i], XtNheight, &pw_height); i++;
3949 XtGetValues(errorShell, args, i);
3952 /* This code seems to tickle an X bug if it is executed too soon
3953 after xboard starts up. The coordinates get transformed as if
3954 the main window was positioned at (0, 0).
3956 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3957 0 - pw_height + squareSize / 3, &x, &y);
3959 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
3960 RootWindowOfScreen(XtScreen(boardWidget)),
3961 (bw_width - pw_width) / 2,
3962 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
3966 if (y < 0) y = 0; /*avoid positioning top offscreen*/
3969 XtSetArg(args[i], XtNx, x); i++;
3970 XtSetArg(args[i], XtNy, y); i++;
3971 XtSetValues(errorShell, args, i);
3974 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
3977 /* Disable all user input other than deleting the window */
3978 static int frozen = 0;
3984 /* Grab by a widget that doesn't accept input */
3985 XtAddGrab(messageWidget, TRUE, FALSE);
3989 /* Undo a FreezeUI */
3993 if (!frozen) return;
3994 XtRemoveGrab(messageWidget);
4002 static int oldPausing = FALSE;
4003 static GameMode oldmode = (GameMode) -1;
4006 if (!boardWidget || !XtIsRealized(boardWidget)) return;
4008 if (pausing != oldPausing) {
4009 oldPausing = pausing;
4010 MarkMenuItem("Pause", pausing);
4012 if (appData.showButtonBar) {
4013 /* Always toggle, don't set. Previous code messes up when
4014 invoked while the button is pressed, as releasing it
4015 toggles the state again. */
4018 XtSetArg(args[0], XtNbackground, &oldbg);
4019 XtSetArg(args[1], XtNforeground, &oldfg);
4020 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
4022 XtSetArg(args[0], XtNbackground, oldfg);
4023 XtSetArg(args[1], XtNforeground, oldbg);
4025 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
4029 wname = ModeToWidgetName(oldmode);
4030 if (wname != NULL) {
4031 MarkMenuItem(wname, False);
4033 wname = ModeToWidgetName(gameMode);
4034 if (wname != NULL) {
4035 MarkMenuItem(wname, True);
4038 MarkMenuItem("Machine Match", matchMode && matchGame < appData.matchGames);
4040 /* Maybe all the enables should be handled here, not just this one */
4041 EnableMenuItem("Training", gameMode == Training || gameMode == PlayFromGameFile);
4046 * Button/menu procedures
4049 LoadGamePopUp (FILE *f, int gameNumber, char *title)
4051 cmailMsgLoaded = FALSE;
4052 if (gameNumber == 0) {
4053 int error = GameListBuild(f);
4055 DisplayError(_("Cannot build game list"), error);
4056 } else if (!ListEmpty(&gameList) &&
4057 ((ListGame *) gameList.tailPred)->number > 1) {
4058 GameListPopUp(f, title);
4064 return LoadGame(f, gameNumber, title, FALSE);
4067 /* this variable is shared between CopyPositionProc and SendPositionSelection */
4068 char *selected_fen_position=NULL;
4071 SendPositionSelection (Widget w, Atom *selection, Atom *target,
4072 Atom *type_return, XtPointer *value_return,
4073 unsigned long *length_return, int *format_return)
4075 char *selection_tmp;
4077 // if (!selected_fen_position) return False; /* should never happen */
4078 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4079 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
4080 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
4083 if (f == NULL) return False;
4087 selection_tmp = XtMalloc(len + 1);
4088 count = fread(selection_tmp, 1, len, f);
4091 XtFree(selection_tmp);
4094 selection_tmp[len] = NULLCHAR;
4096 /* note: since no XtSelectionDoneProc was registered, Xt will
4097 * automatically call XtFree on the value returned. So have to
4098 * make a copy of it allocated with XtMalloc */
4099 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
4100 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
4103 *value_return=selection_tmp;
4104 *length_return=strlen(selection_tmp);
4105 *type_return=*target;
4106 *format_return = 8; /* bits per byte */
4108 } else if (*target == XA_TARGETS(xDisplay)) {
4109 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4110 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4111 targets_tmp[1] = XA_STRING;
4112 *value_return = targets_tmp;
4113 *type_return = XA_ATOM;
4116 // This code leads to a read of value_return out of bounds on 64-bit systems.
4117 // Other code which I have seen always sets *format_return to 32 independent of
4118 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
4119 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
4120 *format_return = 8 * sizeof(Atom);
4121 if (*format_return > 32) {
4122 *length_return *= *format_return / 32;
4123 *format_return = 32;
4126 *format_return = 32;
4134 /* note: when called from menu all parameters are NULL, so no clue what the
4135 * Widget which was clicked on was, or what the click event was
4138 CopySomething (char *src)
4140 selected_fen_position = src;
4142 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4143 * have a notion of a position that is selected but not copied.
4144 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4146 XtOwnSelection(menuBarWidget, XA_PRIMARY,
4148 SendPositionSelection,
4149 NULL/* lose_ownership_proc */ ,
4150 NULL/* transfer_done_proc */);
4151 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4153 SendPositionSelection,
4154 NULL/* lose_ownership_proc */ ,
4155 NULL/* transfer_done_proc */);
4158 /* function called when the data to Paste is ready */
4160 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
4161 Atom *type, XtPointer value, unsigned long *len, int *format)
4164 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
4165 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
4166 EditPositionPasteFEN(fenstr);
4170 /* called when Paste Position button is pressed,
4171 * all parameters will be NULL */
4173 PastePositionProc ()
4175 XtGetSelectionValue(menuBarWidget,
4176 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4177 /* (XtSelectionCallbackProc) */ PastePositionCB,
4178 NULL, /* client_data passed to PastePositionCB */
4180 /* better to use the time field from the event that triggered the
4181 * call to this function, but that isn't trivial to get
4188 /* note: when called from menu all parameters are NULL, so no clue what the
4189 * Widget which was clicked on was, or what the click event was
4191 /* function called when the data to Paste is ready */
4193 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
4194 Atom *type, XtPointer value, unsigned long *len, int *format)
4197 if (value == NULL || *len == 0) {
4198 return; /* nothing had been selected to copy */
4200 f = fopen(gamePasteFilename, "w");
4202 DisplayError(_("Can't open temp file"), errno);
4205 fwrite(value, 1, *len, f);
4208 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
4211 /* called when Paste Game button is pressed,
4212 * all parameters will be NULL */
4216 XtGetSelectionValue(menuBarWidget,
4217 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4218 /* (XtSelectionCallbackProc) */ PasteGameCB,
4219 NULL, /* client_data passed to PasteGameCB */
4221 /* better to use the time field from the event that triggered the
4222 * call to this function, but that isn't trivial to get
4231 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4237 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
4239 char buf[10], keys[32];
4241 KeyCode metaL, metaR; //, ctrlL, ctrlR;
4242 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
4243 XQueryKeymap(xDisplay,keys);
4244 metaL = XKeysymToKeycode(xDisplay, XK_Meta_L);
4245 metaR = XKeysymToKeycode(xDisplay, XK_Meta_R);
4246 // ctrlL = XKeysymToKeycode(xDisplay, XK_Control_L);
4247 // ctrlR = XKeysymToKeycode(xDisplay, XK_Control_R);
4248 if ( n == 1 && *buf >= 32 // printable
4249 && !(keys[metaL>>3]&1<<(metaL&7)) && !(keys[metaR>>3]&1<<(metaR&7)) // no alt key pressed
4250 // && !(keys[ctrlL>>3]&1<<(ctrlL&7)) && !(keys[ctrlR>>3]&1<<(ctrlR&7)) // no ctrl key pressed
4251 ) BoxAutoPopUp (buf);
4255 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4256 { // [HGM] input: let up-arrow recall previous line from history
4261 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4262 { // [HGM] input: let down-arrow recall next line from history
4267 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4273 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4275 if (!TempBackwardActive) {
4276 TempBackwardActive = True;
4282 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4284 /* Check to see if triggered by a key release event for a repeating key.
4285 * If so the next queued event will be a key press of the same key at the same time */
4286 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
4288 XPeekEvent(xDisplay, &next);
4289 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
4290 next.xkey.keycode == event->xkey.keycode)
4294 TempBackwardActive = False;
4298 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4299 { // called as key binding
4302 if (nprms && *nprms > 0)
4306 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
4311 DisplayMessage (char *message, char *extMessage)
4313 /* display a message in the message widget */
4322 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
4327 message = extMessage;
4331 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
4333 /* need to test if messageWidget already exists, since this function
4334 can also be called during the startup, if for example a Xresource
4335 is not set up correctly */
4338 XtSetArg(arg, XtNlabel, message);
4339 XtSetValues(messageWidget, &arg, 1);
4346 SetWindowTitle (char *text, char *title, char *icon)
4350 if (appData.titleInWindow) {
4352 XtSetArg(args[i], XtNlabel, text); i++;
4353 XtSetValues(titleWidget, args, i);
4356 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
4357 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
4358 XtSetValues(shellWidget, args, i);
4359 XSync(xDisplay, False);
4364 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
4370 DisplayIcsInteractionTitle (String message)
4372 if (oldICSInteractionTitle == NULL) {
4373 /* Magic to find the old window title, adapted from vim */
4374 char *wina = getenv("WINDOWID");
4376 Window win = (Window) atoi(wina);
4377 Window root, parent, *children;
4378 unsigned int nchildren;
4379 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4381 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4382 if (!XQueryTree(xDisplay, win, &root, &parent,
4383 &children, &nchildren)) break;
4384 if (children) XFree((void *)children);
4385 if (parent == root || parent == 0) break;
4388 XSetErrorHandler(oldHandler);
4390 if (oldICSInteractionTitle == NULL) {
4391 oldICSInteractionTitle = "xterm";
4394 printf("\033]0;%s\007", message);
4398 char pendingReplyPrefix[MSG_SIZ];
4399 ProcRef pendingReplyPR;
4402 AskQuestionPopDown ()
4404 if (!askQuestionUp) return;
4405 XtPopdown(askQuestionShell);
4406 XtDestroyWidget(askQuestionShell);
4407 askQuestionUp = False;
4411 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4417 reply = XawDialogGetValueString(w = XtParent(w));
4418 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
4419 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
4420 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
4421 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
4422 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
4423 AskQuestionPopDown();
4425 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
4429 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
4434 XtSetArg(args[0], XtNlabel, &name);
4435 XtGetValues(w, args, 1);
4437 if (strcmp(name, _("cancel")) == 0) {
4438 AskQuestionPopDown();
4440 AskQuestionReplyAction(w, NULL, NULL, NULL);
4445 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
4448 Widget popup, layout, dialog, edit;
4454 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
4455 pendingReplyPR = pr;
4458 XtSetArg(args[i], XtNresizable, True); i++;
4459 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
4460 askQuestionShell = popup =
4461 XtCreatePopupShell(title, transientShellWidgetClass,
4462 shellWidget, args, i);
4465 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
4466 layoutArgs, XtNumber(layoutArgs));
4469 XtSetArg(args[i], XtNlabel, question); i++;
4470 XtSetArg(args[i], XtNvalue, ""); i++;
4471 XtSetArg(args[i], XtNborderWidth, 0); i++;
4472 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
4475 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
4476 (XtPointer) dialog);
4477 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
4478 (XtPointer) dialog);
4480 XtRealizeWidget(popup);
4481 CatchDeleteWindow(popup, "AskQuestionPopDown");
4483 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
4484 &x, &y, &win_x, &win_y, &mask);
4486 XtSetArg(args[0], XtNx, x - 10);
4487 XtSetArg(args[1], XtNy, y - 30);
4488 XtSetValues(popup, args, 2);
4490 XtPopup(popup, XtGrabExclusive);
4491 askQuestionUp = True;
4493 edit = XtNameToWidget(dialog, "*value");
4494 XtSetKeyboardFocus(popup, edit);
4499 PlaySound (char *name)
4501 if (*name == NULLCHAR) {
4503 } else if (strcmp(name, "$") == 0) {
4504 putc(BELLCHAR, stderr);
4507 char *prefix = "", *sep = "";
4508 if(appData.soundProgram[0] == NULLCHAR) return;
4509 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
4510 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
4518 PlaySound(appData.soundMove);
4524 PlaySound(appData.soundIcsWin);
4530 PlaySound(appData.soundIcsLoss);
4536 PlaySound(appData.soundIcsDraw);
4540 PlayIcsUnfinishedSound ()
4542 PlaySound(appData.soundIcsUnfinished);
4548 PlaySound(appData.soundIcsAlarm);
4554 PlaySound(appData.soundTell);
4560 system("stty echo");
4567 system("stty -echo");
4572 RunCommand (char *buf)
4578 Colorize (ColorClass cc, int continuation)
4581 int count, outCount, error;
4583 if (textColors[(int)cc].bg > 0) {
4584 if (textColors[(int)cc].fg > 0) {
4585 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
4586 textColors[(int)cc].fg, textColors[(int)cc].bg);
4588 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
4589 textColors[(int)cc].bg);
4592 if (textColors[(int)cc].fg > 0) {
4593 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
4594 textColors[(int)cc].fg);
4596 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
4599 count = strlen(buf);
4600 outCount = OutputToProcess(NoProc, buf, count, &error);
4601 if (outCount < count) {
4602 DisplayFatalError(_("Error writing to display"), error, 1);
4605 if (continuation) return;
4608 PlaySound(appData.soundShout);
4611 PlaySound(appData.soundSShout);
4614 PlaySound(appData.soundChannel1);
4617 PlaySound(appData.soundChannel);
4620 PlaySound(appData.soundKibitz);
4623 PlaySound(appData.soundTell);
4625 case ColorChallenge:
4626 PlaySound(appData.soundChallenge);
4629 PlaySound(appData.soundRequest);
4632 PlaySound(appData.soundSeek);
4644 return getpwuid(getuid())->pw_name;
4648 ExpandPathName (char *path)
4650 static char static_buf[4*MSG_SIZ];
4651 char *d, *s, buf[4*MSG_SIZ];
4657 while (*s && isspace(*s))
4666 if (*(s+1) == '/') {
4667 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
4671 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
4672 { char *p; if(p = strchr(buf, '/')) *p = 0; }
4673 pwd = getpwnam(buf);
4676 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
4680 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
4681 strcat(d, strchr(s+1, '/'));
4685 safeStrCpy(d, s, 4*MSG_SIZ );
4693 static char host_name[MSG_SIZ];
4695 #if HAVE_GETHOSTNAME
4696 gethostname(host_name, MSG_SIZ);
4698 #else /* not HAVE_GETHOSTNAME */
4699 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
4700 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
4702 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4704 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4705 #endif /* not HAVE_GETHOSTNAME */
4708 XtIntervalId delayedEventTimerXID = 0;
4709 DelayedEventCallback delayedEventCallback = 0;
4714 delayedEventTimerXID = 0;
4715 delayedEventCallback();
4719 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
4721 if(delayedEventTimerXID && delayedEventCallback == cb)
4722 // [HGM] alive: replace, rather than add or flush identical event
4723 XtRemoveTimeOut(delayedEventTimerXID);
4724 delayedEventCallback = cb;
4725 delayedEventTimerXID =
4726 XtAppAddTimeOut(appContext, millisec,
4727 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
4730 DelayedEventCallback
4733 if (delayedEventTimerXID) {
4734 return delayedEventCallback;
4741 CancelDelayedEvent ()
4743 if (delayedEventTimerXID) {
4744 XtRemoveTimeOut(delayedEventTimerXID);
4745 delayedEventTimerXID = 0;
4749 XtIntervalId loadGameTimerXID = 0;
4752 LoadGameTimerRunning ()
4754 return loadGameTimerXID != 0;
4758 StopLoadGameTimer ()
4760 if (loadGameTimerXID != 0) {
4761 XtRemoveTimeOut(loadGameTimerXID);
4762 loadGameTimerXID = 0;
4770 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
4772 loadGameTimerXID = 0;
4777 StartLoadGameTimer (long millisec)
4780 XtAppAddTimeOut(appContext, millisec,
4781 (XtTimerCallbackProc) LoadGameTimerCallback,
4785 XtIntervalId analysisClockXID = 0;
4788 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
4790 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
4791 || appData.icsEngineAnalyze) { // [DM]
4792 AnalysisPeriodicEvent(0);
4793 StartAnalysisClock();
4798 StartAnalysisClock ()
4801 XtAppAddTimeOut(appContext, 2000,
4802 (XtTimerCallbackProc) AnalysisClockCallback,
4806 XtIntervalId clockTimerXID = 0;
4809 ClockTimerRunning ()
4811 return clockTimerXID != 0;
4817 if (clockTimerXID != 0) {
4818 XtRemoveTimeOut(clockTimerXID);
4827 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
4834 StartClockTimer (long millisec)
4837 XtAppAddTimeOut(appContext, millisec,
4838 (XtTimerCallbackProc) ClockTimerCallback,
4843 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
4848 /* check for low time warning */
4849 Pixel foregroundOrWarningColor = timerForegroundPixel;
4852 appData.lowTimeWarning &&
4853 (timer / 1000) < appData.icsAlarmTime)
4854 foregroundOrWarningColor = lowTimeWarningColor;
4856 if (appData.clockMode) {
4857 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
4858 XtSetArg(args[0], XtNlabel, buf);
4860 snprintf(buf, MSG_SIZ, "%s ", color);
4861 XtSetArg(args[0], XtNlabel, buf);
4866 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
4867 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
4869 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
4870 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
4873 XtSetValues(w, args, 3);
4877 DisplayWhiteClock (long timeRemaining, int highlight)
4881 if(appData.noGUI) return;
4882 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
4883 if (highlight && iconPixmap == bIconPixmap) {
4884 iconPixmap = wIconPixmap;
4885 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
4886 XtSetValues(shellWidget, args, 1);
4891 DisplayBlackClock (long timeRemaining, int highlight)
4895 if(appData.noGUI) return;
4896 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
4897 if (highlight && iconPixmap == wIconPixmap) {
4898 iconPixmap = bIconPixmap;
4899 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
4900 XtSetValues(shellWidget, args, 1);
4919 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
4923 int to_prog[2], from_prog[2];
4927 if (appData.debugMode) {
4928 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
4931 /* We do NOT feed the cmdLine to the shell; we just
4932 parse it into blank-separated arguments in the
4933 most simple-minded way possible.
4936 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
4939 while(*p == ' ') p++;
4941 if(*p == '"' || *p == '\'')
4942 p = strchr(++argv[i-1], *p);
4943 else p = strchr(p, ' ');
4944 if (p == NULL) break;
4949 SetUpChildIO(to_prog, from_prog);
4951 if ((pid = fork()) == 0) {
4953 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
4954 close(to_prog[1]); // first close the unused pipe ends
4955 close(from_prog[0]);
4956 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
4957 dup2(from_prog[1], 1);
4958 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
4959 close(from_prog[1]); // and closing again loses one of the pipes!
4960 if(fileno(stderr) >= 2) // better safe than sorry...
4961 dup2(1, fileno(stderr)); /* force stderr to the pipe */
4963 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
4968 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
4970 execvp(argv[0], argv);
4972 /* If we get here, exec failed */
4977 /* Parent process */
4979 close(from_prog[1]);
4981 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
4984 cp->fdFrom = from_prog[0];
4985 cp->fdTo = to_prog[1];
4990 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
4992 AlarmCallBack (int n)
4998 DestroyChildProcess (ProcRef pr, int signalType)
5000 ChildProc *cp = (ChildProc *) pr;
5002 if (cp->kind != CPReal) return;
5004 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
5005 signal(SIGALRM, AlarmCallBack);
5007 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
5008 kill(cp->pid, SIGKILL); // kill it forcefully
5009 wait((int *) 0); // and wait again
5013 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
5015 /* Process is exiting either because of the kill or because of
5016 a quit command sent by the backend; either way, wait for it to die.
5025 InterruptChildProcess (ProcRef pr)
5027 ChildProc *cp = (ChildProc *) pr;
5029 if (cp->kind != CPReal) return;
5030 (void) kill(cp->pid, SIGINT); /* stop it thinking */
5034 OpenTelnet (char *host, char *port, ProcRef *pr)
5036 char cmdLine[MSG_SIZ];
5038 if (port[0] == NULLCHAR) {
5039 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
5041 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
5043 return StartChildProcess(cmdLine, "", pr);
5047 OpenTCP (char *host, char *port, ProcRef *pr)
5050 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
5051 #else /* !OMIT_SOCKETS */
5052 struct addrinfo hints;
5053 struct addrinfo *ais, *ai;
5058 memset(&hints, 0, sizeof(hints));
5059 hints.ai_family = AF_UNSPEC;
5060 hints.ai_socktype = SOCK_STREAM;
5062 error = getaddrinfo(host, port, &hints, &ais);
5064 /* a getaddrinfo error is not an errno, so can't return it */
5065 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
5066 host, port, gai_strerror(error));
5070 for (ai = ais; ai != NULL; ai = ai->ai_next) {
5071 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
5075 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
5088 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5094 #endif /* !OMIT_SOCKETS */
5100 OpenCommPort (char *name, ProcRef *pr)
5105 fd = open(name, 2, 0);
5106 if (fd < 0) return errno;
5108 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5119 OpenLoopback (ProcRef *pr)
5124 SetUpChildIO(to, from);
5126 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5129 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
5137 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
5139 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
5143 #define INPUT_SOURCE_BUF_SIZE 8192
5152 char buf[INPUT_SOURCE_BUF_SIZE];
5157 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
5159 InputSource *is = (InputSource *) closure;
5164 if (is->lineByLine) {
5165 count = read(is->fd, is->unused,
5166 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
5168 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
5171 is->unused += count;
5173 while (p < is->unused) {
5174 q = memchr(p, '\n', is->unused - p);
5175 if (q == NULL) break;
5177 (is->func)(is, is->closure, p, q - p, 0);
5181 while (p < is->unused) {
5186 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
5191 (is->func)(is, is->closure, is->buf, count, error);
5196 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
5199 ChildProc *cp = (ChildProc *) pr;
5201 is = (InputSource *) calloc(1, sizeof(InputSource));
5202 is->lineByLine = lineByLine;
5206 is->fd = fileno(stdin);
5208 is->kind = cp->kind;
5209 is->fd = cp->fdFrom;
5212 is->unused = is->buf;
5215 is->xid = XtAppAddInput(appContext, is->fd,
5216 (XtPointer) (XtInputReadMask),
5217 (XtInputCallbackProc) DoInputCallback,
5219 is->closure = closure;
5220 return (InputSourceRef) is;
5224 RemoveInputSource (InputSourceRef isr)
5226 InputSource *is = (InputSource *) isr;
5228 if (is->xid == 0) return;
5229 XtRemoveInput(is->xid);
5234 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
5236 static int line = 0;
5237 ChildProc *cp = (ChildProc *) pr;
5242 if (appData.noJoin || !appData.useInternalWrap)
5243 outCount = fwrite(message, 1, count, stdout);
5246 int width = get_term_width();
5247 int len = wrap(NULL, message, count, width, &line);
5248 char *msg = malloc(len);
5252 outCount = fwrite(message, 1, count, stdout);
5255 dbgchk = wrap(msg, message, count, width, &line);
5256 if (dbgchk != len && appData.debugMode)
5257 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
5258 outCount = fwrite(msg, 1, dbgchk, stdout);
5264 outCount = write(cp->fdTo, message, count);
5274 /* Output message to process, with "ms" milliseconds of delay
5275 between each character. This is needed when sending the logon
5276 script to ICC, which for some reason doesn't like the
5277 instantaneous send. */
5279 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
5281 ChildProc *cp = (ChildProc *) pr;
5286 r = write(cp->fdTo, message++, 1);
5299 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
5301 /* Masks for XPM pieces. Black and white pieces can have
5302 different shapes, but in the interest of retaining my
5303 sanity pieces must have the same outline on both light
5304 and dark squares, and all pieces must use the same
5305 background square colors/images. */
5307 static int xpmDone = 0;
5308 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
5309 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
5312 CreateAnimMasks (int pieceDepth)
5318 unsigned long plane;
5321 /* Need a bitmap just to get a GC with right depth */
5322 buf = XCreatePixmap(xDisplay, xBoardWindow,
5324 values.foreground = 1;
5325 values.background = 0;
5326 /* Don't use XtGetGC, not read only */
5327 maskGC = XCreateGC(xDisplay, buf,
5328 GCForeground | GCBackground, &values);
5329 XFreePixmap(xDisplay, buf);
5331 buf = XCreatePixmap(xDisplay, xBoardWindow,
5332 squareSize, squareSize, pieceDepth);
5333 values.foreground = XBlackPixel(xDisplay, xScreen);
5334 values.background = XWhitePixel(xDisplay, xScreen);
5335 bufGC = XCreateGC(xDisplay, buf,
5336 GCForeground | GCBackground, &values);
5338 for (piece = WhitePawn; piece <= BlackKing; piece++) {
5339 /* Begin with empty mask */
5340 if(!xpmDone) // [HGM] pieces: keep using existing
5341 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
5342 squareSize, squareSize, 1);
5343 XSetFunction(xDisplay, maskGC, GXclear);
5344 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
5345 0, 0, squareSize, squareSize);
5347 /* Take a copy of the piece */
5352 XSetFunction(xDisplay, bufGC, GXcopy);
5353 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
5355 0, 0, squareSize, squareSize, 0, 0);
5357 /* XOR the background (light) over the piece */
5358 XSetFunction(xDisplay, bufGC, GXxor);
5360 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
5361 0, 0, squareSize, squareSize, 0, 0);
5363 XSetForeground(xDisplay, bufGC, lightSquareColor);
5364 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
5367 /* We now have an inverted piece image with the background
5368 erased. Construct mask by just selecting all the non-zero
5369 pixels - no need to reconstruct the original image. */
5370 XSetFunction(xDisplay, maskGC, GXor);
5372 /* Might be quicker to download an XImage and create bitmap
5373 data from it rather than this N copies per piece, but it
5374 only takes a fraction of a second and there is a much
5375 longer delay for loading the pieces. */
5376 for (n = 0; n < pieceDepth; n ++) {
5377 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
5378 0, 0, squareSize, squareSize,
5384 XFreePixmap(xDisplay, buf);
5385 XFreeGC(xDisplay, bufGC);
5386 XFreeGC(xDisplay, maskGC);
5390 InitAnimState (AnimNr anr, XWindowAttributes *info)
5395 /* Each buffer is square size, same depth as window */
5396 animBufs[anr+4] = xBoardWindow;
5397 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
5398 squareSize, squareSize, info->depth);
5399 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
5400 squareSize, squareSize, info->depth);
5402 /* Create a plain GC for blitting */
5403 mask = GCForeground | GCBackground | GCFunction |
5404 GCPlaneMask | GCGraphicsExposures;
5405 values.foreground = XBlackPixel(xDisplay, xScreen);
5406 values.background = XWhitePixel(xDisplay, xScreen);
5407 values.function = GXcopy;
5408 values.plane_mask = AllPlanes;
5409 values.graphics_exposures = False;
5410 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
5412 /* Piece will be copied from an existing context at
5413 the start of each new animation/drag. */
5414 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
5416 /* Outline will be a read-only copy of an existing */
5417 animGCs[anr+4] = None;
5423 XWindowAttributes info;
5425 if (xpmDone && gameInfo.variant == oldVariant) return;
5426 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
5427 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
5429 InitAnimState(Game, &info);
5430 InitAnimState(Player, &info);
5432 /* For XPM pieces, we need bitmaps to use as masks. */
5434 CreateAnimMasks(info.depth), xpmDone = 1;
5439 static Boolean frameWaiting;
5442 FrameAlarm (int sig)
5444 frameWaiting = False;
5445 /* In case System-V style signals. Needed?? */
5446 signal(SIGALRM, FrameAlarm);
5450 FrameDelay (int time)
5452 struct itimerval delay;
5454 XSync(xDisplay, False);
5457 frameWaiting = True;
5458 signal(SIGALRM, FrameAlarm);
5459 delay.it_interval.tv_sec =
5460 delay.it_value.tv_sec = time / 1000;
5461 delay.it_interval.tv_usec =
5462 delay.it_value.tv_usec = (time % 1000) * 1000;
5463 setitimer(ITIMER_REAL, &delay, NULL);
5464 while (frameWaiting) pause();
5465 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
5466 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
5467 setitimer(ITIMER_REAL, &delay, NULL);
5474 FrameDelay (int time)
5476 XSync(xDisplay, False);
5478 usleep(time * 1000);
5484 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
5488 /* Bitmap for piece being moved. */
5489 if (appData.monoMode) {
5490 *mask = *pieceToSolid(piece);
5491 } else if (useImages) {
5493 *mask = xpmMask[piece];
5495 *mask = ximMaskPm[piece];
5498 *mask = *pieceToSolid(piece);
5501 /* GC for piece being moved. Square color doesn't matter, but
5502 since it gets modified we make a copy of the original. */
5504 if (appData.monoMode)
5509 if (appData.monoMode)
5514 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
5516 /* Outline only used in mono mode and is not modified */
5518 *outline = bwPieceGC;
5520 *outline = wbPieceGC;
5524 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
5529 /* Draw solid rectangle which will be clipped to shape of piece */
5530 XFillRectangle(xDisplay, dest, clip,
5531 0, 0, squareSize, squareSize);
5532 if (appData.monoMode)
5533 /* Also draw outline in contrasting color for black
5534 on black / white on white cases */
5535 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
5536 0, 0, squareSize, squareSize, 0, 0, 1);
5538 /* Copy the piece */
5543 if(appData.upsideDown && flipView) kind ^= 2;
5544 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
5546 0, 0, squareSize, squareSize,
5552 InsertPiece (AnimNr anr, ChessSquare piece)
5554 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
5558 DrawBlank (AnimNr anr, int x, int y, int startColor)
5560 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
5563 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
5564 int srcX, int srcY, int width, int height, int destX, int destY)
5566 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
5567 srcX, srcY, width, height, destX, destY);
5571 SetDragPiece (AnimNr anr, ChessSquare piece)
5574 /* The piece will be drawn using its own bitmap as a matte */
5575 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
5576 XSetClipMask(xDisplay, animGCs[anr+2], mask);
5579 #include <sys/ioctl.h>
5583 int fd, default_width;
5586 default_width = 79; // this is FICS default anyway...
5588 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
5590 if (!ioctl(fd, TIOCGSIZE, &win))
5591 default_width = win.ts_cols;
5592 #elif defined(TIOCGWINSZ)
5594 if (!ioctl(fd, TIOCGWINSZ, &win))
5595 default_width = win.ws_col;
5597 return default_width;
5603 static int old_width = 0;
5604 int new_width = get_term_width();
5606 if (old_width != new_width)
5607 ics_printf("set width %d\n", new_width);
5608 old_width = new_width;
5612 NotifyFrontendLogin ()
5617 /* [AS] Arrow highlighting support */
5620 DrawPolygon (Pnt arrow[], int nr)
5624 for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
5625 XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
5626 if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
5630 UpdateLogos (int displ)
5632 return; // no logos in XBoard yet