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"
217 #define usleep(t) _sleep2(((t)+500)/1000)
221 # define _(s) gettext (s)
222 # define N_(s) gettext_noop (s)
228 int main P((int argc, char **argv));
229 RETSIGTYPE CmailSigHandler P((int sig));
230 RETSIGTYPE IntSigHandler P((int sig));
231 RETSIGTYPE TermSizeSigHandler P((int sig));
232 static void CreateGCs P((int redo));
233 static void CreateAnyPieces P((void));
234 void CreateXIMPieces P((void));
235 void CreateXPMPieces P((void));
236 void CreateXPMBoard P((char *s, int n));
237 void CreatePieces P((void));
238 void CreatePieceMenus P((void));
239 Widget CreateMenuBar P((Menu *mb, int boardWidth));
240 Widget CreateButtonBar P ((MenuItem *mi));
242 char *InsertPxlSize P((char *pattern, int targetPxlSize));
243 XFontSet CreateFontSet P((char *base_fnt_lst));
245 char *FindFont P((char *pattern, int targetPxlSize));
247 void PieceMenuPopup P((Widget w, XEvent *event,
248 String *params, Cardinal *num_params));
249 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
250 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
251 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
252 u_int wreq, u_int hreq));
253 void CreateGrid P((void));
254 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
255 void DelayedDrag P((void));
256 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
257 void HandleUserMove P((Widget w, XEvent *event,
258 String *prms, Cardinal *nprms));
259 void AnimateUserMove P((Widget w, XEvent * event,
260 String * params, Cardinal * nParams));
261 void HandlePV P((Widget w, XEvent * event,
262 String * params, Cardinal * nParams));
263 void SelectPV P((Widget w, XEvent * event,
264 String * params, Cardinal * nParams));
265 void StopPV P((Widget w, XEvent * event,
266 String * params, Cardinal * nParams));
267 void WhiteClock P((Widget w, XEvent *event,
268 String *prms, Cardinal *nprms));
269 void BlackClock P((Widget w, XEvent *event,
270 String *prms, Cardinal *nprms));
271 void DrawPositionProc P((Widget w, XEvent *event,
272 String *prms, Cardinal *nprms));
273 void CommentClick P((Widget w, XEvent * event,
274 String * params, Cardinal * nParams));
275 void ICSInputBoxPopUp P((void));
276 void FileNamePopUp P((char *label, char *def, char *filter,
277 FileProc proc, char *openMode));
278 void AskQuestionReplyAction P((Widget w, XEvent *event,
279 String *prms, Cardinal *nprms));
280 void AskQuestionPopDown P((void));
281 void PromotionPopDown P((void));
282 void PromotionCallback P((Widget w, XtPointer client_data,
283 XtPointer call_data));
284 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
285 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
286 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
287 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
288 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
289 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
290 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
291 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
292 Boolean TempBackwardActive = False;
293 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
294 void DisplayMove P((int moveNumber));
295 void ICSInitScript P((void));
296 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;
337 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
339 Position commentX = -1, commentY = -1;
340 Dimension commentW, commentH;
341 typedef unsigned int BoardSize;
343 Boolean chessProgram;
345 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
346 int smallLayout = 0, tinyLayout = 0,
347 marginW, marginH, // [HGM] for run-time resizing
348 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
349 ICSInputBoxUp = False, askQuestionUp = False,
350 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
351 errorUp = False, errorExitStatus = -1, defaultLineGap;
352 Dimension textHeight;
353 Pixel timerForegroundPixel, timerBackgroundPixel;
354 Pixel buttonForegroundPixel, buttonBackgroundPixel;
355 char *chessDir, *programName, *programVersion;
356 Boolean alwaysOnTop = False;
357 char *icsTextMenuString;
359 char *firstChessProgramNames;
360 char *secondChessProgramNames;
362 WindowPlacement wpMain;
363 WindowPlacement wpConsole;
364 WindowPlacement wpComment;
365 WindowPlacement wpMoveHistory;
366 WindowPlacement wpEvalGraph;
367 WindowPlacement wpEngineOutput;
368 WindowPlacement wpGameList;
369 WindowPlacement wpTags;
374 Pixmap pieceBitmap[2][(int)BlackPawn];
375 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
376 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
377 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
378 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
379 Pixmap xpmBoardBitmap[2];
380 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
381 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
382 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
383 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
384 XImage *ximLightSquare, *ximDarkSquare;
387 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
388 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
390 #define White(piece) ((int)(piece) < (int)BlackPawn)
392 /* Bitmaps for use as masks when drawing XPM pieces.
393 Need one for each black and white piece. */
394 static Pixmap xpmMask[BlackKing + 1];
396 /* This magic number is the number of intermediate frames used
397 in each half of the animation. For short moves it's reduced
398 by 1. The total number of frames will be factor * 2 + 1. */
401 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
403 #define PAUSE_BUTTON "P"
404 MenuItem buttonBar[] = {
405 {"<<", "<<", ToStartEvent},
406 {"<", "<", BackwardEvent},
407 {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseEvent},
408 {">", ">", ForwardEvent},
409 {">>", ">>", ToEndEvent},
413 #define PIECE_MENU_SIZE 18
414 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
415 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
416 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
417 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
418 N_("Empty square"), N_("Clear board") },
419 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
420 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
421 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
422 N_("Empty square"), N_("Clear board") }
424 /* must be in same order as pieceMenuStrings! */
425 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
426 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
427 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
428 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
429 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
430 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
431 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
432 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
433 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
436 #define DROP_MENU_SIZE 6
437 String dropMenuStrings[DROP_MENU_SIZE] = {
438 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
440 /* must be in same order as dropMenuStrings! */
441 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
442 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
443 WhiteRook, WhiteQueen
451 DropMenuEnables dmEnables[] = {
469 { XtNborderWidth, 0 },
470 { XtNdefaultDistance, 0 },
474 { XtNborderWidth, 0 },
475 { XtNresizable, (XtArgVal) True },
479 { XtNborderWidth, 0 },
485 { XtNjustify, (XtArgVal) XtJustifyRight },
486 { XtNlabel, (XtArgVal) "..." },
487 { XtNresizable, (XtArgVal) True },
488 { XtNresize, (XtArgVal) False }
491 Arg messageArgs[] = {
492 { XtNjustify, (XtArgVal) XtJustifyLeft },
493 { XtNlabel, (XtArgVal) "..." },
494 { XtNresizable, (XtArgVal) True },
495 { XtNresize, (XtArgVal) False }
499 { XtNborderWidth, 0 },
500 { XtNjustify, (XtArgVal) XtJustifyLeft }
503 XtResource clientResources[] = {
504 { "flashCount", "flashCount", XtRInt, sizeof(int),
505 XtOffset(AppDataPtr, flashCount), XtRImmediate,
506 (XtPointer) FLASH_COUNT },
509 XrmOptionDescRec shellOptions[] = {
510 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
511 { "-flash", "flashCount", XrmoptionNoArg, "3" },
512 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
515 XtActionsRec boardActions[] = {
516 { "DrawPosition", DrawPositionProc },
517 { "HandleUserMove", HandleUserMove },
518 { "AnimateUserMove", AnimateUserMove },
519 { "HandlePV", HandlePV },
520 { "SelectPV", SelectPV },
521 { "StopPV", StopPV },
522 { "AskQuestionReplyAction", AskQuestionReplyAction },
523 { "PieceMenuPopup", PieceMenuPopup },
524 { "WhiteClock", WhiteClock },
525 { "BlackClock", BlackClock },
526 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
527 { "QuitProc", QuitWrapper },
528 { "ManProc", ManInner },
529 { "TempBackwardProc", TempBackwardProc },
530 { "TempForwardProc", TempForwardProc },
531 { "CommentClick", (XtActionProc) CommentClick },
532 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
533 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
534 { "GameListPopDown", (XtActionProc) GameListPopDown },
535 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
536 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
537 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
538 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
539 { "GenericPopDown", (XtActionProc) GenericPopDown },
540 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
541 { "SelectMove", (XtActionProc) SelectMove },
542 { "LoadSelectedProc", LoadSelectedProc },
543 { "SetFilterProc", SetFilterProc },
544 { "TypeInProc", TypeInProc },
545 { "EnterKeyProc", EnterKeyProc },
546 { "UpKeyProc", UpKeyProc },
547 { "DownKeyProc", DownKeyProc },
550 char globalTranslations[] =
551 ":<Key>F9: MenuItem(ResignProc) \n \
552 :Ctrl<Key>n: MenuItem(NewGame) \n \
553 :Meta<Key>V: MenuItem(NewVariant) \n \
554 :Ctrl<Key>o: MenuItem(LoadGame) \n \
555 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
556 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
557 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
558 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
559 :Ctrl<Key>s: MenuItem(SaveGame) \n \
560 :Ctrl<Key>c: MenuItem(CopyGame) \n \
561 :Ctrl<Key>v: MenuItem(PasteGame) \n \
562 :Ctrl<Key>O: MenuItem(LoadPosition) \n \
563 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
564 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
565 :Ctrl<Key>S: MenuItem(SavePosition) \n \
566 :Ctrl<Key>C: MenuItem(CopyPosition) \n \
567 :Ctrl<Key>V: MenuItem(PastePosition) \n \
568 :Ctrl<Key>q: MenuItem(Exit) \n \
569 :Ctrl<Key>w: MenuItem(MachineWhite) \n \
570 :Ctrl<Key>b: MenuItem(MachineBlack) \n \
571 :Ctrl<Key>t: MenuItem(TwoMachines) \n \
572 :Ctrl<Key>a: MenuItem(AnalysisMode) \n \
573 :Ctrl<Key>g: MenuItem(AnalyzeFile) \n \
574 :Ctrl<Key>e: MenuItem(EditGame) \n \
575 :Ctrl<Key>E: MenuItem(EditPosition) \n \
576 :Meta<Key>O: MenuItem(ShowEngineOutput) \n \
577 :Meta<Key>E: MenuItem(ShowEvaluationGraph) \n \
578 :Meta<Key>G: MenuItem(ShowGameList) \n \
579 :Meta<Key>H: MenuItem(ShowMoveHistory) \n \
580 :<Key>Pause: MenuItem(Pause) \n \
581 :<Key>F3: MenuItem(Accept) \n \
582 :<Key>F4: MenuItem(Decline) \n \
583 :<Key>F12: MenuItem(Rematch) \n \
584 :<Key>F5: MenuItem(CallFlag) \n \
585 :<Key>F6: MenuItem(Draw) \n \
586 :<Key>F7: MenuItem(Adjourn) \n \
587 :<Key>F8: MenuItem(Abort) \n \
588 :<Key>F10: MenuItem(StopObserving) \n \
589 :<Key>F11: MenuItem(StopExamining) \n \
590 :Ctrl<Key>d: MenuItem(DebugProc) \n \
591 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
592 :Meta<Key>End: MenuItem(ToEnd) \n \
593 :Meta<Key>Right: MenuItem(Forward) \n \
594 :Meta<Key>Home: MenuItem(ToStart) \n \
595 :Meta<Key>Left: MenuItem(Backward) \n \
596 :<Key>Left: MenuItem(Backward) \n \
597 :<Key>Right: MenuItem(Forward) \n \
598 :<Key>Home: MenuItem(Revert) \n \
599 :<Key>End: MenuItem(TruncateGame) \n \
600 :Ctrl<Key>m: MenuItem(MoveNow) \n \
601 :Ctrl<Key>x: MenuItem(RetractMove) \n \
602 :Meta<Key>J: MenuItem(Adjudications) \n \
603 :Meta<Key>U: MenuItem(CommonEngine) \n \
604 :Meta<Key>T: MenuItem(TimeControl) \n \
605 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
606 #ifndef OPTIONSDIALOG
608 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
609 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
610 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
611 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
612 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
615 :<Key>F1: MenuItem(Manual) \n \
616 :<Key>F2: MenuItem(FlipView) \n \
617 :<KeyDown>Return: TempBackwardProc() \n \
618 :<KeyUp>Return: TempForwardProc() \n";
620 char boardTranslations[] =
621 "<Btn1Down>: HandleUserMove(0) \n \
622 Shift<Btn1Up>: HandleUserMove(1) \n \
623 <Btn1Up>: HandleUserMove(0) \n \
624 <Btn1Motion>: AnimateUserMove() \n \
625 <Btn3Motion>: HandlePV() \n \
626 <Btn2Motion>: HandlePV() \n \
627 <Btn3Up>: PieceMenuPopup(menuB) \n \
628 <Btn2Up>: PieceMenuPopup(menuB) \n \
629 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
630 PieceMenuPopup(menuB) \n \
631 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
632 PieceMenuPopup(menuW) \n \
633 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
634 PieceMenuPopup(menuW) \n \
635 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
636 PieceMenuPopup(menuB) \n";
638 char whiteTranslations[] =
639 "Shift<BtnDown>: WhiteClock(1)\n \
640 <BtnDown>: WhiteClock(0)\n";
641 char blackTranslations[] =
642 "Shift<BtnDown>: BlackClock(1)\n \
643 <BtnDown>: BlackClock(0)\n";
645 char ICSInputTranslations[] =
646 "<Key>Up: UpKeyProc() \n "
647 "<Key>Down: DownKeyProc() \n "
648 "<Key>Return: EnterKeyProc() \n";
650 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
651 // as the widget is destroyed before the up-click can call extend-end
652 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
654 String xboardResources[] = {
655 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
656 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
661 /* Max possible square size */
662 #define MAXSQSIZE 256
664 static int xpm_avail[MAXSQSIZE];
666 #ifdef HAVE_DIR_STRUCT
668 /* Extract piece size from filename */
670 xpm_getsize (char *name, int len, char *ext)
678 if ((p=strchr(name, '.')) == NULL ||
679 StrCaseCmp(p+1, ext) != 0)
685 while (*p && isdigit(*p))
692 /* Setup xpm_avail */
694 xpm_getavail (char *dirname, char *ext)
700 for (i=0; i<MAXSQSIZE; ++i)
703 if (appData.debugMode)
704 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
706 dir = opendir(dirname);
709 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
710 programName, dirname);
714 while ((ent=readdir(dir)) != NULL) {
715 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
716 if (i > 0 && i < MAXSQSIZE)
726 xpm_print_avail (FILE *fp, char *ext)
730 fprintf(fp, _("Available `%s' sizes:\n"), ext);
731 for (i=1; i<MAXSQSIZE; ++i) {
737 /* Return XPM piecesize closest to size */
739 xpm_closest_to (char *dirname, int size, char *ext)
742 int sm_diff = MAXSQSIZE;
746 xpm_getavail(dirname, ext);
748 if (appData.debugMode)
749 xpm_print_avail(stderr, ext);
751 for (i=1; i<MAXSQSIZE; ++i) {
754 diff = (diff<0) ? -diff : diff;
755 if (diff < sm_diff) {
763 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
769 #else /* !HAVE_DIR_STRUCT */
770 /* If we are on a system without a DIR struct, we can't
771 read the directory, so we can't collect a list of
772 filenames, etc., so we can't do any size-fitting. */
774 xpm_closest_to (char *dirname, int size, char *ext)
777 Warning: No DIR structure found on this system --\n\
778 Unable to autosize for XPM/XIM pieces.\n\
779 Please report this error to %s.\n\
780 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
783 #endif /* HAVE_DIR_STRUCT */
786 /* Arrange to catch delete-window events */
787 Atom wm_delete_window;
789 CatchDeleteWindow (Widget w, String procname)
792 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
793 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
794 XtAugmentTranslations(w, XtParseTranslationTable(buf));
801 XtSetArg(args[0], XtNiconic, False);
802 XtSetValues(shellWidget, args, 1);
804 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
807 //---------------------------------------------------------------------------------------------------------
808 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
811 #define CW_USEDEFAULT (1<<31)
812 #define ICS_TEXT_MENU_SIZE 90
813 #define DEBUG_FILE "xboard.debug"
814 #define SetCurrentDirectory chdir
815 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
819 // these two must some day move to frontend.h, when they are implemented
820 Boolean GameListIsUp();
822 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
825 // front-end part of option handling
827 // [HGM] This platform-dependent table provides the location for storing the color info
828 extern char *crWhite, * crBlack;
832 &appData.whitePieceColor,
833 &appData.blackPieceColor,
834 &appData.lightSquareColor,
835 &appData.darkSquareColor,
836 &appData.highlightSquareColor,
837 &appData.premoveHighlightColor,
838 &appData.lowTimeWarningColor,
849 // [HGM] font: keep a font for each square size, even non-stndard ones
852 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
853 char *fontTable[NUM_FONTS][MAX_SIZE];
856 ParseFont (char *name, int number)
857 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
859 if(sscanf(name, "size%d:", &size)) {
860 // [HGM] font: font is meant for specific boardSize (likely from settings file);
861 // defer processing it until we know if it matches our board size
862 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
863 fontTable[number][size] = strdup(strchr(name, ':')+1);
864 fontValid[number][size] = True;
869 case 0: // CLOCK_FONT
870 appData.clockFont = strdup(name);
872 case 1: // MESSAGE_FONT
873 appData.font = strdup(name);
875 case 2: // COORD_FONT
876 appData.coordFont = strdup(name);
881 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
886 { // only 2 fonts currently
887 appData.clockFont = CLOCK_FONT_NAME;
888 appData.coordFont = COORD_FONT_NAME;
889 appData.font = DEFAULT_FONT_NAME;
894 { // no-op, until we identify the code for this already in XBoard and move it here
898 ParseColor (int n, char *name)
899 { // in XBoard, just copy the color-name string
900 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
904 ParseTextAttribs (ColorClass cc, char *s)
906 (&appData.colorShout)[cc] = strdup(s);
910 ParseBoardSize (void *addr, char *name)
912 appData.boardSize = strdup(name);
917 { // In XBoard the sound-playing program takes care of obtaining the actual sound
921 SetCommPortDefaults ()
922 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
925 // [HGM] args: these three cases taken out to stay in front-end
927 SaveFontArg (FILE *f, ArgDescriptor *ad)
930 int i, n = (int)(intptr_t)ad->argLoc;
932 case 0: // CLOCK_FONT
933 name = appData.clockFont;
935 case 1: // MESSAGE_FONT
938 case 2: // COORD_FONT
939 name = appData.coordFont;
944 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
945 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
946 fontTable[n][squareSize] = strdup(name);
947 fontValid[n][squareSize] = True;
950 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
951 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
956 { // nothing to do, as the sounds are at all times represented by their text-string names already
960 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
961 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
962 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
966 SaveColor (FILE *f, ArgDescriptor *ad)
967 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
968 if(colorVariable[(int)(intptr_t)ad->argLoc])
969 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
973 SaveBoardSize (FILE *f, char *name, void *addr)
974 { // wrapper to shield back-end from BoardSize & sizeInfo
975 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
979 ParseCommPortSettings (char *s)
980 { // no such option in XBoard (yet)
983 extern Widget engineOutputShell;
987 GetActualPlacement (Widget wg, WindowPlacement *wp)
992 XWindowAttributes winAt;
999 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
1000 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
1001 wp->x = rx - winAt.x;
1002 wp->y = ry - winAt.y;
1003 wp->height = winAt.height;
1004 wp->width = winAt.width;
1005 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
1010 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1011 // In XBoard this will have to wait until awareness of window parameters is implemented
1012 GetActualPlacement(shellWidget, &wpMain);
1013 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1014 if(MoveHistoryIsUp()) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
1015 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1016 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1017 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
1018 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
1022 PrintCommPortSettings (FILE *f, char *name)
1023 { // This option does not exist in XBoard
1027 EnsureOnScreen (int *x, int *y, int minX, int minY)
1034 { // [HGM] args: allows testing if main window is realized from back-end
1035 return xBoardWindow != 0;
1039 PopUpStartupDialog ()
1040 { // start menu not implemented in XBoard
1044 ConvertToLine (int argc, char **argv)
1046 static char line[128*1024], buf[1024];
1050 for(i=1; i<argc; i++)
1052 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1053 && argv[i][0] != '{' )
1054 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1056 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1057 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1060 line[strlen(line)-1] = NULLCHAR;
1064 //--------------------------------------------------------------------------------------------
1067 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1069 #define BoardSize int
1071 InitDrawingSizes (BoardSize boardSize, int flags)
1072 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1073 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1075 XtGeometryResult gres;
1077 static Dimension oldWidth, oldHeight;
1078 static VariantClass oldVariant;
1079 static int oldDual = -1, oldMono = -1;
1081 if(!formWidget) return;
1083 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1084 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1085 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1087 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1089 * Enable shell resizing.
1091 shellArgs[0].value = (XtArgVal) &w;
1092 shellArgs[1].value = (XtArgVal) &h;
1093 XtGetValues(shellWidget, shellArgs, 2);
1095 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1096 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1097 XtSetValues(shellWidget, &shellArgs[2], 4);
1099 XtSetArg(args[0], XtNdefaultDistance, &sep);
1100 XtGetValues(formWidget, args, 1);
1102 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1104 hOffset = boardWidth + 10;
1105 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1106 secondSegments[i] = gridSegments[i];
1107 secondSegments[i].x1 += hOffset;
1108 secondSegments[i].x2 += hOffset;
1111 XtSetArg(args[0], XtNwidth, boardWidth);
1112 XtSetArg(args[1], XtNheight, boardHeight);
1113 XtSetValues(boardWidget, args, 2);
1115 timerWidth = (boardWidth - sep) / 2;
1116 XtSetArg(args[0], XtNwidth, timerWidth);
1117 XtSetValues(whiteTimerWidget, args, 1);
1118 XtSetValues(blackTimerWidget, args, 1);
1120 XawFormDoLayout(formWidget, False);
1122 if (appData.titleInWindow) {
1124 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1125 XtSetArg(args[i], XtNheight, &h); i++;
1126 XtGetValues(titleWidget, args, i);
1128 w = boardWidth - 2*bor;
1130 XtSetArg(args[0], XtNwidth, &w);
1131 XtGetValues(menuBarWidget, args, 1);
1132 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1135 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1136 if (gres != XtGeometryYes && appData.debugMode) {
1138 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1139 programName, gres, w, h, wr, hr);
1143 XawFormDoLayout(formWidget, True);
1146 * Inhibit shell resizing.
1148 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1149 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1150 shellArgs[4].value = shellArgs[2].value = w;
1151 shellArgs[5].value = shellArgs[3].value = h;
1152 XtSetValues(shellWidget, &shellArgs[0], 6);
1154 XSync(xDisplay, False);
1158 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1161 if(gameInfo.variant != oldVariant) { // and only if variant changed
1164 for(i=0; i<4; i++) {
1166 for(p=0; p<=(int)WhiteKing; p++)
1167 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1168 if(gameInfo.variant == VariantShogi) {
1169 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1170 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1171 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1172 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1173 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1176 if(gameInfo.variant == VariantGothic) {
1177 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1180 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1181 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1182 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1185 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1186 for(p=0; p<=(int)WhiteKing; p++)
1187 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1188 if(gameInfo.variant == VariantShogi) {
1189 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1190 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1191 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1192 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1193 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1196 if(gameInfo.variant == VariantGothic) {
1197 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1200 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1201 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1202 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1207 for(i=0; i<2; i++) {
1209 for(p=0; p<=(int)WhiteKing; p++)
1210 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1211 if(gameInfo.variant == VariantShogi) {
1212 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1213 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1214 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1215 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1216 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1219 if(gameInfo.variant == VariantGothic) {
1220 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1223 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1224 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1225 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1229 oldMono = -10; // kludge to force recreation of animation masks
1230 oldVariant = gameInfo.variant;
1233 if(appData.monoMode != oldMono)
1236 oldMono = appData.monoMode;
1241 MakeOneColor (char *name, Pixel *color)
1243 XrmValue vFrom, vTo;
1244 if (!appData.monoMode) {
1245 vFrom.addr = (caddr_t) name;
1246 vFrom.size = strlen(name);
1247 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1248 if (vTo.addr == NULL) {
1249 appData.monoMode = True;
1252 *color = *(Pixel *) vTo.addr;
1260 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1261 int forceMono = False;
1263 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1264 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1265 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1266 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1267 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1268 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1275 { // [HGM] taken out of main
1277 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1278 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1279 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1281 if (appData.bitmapDirectory[0] != NULLCHAR) {
1285 CreateXPMBoard(appData.liteBackTextureFile, 1);
1286 CreateXPMBoard(appData.darkBackTextureFile, 0);
1290 /* Create regular pieces */
1291 if (!useImages) CreatePieces();
1296 InitDrawingParams ()
1298 MakeColors(); CreateGCs(True);
1303 main (int argc, char **argv)
1305 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1306 XSetWindowAttributes window_attributes;
1308 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1309 XrmValue vFrom, vTo;
1310 XtGeometryResult gres;
1313 int forceMono = False;
1315 srandom(time(0)); // [HGM] book: make random truly random
1317 setbuf(stdout, NULL);
1318 setbuf(stderr, NULL);
1321 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1322 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1326 programName = strrchr(argv[0], '/');
1327 if (programName == NULL)
1328 programName = argv[0];
1333 XtSetLanguageProc(NULL, NULL, NULL);
1334 bindtextdomain(PACKAGE, LOCALEDIR);
1335 textdomain(PACKAGE);
1339 XtAppInitialize(&appContext, "XBoard", shellOptions,
1340 XtNumber(shellOptions),
1341 &argc, argv, xboardResources, NULL, 0);
1342 appData.boardSize = "";
1343 InitAppData(ConvertToLine(argc, argv));
1345 if (p == NULL) p = "/tmp";
1346 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1347 gameCopyFilename = (char*) malloc(i);
1348 gamePasteFilename = (char*) malloc(i);
1349 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1350 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1352 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1353 clientResources, XtNumber(clientResources),
1356 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1357 static char buf[MSG_SIZ];
1358 EscapeExpand(buf, appData.firstInitString);
1359 appData.firstInitString = strdup(buf);
1360 EscapeExpand(buf, appData.secondInitString);
1361 appData.secondInitString = strdup(buf);
1362 EscapeExpand(buf, appData.firstComputerString);
1363 appData.firstComputerString = strdup(buf);
1364 EscapeExpand(buf, appData.secondComputerString);
1365 appData.secondComputerString = strdup(buf);
1368 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1371 if (chdir(chessDir) != 0) {
1372 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1378 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1379 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1380 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1381 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1384 setbuf(debugFP, NULL);
1388 if (appData.debugMode) {
1389 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1393 /* [HGM,HR] make sure board size is acceptable */
1394 if(appData.NrFiles > BOARD_FILES ||
1395 appData.NrRanks > BOARD_RANKS )
1396 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1399 /* This feature does not work; animation needs a rewrite */
1400 appData.highlightDragging = FALSE;
1404 xDisplay = XtDisplay(shellWidget);
1405 xScreen = DefaultScreen(xDisplay);
1406 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1408 gameInfo.variant = StringToVariant(appData.variant);
1409 InitPosition(FALSE);
1412 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1414 if (isdigit(appData.boardSize[0])) {
1415 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1416 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1417 &fontPxlSize, &smallLayout, &tinyLayout);
1419 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1420 programName, appData.boardSize);
1424 /* Find some defaults; use the nearest known size */
1425 SizeDefaults *szd, *nearest;
1426 int distance = 99999;
1427 nearest = szd = sizeDefaults;
1428 while (szd->name != NULL) {
1429 if (abs(szd->squareSize - squareSize) < distance) {
1431 distance = abs(szd->squareSize - squareSize);
1432 if (distance == 0) break;
1436 if (i < 2) lineGap = nearest->lineGap;
1437 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1438 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1439 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1440 if (i < 6) smallLayout = nearest->smallLayout;
1441 if (i < 7) tinyLayout = nearest->tinyLayout;
1444 SizeDefaults *szd = sizeDefaults;
1445 if (*appData.boardSize == NULLCHAR) {
1446 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1447 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1450 if (szd->name == NULL) szd--;
1451 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1453 while (szd->name != NULL &&
1454 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1455 if (szd->name == NULL) {
1456 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1457 programName, appData.boardSize);
1461 squareSize = szd->squareSize;
1462 lineGap = szd->lineGap;
1463 clockFontPxlSize = szd->clockFontPxlSize;
1464 coordFontPxlSize = szd->coordFontPxlSize;
1465 fontPxlSize = szd->fontPxlSize;
1466 smallLayout = szd->smallLayout;
1467 tinyLayout = szd->tinyLayout;
1468 // [HGM] font: use defaults from settings file if available and not overruled
1470 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1471 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1472 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1473 appData.font = fontTable[MESSAGE_FONT][squareSize];
1474 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1475 appData.coordFont = fontTable[COORD_FONT][squareSize];
1477 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1478 if (strlen(appData.pixmapDirectory) > 0) {
1479 p = ExpandPathName(appData.pixmapDirectory);
1481 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1482 appData.pixmapDirectory);
1485 if (appData.debugMode) {
1486 fprintf(stderr, _("\
1487 XBoard square size (hint): %d\n\
1488 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1490 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1491 if (appData.debugMode) {
1492 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1495 defaultLineGap = lineGap;
1496 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1498 /* [HR] height treated separately (hacked) */
1499 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1500 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1501 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1502 XtSetArg(boardArgs[2], XtNheight, boardHeight);
1505 * Determine what fonts to use.
1508 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1509 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1510 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1511 fontSet = CreateFontSet(appData.font);
1512 clockFontSet = CreateFontSet(appData.clockFont);
1514 /* For the coordFont, use the 0th font of the fontset. */
1515 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1516 XFontStruct **font_struct_list;
1517 XFontSetExtents *fontSize;
1518 char **font_name_list;
1519 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1520 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1521 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1522 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1523 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1526 appData.font = FindFont(appData.font, fontPxlSize);
1527 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1528 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1529 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1530 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1531 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1532 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1534 countFontID = coordFontID; // [HGM] holdings
1535 countFontStruct = coordFontStruct;
1537 xdb = XtDatabase(xDisplay);
1539 XrmPutLineResource(&xdb, "*international: True");
1540 vTo.size = sizeof(XFontSet);
1541 vTo.addr = (XtPointer) &fontSet;
1542 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1544 XrmPutStringResource(&xdb, "*font", appData.font);
1548 * Detect if there are not enough colors available and adapt.
1550 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1551 appData.monoMode = True;
1554 forceMono = MakeColors();
1557 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1559 appData.monoMode = True;
1562 if (appData.lowTimeWarning && !appData.monoMode) {
1563 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1564 vFrom.size = strlen(appData.lowTimeWarningColor);
1565 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1566 if (vTo.addr == NULL)
1567 appData.monoMode = True;
1569 lowTimeWarningColor = *(Pixel *) vTo.addr;
1572 if (appData.monoMode && appData.debugMode) {
1573 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1574 (unsigned long) XWhitePixel(xDisplay, xScreen),
1575 (unsigned long) XBlackPixel(xDisplay, xScreen));
1578 ParseIcsTextColors();
1580 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1586 layoutName = "tinyLayout";
1587 } else if (smallLayout) {
1588 layoutName = "smallLayout";
1590 layoutName = "normalLayout";
1592 /* Outer layoutWidget is there only to provide a name for use in
1593 resources that depend on the layout style */
1595 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
1596 layoutArgs, XtNumber(layoutArgs));
1598 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
1599 formArgs, XtNumber(formArgs));
1600 XtSetArg(args[0], XtNdefaultDistance, &sep);
1601 XtGetValues(formWidget, args, 1);
1604 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
1605 XtSetArg(args[0], XtNtop, XtChainTop);
1606 XtSetArg(args[1], XtNbottom, XtChainTop);
1607 XtSetArg(args[2], XtNright, XtChainLeft);
1608 XtSetValues(menuBarWidget, args, 3);
1610 widgetList[j++] = whiteTimerWidget =
1611 XtCreateWidget("whiteTime", labelWidgetClass,
1612 formWidget, timerArgs, XtNumber(timerArgs));
1614 XtSetArg(args[0], XtNfontSet, clockFontSet);
1616 XtSetArg(args[0], XtNfont, clockFontStruct);
1618 XtSetArg(args[1], XtNtop, XtChainTop);
1619 XtSetArg(args[2], XtNbottom, XtChainTop);
1620 XtSetValues(whiteTimerWidget, args, 3);
1622 widgetList[j++] = blackTimerWidget =
1623 XtCreateWidget("blackTime", labelWidgetClass,
1624 formWidget, timerArgs, XtNumber(timerArgs));
1626 XtSetArg(args[0], XtNfontSet, clockFontSet);
1628 XtSetArg(args[0], XtNfont, clockFontStruct);
1630 XtSetArg(args[1], XtNtop, XtChainTop);
1631 XtSetArg(args[2], XtNbottom, XtChainTop);
1632 XtSetValues(blackTimerWidget, args, 3);
1634 if (appData.titleInWindow) {
1635 widgetList[j++] = titleWidget =
1636 XtCreateWidget("title", labelWidgetClass, formWidget,
1637 titleArgs, XtNumber(titleArgs));
1638 XtSetArg(args[0], XtNtop, XtChainTop);
1639 XtSetArg(args[1], XtNbottom, XtChainTop);
1640 XtSetValues(titleWidget, args, 2);
1643 if (appData.showButtonBar) {
1644 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
1645 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
1646 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
1647 XtSetArg(args[2], XtNtop, XtChainTop);
1648 XtSetArg(args[3], XtNbottom, XtChainTop);
1649 XtSetValues(buttonBarWidget, args, 4);
1652 widgetList[j++] = messageWidget =
1653 XtCreateWidget("message", labelWidgetClass, formWidget,
1654 messageArgs, XtNumber(messageArgs));
1655 XtSetArg(args[0], XtNtop, XtChainTop);
1656 XtSetArg(args[1], XtNbottom, XtChainTop);
1657 XtSetValues(messageWidget, args, 2);
1659 widgetList[j++] = boardWidget =
1660 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
1661 XtNumber(boardArgs));
1663 XtManageChildren(widgetList, j);
1665 timerWidth = (boardWidth - sep) / 2;
1666 XtSetArg(args[0], XtNwidth, timerWidth);
1667 XtSetValues(whiteTimerWidget, args, 1);
1668 XtSetValues(blackTimerWidget, args, 1);
1670 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1671 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1672 XtGetValues(whiteTimerWidget, args, 2);
1674 if (appData.showButtonBar) {
1675 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1676 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1677 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
1681 * formWidget uses these constraints but they are stored
1685 XtSetArg(args[i], XtNfromHoriz, 0); i++;
1686 XtSetValues(menuBarWidget, args, i);
1687 if (appData.titleInWindow) {
1690 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1691 XtSetValues(whiteTimerWidget, args, i);
1693 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1694 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1695 XtSetValues(blackTimerWidget, args, i);
1697 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1698 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
1699 XtSetValues(titleWidget, args, i);
1701 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1702 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1703 XtSetValues(messageWidget, args, i);
1704 if (appData.showButtonBar) {
1706 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1707 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1708 XtSetValues(buttonBarWidget, args, i);
1712 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1713 XtSetValues(whiteTimerWidget, args, i);
1715 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1716 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1717 XtSetValues(blackTimerWidget, args, i);
1719 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
1720 XtSetValues(titleWidget, args, i);
1722 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1723 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1724 XtSetValues(messageWidget, args, i);
1725 if (appData.showButtonBar) {
1727 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1728 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1729 XtSetValues(buttonBarWidget, args, i);
1734 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1735 XtSetValues(whiteTimerWidget, args, i);
1737 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1738 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1739 XtSetValues(blackTimerWidget, args, i);
1741 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1742 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1743 XtSetValues(messageWidget, args, i);
1744 if (appData.showButtonBar) {
1746 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1747 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1748 XtSetValues(buttonBarWidget, args, i);
1752 XtSetArg(args[0], XtNfromVert, messageWidget);
1753 XtSetArg(args[1], XtNtop, XtChainTop);
1754 XtSetArg(args[2], XtNbottom, XtChainBottom);
1755 XtSetArg(args[3], XtNleft, XtChainLeft);
1756 XtSetArg(args[4], XtNright, XtChainRight);
1757 XtSetValues(boardWidget, args, 5);
1759 XtRealizeWidget(shellWidget);
1762 XtSetArg(args[0], XtNx, wpMain.x);
1763 XtSetArg(args[1], XtNy, wpMain.y);
1764 XtSetValues(shellWidget, args, 2);
1768 * Correct the width of the message and title widgets.
1769 * It is not known why some systems need the extra fudge term.
1770 * The value "2" is probably larger than needed.
1772 XawFormDoLayout(formWidget, False);
1774 #define WIDTH_FUDGE 2
1776 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1777 XtSetArg(args[i], XtNheight, &h); i++;
1778 XtGetValues(messageWidget, args, i);
1779 if (appData.showButtonBar) {
1781 XtSetArg(args[i], XtNwidth, &w); i++;
1782 XtGetValues(buttonBarWidget, args, i);
1783 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1785 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
1788 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1789 if (gres != XtGeometryYes && appData.debugMode) {
1790 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1791 programName, gres, w, h, wr, hr);
1794 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
1795 /* The size used for the child widget in layout lags one resize behind
1796 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
1798 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1799 if (gres != XtGeometryYes && appData.debugMode) {
1800 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1801 programName, gres, w, h, wr, hr);
1804 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
1805 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
1806 XtSetArg(args[1], XtNright, XtChainRight);
1807 XtSetValues(messageWidget, args, 2);
1809 if (appData.titleInWindow) {
1811 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1812 XtSetArg(args[i], XtNheight, &h); i++;
1813 XtGetValues(titleWidget, args, i);
1815 w = boardWidth - 2*bor;
1817 XtSetArg(args[0], XtNwidth, &w);
1818 XtGetValues(menuBarWidget, args, 1);
1819 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1822 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1823 if (gres != XtGeometryYes && appData.debugMode) {
1825 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1826 programName, gres, w, h, wr, hr);
1829 XawFormDoLayout(formWidget, True);
1831 xBoardWindow = XtWindow(boardWidget);
1833 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1834 // not need to go into InitDrawingSizes().
1838 * Create X checkmark bitmap and initialize option menu checks.
1840 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1841 checkmark_bits, checkmark_width, checkmark_height);
1847 ReadBitmap(&wIconPixmap, "icon_white.bm",
1848 icon_white_bits, icon_white_width, icon_white_height);
1849 ReadBitmap(&bIconPixmap, "icon_black.bm",
1850 icon_black_bits, icon_black_width, icon_black_height);
1851 iconPixmap = wIconPixmap;
1853 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1854 XtSetValues(shellWidget, args, i);
1857 * Create a cursor for the board widget.
1859 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1860 XChangeWindowAttributes(xDisplay, xBoardWindow,
1861 CWCursor, &window_attributes);
1864 * Inhibit shell resizing.
1866 shellArgs[0].value = (XtArgVal) &w;
1867 shellArgs[1].value = (XtArgVal) &h;
1868 XtGetValues(shellWidget, shellArgs, 2);
1869 shellArgs[4].value = shellArgs[2].value = w;
1870 shellArgs[5].value = shellArgs[3].value = h;
1871 XtSetValues(shellWidget, &shellArgs[2], 4);
1872 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1873 marginH = h - boardHeight;
1875 CatchDeleteWindow(shellWidget, "QuitProc");
1883 if (appData.animate || appData.animateDragging)
1886 XtAugmentTranslations(formWidget,
1887 XtParseTranslationTable(globalTranslations));
1888 XtAugmentTranslations(boardWidget,
1889 XtParseTranslationTable(boardTranslations));
1890 XtAugmentTranslations(whiteTimerWidget,
1891 XtParseTranslationTable(whiteTranslations));
1892 XtAugmentTranslations(blackTimerWidget,
1893 XtParseTranslationTable(blackTranslations));
1895 /* Why is the following needed on some versions of X instead
1896 * of a translation? */
1897 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
1898 (XtEventHandler) EventProc, NULL);
1900 XtAddEventHandler(formWidget, KeyPressMask, False,
1901 (XtEventHandler) MoveTypeInProc, NULL);
1902 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1903 (XtEventHandler) EventProc, NULL);
1905 /* [AS] Restore layout */
1906 if( wpMoveHistory.visible ) {
1910 if( wpEvalGraph.visible )
1915 if( wpEngineOutput.visible ) {
1916 EngineOutputPopUp();
1921 if (errorExitStatus == -1) {
1922 if (appData.icsActive) {
1923 /* We now wait until we see "login:" from the ICS before
1924 sending the logon script (problems with timestamp otherwise) */
1925 /*ICSInitScript();*/
1926 if (appData.icsInputBox) ICSInputBoxPopUp();
1930 signal(SIGWINCH, TermSizeSigHandler);
1932 signal(SIGINT, IntSigHandler);
1933 signal(SIGTERM, IntSigHandler);
1934 if (*appData.cmailGameName != NULLCHAR) {
1935 signal(SIGUSR1, CmailSigHandler);
1939 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1941 // XtSetKeyboardFocus(shellWidget, formWidget);
1942 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1944 XtAppMainLoop(appContext);
1945 if (appData.debugMode) fclose(debugFP); // [DM] debug
1950 TermSizeSigHandler (int sig)
1956 IntSigHandler (int sig)
1962 CmailSigHandler (int sig)
1967 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1969 /* Activate call-back function CmailSigHandlerCallBack() */
1970 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1972 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1976 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1979 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1981 /**** end signal code ****/
1984 #define Abs(n) ((n)<0 ? -(n) : (n))
1988 InsertPxlSize (char *pattern, int targetPxlSize)
1990 char *base_fnt_lst, strInt[12], *p, *q;
1991 int alternatives, i, len, strIntLen;
1994 * Replace the "*" (if present) in the pixel-size slot of each
1995 * alternative with the targetPxlSize.
1999 while ((p = strchr(p, ',')) != NULL) {
2003 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
2004 strIntLen = strlen(strInt);
2005 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
2009 while (alternatives--) {
2010 char *comma = strchr(p, ',');
2011 for (i=0; i<14; i++) {
2012 char *hyphen = strchr(p, '-');
2014 if (comma && hyphen > comma) break;
2015 len = hyphen + 1 - p;
2016 if (i == 7 && *p == '*' && len == 2) {
2018 memcpy(q, strInt, strIntLen);
2028 len = comma + 1 - p;
2035 return base_fnt_lst;
2039 CreateFontSet (char *base_fnt_lst)
2042 char **missing_list;
2046 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
2047 &missing_list, &missing_count, &def_string);
2048 if (appData.debugMode) {
2050 XFontStruct **font_struct_list;
2051 char **font_name_list;
2052 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
2054 fprintf(debugFP, " got list %s, locale %s\n",
2055 XBaseFontNameListOfFontSet(fntSet),
2056 XLocaleOfFontSet(fntSet));
2057 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
2058 for (i = 0; i < count; i++) {
2059 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
2062 for (i = 0; i < missing_count; i++) {
2063 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
2066 if (fntSet == NULL) {
2067 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
2072 #else // not ENABLE_NLS
2074 * Find a font that matches "pattern" that is as close as
2075 * possible to the targetPxlSize. Prefer fonts that are k
2076 * pixels smaller to fonts that are k pixels larger. The
2077 * pattern must be in the X Consortium standard format,
2078 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2079 * The return value should be freed with XtFree when no
2083 FindFont (char *pattern, int targetPxlSize)
2085 char **fonts, *p, *best, *scalable, *scalableTail;
2086 int i, j, nfonts, minerr, err, pxlSize;
2088 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2090 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2091 programName, pattern);
2098 for (i=0; i<nfonts; i++) {
2101 if (*p != '-') continue;
2103 if (*p == NULLCHAR) break;
2104 if (*p++ == '-') j++;
2106 if (j < 7) continue;
2109 scalable = fonts[i];
2112 err = pxlSize - targetPxlSize;
2113 if (Abs(err) < Abs(minerr) ||
2114 (minerr > 0 && err < 0 && -err == minerr)) {
2120 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2121 /* If the error is too big and there is a scalable font,
2122 use the scalable font. */
2123 int headlen = scalableTail - scalable;
2124 p = (char *) XtMalloc(strlen(scalable) + 10);
2125 while (isdigit(*scalableTail)) scalableTail++;
2126 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2128 p = (char *) XtMalloc(strlen(best) + 2);
2129 safeStrCpy(p, best, strlen(best)+1 );
2131 if (appData.debugMode) {
2132 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2133 pattern, targetPxlSize, p);
2135 XFreeFontNames(fonts);
2142 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
2143 // must be called before all non-first callse to CreateGCs()
2144 XtReleaseGC(shellWidget, highlineGC);
2145 XtReleaseGC(shellWidget, lightSquareGC);
2146 XtReleaseGC(shellWidget, darkSquareGC);
2147 XtReleaseGC(shellWidget, lineGC);
2148 if (appData.monoMode) {
2149 if (DefaultDepth(xDisplay, xScreen) == 1) {
2150 XtReleaseGC(shellWidget, wbPieceGC);
2152 XtReleaseGC(shellWidget, bwPieceGC);
2155 XtReleaseGC(shellWidget, prelineGC);
2156 XtReleaseGC(shellWidget, wdPieceGC);
2157 XtReleaseGC(shellWidget, wlPieceGC);
2158 XtReleaseGC(shellWidget, bdPieceGC);
2159 XtReleaseGC(shellWidget, blPieceGC);
2164 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
2166 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2167 | GCBackground | GCFunction | GCPlaneMask;
2168 gc_values->foreground = foreground;
2169 gc_values->background = background;
2170 return XtGetGC(shellWidget, value_mask, gc_values);
2174 CreateGCs (int redo)
2176 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2177 | GCBackground | GCFunction | GCPlaneMask;
2178 XGCValues gc_values;
2180 Pixel white = XWhitePixel(xDisplay, xScreen);
2181 Pixel black = XBlackPixel(xDisplay, xScreen);
2183 gc_values.plane_mask = AllPlanes;
2184 gc_values.line_width = lineGap;
2185 gc_values.line_style = LineSolid;
2186 gc_values.function = GXcopy;
2189 DeleteGCs(); // called a second time; clean up old GCs first
2190 } else { // [HGM] grid and font GCs created on first call only
2191 coordGC = CreateOneGC(&gc_values, black, white);
2192 XSetFont(xDisplay, coordGC, coordFontID);
2194 // [HGM] make font for holdings counts (white on black)
2195 countGC = CreateOneGC(&gc_values, white, black);
2196 XSetFont(xDisplay, countGC, countFontID);
2198 lineGC = CreateOneGC(&gc_values, black, black);
2200 if (appData.monoMode) {
2202 highlineGC = CreateOneGC(&gc_values, white, white);
2203 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
2204 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
2206 if (DefaultDepth(xDisplay, xScreen) == 1) {
2207 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2208 gc_values.function = GXcopyInverted;
2209 copyInvertedGC = CreateOneGC(&gc_values, black, white);
2210 gc_values.function = GXcopy;
2211 if (XBlackPixel(xDisplay, xScreen) == 1) {
2212 bwPieceGC = darkSquareGC;
2213 wbPieceGC = copyInvertedGC;
2215 bwPieceGC = copyInvertedGC;
2216 wbPieceGC = lightSquareGC;
2221 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
2222 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
2223 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
2224 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
2225 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
2226 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
2227 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
2228 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
2233 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
2241 fp = fopen(filename, "rb");
2243 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
2250 for (y=0; y<h; ++y) {
2251 for (x=0; x<h; ++x) {
2256 XPutPixel(xim, x, y, blackPieceColor);
2258 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2261 XPutPixel(xim, x, y, darkSquareColor);
2263 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2266 XPutPixel(xim, x, y, whitePieceColor);
2268 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2271 XPutPixel(xim, x, y, lightSquareColor);
2273 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2281 /* create Pixmap of piece */
2282 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2284 XPutImage(xDisplay, *dest, lightSquareGC, xim,
2287 /* create Pixmap of clipmask
2288 Note: We assume the white/black pieces have the same
2289 outline, so we make only 6 masks. This is okay
2290 since the XPM clipmask routines do the same. */
2292 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2294 XPutImage(xDisplay, temp, lightSquareGC, xmask,
2297 /* now create the 1-bit version */
2298 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2301 values.foreground = 1;
2302 values.background = 0;
2304 /* Don't use XtGetGC, not read only */
2305 maskGC = XCreateGC(xDisplay, *mask,
2306 GCForeground | GCBackground, &values);
2307 XCopyPlane(xDisplay, temp, *mask, maskGC,
2308 0, 0, squareSize, squareSize, 0, 0, 1);
2309 XFreePixmap(xDisplay, temp);
2314 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
2322 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
2327 /* The XSynchronize calls were copied from CreatePieces.
2328 Not sure if needed, but can't hurt */
2329 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2332 /* temp needed by loadXIM() */
2333 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2334 0, 0, ss, ss, AllPlanes, XYPixmap);
2336 if (strlen(appData.pixmapDirectory) == 0) {
2340 if (appData.monoMode) {
2341 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
2345 fprintf(stderr, _("\nLoading XIMs...\n"));
2347 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2348 fprintf(stderr, "%d", piece+1);
2349 for (kind=0; kind<4; kind++) {
2350 fprintf(stderr, ".");
2351 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2352 ExpandPathName(appData.pixmapDirectory),
2353 piece <= (int) WhiteKing ? "" : "w",
2354 pieceBitmapNames[piece],
2356 ximPieceBitmap[kind][piece] =
2357 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2358 0, 0, ss, ss, AllPlanes, XYPixmap);
2359 if (appData.debugMode)
2360 fprintf(stderr, _("(File:%s:) "), buf);
2361 loadXIM(ximPieceBitmap[kind][piece],
2363 &(xpmPieceBitmap2[kind][piece]),
2364 &(ximMaskPm2[piece]));
2365 if(piece <= (int)WhiteKing)
2366 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2368 fprintf(stderr," ");
2370 /* Load light and dark squares */
2371 /* If the LSQ and DSQ pieces don't exist, we will
2372 draw them with solid squares. */
2373 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2374 if (access(buf, 0) != 0) {
2378 fprintf(stderr, _("light square "));
2380 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2381 0, 0, ss, ss, AllPlanes, XYPixmap);
2382 if (appData.debugMode)
2383 fprintf(stderr, _("(File:%s:) "), buf);
2385 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2386 fprintf(stderr, _("dark square "));
2387 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2388 ExpandPathName(appData.pixmapDirectory), ss);
2389 if (appData.debugMode)
2390 fprintf(stderr, _("(File:%s:) "), buf);
2392 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2393 0, 0, ss, ss, AllPlanes, XYPixmap);
2394 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2395 xpmJailSquare = xpmLightSquare;
2397 fprintf(stderr, _("Done.\n"));
2399 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2402 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2406 CreateXPMBoard (char *s, int kind)
2410 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2411 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2412 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2418 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2419 // thisroutine has to be called t free the old piece pixmaps
2421 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2422 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2424 XFreePixmap(xDisplay, xpmLightSquare);
2425 XFreePixmap(xDisplay, xpmDarkSquare);
2434 u_int ss = squareSize;
2436 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2437 XpmColorSymbol symbols[4];
2438 static int redo = False;
2440 if(redo) FreeXPMPieces(); else redo = 1;
2442 /* The XSynchronize calls were copied from CreatePieces.
2443 Not sure if needed, but can't hurt */
2444 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2446 /* Setup translations so piece colors match square colors */
2447 symbols[0].name = "light_piece";
2448 symbols[0].value = appData.whitePieceColor;
2449 symbols[1].name = "dark_piece";
2450 symbols[1].value = appData.blackPieceColor;
2451 symbols[2].name = "light_square";
2452 symbols[2].value = appData.lightSquareColor;
2453 symbols[3].name = "dark_square";
2454 symbols[3].value = appData.darkSquareColor;
2456 attr.valuemask = XpmColorSymbols;
2457 attr.colorsymbols = symbols;
2458 attr.numsymbols = 4;
2460 if (appData.monoMode) {
2461 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2465 if (strlen(appData.pixmapDirectory) == 0) {
2466 XpmPieces* pieces = builtInXpms;
2469 while (pieces->size != squareSize && pieces->size) pieces++;
2470 if (!pieces->size) {
2471 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2474 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2475 for (kind=0; kind<4; kind++) {
2477 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2478 pieces->xpm[piece][kind],
2479 &(xpmPieceBitmap2[kind][piece]),
2480 NULL, &attr)) != 0) {
2481 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2485 if(piece <= (int) WhiteKing)
2486 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2490 xpmJailSquare = xpmLightSquare;
2494 fprintf(stderr, _("\nLoading XPMs...\n"));
2497 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2498 fprintf(stderr, "%d ", piece+1);
2499 for (kind=0; kind<4; kind++) {
2500 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2501 ExpandPathName(appData.pixmapDirectory),
2502 piece > (int) WhiteKing ? "w" : "",
2503 pieceBitmapNames[piece],
2505 if (appData.debugMode) {
2506 fprintf(stderr, _("(File:%s:) "), buf);
2508 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2509 &(xpmPieceBitmap2[kind][piece]),
2510 NULL, &attr)) != 0) {
2511 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2512 // [HGM] missing: read of unorthodox piece failed; substitute King.
2513 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2514 ExpandPathName(appData.pixmapDirectory),
2516 if (appData.debugMode) {
2517 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2519 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2520 &(xpmPieceBitmap2[kind][piece]),
2524 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2529 if(piece <= (int) WhiteKing)
2530 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2533 /* Load light and dark squares */
2534 /* If the LSQ and DSQ pieces don't exist, we will
2535 draw them with solid squares. */
2536 fprintf(stderr, _("light square "));
2537 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2538 if (access(buf, 0) != 0) {
2542 if (appData.debugMode)
2543 fprintf(stderr, _("(File:%s:) "), buf);
2545 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2546 &xpmLightSquare, NULL, &attr)) != 0) {
2547 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2550 fprintf(stderr, _("dark square "));
2551 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2552 ExpandPathName(appData.pixmapDirectory), ss);
2553 if (appData.debugMode) {
2554 fprintf(stderr, _("(File:%s:) "), buf);
2556 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2557 &xpmDarkSquare, NULL, &attr)) != 0) {
2558 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2562 xpmJailSquare = xpmLightSquare;
2563 fprintf(stderr, _("Done.\n"));
2565 oldVariant = -1; // kludge to force re-makig of animation masks
2566 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2569 #endif /* HAVE_LIBXPM */
2572 /* No built-in bitmaps */
2577 u_int ss = squareSize;
2579 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2582 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2583 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2584 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2585 pieceBitmapNames[piece],
2586 ss, kind == SOLID ? 's' : 'o');
2587 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2588 if(piece <= (int)WhiteKing)
2589 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2593 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2597 /* With built-in bitmaps */
2601 BuiltInBits* bib = builtInBits;
2604 u_int ss = squareSize;
2606 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2609 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2611 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2612 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2613 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2614 pieceBitmapNames[piece],
2615 ss, kind == SOLID ? 's' : 'o');
2616 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2617 bib->bits[kind][piece], ss, ss);
2618 if(piece <= (int)WhiteKing)
2619 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2623 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2629 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2634 char msg[MSG_SIZ], fullname[MSG_SIZ];
2636 if (*appData.bitmapDirectory != NULLCHAR) {
2637 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2638 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2639 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2640 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2641 &w, &h, pm, &x_hot, &y_hot);
2642 fprintf(stderr, "load %s\n", name);
2643 if (errcode != BitmapSuccess) {
2645 case BitmapOpenFailed:
2646 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2648 case BitmapFileInvalid:
2649 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2651 case BitmapNoMemory:
2652 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2656 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2660 fprintf(stderr, _("%s: %s...using built-in\n"),
2662 } else if (w != wreq || h != hreq) {
2664 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2665 programName, fullname, w, h, wreq, hreq);
2671 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2681 if (lineGap == 0) return;
2683 /* [HR] Split this into 2 loops for non-square boards. */
2685 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2686 gridSegments[i].x1 = 0;
2687 gridSegments[i].x2 =
2688 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2689 gridSegments[i].y1 = gridSegments[i].y2
2690 = lineGap / 2 + (i * (squareSize + lineGap));
2693 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2694 gridSegments[j + i].y1 = 0;
2695 gridSegments[j + i].y2 =
2696 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2697 gridSegments[j + i].x1 = gridSegments[j + i].x2
2698 = lineGap / 2 + (j * (squareSize + lineGap));
2702 int nrOfMenuItems = 7;
2703 Widget menuWidget[150];
2704 MenuListItem menuItemList[150] = {
2705 { "LoadNextGameProc", LoadNextGameProc },
2706 { "LoadPrevGameProc", LoadPrevGameProc },
2707 { "ReloadGameProc", ReloadGameProc },
2708 { "ReloadPositionProc", ReloadPositionProc },
2709 #ifndef OPTIONSDIALOG
2710 { "AlwaysQueenProc", AlwaysQueenProc },
2711 { "AnimateDraggingProc", AnimateDraggingProc },
2712 { "AnimateMovingProc", AnimateMovingProc },
2713 { "AutoflagProc", AutoflagProc },
2714 { "AutoflipProc", AutoflipProc },
2715 { "BlindfoldProc", BlindfoldProc },
2716 { "FlashMovesProc", FlashMovesProc },
2718 { "HighlightDraggingProc", HighlightDraggingProc },
2720 { "HighlightLastMoveProc", HighlightLastMoveProc },
2721 // { "IcsAlarmProc", IcsAlarmProc },
2722 { "MoveSoundProc", MoveSoundProc },
2723 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
2724 { "PopupExitMessageProc", PopupExitMessageProc },
2725 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
2726 // { "PremoveProc", PremoveProc },
2727 { "ShowCoordsProc", ShowCoordsProc },
2728 { "ShowThinkingProc", ShowThinkingProc },
2729 { "HideThinkingProc", HideThinkingProc },
2730 { "TestLegalityProc", TestLegalityProc },
2732 { "AboutGameProc", AboutGameEvent },
2733 { "DebugProc", DebugProc },
2734 { "NothingProc", NothingProc },
2739 MarkMenuItem (char *menuRef, int state)
2741 int nr = MenuToNumber(menuRef);
2744 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2745 XtSetValues(menuWidget[nr], args, 1);
2750 EnableMenuItem (char *menuRef, int state)
2752 int nr = MenuToNumber(menuRef);
2753 if(nr >= 0) XtSetSensitive(menuWidget[nr], state);
2757 EnableButtonBar (int state)
2759 XtSetSensitive(buttonBarWidget, state);
2764 SetMenuEnables (Enables *enab)
2766 while (enab->name != NULL) {
2767 EnableMenuItem(enab->name, enab->value);
2773 Equal(char *p, char *s)
2774 { // compare strings skipping spaces in second
2776 if(*s == ' ') { s++; continue; }
2777 if(*s++ != *p++) return 0;
2783 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2784 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2786 if(*nprms == 0) return;
2787 for(i=0; menuItemList[i].name; i++) {
2788 if(Equal(prms[0], menuItemList[i].name)) {
2789 (menuItemList[i].proc) ();
2796 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
2798 MenuProc *proc = (MenuProc *) addr;
2804 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2806 RecentEngineEvent((int) (intptr_t) addr);
2809 // some stuff that must remain in front-end
2810 static Widget mainBar, currentMenu;
2811 static int wtot, nr = 0, widths[10];
2814 AppendMenuItem (char *text, char *name, MenuProc *action)
2821 XtSetArg(args[j], XtNleftMargin, 20); j++;
2822 XtSetArg(args[j], XtNrightMargin, 20); j++;
2824 if (strcmp(text, "----") == 0) {
2825 entry = XtCreateManagedWidget(text, smeLineObjectClass,
2826 currentMenu, args, j);
2828 XtSetArg(args[j], XtNlabel, XtNewString(_(text)));
2829 entry = XtCreateManagedWidget(name, smeBSBObjectClass,
2830 currentMenu, args, j+1);
2831 XtAddCallback(entry, XtNcallback,
2832 (XtCallbackProc) (strcmp(name, "recent") ? MenuBarSelect : MenuEngineSelect),
2834 menuWidget[nrOfMenuItems] = entry;
2839 CreateMenuButton (char *name, Menu *mb)
2840 { // create menu button on main bar, and shell for pull-down list
2846 XtSetArg(args[j], XtNmenuName, XtNewString(name)); j++;
2847 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
2848 XtSetArg(args[j], XtNborderWidth, 0); j++;
2849 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
2851 currentMenu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2854 XtSetArg(args[j], XtNwidth, &w); j++;
2855 XtGetValues(mb->subMenu, args, j);
2856 wtot += mb->textWidth = widths[nr++] = w;
2860 CreateMenuBar (Menu *mb, int boardWidth)
2864 char menuName[MSG_SIZ];
2868 // create bar itself
2870 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2871 XtSetArg(args[j], XtNvSpace, 0); j++;
2872 XtSetArg(args[j], XtNborderWidth, 0); j++;
2873 mainBar = XtCreateWidget("menuBar", boxWidgetClass,
2874 formWidget, args, j);
2876 CreateMainMenus(mb); // put menus in bar according to description in back-end
2878 // size buttons to make menu bar fit, clipping menu names where necessary
2879 while(wtot > boardWidth - 40) {
2881 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
2885 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
2887 XtSetArg(args[j], XtNwidth, widths[i]); j++;
2888 XtSetValues(ma[i].subMenu, args, j);
2895 CreateButtonBar (MenuItem *mi)
2898 Widget button, buttonBar;
2902 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2904 XtSetArg(args[j], XtNhSpace, 0); j++;
2906 XtSetArg(args[j], XtNborderWidth, 0); j++;
2907 XtSetArg(args[j], XtNvSpace, 0); j++;
2908 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
2909 formWidget, args, j);
2911 while (mi->string != NULL) {
2914 XtSetArg(args[j], XtNinternalWidth, 2); j++;
2915 XtSetArg(args[j], XtNborderWidth, 0); j++;
2917 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
2918 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
2919 buttonBar, args, j);
2920 XtAddCallback(button, XtNcallback,
2921 (XtCallbackProc) MenuBarSelect,
2922 (caddr_t) mi->proc);
2929 CreatePieceMenu (char *name, int color)
2934 ChessSquare selection;
2936 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2937 boardWidget, args, 0);
2939 for (i = 0; i < PIECE_MENU_SIZE; i++) {
2940 String item = pieceMenuStrings[color][i];
2942 if (strcmp(item, "----") == 0) {
2943 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2946 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2947 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2949 selection = pieceMenuTranslation[color][i];
2950 XtAddCallback(entry, XtNcallback,
2951 (XtCallbackProc) PieceMenuSelect,
2952 (caddr_t) selection);
2953 if (selection == WhitePawn || selection == BlackPawn) {
2954 XtSetArg(args[0], XtNpopupOnEntry, entry);
2955 XtSetValues(menu, args, 1);
2968 ChessSquare selection;
2970 whitePieceMenu = CreatePieceMenu("menuW", 0);
2971 blackPieceMenu = CreatePieceMenu("menuB", 1);
2973 if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
2974 XtRegisterGrabAction(PieceMenuPopup, True,
2975 (unsigned)(ButtonPressMask|ButtonReleaseMask),
2976 GrabModeAsync, GrabModeAsync);
2978 XtSetArg(args[0], XtNlabel, _("Drop"));
2979 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
2980 boardWidget, args, 1);
2981 for (i = 0; i < DROP_MENU_SIZE; i++) {
2982 String item = dropMenuStrings[i];
2984 if (strcmp(item, "----") == 0) {
2985 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2988 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2989 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2991 selection = dropMenuTranslation[i];
2992 XtAddCallback(entry, XtNcallback,
2993 (XtCallbackProc) DropMenuSelect,
2994 (caddr_t) selection);
3008 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3009 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3010 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3011 dmEnables[i].piece);
3012 XtSetSensitive(entry, p != NULL || !appData.testLegality
3013 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3014 && !appData.icsActive));
3016 while (p && *p++ == dmEnables[i].piece) count++;
3017 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3019 XtSetArg(args[j], XtNlabel, label); j++;
3020 XtSetValues(entry, args, j);
3025 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
3027 String whichMenu; int menuNr = -2;
3028 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3029 if (event->type == ButtonRelease)
3030 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3031 else if (event->type == ButtonPress)
3032 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3034 case 0: whichMenu = params[0]; break;
3035 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3037 case -1: if (errorUp) ErrorPopDown();
3040 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3044 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3046 if (pmFromX < 0 || pmFromY < 0) return;
3047 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3051 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3053 if (pmFromX < 0 || pmFromY < 0) return;
3054 DropMenuEvent(piece, pmFromX, pmFromY);
3058 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3060 shiftKey = prms[0][0] & 1;
3065 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3067 shiftKey = prms[0][0] & 1;
3073 do_flash_delay (unsigned long msec)
3079 DrawBorder (int x, int y, int type)
3083 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
3085 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3086 squareSize+lineGap, squareSize+lineGap);
3090 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
3092 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
3093 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
3095 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
3096 if(textureW[kind] < W*squareSize)
3097 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
3099 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
3100 if(textureH[kind] < H*squareSize)
3101 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
3103 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
3108 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
3109 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
3111 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
3112 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
3113 squareSize, squareSize, x*fac, y*fac);
3115 if (useImages && useImageSqs) {
3119 pm = xpmLightSquare;
3124 case 2: /* neutral */
3126 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
3129 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3130 squareSize, squareSize, x*fac, y*fac);
3140 case 2: /* neutral */
3145 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
3150 I split out the routines to draw a piece so that I could
3151 make a generic flash routine.
3154 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3156 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3157 switch (square_color) {
3159 case 2: /* neutral */
3161 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3162 ? *pieceToOutline(piece)
3163 : *pieceToSolid(piece),
3164 dest, bwPieceGC, 0, 0,
3165 squareSize, squareSize, x, y);
3168 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3169 ? *pieceToSolid(piece)
3170 : *pieceToOutline(piece),
3171 dest, wbPieceGC, 0, 0,
3172 squareSize, squareSize, x, y);
3178 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3180 switch (square_color) {
3182 case 2: /* neutral */
3184 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3185 ? *pieceToOutline(piece)
3186 : *pieceToSolid(piece),
3187 dest, bwPieceGC, 0, 0,
3188 squareSize, squareSize, x, y, 1);
3191 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3192 ? *pieceToSolid(piece)
3193 : *pieceToOutline(piece),
3194 dest, wbPieceGC, 0, 0,
3195 squareSize, squareSize, x, y, 1);
3201 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3203 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
3204 switch (square_color) {
3206 XCopyPlane(xDisplay, *pieceToSolid(piece),
3207 dest, (int) piece < (int) BlackPawn
3208 ? wlPieceGC : blPieceGC, 0, 0,
3209 squareSize, squareSize, x, y, 1);
3212 XCopyPlane(xDisplay, *pieceToSolid(piece),
3213 dest, (int) piece < (int) BlackPawn
3214 ? wdPieceGC : bdPieceGC, 0, 0,
3215 squareSize, squareSize, x, y, 1);
3217 case 2: /* neutral */
3219 break; // should never contain pieces
3224 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3226 int kind, p = piece;
3228 switch (square_color) {
3230 case 2: /* neutral */
3232 if ((int)piece < (int) BlackPawn) {
3240 if ((int)piece < (int) BlackPawn) {
3248 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
3249 if(useTexture & square_color+1) {
3250 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
3251 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
3252 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
3253 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
3254 XSetClipMask(xDisplay, wlPieceGC, None);
3255 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
3257 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3258 dest, wlPieceGC, 0, 0,
3259 squareSize, squareSize, x, y);
3262 typedef void (*DrawFunc)();
3267 if (appData.monoMode) {
3268 if (DefaultDepth(xDisplay, xScreen) == 1) {
3269 return monoDrawPiece_1bit;
3271 return monoDrawPiece;
3275 return colorDrawPieceImage;
3277 return colorDrawPiece;
3282 DrawDot (int marker, int x, int y, int r)
3284 if(appData.monoMode) {
3285 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
3286 x, y, r, r, 0, 64*360);
3287 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
3288 x, y, r, r, 0, 64*360);
3290 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
3291 x, y, r, r, 0, 64*360);
3295 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
3296 { // basic front-end board-draw function: takes care of everything that can be in square:
3297 // piece, background, coordinate/count, marker dot
3298 int direction, font_ascent, font_descent;
3299 XCharStruct overall;
3302 if (piece == EmptySquare) {
3303 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
3305 drawfunc = ChooseDrawFunc();
3306 drawfunc(piece, square_color, x, y, xBoardWindow);
3309 if(align) { // square carries inscription (coord or piece count)
3311 GC hGC = align < 3 ? coordGC : countGC;
3312 // first calculate where it goes
3313 XTextExtents(countFontStruct, string, 1, &direction,
3314 &font_ascent, &font_descent, &overall);
3316 xx += squareSize - overall.width - 2;
3317 yy += squareSize - font_descent - 1;
3318 } else if (align == 2) {
3319 xx += 2, yy += font_ascent + 1;
3320 } else if (align == 3) {
3321 xx += squareSize - overall.width - 2;
3322 yy += font_ascent + 1;
3323 } else if (align == 4) {
3324 xx += 2, yy += font_ascent + 1;
3327 if (appData.monoMode) {
3328 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3330 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3334 if(marker) { // print fat marker dot, if requested
3335 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
3340 FlashDelay (int flash_delay)
3342 XSync(xDisplay, False);
3343 if(flash_delay) do_flash_delay(flash_delay);
3347 Fraction (int x, int start, int stop)
3349 double f = ((double) x - start)/(stop - start);
3350 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
3354 static WindowPlacement wpNew;
3357 CoDrag (Widget sh, WindowPlacement *wp)
3360 int j=0, touch=0, fudge = 2;
3361 GetActualPlacement(sh, wp);
3362 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
3363 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
3364 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
3365 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
3366 if(!touch ) return; // only windows that touch co-move
3367 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
3368 int heightInc = wpNew.height - wpMain.height;
3369 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3370 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3371 wp->y += fracTop * heightInc;
3372 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
3373 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
3374 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
3375 int widthInc = wpNew.width - wpMain.width;
3376 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3377 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3378 wp->y += fracLeft * widthInc;
3379 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
3380 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
3382 wp->x += wpNew.x - wpMain.x;
3383 wp->y += wpNew.y - wpMain.y;
3384 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
3385 if(touch == 3) wp->y += wpNew.height - wpMain.height;
3386 XtSetArg(args[j], XtNx, wp->x); j++;
3387 XtSetArg(args[j], XtNy, wp->y); j++;
3388 XtSetValues(sh, args, j);
3391 static XtIntervalId delayedDragID = 0;
3396 GetActualPlacement(shellWidget, &wpNew);
3397 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
3398 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
3399 return; // false alarm
3400 if(EngineOutputIsUp()) CoDrag(engineOutputShell, &wpEngineOutput);
3401 if(MoveHistoryIsUp()) CoDrag(shells[HistoryDlg], &wpMoveHistory);
3402 if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
3403 if(GameListIsUp()) CoDrag(gameListShell, &wpGameList);
3405 DrawPosition(True, NULL);
3406 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
3413 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
3415 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
3418 /* Why is this needed on some versions of X? */
3420 EventProc (Widget widget, caddr_t unused, XEvent *event)
3422 if (!XtIsRealized(widget))
3424 switch (event->type) {
3425 case ConfigureNotify: // main window is being dragged: drag attached windows with it
3426 if(appData.useStickyWindows)
3427 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
3430 if (event->xexpose.count > 0) return; /* no clipping is done */
3431 DrawPosition(True, NULL);
3432 if(twoBoards) { // [HGM] dual: draw other board in other orientation
3433 flipView = !flipView; partnerUp = !partnerUp;
3434 DrawPosition(True, NULL);
3435 flipView = !flipView; partnerUp = !partnerUp;
3439 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
3446 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
3448 DrawSeekAxis (int x, int y, int xTo, int yTo)
3450 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
3454 DrawSeekBackground (int left, int top, int right, int bottom)
3456 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
3460 DrawSeekText (char *buf, int x, int y)
3462 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
3466 DrawSeekDot (int x, int y, int colorNr)
3468 int square = colorNr & 0x80;
3471 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
3473 XFillRectangle(xDisplay, xBoardWindow, color,
3474 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3476 XFillArc(xDisplay, xBoardWindow, color,
3477 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
3481 DrawGrid (int second)
3483 XDrawSegments(xDisplay, xBoardWindow, lineGC,
3484 second ? secondSegments : // [HGM] dual
3485 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
3490 * event handler for redrawing the board
3493 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3495 DrawPosition(True, NULL);
3500 * event handler for parsing user moves
3502 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
3503 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
3504 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
3505 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
3506 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
3507 // and at the end FinishMove() to perform the move after optional promotion popups.
3508 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
3510 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3512 if (w != boardWidget || errorExitStatus != -1) return;
3513 if(nprms) shiftKey = !strcmp(prms[0], "1");
3516 if (event->type == ButtonPress) {
3517 XtPopdown(promotionShell);
3518 XtDestroyWidget(promotionShell);
3519 promotionUp = False;
3527 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
3528 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
3529 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
3533 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
3535 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
3536 DragPieceMove(event->xmotion.x, event->xmotion.y);
3540 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3541 { // [HGM] pv: walk PV
3542 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3545 static int savedIndex; /* gross that this is global */
3548 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3551 XawTextPosition index, dummy;
3554 XawTextGetSelectionPos(w, &index, &dummy);
3555 XtSetArg(arg, XtNstring, &val);
3556 XtGetValues(w, &arg, 1);
3557 ReplaceComment(savedIndex, val);
3558 if(savedIndex != currentMove) ToNrEvent(savedIndex);
3559 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3563 EditCommentPopUp (int index, char *title, char *text)
3566 if (text == NULL) text = "";
3567 NewCommentPopup(title, text, index);
3571 CommentPopUp (char *title, char *text)
3573 savedIndex = currentMove; // [HGM] vari
3574 NewCommentPopup(title, text, currentMove);
3580 PopDown(CommentDlg);
3583 static char *openName;
3589 (void) (*fileProc)(openFP, 0, openName);
3593 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
3595 fileProc = proc; /* I can't see a way not */
3596 fileOpenMode = openMode; /* to use globals here */
3597 { // [HGM] use file-selector dialog stolen from Ghostview
3598 int index; // this is not supported yet
3599 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
3600 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
3601 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
3602 ScheduleDelayedEvent(&DelayedLoad, 50);
3610 Widget dialog, layout;
3612 Dimension bw_width, pw_width;
3614 char *PromoChars = "wglcqrbnkac+=\0";
3617 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3618 XtGetValues(boardWidget, args, j);
3621 XtSetArg(args[j], XtNresizable, True); j++;
3622 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
3624 XtCreatePopupShell("Promotion", transientShellWidgetClass,
3625 shellWidget, args, j);
3627 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
3628 layoutArgs, XtNumber(layoutArgs));
3631 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
3632 XtSetArg(args[j], XtNborderWidth, 0); j++;
3633 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
3636 if(gameInfo.variant != VariantShogi) {
3637 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
3638 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
3639 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
3640 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
3641 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
3643 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
3644 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
3645 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
3646 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
3648 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
3649 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
3650 gameInfo.variant == VariantGiveaway) {
3651 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
3653 if(gameInfo.variant == VariantCapablanca ||
3654 gameInfo.variant == VariantGothic ||
3655 gameInfo.variant == VariantCapaRandom) {
3656 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
3657 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
3659 } else // [HGM] shogi
3661 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
3662 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
3664 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
3666 XtRealizeWidget(promotionShell);
3667 CatchDeleteWindow(promotionShell, "PromotionPopDown");
3670 XtSetArg(args[j], XtNwidth, &pw_width); j++;
3671 XtGetValues(promotionShell, args, j);
3673 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3674 lineGap + squareSize/3 +
3675 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
3676 0 : 6*(squareSize + lineGap)), &x, &y);
3679 XtSetArg(args[j], XtNx, x); j++;
3680 XtSetArg(args[j], XtNy, y); j++;
3681 XtSetValues(promotionShell, args, j);
3683 XtPopup(promotionShell, XtGrabNone);
3691 if (!promotionUp) return;
3692 XtPopdown(promotionShell);
3693 XtDestroyWidget(promotionShell);
3694 promotionUp = False;
3698 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
3700 int promoChar = * (const char *) client_data;
3704 if (fromX == -1) return;
3711 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
3713 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
3714 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3720 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
3722 dialogError = errorUp = False;
3723 XtPopdown(w = XtParent(XtParent(XtParent(w))));
3725 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3732 if (!errorUp) return;
3733 dialogError = errorUp = False;
3734 XtPopdown(errorShell);
3735 XtDestroyWidget(errorShell);
3736 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3740 ErrorPopUp (char *title, char *label, int modal)
3743 Widget dialog, layout;
3747 Dimension bw_width, pw_width;
3748 Dimension pw_height;
3752 XtSetArg(args[i], XtNresizable, True); i++;
3753 XtSetArg(args[i], XtNtitle, title); i++;
3755 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
3756 shellUp[TransientDlg] ? (dialogError = modal = TRUE, shells[TransientDlg]) : shellWidget, args, i);
3758 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
3759 layoutArgs, XtNumber(layoutArgs));
3762 XtSetArg(args[i], XtNlabel, label); i++;
3763 XtSetArg(args[i], XtNborderWidth, 0); i++;
3764 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
3767 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
3769 XtRealizeWidget(errorShell);
3770 CatchDeleteWindow(errorShell, "ErrorPopDown");
3773 XtSetArg(args[i], XtNwidth, &bw_width); i++;
3774 XtGetValues(boardWidget, args, i);
3776 XtSetArg(args[i], XtNwidth, &pw_width); i++;
3777 XtSetArg(args[i], XtNheight, &pw_height); i++;
3778 XtGetValues(errorShell, args, i);
3781 /* This code seems to tickle an X bug if it is executed too soon
3782 after xboard starts up. The coordinates get transformed as if
3783 the main window was positioned at (0, 0).
3785 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3786 0 - pw_height + squareSize / 3, &x, &y);
3788 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
3789 RootWindowOfScreen(XtScreen(boardWidget)),
3790 (bw_width - pw_width) / 2,
3791 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
3795 if (y < 0) y = 0; /*avoid positioning top offscreen*/
3798 XtSetArg(args[i], XtNx, x); i++;
3799 XtSetArg(args[i], XtNy, y); i++;
3800 XtSetValues(errorShell, args, i);
3803 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
3806 /* Disable all user input other than deleting the window */
3807 static int frozen = 0;
3813 /* Grab by a widget that doesn't accept input */
3814 XtAddGrab(messageWidget, TRUE, FALSE);
3818 /* Undo a FreezeUI */
3822 if (!frozen) return;
3823 XtRemoveGrab(messageWidget);
3831 static int oldPausing = FALSE;
3832 static GameMode oldmode = (GameMode) -1;
3835 if (!boardWidget || !XtIsRealized(boardWidget)) return;
3837 if (pausing != oldPausing) {
3838 oldPausing = pausing;
3839 MarkMenuItem("Pause", pausing);
3841 if (appData.showButtonBar) {
3842 /* Always toggle, don't set. Previous code messes up when
3843 invoked while the button is pressed, as releasing it
3844 toggles the state again. */
3847 XtSetArg(args[0], XtNbackground, &oldbg);
3848 XtSetArg(args[1], XtNforeground, &oldfg);
3849 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
3851 XtSetArg(args[0], XtNbackground, oldfg);
3852 XtSetArg(args[1], XtNforeground, oldbg);
3854 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
3858 wname = ModeToWidgetName(oldmode);
3859 if (wname != NULL) {
3860 MarkMenuItem(wname, False);
3862 wname = ModeToWidgetName(gameMode);
3863 if (wname != NULL) {
3864 MarkMenuItem(wname, True);
3867 MarkMenuItem("Machine Match", matchMode && matchGame < appData.matchGames);
3869 /* Maybe all the enables should be handled here, not just this one */
3870 EnableMenuItem("Training", gameMode == Training || gameMode == PlayFromGameFile);
3875 * Button/menu procedures
3878 LoadGamePopUp (FILE *f, int gameNumber, char *title)
3880 cmailMsgLoaded = FALSE;
3881 if (gameNumber == 0) {
3882 int error = GameListBuild(f);
3884 DisplayError(_("Cannot build game list"), error);
3885 } else if (!ListEmpty(&gameList) &&
3886 ((ListGame *) gameList.tailPred)->number > 1) {
3887 GameListPopUp(f, title);
3893 return LoadGame(f, gameNumber, title, FALSE);
3896 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3897 char *selected_fen_position=NULL;
3900 SendPositionSelection (Widget w, Atom *selection, Atom *target,
3901 Atom *type_return, XtPointer *value_return,
3902 unsigned long *length_return, int *format_return)
3904 char *selection_tmp;
3906 // if (!selected_fen_position) return False; /* should never happen */
3907 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3908 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
3909 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
3912 if (f == NULL) return False;
3916 selection_tmp = XtMalloc(len + 1);
3917 count = fread(selection_tmp, 1, len, f);
3920 XtFree(selection_tmp);
3923 selection_tmp[len] = NULLCHAR;
3925 /* note: since no XtSelectionDoneProc was registered, Xt will
3926 * automatically call XtFree on the value returned. So have to
3927 * make a copy of it allocated with XtMalloc */
3928 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3929 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
3932 *value_return=selection_tmp;
3933 *length_return=strlen(selection_tmp);
3934 *type_return=*target;
3935 *format_return = 8; /* bits per byte */
3937 } else if (*target == XA_TARGETS(xDisplay)) {
3938 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3939 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3940 targets_tmp[1] = XA_STRING;
3941 *value_return = targets_tmp;
3942 *type_return = XA_ATOM;
3945 // This code leads to a read of value_return out of bounds on 64-bit systems.
3946 // Other code which I have seen always sets *format_return to 32 independent of
3947 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
3948 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
3949 *format_return = 8 * sizeof(Atom);
3950 if (*format_return > 32) {
3951 *length_return *= *format_return / 32;
3952 *format_return = 32;
3955 *format_return = 32;
3963 /* note: when called from menu all parameters are NULL, so no clue what the
3964 * Widget which was clicked on was, or what the click event was
3967 CopySomething (char *src)
3969 selected_fen_position = src;
3971 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3972 * have a notion of a position that is selected but not copied.
3973 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3975 XtOwnSelection(menuBarWidget, XA_PRIMARY,
3977 SendPositionSelection,
3978 NULL/* lose_ownership_proc */ ,
3979 NULL/* transfer_done_proc */);
3980 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3982 SendPositionSelection,
3983 NULL/* lose_ownership_proc */ ,
3984 NULL/* transfer_done_proc */);
3987 /* function called when the data to Paste is ready */
3989 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3990 Atom *type, XtPointer value, unsigned long *len, int *format)
3993 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3994 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3995 EditPositionPasteFEN(fenstr);
3999 /* called when Paste Position button is pressed,
4000 * all parameters will be NULL */
4002 PastePositionProc ()
4004 XtGetSelectionValue(menuBarWidget,
4005 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4006 /* (XtSelectionCallbackProc) */ PastePositionCB,
4007 NULL, /* client_data passed to PastePositionCB */
4009 /* better to use the time field from the event that triggered the
4010 * call to this function, but that isn't trivial to get
4017 /* note: when called from menu all parameters are NULL, so no clue what the
4018 * Widget which was clicked on was, or what the click event was
4020 /* function called when the data to Paste is ready */
4022 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
4023 Atom *type, XtPointer value, unsigned long *len, int *format)
4026 if (value == NULL || *len == 0) {
4027 return; /* nothing had been selected to copy */
4029 f = fopen(gamePasteFilename, "w");
4031 DisplayError(_("Can't open temp file"), errno);
4034 fwrite(value, 1, *len, f);
4037 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
4040 /* called when Paste Game button is pressed,
4041 * all parameters will be NULL */
4045 XtGetSelectionValue(menuBarWidget,
4046 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4047 /* (XtSelectionCallbackProc) */ PasteGameCB,
4048 NULL, /* client_data passed to PasteGameCB */
4050 /* better to use the time field from the event that triggered the
4051 * call to this function, but that isn't trivial to get
4060 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4067 { // bassic primitive for determining if modifier keys are pressed
4068 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
4071 XQueryKeymap(xDisplay,keys);
4072 for(i=0; i<6; i++) {
4074 j = XKeysymToKeycode(xDisplay, codes[i]);
4075 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
4081 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
4085 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
4086 if ( n == 1 && *buf >= 32 // printable
4087 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
4088 ) BoxAutoPopUp (buf);
4092 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4093 { // [HGM] input: let up-arrow recall previous line from history
4098 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4099 { // [HGM] input: let down-arrow recall next line from history
4104 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4110 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4112 if (!TempBackwardActive) {
4113 TempBackwardActive = True;
4119 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4121 /* Check to see if triggered by a key release event for a repeating key.
4122 * If so the next queued event will be a key press of the same key at the same time */
4123 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
4125 XPeekEvent(xDisplay, &next);
4126 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
4127 next.xkey.keycode == event->xkey.keycode)
4131 TempBackwardActive = False;
4135 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4136 { // called as key binding
4139 if (nprms && *nprms > 0)
4143 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
4148 DisplayMessage (char *message, char *extMessage)
4150 /* display a message in the message widget */
4159 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
4164 message = extMessage;
4168 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
4170 /* need to test if messageWidget already exists, since this function
4171 can also be called during the startup, if for example a Xresource
4172 is not set up correctly */
4175 XtSetArg(arg, XtNlabel, message);
4176 XtSetValues(messageWidget, &arg, 1);
4183 SetWindowTitle (char *text, char *title, char *icon)
4187 if (appData.titleInWindow) {
4189 XtSetArg(args[i], XtNlabel, text); i++;
4190 XtSetValues(titleWidget, args, i);
4193 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
4194 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
4195 XtSetValues(shellWidget, args, i);
4196 XSync(xDisplay, False);
4201 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
4207 DisplayIcsInteractionTitle (String message)
4209 if (oldICSInteractionTitle == NULL) {
4210 /* Magic to find the old window title, adapted from vim */
4211 char *wina = getenv("WINDOWID");
4213 Window win = (Window) atoi(wina);
4214 Window root, parent, *children;
4215 unsigned int nchildren;
4216 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4218 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4219 if (!XQueryTree(xDisplay, win, &root, &parent,
4220 &children, &nchildren)) break;
4221 if (children) XFree((void *)children);
4222 if (parent == root || parent == 0) break;
4225 XSetErrorHandler(oldHandler);
4227 if (oldICSInteractionTitle == NULL) {
4228 oldICSInteractionTitle = "xterm";
4231 printf("\033]0;%s\007", message);
4235 char pendingReplyPrefix[MSG_SIZ];
4236 ProcRef pendingReplyPR;
4239 AskQuestionPopDown ()
4241 if (!askQuestionUp) return;
4242 XtPopdown(askQuestionShell);
4243 XtDestroyWidget(askQuestionShell);
4244 askQuestionUp = False;
4248 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4254 reply = XawDialogGetValueString(w = XtParent(w));
4255 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
4256 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
4257 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
4258 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
4259 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
4260 AskQuestionPopDown();
4262 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
4266 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
4271 XtSetArg(args[0], XtNlabel, &name);
4272 XtGetValues(w, args, 1);
4274 if (strcmp(name, _("cancel")) == 0) {
4275 AskQuestionPopDown();
4277 AskQuestionReplyAction(w, NULL, NULL, NULL);
4282 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
4285 Widget popup, layout, dialog, edit;
4291 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
4292 pendingReplyPR = pr;
4295 XtSetArg(args[i], XtNresizable, True); i++;
4296 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
4297 askQuestionShell = popup =
4298 XtCreatePopupShell(title, transientShellWidgetClass,
4299 shellWidget, args, i);
4302 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
4303 layoutArgs, XtNumber(layoutArgs));
4306 XtSetArg(args[i], XtNlabel, question); i++;
4307 XtSetArg(args[i], XtNvalue, ""); i++;
4308 XtSetArg(args[i], XtNborderWidth, 0); i++;
4309 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
4312 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
4313 (XtPointer) dialog);
4314 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
4315 (XtPointer) dialog);
4317 XtRealizeWidget(popup);
4318 CatchDeleteWindow(popup, "AskQuestionPopDown");
4320 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
4321 &x, &y, &win_x, &win_y, &mask);
4323 XtSetArg(args[0], XtNx, x - 10);
4324 XtSetArg(args[1], XtNy, y - 30);
4325 XtSetValues(popup, args, 2);
4327 XtPopup(popup, XtGrabExclusive);
4328 askQuestionUp = True;
4330 edit = XtNameToWidget(dialog, "*value");
4331 XtSetKeyboardFocus(popup, edit);
4335 XtIntervalId delayedEventTimerXID = 0;
4336 DelayedEventCallback delayedEventCallback = 0;
4341 delayedEventTimerXID = 0;
4342 delayedEventCallback();
4346 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
4348 if(delayedEventTimerXID && delayedEventCallback == cb)
4349 // [HGM] alive: replace, rather than add or flush identical event
4350 XtRemoveTimeOut(delayedEventTimerXID);
4351 delayedEventCallback = cb;
4352 delayedEventTimerXID =
4353 XtAppAddTimeOut(appContext, millisec,
4354 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
4357 DelayedEventCallback
4360 if (delayedEventTimerXID) {
4361 return delayedEventCallback;
4368 CancelDelayedEvent ()
4370 if (delayedEventTimerXID) {
4371 XtRemoveTimeOut(delayedEventTimerXID);
4372 delayedEventTimerXID = 0;
4376 XtIntervalId loadGameTimerXID = 0;
4379 LoadGameTimerRunning ()
4381 return loadGameTimerXID != 0;
4385 StopLoadGameTimer ()
4387 if (loadGameTimerXID != 0) {
4388 XtRemoveTimeOut(loadGameTimerXID);
4389 loadGameTimerXID = 0;
4397 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
4399 loadGameTimerXID = 0;
4404 StartLoadGameTimer (long millisec)
4407 XtAppAddTimeOut(appContext, millisec,
4408 (XtTimerCallbackProc) LoadGameTimerCallback,
4412 XtIntervalId analysisClockXID = 0;
4415 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
4417 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
4418 || appData.icsEngineAnalyze) { // [DM]
4419 AnalysisPeriodicEvent(0);
4420 StartAnalysisClock();
4425 StartAnalysisClock ()
4428 XtAppAddTimeOut(appContext, 2000,
4429 (XtTimerCallbackProc) AnalysisClockCallback,
4433 XtIntervalId clockTimerXID = 0;
4436 ClockTimerRunning ()
4438 return clockTimerXID != 0;
4444 if (clockTimerXID != 0) {
4445 XtRemoveTimeOut(clockTimerXID);
4454 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
4461 StartClockTimer (long millisec)
4464 XtAppAddTimeOut(appContext, millisec,
4465 (XtTimerCallbackProc) ClockTimerCallback,
4470 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
4475 /* check for low time warning */
4476 Pixel foregroundOrWarningColor = timerForegroundPixel;
4479 appData.lowTimeWarning &&
4480 (timer / 1000) < appData.icsAlarmTime)
4481 foregroundOrWarningColor = lowTimeWarningColor;
4483 if (appData.clockMode) {
4484 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
4485 XtSetArg(args[0], XtNlabel, buf);
4487 snprintf(buf, MSG_SIZ, "%s ", color);
4488 XtSetArg(args[0], XtNlabel, buf);
4493 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
4494 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
4496 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
4497 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
4500 XtSetValues(w, args, 3);
4504 DisplayWhiteClock (long timeRemaining, int highlight)
4508 if(appData.noGUI) return;
4509 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
4510 if (highlight && iconPixmap == bIconPixmap) {
4511 iconPixmap = wIconPixmap;
4512 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
4513 XtSetValues(shellWidget, args, 1);
4518 DisplayBlackClock (long timeRemaining, int highlight)
4522 if(appData.noGUI) return;
4523 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
4524 if (highlight && iconPixmap == wIconPixmap) {
4525 iconPixmap = bIconPixmap;
4526 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
4527 XtSetValues(shellWidget, args, 1);
4532 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
4534 InputSource *is = (InputSource *) closure;
4539 if (is->lineByLine) {
4540 count = read(is->fd, is->unused,
4541 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
4543 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
4546 is->unused += count;
4548 while (p < is->unused) {
4549 q = memchr(p, '\n', is->unused - p);
4550 if (q == NULL) break;
4552 (is->func)(is, is->closure, p, q - p, 0);
4556 while (p < is->unused) {
4561 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
4566 (is->func)(is, is->closure, is->buf, count, error);
4571 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
4574 ChildProc *cp = (ChildProc *) pr;
4576 is = (InputSource *) calloc(1, sizeof(InputSource));
4577 is->lineByLine = lineByLine;
4581 is->fd = fileno(stdin);
4583 is->kind = cp->kind;
4584 is->fd = cp->fdFrom;
4587 is->unused = is->buf;
4590 is->xid = XtAppAddInput(appContext, is->fd,
4591 (XtPointer) (XtInputReadMask),
4592 (XtInputCallbackProc) DoInputCallback,
4594 is->closure = closure;
4595 return (InputSourceRef) is;
4599 RemoveInputSource (InputSourceRef isr)
4601 InputSource *is = (InputSource *) isr;
4603 if (is->xid == 0) return;
4604 XtRemoveInput(is->xid);
4608 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
4610 /* Masks for XPM pieces. Black and white pieces can have
4611 different shapes, but in the interest of retaining my
4612 sanity pieces must have the same outline on both light
4613 and dark squares, and all pieces must use the same
4614 background square colors/images. */
4616 static int xpmDone = 0;
4617 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
4618 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
4621 CreateAnimMasks (int pieceDepth)
4627 unsigned long plane;
4630 /* Need a bitmap just to get a GC with right depth */
4631 buf = XCreatePixmap(xDisplay, xBoardWindow,
4633 values.foreground = 1;
4634 values.background = 0;
4635 /* Don't use XtGetGC, not read only */
4636 maskGC = XCreateGC(xDisplay, buf,
4637 GCForeground | GCBackground, &values);
4638 XFreePixmap(xDisplay, buf);
4640 buf = XCreatePixmap(xDisplay, xBoardWindow,
4641 squareSize, squareSize, pieceDepth);
4642 values.foreground = XBlackPixel(xDisplay, xScreen);
4643 values.background = XWhitePixel(xDisplay, xScreen);
4644 bufGC = XCreateGC(xDisplay, buf,
4645 GCForeground | GCBackground, &values);
4647 for (piece = WhitePawn; piece <= BlackKing; piece++) {
4648 /* Begin with empty mask */
4649 if(!xpmDone) // [HGM] pieces: keep using existing
4650 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
4651 squareSize, squareSize, 1);
4652 XSetFunction(xDisplay, maskGC, GXclear);
4653 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
4654 0, 0, squareSize, squareSize);
4656 /* Take a copy of the piece */
4661 XSetFunction(xDisplay, bufGC, GXcopy);
4662 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
4664 0, 0, squareSize, squareSize, 0, 0);
4666 /* XOR the background (light) over the piece */
4667 XSetFunction(xDisplay, bufGC, GXxor);
4669 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
4670 0, 0, squareSize, squareSize, 0, 0);
4672 XSetForeground(xDisplay, bufGC, lightSquareColor);
4673 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
4676 /* We now have an inverted piece image with the background
4677 erased. Construct mask by just selecting all the non-zero
4678 pixels - no need to reconstruct the original image. */
4679 XSetFunction(xDisplay, maskGC, GXor);
4681 /* Might be quicker to download an XImage and create bitmap
4682 data from it rather than this N copies per piece, but it
4683 only takes a fraction of a second and there is a much
4684 longer delay for loading the pieces. */
4685 for (n = 0; n < pieceDepth; n ++) {
4686 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
4687 0, 0, squareSize, squareSize,
4693 XFreePixmap(xDisplay, buf);
4694 XFreeGC(xDisplay, bufGC);
4695 XFreeGC(xDisplay, maskGC);
4699 InitAnimState (AnimNr anr, XWindowAttributes *info)
4704 /* Each buffer is square size, same depth as window */
4705 animBufs[anr+4] = xBoardWindow;
4706 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
4707 squareSize, squareSize, info->depth);
4708 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
4709 squareSize, squareSize, info->depth);
4711 /* Create a plain GC for blitting */
4712 mask = GCForeground | GCBackground | GCFunction |
4713 GCPlaneMask | GCGraphicsExposures;
4714 values.foreground = XBlackPixel(xDisplay, xScreen);
4715 values.background = XWhitePixel(xDisplay, xScreen);
4716 values.function = GXcopy;
4717 values.plane_mask = AllPlanes;
4718 values.graphics_exposures = False;
4719 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
4721 /* Piece will be copied from an existing context at
4722 the start of each new animation/drag. */
4723 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
4725 /* Outline will be a read-only copy of an existing */
4726 animGCs[anr+4] = None;
4732 XWindowAttributes info;
4734 if (xpmDone && gameInfo.variant == oldVariant) return;
4735 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
4736 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
4738 InitAnimState(Game, &info);
4739 InitAnimState(Player, &info);
4741 /* For XPM pieces, we need bitmaps to use as masks. */
4743 CreateAnimMasks(info.depth), xpmDone = 1;
4748 static Boolean frameWaiting;
4751 FrameAlarm (int sig)
4753 frameWaiting = False;
4754 /* In case System-V style signals. Needed?? */
4755 signal(SIGALRM, FrameAlarm);
4759 FrameDelay (int time)
4761 struct itimerval delay;
4763 XSync(xDisplay, False);
4766 frameWaiting = True;
4767 signal(SIGALRM, FrameAlarm);
4768 delay.it_interval.tv_sec =
4769 delay.it_value.tv_sec = time / 1000;
4770 delay.it_interval.tv_usec =
4771 delay.it_value.tv_usec = (time % 1000) * 1000;
4772 setitimer(ITIMER_REAL, &delay, NULL);
4773 while (frameWaiting) pause();
4774 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
4775 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
4776 setitimer(ITIMER_REAL, &delay, NULL);
4783 FrameDelay (int time)
4785 XSync(xDisplay, False);
4787 usleep(time * 1000);
4793 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
4797 /* Bitmap for piece being moved. */
4798 if (appData.monoMode) {
4799 *mask = *pieceToSolid(piece);
4800 } else if (useImages) {
4802 *mask = xpmMask[piece];
4804 *mask = ximMaskPm[piece];
4807 *mask = *pieceToSolid(piece);
4810 /* GC for piece being moved. Square color doesn't matter, but
4811 since it gets modified we make a copy of the original. */
4813 if (appData.monoMode)
4818 if (appData.monoMode)
4823 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
4825 /* Outline only used in mono mode and is not modified */
4827 *outline = bwPieceGC;
4829 *outline = wbPieceGC;
4833 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
4838 /* Draw solid rectangle which will be clipped to shape of piece */
4839 XFillRectangle(xDisplay, dest, clip,
4840 0, 0, squareSize, squareSize);
4841 if (appData.monoMode)
4842 /* Also draw outline in contrasting color for black
4843 on black / white on white cases */
4844 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
4845 0, 0, squareSize, squareSize, 0, 0, 1);
4847 /* Copy the piece */
4852 if(appData.upsideDown && flipView) kind ^= 2;
4853 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4855 0, 0, squareSize, squareSize,
4861 InsertPiece (AnimNr anr, ChessSquare piece)
4863 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
4867 DrawBlank (AnimNr anr, int x, int y, int startColor)
4869 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
4872 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
4873 int srcX, int srcY, int width, int height, int destX, int destY)
4875 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
4876 srcX, srcY, width, height, destX, destY);
4880 SetDragPiece (AnimNr anr, ChessSquare piece)
4883 /* The piece will be drawn using its own bitmap as a matte */
4884 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
4885 XSetClipMask(xDisplay, animGCs[anr+2], mask);
4888 /* [AS] Arrow highlighting support */
4891 DrawPolygon (Pnt arrow[], int nr)
4895 for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
4896 XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
4897 if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
4901 UpdateLogos (int displ)
4903 return; // no logos in XBoard yet