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 },
548 { "WheelProc", WheelProc },
549 { "TabProc", TabProc },
552 char globalTranslations[] =
553 ":<Key>F9: MenuItem(ResignProc) \n \
554 :Ctrl<Key>n: MenuItem(NewGame) \n \
555 :Meta<Key>V: MenuItem(NewVariant) \n \
556 :Ctrl<Key>o: MenuItem(LoadGame) \n \
557 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
558 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
559 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
560 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
561 :Ctrl<Key>s: MenuItem(SaveGame) \n \
562 :Ctrl<Key>c: MenuItem(CopyGame) \n \
563 :Ctrl<Key>v: MenuItem(PasteGame) \n \
564 :Ctrl<Key>O: MenuItem(LoadPosition) \n \
565 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
566 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
567 :Ctrl<Key>S: MenuItem(SavePosition) \n \
568 :Ctrl<Key>C: MenuItem(CopyPosition) \n \
569 :Ctrl<Key>V: MenuItem(PastePosition) \n \
570 :Ctrl<Key>q: MenuItem(Exit) \n \
571 :Ctrl<Key>w: MenuItem(MachineWhite) \n \
572 :Ctrl<Key>b: MenuItem(MachineBlack) \n \
573 :Ctrl<Key>t: MenuItem(TwoMachines) \n \
574 :Ctrl<Key>a: MenuItem(AnalysisMode) \n \
575 :Ctrl<Key>g: MenuItem(AnalyzeFile) \n \
576 :Ctrl<Key>e: MenuItem(EditGame) \n \
577 :Ctrl<Key>E: MenuItem(EditPosition) \n \
578 :Meta<Key>O: MenuItem(ShowEngineOutput) \n \
579 :Meta<Key>E: MenuItem(ShowEvaluationGraph) \n \
580 :Meta<Key>G: MenuItem(ShowGameList) \n \
581 :Meta<Key>H: MenuItem(ShowMoveHistory) \n \
582 :<Key>Pause: MenuItem(Pause) \n \
583 :<Key>F3: MenuItem(Accept) \n \
584 :<Key>F4: MenuItem(Decline) \n \
585 :<Key>F12: MenuItem(Rematch) \n \
586 :<Key>F5: MenuItem(CallFlag) \n \
587 :<Key>F6: MenuItem(Draw) \n \
588 :<Key>F7: MenuItem(Adjourn) \n \
589 :<Key>F8: MenuItem(Abort) \n \
590 :<Key>F10: MenuItem(StopObserving) \n \
591 :<Key>F11: MenuItem(StopExamining) \n \
592 :Ctrl<Key>d: MenuItem(DebugProc) \n \
593 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
594 :Meta<Key>End: MenuItem(ToEnd) \n \
595 :Meta<Key>Right: MenuItem(Forward) \n \
596 :Meta<Key>Home: MenuItem(ToStart) \n \
597 :Meta<Key>Left: MenuItem(Backward) \n \
598 :<Key>Left: MenuItem(Backward) \n \
599 :<Key>Right: MenuItem(Forward) \n \
600 :<Key>Home: MenuItem(Revert) \n \
601 :<Key>End: MenuItem(TruncateGame) \n \
602 :Ctrl<Key>m: MenuItem(MoveNow) \n \
603 :Ctrl<Key>x: MenuItem(RetractMove) \n \
604 :Meta<Key>J: MenuItem(Adjudications) \n \
605 :Meta<Key>U: MenuItem(CommonEngine) \n \
606 :Meta<Key>T: MenuItem(TimeControl) \n \
607 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
608 #ifndef OPTIONSDIALOG
610 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
611 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
612 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
613 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
614 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
617 :<Key>F1: MenuItem(Manual) \n \
618 :<Key>F2: MenuItem(FlipView) \n \
619 :<KeyDown>Return: TempBackwardProc() \n \
620 :<KeyUp>Return: TempForwardProc() \n";
622 char boardTranslations[] =
623 "<Btn1Down>: HandleUserMove(0) \n \
624 Shift<Btn1Up>: HandleUserMove(1) \n \
625 <Btn1Up>: HandleUserMove(0) \n \
626 <Btn1Motion>: AnimateUserMove() \n \
627 <Btn3Motion>: HandlePV() \n \
628 <Btn2Motion>: HandlePV() \n \
629 <Btn3Up>: PieceMenuPopup(menuB) \n \
630 <Btn2Up>: PieceMenuPopup(menuB) \n \
631 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
632 PieceMenuPopup(menuB) \n \
633 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
634 PieceMenuPopup(menuW) \n \
635 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
636 PieceMenuPopup(menuW) \n \
637 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
638 PieceMenuPopup(menuB) \n";
640 char whiteTranslations[] =
641 "Shift<BtnDown>: WhiteClock(1)\n \
642 <BtnDown>: WhiteClock(0)\n";
643 char blackTranslations[] =
644 "Shift<BtnDown>: BlackClock(1)\n \
645 <BtnDown>: BlackClock(0)\n";
647 char ICSInputTranslations[] =
648 "<Key>Up: UpKeyProc() \n "
649 "<Key>Down: DownKeyProc() \n "
650 "<Key>Return: EnterKeyProc() \n";
652 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
653 // as the widget is destroyed before the up-click can call extend-end
654 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
656 String xboardResources[] = {
657 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
658 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
663 /* Max possible square size */
664 #define MAXSQSIZE 256
666 static int xpm_avail[MAXSQSIZE];
668 #ifdef HAVE_DIR_STRUCT
670 /* Extract piece size from filename */
672 xpm_getsize (char *name, int len, char *ext)
680 if ((p=strchr(name, '.')) == NULL ||
681 StrCaseCmp(p+1, ext) != 0)
687 while (*p && isdigit(*p))
694 /* Setup xpm_avail */
696 xpm_getavail (char *dirname, char *ext)
702 for (i=0; i<MAXSQSIZE; ++i)
705 if (appData.debugMode)
706 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
708 dir = opendir(dirname);
711 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
712 programName, dirname);
716 while ((ent=readdir(dir)) != NULL) {
717 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
718 if (i > 0 && i < MAXSQSIZE)
728 xpm_print_avail (FILE *fp, char *ext)
732 fprintf(fp, _("Available `%s' sizes:\n"), ext);
733 for (i=1; i<MAXSQSIZE; ++i) {
739 /* Return XPM piecesize closest to size */
741 xpm_closest_to (char *dirname, int size, char *ext)
744 int sm_diff = MAXSQSIZE;
748 xpm_getavail(dirname, ext);
750 if (appData.debugMode)
751 xpm_print_avail(stderr, ext);
753 for (i=1; i<MAXSQSIZE; ++i) {
756 diff = (diff<0) ? -diff : diff;
757 if (diff < sm_diff) {
765 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
771 #else /* !HAVE_DIR_STRUCT */
772 /* If we are on a system without a DIR struct, we can't
773 read the directory, so we can't collect a list of
774 filenames, etc., so we can't do any size-fitting. */
776 xpm_closest_to (char *dirname, int size, char *ext)
779 Warning: No DIR structure found on this system --\n\
780 Unable to autosize for XPM/XIM pieces.\n\
781 Please report this error to %s.\n\
782 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
785 #endif /* HAVE_DIR_STRUCT */
788 /* Arrange to catch delete-window events */
789 Atom wm_delete_window;
791 CatchDeleteWindow (Widget w, String procname)
794 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
795 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
796 XtAugmentTranslations(w, XtParseTranslationTable(buf));
803 XtSetArg(args[0], XtNiconic, False);
804 XtSetValues(shellWidget, args, 1);
806 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
809 //---------------------------------------------------------------------------------------------------------
810 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
813 #define CW_USEDEFAULT (1<<31)
814 #define ICS_TEXT_MENU_SIZE 90
815 #define DEBUG_FILE "xboard.debug"
816 #define SetCurrentDirectory chdir
817 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
821 // these two must some day move to frontend.h, when they are implemented
822 Boolean GameListIsUp();
824 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
827 // front-end part of option handling
829 // [HGM] This platform-dependent table provides the location for storing the color info
830 extern char *crWhite, * crBlack;
834 &appData.whitePieceColor,
835 &appData.blackPieceColor,
836 &appData.lightSquareColor,
837 &appData.darkSquareColor,
838 &appData.highlightSquareColor,
839 &appData.premoveHighlightColor,
840 &appData.lowTimeWarningColor,
851 // [HGM] font: keep a font for each square size, even non-stndard ones
854 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
855 char *fontTable[NUM_FONTS][MAX_SIZE];
858 ParseFont (char *name, int number)
859 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
861 if(sscanf(name, "size%d:", &size)) {
862 // [HGM] font: font is meant for specific boardSize (likely from settings file);
863 // defer processing it until we know if it matches our board size
864 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
865 fontTable[number][size] = strdup(strchr(name, ':')+1);
866 fontValid[number][size] = True;
871 case 0: // CLOCK_FONT
872 appData.clockFont = strdup(name);
874 case 1: // MESSAGE_FONT
875 appData.font = strdup(name);
877 case 2: // COORD_FONT
878 appData.coordFont = strdup(name);
883 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
888 { // only 2 fonts currently
889 appData.clockFont = CLOCK_FONT_NAME;
890 appData.coordFont = COORD_FONT_NAME;
891 appData.font = DEFAULT_FONT_NAME;
896 { // no-op, until we identify the code for this already in XBoard and move it here
900 ParseColor (int n, char *name)
901 { // in XBoard, just copy the color-name string
902 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
906 ParseTextAttribs (ColorClass cc, char *s)
908 (&appData.colorShout)[cc] = strdup(s);
912 ParseBoardSize (void *addr, char *name)
914 appData.boardSize = strdup(name);
919 { // In XBoard the sound-playing program takes care of obtaining the actual sound
923 SetCommPortDefaults ()
924 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
927 // [HGM] args: these three cases taken out to stay in front-end
929 SaveFontArg (FILE *f, ArgDescriptor *ad)
932 int i, n = (int)(intptr_t)ad->argLoc;
934 case 0: // CLOCK_FONT
935 name = appData.clockFont;
937 case 1: // MESSAGE_FONT
940 case 2: // COORD_FONT
941 name = appData.coordFont;
946 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
947 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
948 fontTable[n][squareSize] = strdup(name);
949 fontValid[n][squareSize] = True;
952 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
953 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
958 { // nothing to do, as the sounds are at all times represented by their text-string names already
962 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
963 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
964 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
968 SaveColor (FILE *f, ArgDescriptor *ad)
969 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
970 if(colorVariable[(int)(intptr_t)ad->argLoc])
971 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
975 SaveBoardSize (FILE *f, char *name, void *addr)
976 { // wrapper to shield back-end from BoardSize & sizeInfo
977 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
981 ParseCommPortSettings (char *s)
982 { // no such option in XBoard (yet)
985 extern Widget engineOutputShell;
989 GetActualPlacement (Widget wg, WindowPlacement *wp)
994 XWindowAttributes winAt;
1001 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
1002 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
1003 wp->x = rx - winAt.x;
1004 wp->y = ry - winAt.y;
1005 wp->height = winAt.height;
1006 wp->width = winAt.width;
1007 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
1012 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1013 // In XBoard this will have to wait until awareness of window parameters is implemented
1014 GetActualPlacement(shellWidget, &wpMain);
1015 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1016 if(MoveHistoryIsUp()) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
1017 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1018 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1019 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
1020 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
1024 PrintCommPortSettings (FILE *f, char *name)
1025 { // This option does not exist in XBoard
1029 EnsureOnScreen (int *x, int *y, int minX, int minY)
1036 { // [HGM] args: allows testing if main window is realized from back-end
1037 return xBoardWindow != 0;
1041 PopUpStartupDialog ()
1042 { // start menu not implemented in XBoard
1046 ConvertToLine (int argc, char **argv)
1048 static char line[128*1024], buf[1024];
1052 for(i=1; i<argc; i++)
1054 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1055 && argv[i][0] != '{' )
1056 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1058 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1059 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1062 line[strlen(line)-1] = NULLCHAR;
1066 //--------------------------------------------------------------------------------------------
1069 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1071 #define BoardSize int
1073 InitDrawingSizes (BoardSize boardSize, int flags)
1074 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1075 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1077 XtGeometryResult gres;
1079 static Dimension oldWidth, oldHeight;
1080 static VariantClass oldVariant;
1081 static int oldDual = -1, oldMono = -1;
1083 if(!formWidget) return;
1085 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1086 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1087 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1089 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1091 * Enable shell resizing.
1093 shellArgs[0].value = (XtArgVal) &w;
1094 shellArgs[1].value = (XtArgVal) &h;
1095 XtGetValues(shellWidget, shellArgs, 2);
1097 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1098 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1099 XtSetValues(shellWidget, &shellArgs[2], 4);
1101 XtSetArg(args[0], XtNdefaultDistance, &sep);
1102 XtGetValues(formWidget, args, 1);
1104 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1106 hOffset = boardWidth + 10;
1107 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1108 secondSegments[i] = gridSegments[i];
1109 secondSegments[i].x1 += hOffset;
1110 secondSegments[i].x2 += hOffset;
1113 XtSetArg(args[0], XtNwidth, boardWidth);
1114 XtSetArg(args[1], XtNheight, boardHeight);
1115 XtSetValues(boardWidget, args, 2);
1117 timerWidth = (boardWidth - sep) / 2;
1118 XtSetArg(args[0], XtNwidth, timerWidth);
1119 XtSetValues(whiteTimerWidget, args, 1);
1120 XtSetValues(blackTimerWidget, args, 1);
1122 XawFormDoLayout(formWidget, False);
1124 if (appData.titleInWindow) {
1126 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1127 XtSetArg(args[i], XtNheight, &h); i++;
1128 XtGetValues(titleWidget, args, i);
1130 w = boardWidth - 2*bor;
1132 XtSetArg(args[0], XtNwidth, &w);
1133 XtGetValues(menuBarWidget, args, 1);
1134 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1137 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1138 if (gres != XtGeometryYes && appData.debugMode) {
1140 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1141 programName, gres, w, h, wr, hr);
1145 XawFormDoLayout(formWidget, True);
1148 * Inhibit shell resizing.
1150 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1151 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1152 shellArgs[4].value = shellArgs[2].value = w;
1153 shellArgs[5].value = shellArgs[3].value = h;
1154 XtSetValues(shellWidget, &shellArgs[0], 6);
1156 XSync(xDisplay, False);
1160 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1163 if(gameInfo.variant != oldVariant) { // and only if variant changed
1166 for(i=0; i<4; i++) {
1168 for(p=0; p<=(int)WhiteKing; p++)
1169 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1170 if(gameInfo.variant == VariantShogi) {
1171 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1172 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1173 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1174 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1175 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1178 if(gameInfo.variant == VariantGothic) {
1179 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1182 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1183 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1184 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1187 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1188 for(p=0; p<=(int)WhiteKing; p++)
1189 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1190 if(gameInfo.variant == VariantShogi) {
1191 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1192 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1193 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1194 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1195 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1198 if(gameInfo.variant == VariantGothic) {
1199 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1202 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1203 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1204 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1209 for(i=0; i<2; i++) {
1211 for(p=0; p<=(int)WhiteKing; p++)
1212 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1213 if(gameInfo.variant == VariantShogi) {
1214 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1215 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1216 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1217 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1218 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1221 if(gameInfo.variant == VariantGothic) {
1222 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1225 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1226 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1227 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1231 oldMono = -10; // kludge to force recreation of animation masks
1232 oldVariant = gameInfo.variant;
1235 if(appData.monoMode != oldMono)
1238 oldMono = appData.monoMode;
1243 MakeOneColor (char *name, Pixel *color)
1245 XrmValue vFrom, vTo;
1246 if (!appData.monoMode) {
1247 vFrom.addr = (caddr_t) name;
1248 vFrom.size = strlen(name);
1249 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1250 if (vTo.addr == NULL) {
1251 appData.monoMode = True;
1254 *color = *(Pixel *) vTo.addr;
1262 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1263 int forceMono = False;
1265 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1266 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1267 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1268 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1269 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1270 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1277 { // [HGM] taken out of main
1279 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1280 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1281 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1283 if (appData.bitmapDirectory[0] != NULLCHAR) {
1287 CreateXPMBoard(appData.liteBackTextureFile, 1);
1288 CreateXPMBoard(appData.darkBackTextureFile, 0);
1292 /* Create regular pieces */
1293 if (!useImages) CreatePieces();
1298 InitDrawingParams ()
1300 MakeColors(); CreateGCs(True);
1305 main (int argc, char **argv)
1307 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1308 XSetWindowAttributes window_attributes;
1310 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1311 XrmValue vFrom, vTo;
1312 XtGeometryResult gres;
1315 int forceMono = False;
1317 srandom(time(0)); // [HGM] book: make random truly random
1319 setbuf(stdout, NULL);
1320 setbuf(stderr, NULL);
1323 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1324 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1328 programName = strrchr(argv[0], '/');
1329 if (programName == NULL)
1330 programName = argv[0];
1335 XtSetLanguageProc(NULL, NULL, NULL);
1336 bindtextdomain(PACKAGE, LOCALEDIR);
1337 textdomain(PACKAGE);
1341 XtAppInitialize(&appContext, "XBoard", shellOptions,
1342 XtNumber(shellOptions),
1343 &argc, argv, xboardResources, NULL, 0);
1344 appData.boardSize = "";
1345 InitAppData(ConvertToLine(argc, argv));
1347 if (p == NULL) p = "/tmp";
1348 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1349 gameCopyFilename = (char*) malloc(i);
1350 gamePasteFilename = (char*) malloc(i);
1351 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1352 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1354 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1355 clientResources, XtNumber(clientResources),
1358 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1359 static char buf[MSG_SIZ];
1360 EscapeExpand(buf, appData.firstInitString);
1361 appData.firstInitString = strdup(buf);
1362 EscapeExpand(buf, appData.secondInitString);
1363 appData.secondInitString = strdup(buf);
1364 EscapeExpand(buf, appData.firstComputerString);
1365 appData.firstComputerString = strdup(buf);
1366 EscapeExpand(buf, appData.secondComputerString);
1367 appData.secondComputerString = strdup(buf);
1370 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1373 if (chdir(chessDir) != 0) {
1374 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1380 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1381 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1382 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1383 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1386 setbuf(debugFP, NULL);
1390 if (appData.debugMode) {
1391 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1395 /* [HGM,HR] make sure board size is acceptable */
1396 if(appData.NrFiles > BOARD_FILES ||
1397 appData.NrRanks > BOARD_RANKS )
1398 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1401 /* This feature does not work; animation needs a rewrite */
1402 appData.highlightDragging = FALSE;
1406 xDisplay = XtDisplay(shellWidget);
1407 xScreen = DefaultScreen(xDisplay);
1408 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1410 gameInfo.variant = StringToVariant(appData.variant);
1411 InitPosition(FALSE);
1414 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1416 if (isdigit(appData.boardSize[0])) {
1417 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1418 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1419 &fontPxlSize, &smallLayout, &tinyLayout);
1421 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1422 programName, appData.boardSize);
1426 /* Find some defaults; use the nearest known size */
1427 SizeDefaults *szd, *nearest;
1428 int distance = 99999;
1429 nearest = szd = sizeDefaults;
1430 while (szd->name != NULL) {
1431 if (abs(szd->squareSize - squareSize) < distance) {
1433 distance = abs(szd->squareSize - squareSize);
1434 if (distance == 0) break;
1438 if (i < 2) lineGap = nearest->lineGap;
1439 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1440 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1441 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1442 if (i < 6) smallLayout = nearest->smallLayout;
1443 if (i < 7) tinyLayout = nearest->tinyLayout;
1446 SizeDefaults *szd = sizeDefaults;
1447 if (*appData.boardSize == NULLCHAR) {
1448 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1449 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1452 if (szd->name == NULL) szd--;
1453 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1455 while (szd->name != NULL &&
1456 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1457 if (szd->name == NULL) {
1458 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1459 programName, appData.boardSize);
1463 squareSize = szd->squareSize;
1464 lineGap = szd->lineGap;
1465 clockFontPxlSize = szd->clockFontPxlSize;
1466 coordFontPxlSize = szd->coordFontPxlSize;
1467 fontPxlSize = szd->fontPxlSize;
1468 smallLayout = szd->smallLayout;
1469 tinyLayout = szd->tinyLayout;
1470 // [HGM] font: use defaults from settings file if available and not overruled
1472 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1473 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1474 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1475 appData.font = fontTable[MESSAGE_FONT][squareSize];
1476 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1477 appData.coordFont = fontTable[COORD_FONT][squareSize];
1479 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1480 if (strlen(appData.pixmapDirectory) > 0) {
1481 p = ExpandPathName(appData.pixmapDirectory);
1483 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1484 appData.pixmapDirectory);
1487 if (appData.debugMode) {
1488 fprintf(stderr, _("\
1489 XBoard square size (hint): %d\n\
1490 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1492 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1493 if (appData.debugMode) {
1494 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1497 defaultLineGap = lineGap;
1498 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1500 /* [HR] height treated separately (hacked) */
1501 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1502 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1503 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1504 XtSetArg(boardArgs[2], XtNheight, boardHeight);
1507 * Determine what fonts to use.
1510 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1511 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1512 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1513 fontSet = CreateFontSet(appData.font);
1514 clockFontSet = CreateFontSet(appData.clockFont);
1516 /* For the coordFont, use the 0th font of the fontset. */
1517 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1518 XFontStruct **font_struct_list;
1519 XFontSetExtents *fontSize;
1520 char **font_name_list;
1521 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1522 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1523 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1524 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1525 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1528 appData.font = FindFont(appData.font, fontPxlSize);
1529 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1530 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1531 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1532 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1533 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1534 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1536 countFontID = coordFontID; // [HGM] holdings
1537 countFontStruct = coordFontStruct;
1539 xdb = XtDatabase(xDisplay);
1541 XrmPutLineResource(&xdb, "*international: True");
1542 vTo.size = sizeof(XFontSet);
1543 vTo.addr = (XtPointer) &fontSet;
1544 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1546 XrmPutStringResource(&xdb, "*font", appData.font);
1550 * Detect if there are not enough colors available and adapt.
1552 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1553 appData.monoMode = True;
1556 forceMono = MakeColors();
1559 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1561 appData.monoMode = True;
1564 if (appData.lowTimeWarning && !appData.monoMode) {
1565 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1566 vFrom.size = strlen(appData.lowTimeWarningColor);
1567 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1568 if (vTo.addr == NULL)
1569 appData.monoMode = True;
1571 lowTimeWarningColor = *(Pixel *) vTo.addr;
1574 if (appData.monoMode && appData.debugMode) {
1575 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1576 (unsigned long) XWhitePixel(xDisplay, xScreen),
1577 (unsigned long) XBlackPixel(xDisplay, xScreen));
1580 ParseIcsTextColors();
1582 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1588 layoutName = "tinyLayout";
1589 } else if (smallLayout) {
1590 layoutName = "smallLayout";
1592 layoutName = "normalLayout";
1594 /* Outer layoutWidget is there only to provide a name for use in
1595 resources that depend on the layout style */
1597 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
1598 layoutArgs, XtNumber(layoutArgs));
1600 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
1601 formArgs, XtNumber(formArgs));
1602 XtSetArg(args[0], XtNdefaultDistance, &sep);
1603 XtGetValues(formWidget, args, 1);
1606 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
1607 XtSetArg(args[0], XtNtop, XtChainTop);
1608 XtSetArg(args[1], XtNbottom, XtChainTop);
1609 XtSetArg(args[2], XtNright, XtChainLeft);
1610 XtSetValues(menuBarWidget, args, 3);
1612 widgetList[j++] = whiteTimerWidget =
1613 XtCreateWidget("whiteTime", labelWidgetClass,
1614 formWidget, timerArgs, XtNumber(timerArgs));
1616 XtSetArg(args[0], XtNfontSet, clockFontSet);
1618 XtSetArg(args[0], XtNfont, clockFontStruct);
1620 XtSetArg(args[1], XtNtop, XtChainTop);
1621 XtSetArg(args[2], XtNbottom, XtChainTop);
1622 XtSetValues(whiteTimerWidget, args, 3);
1624 widgetList[j++] = blackTimerWidget =
1625 XtCreateWidget("blackTime", labelWidgetClass,
1626 formWidget, timerArgs, XtNumber(timerArgs));
1628 XtSetArg(args[0], XtNfontSet, clockFontSet);
1630 XtSetArg(args[0], XtNfont, clockFontStruct);
1632 XtSetArg(args[1], XtNtop, XtChainTop);
1633 XtSetArg(args[2], XtNbottom, XtChainTop);
1634 XtSetValues(blackTimerWidget, args, 3);
1636 if (appData.titleInWindow) {
1637 widgetList[j++] = titleWidget =
1638 XtCreateWidget("title", labelWidgetClass, formWidget,
1639 titleArgs, XtNumber(titleArgs));
1640 XtSetArg(args[0], XtNtop, XtChainTop);
1641 XtSetArg(args[1], XtNbottom, XtChainTop);
1642 XtSetValues(titleWidget, args, 2);
1645 if (appData.showButtonBar) {
1646 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
1647 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
1648 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
1649 XtSetArg(args[2], XtNtop, XtChainTop);
1650 XtSetArg(args[3], XtNbottom, XtChainTop);
1651 XtSetValues(buttonBarWidget, args, 4);
1654 widgetList[j++] = messageWidget =
1655 XtCreateWidget("message", labelWidgetClass, formWidget,
1656 messageArgs, XtNumber(messageArgs));
1657 XtSetArg(args[0], XtNtop, XtChainTop);
1658 XtSetArg(args[1], XtNbottom, XtChainTop);
1659 XtSetValues(messageWidget, args, 2);
1661 widgetList[j++] = boardWidget =
1662 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
1663 XtNumber(boardArgs));
1665 XtManageChildren(widgetList, j);
1667 timerWidth = (boardWidth - sep) / 2;
1668 XtSetArg(args[0], XtNwidth, timerWidth);
1669 XtSetValues(whiteTimerWidget, args, 1);
1670 XtSetValues(blackTimerWidget, args, 1);
1672 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1673 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1674 XtGetValues(whiteTimerWidget, args, 2);
1676 if (appData.showButtonBar) {
1677 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1678 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1679 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
1683 * formWidget uses these constraints but they are stored
1687 XtSetArg(args[i], XtNfromHoriz, 0); i++;
1688 XtSetValues(menuBarWidget, args, i);
1689 if (appData.titleInWindow) {
1692 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1693 XtSetValues(whiteTimerWidget, args, i);
1695 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1696 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1697 XtSetValues(blackTimerWidget, args, i);
1699 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1700 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
1701 XtSetValues(titleWidget, args, i);
1703 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1704 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1705 XtSetValues(messageWidget, args, i);
1706 if (appData.showButtonBar) {
1708 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1709 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1710 XtSetValues(buttonBarWidget, args, i);
1714 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1715 XtSetValues(whiteTimerWidget, args, i);
1717 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1718 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1719 XtSetValues(blackTimerWidget, args, i);
1721 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
1722 XtSetValues(titleWidget, args, i);
1724 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1725 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1726 XtSetValues(messageWidget, args, i);
1727 if (appData.showButtonBar) {
1729 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1730 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1731 XtSetValues(buttonBarWidget, args, i);
1736 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1737 XtSetValues(whiteTimerWidget, args, i);
1739 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1740 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1741 XtSetValues(blackTimerWidget, args, i);
1743 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1744 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1745 XtSetValues(messageWidget, args, i);
1746 if (appData.showButtonBar) {
1748 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1749 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1750 XtSetValues(buttonBarWidget, args, i);
1754 XtSetArg(args[0], XtNfromVert, messageWidget);
1755 XtSetArg(args[1], XtNtop, XtChainTop);
1756 XtSetArg(args[2], XtNbottom, XtChainBottom);
1757 XtSetArg(args[3], XtNleft, XtChainLeft);
1758 XtSetArg(args[4], XtNright, XtChainRight);
1759 XtSetValues(boardWidget, args, 5);
1761 XtRealizeWidget(shellWidget);
1764 XtSetArg(args[0], XtNx, wpMain.x);
1765 XtSetArg(args[1], XtNy, wpMain.y);
1766 XtSetValues(shellWidget, args, 2);
1770 * Correct the width of the message and title widgets.
1771 * It is not known why some systems need the extra fudge term.
1772 * The value "2" is probably larger than needed.
1774 XawFormDoLayout(formWidget, False);
1776 #define WIDTH_FUDGE 2
1778 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1779 XtSetArg(args[i], XtNheight, &h); i++;
1780 XtGetValues(messageWidget, args, i);
1781 if (appData.showButtonBar) {
1783 XtSetArg(args[i], XtNwidth, &w); i++;
1784 XtGetValues(buttonBarWidget, args, i);
1785 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1787 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
1790 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1791 if (gres != XtGeometryYes && appData.debugMode) {
1792 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1793 programName, gres, w, h, wr, hr);
1796 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
1797 /* The size used for the child widget in layout lags one resize behind
1798 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
1800 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1801 if (gres != XtGeometryYes && appData.debugMode) {
1802 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1803 programName, gres, w, h, wr, hr);
1806 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
1807 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
1808 XtSetArg(args[1], XtNright, XtChainRight);
1809 XtSetValues(messageWidget, args, 2);
1811 if (appData.titleInWindow) {
1813 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1814 XtSetArg(args[i], XtNheight, &h); i++;
1815 XtGetValues(titleWidget, args, i);
1817 w = boardWidth - 2*bor;
1819 XtSetArg(args[0], XtNwidth, &w);
1820 XtGetValues(menuBarWidget, args, 1);
1821 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1824 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1825 if (gres != XtGeometryYes && appData.debugMode) {
1827 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1828 programName, gres, w, h, wr, hr);
1831 XawFormDoLayout(formWidget, True);
1833 xBoardWindow = XtWindow(boardWidget);
1835 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1836 // not need to go into InitDrawingSizes().
1840 * Create X checkmark bitmap and initialize option menu checks.
1842 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1843 checkmark_bits, checkmark_width, checkmark_height);
1849 ReadBitmap(&wIconPixmap, "icon_white.bm",
1850 icon_white_bits, icon_white_width, icon_white_height);
1851 ReadBitmap(&bIconPixmap, "icon_black.bm",
1852 icon_black_bits, icon_black_width, icon_black_height);
1853 iconPixmap = wIconPixmap;
1855 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1856 XtSetValues(shellWidget, args, i);
1859 * Create a cursor for the board widget.
1861 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1862 XChangeWindowAttributes(xDisplay, xBoardWindow,
1863 CWCursor, &window_attributes);
1866 * Inhibit shell resizing.
1868 shellArgs[0].value = (XtArgVal) &w;
1869 shellArgs[1].value = (XtArgVal) &h;
1870 XtGetValues(shellWidget, shellArgs, 2);
1871 shellArgs[4].value = shellArgs[2].value = w;
1872 shellArgs[5].value = shellArgs[3].value = h;
1873 XtSetValues(shellWidget, &shellArgs[2], 4);
1874 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1875 marginH = h - boardHeight;
1877 CatchDeleteWindow(shellWidget, "QuitProc");
1885 if (appData.animate || appData.animateDragging)
1888 XtAugmentTranslations(formWidget,
1889 XtParseTranslationTable(globalTranslations));
1890 XtAugmentTranslations(boardWidget,
1891 XtParseTranslationTable(boardTranslations));
1892 XtAugmentTranslations(whiteTimerWidget,
1893 XtParseTranslationTable(whiteTranslations));
1894 XtAugmentTranslations(blackTimerWidget,
1895 XtParseTranslationTable(blackTranslations));
1897 /* Why is the following needed on some versions of X instead
1898 * of a translation? */
1899 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
1900 (XtEventHandler) EventProc, NULL);
1902 XtAddEventHandler(formWidget, KeyPressMask, False,
1903 (XtEventHandler) MoveTypeInProc, NULL);
1904 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1905 (XtEventHandler) EventProc, NULL);
1907 /* [AS] Restore layout */
1908 if( wpMoveHistory.visible ) {
1912 if( wpEvalGraph.visible )
1917 if( wpEngineOutput.visible ) {
1918 EngineOutputPopUp();
1923 if (errorExitStatus == -1) {
1924 if (appData.icsActive) {
1925 /* We now wait until we see "login:" from the ICS before
1926 sending the logon script (problems with timestamp otherwise) */
1927 /*ICSInitScript();*/
1928 if (appData.icsInputBox) ICSInputBoxPopUp();
1932 signal(SIGWINCH, TermSizeSigHandler);
1934 signal(SIGINT, IntSigHandler);
1935 signal(SIGTERM, IntSigHandler);
1936 if (*appData.cmailGameName != NULLCHAR) {
1937 signal(SIGUSR1, CmailSigHandler);
1941 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1943 // XtSetKeyboardFocus(shellWidget, formWidget);
1944 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1946 XtAppMainLoop(appContext);
1947 if (appData.debugMode) fclose(debugFP); // [DM] debug
1952 TermSizeSigHandler (int sig)
1958 IntSigHandler (int sig)
1964 CmailSigHandler (int sig)
1969 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1971 /* Activate call-back function CmailSigHandlerCallBack() */
1972 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1974 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1978 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1981 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1983 /**** end signal code ****/
1986 #define Abs(n) ((n)<0 ? -(n) : (n))
1990 InsertPxlSize (char *pattern, int targetPxlSize)
1992 char *base_fnt_lst, strInt[12], *p, *q;
1993 int alternatives, i, len, strIntLen;
1996 * Replace the "*" (if present) in the pixel-size slot of each
1997 * alternative with the targetPxlSize.
2001 while ((p = strchr(p, ',')) != NULL) {
2005 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
2006 strIntLen = strlen(strInt);
2007 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
2011 while (alternatives--) {
2012 char *comma = strchr(p, ',');
2013 for (i=0; i<14; i++) {
2014 char *hyphen = strchr(p, '-');
2016 if (comma && hyphen > comma) break;
2017 len = hyphen + 1 - p;
2018 if (i == 7 && *p == '*' && len == 2) {
2020 memcpy(q, strInt, strIntLen);
2030 len = comma + 1 - p;
2037 return base_fnt_lst;
2041 CreateFontSet (char *base_fnt_lst)
2044 char **missing_list;
2048 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
2049 &missing_list, &missing_count, &def_string);
2050 if (appData.debugMode) {
2052 XFontStruct **font_struct_list;
2053 char **font_name_list;
2054 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
2056 fprintf(debugFP, " got list %s, locale %s\n",
2057 XBaseFontNameListOfFontSet(fntSet),
2058 XLocaleOfFontSet(fntSet));
2059 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
2060 for (i = 0; i < count; i++) {
2061 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
2064 for (i = 0; i < missing_count; i++) {
2065 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
2068 if (fntSet == NULL) {
2069 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
2074 #else // not ENABLE_NLS
2076 * Find a font that matches "pattern" that is as close as
2077 * possible to the targetPxlSize. Prefer fonts that are k
2078 * pixels smaller to fonts that are k pixels larger. The
2079 * pattern must be in the X Consortium standard format,
2080 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2081 * The return value should be freed with XtFree when no
2085 FindFont (char *pattern, int targetPxlSize)
2087 char **fonts, *p, *best, *scalable, *scalableTail;
2088 int i, j, nfonts, minerr, err, pxlSize;
2090 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2092 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2093 programName, pattern);
2100 for (i=0; i<nfonts; i++) {
2103 if (*p != '-') continue;
2105 if (*p == NULLCHAR) break;
2106 if (*p++ == '-') j++;
2108 if (j < 7) continue;
2111 scalable = fonts[i];
2114 err = pxlSize - targetPxlSize;
2115 if (Abs(err) < Abs(minerr) ||
2116 (minerr > 0 && err < 0 && -err == minerr)) {
2122 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2123 /* If the error is too big and there is a scalable font,
2124 use the scalable font. */
2125 int headlen = scalableTail - scalable;
2126 p = (char *) XtMalloc(strlen(scalable) + 10);
2127 while (isdigit(*scalableTail)) scalableTail++;
2128 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2130 p = (char *) XtMalloc(strlen(best) + 2);
2131 safeStrCpy(p, best, strlen(best)+1 );
2133 if (appData.debugMode) {
2134 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2135 pattern, targetPxlSize, p);
2137 XFreeFontNames(fonts);
2144 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
2145 // must be called before all non-first callse to CreateGCs()
2146 XtReleaseGC(shellWidget, highlineGC);
2147 XtReleaseGC(shellWidget, lightSquareGC);
2148 XtReleaseGC(shellWidget, darkSquareGC);
2149 XtReleaseGC(shellWidget, lineGC);
2150 if (appData.monoMode) {
2151 if (DefaultDepth(xDisplay, xScreen) == 1) {
2152 XtReleaseGC(shellWidget, wbPieceGC);
2154 XtReleaseGC(shellWidget, bwPieceGC);
2157 XtReleaseGC(shellWidget, prelineGC);
2158 XtReleaseGC(shellWidget, wdPieceGC);
2159 XtReleaseGC(shellWidget, wlPieceGC);
2160 XtReleaseGC(shellWidget, bdPieceGC);
2161 XtReleaseGC(shellWidget, blPieceGC);
2166 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
2168 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2169 | GCBackground | GCFunction | GCPlaneMask;
2170 gc_values->foreground = foreground;
2171 gc_values->background = background;
2172 return XtGetGC(shellWidget, value_mask, gc_values);
2176 CreateGCs (int redo)
2178 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2179 | GCBackground | GCFunction | GCPlaneMask;
2180 XGCValues gc_values;
2182 Pixel white = XWhitePixel(xDisplay, xScreen);
2183 Pixel black = XBlackPixel(xDisplay, xScreen);
2185 gc_values.plane_mask = AllPlanes;
2186 gc_values.line_width = lineGap;
2187 gc_values.line_style = LineSolid;
2188 gc_values.function = GXcopy;
2191 DeleteGCs(); // called a second time; clean up old GCs first
2192 } else { // [HGM] grid and font GCs created on first call only
2193 coordGC = CreateOneGC(&gc_values, black, white);
2194 XSetFont(xDisplay, coordGC, coordFontID);
2196 // [HGM] make font for holdings counts (white on black)
2197 countGC = CreateOneGC(&gc_values, white, black);
2198 XSetFont(xDisplay, countGC, countFontID);
2200 lineGC = CreateOneGC(&gc_values, black, black);
2202 if (appData.monoMode) {
2204 highlineGC = CreateOneGC(&gc_values, white, white);
2205 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
2206 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
2208 if (DefaultDepth(xDisplay, xScreen) == 1) {
2209 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2210 gc_values.function = GXcopyInverted;
2211 copyInvertedGC = CreateOneGC(&gc_values, black, white);
2212 gc_values.function = GXcopy;
2213 if (XBlackPixel(xDisplay, xScreen) == 1) {
2214 bwPieceGC = darkSquareGC;
2215 wbPieceGC = copyInvertedGC;
2217 bwPieceGC = copyInvertedGC;
2218 wbPieceGC = lightSquareGC;
2223 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
2224 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
2225 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
2226 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
2227 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
2228 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
2229 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
2230 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
2235 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
2243 fp = fopen(filename, "rb");
2245 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
2252 for (y=0; y<h; ++y) {
2253 for (x=0; x<h; ++x) {
2258 XPutPixel(xim, x, y, blackPieceColor);
2260 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2263 XPutPixel(xim, x, y, darkSquareColor);
2265 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2268 XPutPixel(xim, x, y, whitePieceColor);
2270 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2273 XPutPixel(xim, x, y, lightSquareColor);
2275 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2283 /* create Pixmap of piece */
2284 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2286 XPutImage(xDisplay, *dest, lightSquareGC, xim,
2289 /* create Pixmap of clipmask
2290 Note: We assume the white/black pieces have the same
2291 outline, so we make only 6 masks. This is okay
2292 since the XPM clipmask routines do the same. */
2294 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2296 XPutImage(xDisplay, temp, lightSquareGC, xmask,
2299 /* now create the 1-bit version */
2300 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2303 values.foreground = 1;
2304 values.background = 0;
2306 /* Don't use XtGetGC, not read only */
2307 maskGC = XCreateGC(xDisplay, *mask,
2308 GCForeground | GCBackground, &values);
2309 XCopyPlane(xDisplay, temp, *mask, maskGC,
2310 0, 0, squareSize, squareSize, 0, 0, 1);
2311 XFreePixmap(xDisplay, temp);
2316 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
2324 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
2329 /* The XSynchronize calls were copied from CreatePieces.
2330 Not sure if needed, but can't hurt */
2331 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2334 /* temp needed by loadXIM() */
2335 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2336 0, 0, ss, ss, AllPlanes, XYPixmap);
2338 if (strlen(appData.pixmapDirectory) == 0) {
2342 if (appData.monoMode) {
2343 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
2347 fprintf(stderr, _("\nLoading XIMs...\n"));
2349 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2350 fprintf(stderr, "%d", piece+1);
2351 for (kind=0; kind<4; kind++) {
2352 fprintf(stderr, ".");
2353 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2354 ExpandPathName(appData.pixmapDirectory),
2355 piece <= (int) WhiteKing ? "" : "w",
2356 pieceBitmapNames[piece],
2358 ximPieceBitmap[kind][piece] =
2359 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2360 0, 0, ss, ss, AllPlanes, XYPixmap);
2361 if (appData.debugMode)
2362 fprintf(stderr, _("(File:%s:) "), buf);
2363 loadXIM(ximPieceBitmap[kind][piece],
2365 &(xpmPieceBitmap2[kind][piece]),
2366 &(ximMaskPm2[piece]));
2367 if(piece <= (int)WhiteKing)
2368 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2370 fprintf(stderr," ");
2372 /* Load light and dark squares */
2373 /* If the LSQ and DSQ pieces don't exist, we will
2374 draw them with solid squares. */
2375 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2376 if (access(buf, 0) != 0) {
2380 fprintf(stderr, _("light square "));
2382 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2383 0, 0, ss, ss, AllPlanes, XYPixmap);
2384 if (appData.debugMode)
2385 fprintf(stderr, _("(File:%s:) "), buf);
2387 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2388 fprintf(stderr, _("dark square "));
2389 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2390 ExpandPathName(appData.pixmapDirectory), ss);
2391 if (appData.debugMode)
2392 fprintf(stderr, _("(File:%s:) "), buf);
2394 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2395 0, 0, ss, ss, AllPlanes, XYPixmap);
2396 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2397 xpmJailSquare = xpmLightSquare;
2399 fprintf(stderr, _("Done.\n"));
2401 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2404 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2408 CreateXPMBoard (char *s, int kind)
2412 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2413 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2414 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2420 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2421 // thisroutine has to be called t free the old piece pixmaps
2423 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2424 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2426 XFreePixmap(xDisplay, xpmLightSquare);
2427 XFreePixmap(xDisplay, xpmDarkSquare);
2436 u_int ss = squareSize;
2438 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2439 XpmColorSymbol symbols[4];
2440 static int redo = False;
2442 if(redo) FreeXPMPieces(); else redo = 1;
2444 /* The XSynchronize calls were copied from CreatePieces.
2445 Not sure if needed, but can't hurt */
2446 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2448 /* Setup translations so piece colors match square colors */
2449 symbols[0].name = "light_piece";
2450 symbols[0].value = appData.whitePieceColor;
2451 symbols[1].name = "dark_piece";
2452 symbols[1].value = appData.blackPieceColor;
2453 symbols[2].name = "light_square";
2454 symbols[2].value = appData.lightSquareColor;
2455 symbols[3].name = "dark_square";
2456 symbols[3].value = appData.darkSquareColor;
2458 attr.valuemask = XpmColorSymbols;
2459 attr.colorsymbols = symbols;
2460 attr.numsymbols = 4;
2462 if (appData.monoMode) {
2463 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2467 if (strlen(appData.pixmapDirectory) == 0) {
2468 XpmPieces* pieces = builtInXpms;
2471 while (pieces->size != squareSize && pieces->size) pieces++;
2472 if (!pieces->size) {
2473 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2476 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2477 for (kind=0; kind<4; kind++) {
2479 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2480 pieces->xpm[piece][kind],
2481 &(xpmPieceBitmap2[kind][piece]),
2482 NULL, &attr)) != 0) {
2483 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2487 if(piece <= (int) WhiteKing)
2488 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2492 xpmJailSquare = xpmLightSquare;
2496 fprintf(stderr, _("\nLoading XPMs...\n"));
2499 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2500 fprintf(stderr, "%d ", piece+1);
2501 for (kind=0; kind<4; kind++) {
2502 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2503 ExpandPathName(appData.pixmapDirectory),
2504 piece > (int) WhiteKing ? "w" : "",
2505 pieceBitmapNames[piece],
2507 if (appData.debugMode) {
2508 fprintf(stderr, _("(File:%s:) "), buf);
2510 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2511 &(xpmPieceBitmap2[kind][piece]),
2512 NULL, &attr)) != 0) {
2513 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2514 // [HGM] missing: read of unorthodox piece failed; substitute King.
2515 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2516 ExpandPathName(appData.pixmapDirectory),
2518 if (appData.debugMode) {
2519 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2521 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2522 &(xpmPieceBitmap2[kind][piece]),
2526 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2531 if(piece <= (int) WhiteKing)
2532 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2535 /* Load light and dark squares */
2536 /* If the LSQ and DSQ pieces don't exist, we will
2537 draw them with solid squares. */
2538 fprintf(stderr, _("light square "));
2539 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2540 if (access(buf, 0) != 0) {
2544 if (appData.debugMode)
2545 fprintf(stderr, _("(File:%s:) "), buf);
2547 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2548 &xpmLightSquare, NULL, &attr)) != 0) {
2549 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2552 fprintf(stderr, _("dark square "));
2553 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2554 ExpandPathName(appData.pixmapDirectory), ss);
2555 if (appData.debugMode) {
2556 fprintf(stderr, _("(File:%s:) "), buf);
2558 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2559 &xpmDarkSquare, NULL, &attr)) != 0) {
2560 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2564 xpmJailSquare = xpmLightSquare;
2565 fprintf(stderr, _("Done.\n"));
2567 oldVariant = -1; // kludge to force re-makig of animation masks
2568 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2571 #endif /* HAVE_LIBXPM */
2574 /* No built-in bitmaps */
2579 u_int ss = squareSize;
2581 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2584 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2585 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2586 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2587 pieceBitmapNames[piece],
2588 ss, kind == SOLID ? 's' : 'o');
2589 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2590 if(piece <= (int)WhiteKing)
2591 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2595 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2599 /* With built-in bitmaps */
2603 BuiltInBits* bib = builtInBits;
2606 u_int ss = squareSize;
2608 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2611 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2613 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2614 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2615 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2616 pieceBitmapNames[piece],
2617 ss, kind == SOLID ? 's' : 'o');
2618 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2619 bib->bits[kind][piece], ss, ss);
2620 if(piece <= (int)WhiteKing)
2621 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2625 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2631 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2636 char msg[MSG_SIZ], fullname[MSG_SIZ];
2638 if (*appData.bitmapDirectory != NULLCHAR) {
2639 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2640 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2641 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2642 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2643 &w, &h, pm, &x_hot, &y_hot);
2644 fprintf(stderr, "load %s\n", name);
2645 if (errcode != BitmapSuccess) {
2647 case BitmapOpenFailed:
2648 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2650 case BitmapFileInvalid:
2651 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2653 case BitmapNoMemory:
2654 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2658 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2662 fprintf(stderr, _("%s: %s...using built-in\n"),
2664 } else if (w != wreq || h != hreq) {
2666 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2667 programName, fullname, w, h, wreq, hreq);
2673 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2683 if (lineGap == 0) return;
2685 /* [HR] Split this into 2 loops for non-square boards. */
2687 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2688 gridSegments[i].x1 = 0;
2689 gridSegments[i].x2 =
2690 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2691 gridSegments[i].y1 = gridSegments[i].y2
2692 = lineGap / 2 + (i * (squareSize + lineGap));
2695 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2696 gridSegments[j + i].y1 = 0;
2697 gridSegments[j + i].y2 =
2698 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2699 gridSegments[j + i].x1 = gridSegments[j + i].x2
2700 = lineGap / 2 + (j * (squareSize + lineGap));
2704 int nrOfMenuItems = 7;
2705 Widget menuWidget[150];
2706 MenuListItem menuItemList[150] = {
2707 { "LoadNextGameProc", LoadNextGameProc },
2708 { "LoadPrevGameProc", LoadPrevGameProc },
2709 { "ReloadGameProc", ReloadGameProc },
2710 { "ReloadPositionProc", ReloadPositionProc },
2711 #ifndef OPTIONSDIALOG
2712 { "AlwaysQueenProc", AlwaysQueenProc },
2713 { "AnimateDraggingProc", AnimateDraggingProc },
2714 { "AnimateMovingProc", AnimateMovingProc },
2715 { "AutoflagProc", AutoflagProc },
2716 { "AutoflipProc", AutoflipProc },
2717 { "BlindfoldProc", BlindfoldProc },
2718 { "FlashMovesProc", FlashMovesProc },
2720 { "HighlightDraggingProc", HighlightDraggingProc },
2722 { "HighlightLastMoveProc", HighlightLastMoveProc },
2723 // { "IcsAlarmProc", IcsAlarmProc },
2724 { "MoveSoundProc", MoveSoundProc },
2725 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
2726 { "PopupExitMessageProc", PopupExitMessageProc },
2727 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
2728 // { "PremoveProc", PremoveProc },
2729 { "ShowCoordsProc", ShowCoordsProc },
2730 { "ShowThinkingProc", ShowThinkingProc },
2731 { "HideThinkingProc", HideThinkingProc },
2732 { "TestLegalityProc", TestLegalityProc },
2734 { "AboutGameProc", AboutGameEvent },
2735 { "DebugProc", DebugProc },
2736 { "NothingProc", NothingProc },
2741 MarkMenuItem (char *menuRef, int state)
2743 int nr = MenuToNumber(menuRef);
2746 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2747 XtSetValues(menuWidget[nr], args, 1);
2752 EnableMenuItem (char *menuRef, int state)
2754 int nr = MenuToNumber(menuRef);
2755 if(nr >= 0) XtSetSensitive(menuWidget[nr], state);
2759 EnableButtonBar (int state)
2761 XtSetSensitive(buttonBarWidget, state);
2766 SetMenuEnables (Enables *enab)
2768 while (enab->name != NULL) {
2769 EnableMenuItem(enab->name, enab->value);
2775 Equal(char *p, char *s)
2776 { // compare strings skipping spaces in second
2778 if(*s == ' ') { s++; continue; }
2779 if(*s++ != *p++) return 0;
2785 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2786 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2788 if(*nprms == 0) return;
2789 for(i=0; menuItemList[i].name; i++) {
2790 if(Equal(prms[0], menuItemList[i].name)) {
2791 (menuItemList[i].proc) ();
2798 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
2800 MenuProc *proc = (MenuProc *) addr;
2806 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2808 RecentEngineEvent((int) (intptr_t) addr);
2811 // some stuff that must remain in front-end
2812 static Widget mainBar, currentMenu;
2813 static int wtot, nr = 0, widths[10];
2816 AppendMenuItem (char *text, char *name, MenuProc *action)
2823 XtSetArg(args[j], XtNleftMargin, 20); j++;
2824 XtSetArg(args[j], XtNrightMargin, 20); j++;
2826 if (strcmp(text, "----") == 0) {
2827 entry = XtCreateManagedWidget(text, smeLineObjectClass,
2828 currentMenu, args, j);
2830 XtSetArg(args[j], XtNlabel, XtNewString(_(text)));
2831 entry = XtCreateManagedWidget(name, smeBSBObjectClass,
2832 currentMenu, args, j+1);
2833 XtAddCallback(entry, XtNcallback,
2834 (XtCallbackProc) (strcmp(name, "recent") ? MenuBarSelect : MenuEngineSelect),
2836 menuWidget[nrOfMenuItems] = entry;
2841 CreateMenuButton (char *name, Menu *mb)
2842 { // create menu button on main bar, and shell for pull-down list
2848 XtSetArg(args[j], XtNmenuName, XtNewString(name)); j++;
2849 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
2850 XtSetArg(args[j], XtNborderWidth, 0); j++;
2851 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
2853 currentMenu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2856 XtSetArg(args[j], XtNwidth, &w); j++;
2857 XtGetValues(mb->subMenu, args, j);
2858 wtot += mb->textWidth = widths[nr++] = w;
2862 CreateMenuBar (Menu *mb, int boardWidth)
2866 char menuName[MSG_SIZ];
2870 // create bar itself
2872 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2873 XtSetArg(args[j], XtNvSpace, 0); j++;
2874 XtSetArg(args[j], XtNborderWidth, 0); j++;
2875 mainBar = XtCreateWidget("menuBar", boxWidgetClass,
2876 formWidget, args, j);
2878 CreateMainMenus(mb); // put menus in bar according to description in back-end
2880 // size buttons to make menu bar fit, clipping menu names where necessary
2881 while(wtot > boardWidth - 40) {
2883 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
2887 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
2889 XtSetArg(args[j], XtNwidth, widths[i]); j++;
2890 XtSetValues(ma[i].subMenu, args, j);
2897 CreateButtonBar (MenuItem *mi)
2900 Widget button, buttonBar;
2904 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2906 XtSetArg(args[j], XtNhSpace, 0); j++;
2908 XtSetArg(args[j], XtNborderWidth, 0); j++;
2909 XtSetArg(args[j], XtNvSpace, 0); j++;
2910 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
2911 formWidget, args, j);
2913 while (mi->string != NULL) {
2916 XtSetArg(args[j], XtNinternalWidth, 2); j++;
2917 XtSetArg(args[j], XtNborderWidth, 0); j++;
2919 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
2920 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
2921 buttonBar, args, j);
2922 XtAddCallback(button, XtNcallback,
2923 (XtCallbackProc) MenuBarSelect,
2924 (caddr_t) mi->proc);
2931 CreatePieceMenu (char *name, int color)
2936 ChessSquare selection;
2938 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2939 boardWidget, args, 0);
2941 for (i = 0; i < PIECE_MENU_SIZE; i++) {
2942 String item = pieceMenuStrings[color][i];
2944 if (strcmp(item, "----") == 0) {
2945 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2948 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2949 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2951 selection = pieceMenuTranslation[color][i];
2952 XtAddCallback(entry, XtNcallback,
2953 (XtCallbackProc) PieceMenuSelect,
2954 (caddr_t) selection);
2955 if (selection == WhitePawn || selection == BlackPawn) {
2956 XtSetArg(args[0], XtNpopupOnEntry, entry);
2957 XtSetValues(menu, args, 1);
2970 ChessSquare selection;
2972 whitePieceMenu = CreatePieceMenu("menuW", 0);
2973 blackPieceMenu = CreatePieceMenu("menuB", 1);
2975 if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
2976 XtRegisterGrabAction(PieceMenuPopup, True,
2977 (unsigned)(ButtonPressMask|ButtonReleaseMask),
2978 GrabModeAsync, GrabModeAsync);
2980 XtSetArg(args[0], XtNlabel, _("Drop"));
2981 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
2982 boardWidget, args, 1);
2983 for (i = 0; i < DROP_MENU_SIZE; i++) {
2984 String item = dropMenuStrings[i];
2986 if (strcmp(item, "----") == 0) {
2987 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2990 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2991 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2993 selection = dropMenuTranslation[i];
2994 XtAddCallback(entry, XtNcallback,
2995 (XtCallbackProc) DropMenuSelect,
2996 (caddr_t) selection);
3010 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3011 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3012 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3013 dmEnables[i].piece);
3014 XtSetSensitive(entry, p != NULL || !appData.testLegality
3015 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3016 && !appData.icsActive));
3018 while (p && *p++ == dmEnables[i].piece) count++;
3019 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3021 XtSetArg(args[j], XtNlabel, label); j++;
3022 XtSetValues(entry, args, j);
3027 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
3029 String whichMenu; int menuNr = -2;
3030 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3031 if (event->type == ButtonRelease)
3032 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3033 else if (event->type == ButtonPress)
3034 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3036 case 0: whichMenu = params[0]; break;
3037 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3039 case -1: if (errorUp) ErrorPopDown();
3042 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3046 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3048 if (pmFromX < 0 || pmFromY < 0) return;
3049 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3053 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3055 if (pmFromX < 0 || pmFromY < 0) return;
3056 DropMenuEvent(piece, pmFromX, pmFromY);
3060 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3062 shiftKey = prms[0][0] & 1;
3067 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3069 shiftKey = prms[0][0] & 1;
3075 do_flash_delay (unsigned long msec)
3081 DrawBorder (int x, int y, int type)
3085 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
3087 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3088 squareSize+lineGap, squareSize+lineGap);
3092 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
3094 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
3095 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
3097 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
3098 if(textureW[kind] < W*squareSize)
3099 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
3101 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
3102 if(textureH[kind] < H*squareSize)
3103 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
3105 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
3110 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
3111 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
3113 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
3114 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
3115 squareSize, squareSize, x*fac, y*fac);
3117 if (useImages && useImageSqs) {
3121 pm = xpmLightSquare;
3126 case 2: /* neutral */
3128 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
3131 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3132 squareSize, squareSize, x*fac, y*fac);
3142 case 2: /* neutral */
3147 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
3152 I split out the routines to draw a piece so that I could
3153 make a generic flash routine.
3156 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3158 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3159 switch (square_color) {
3161 case 2: /* neutral */
3163 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3164 ? *pieceToOutline(piece)
3165 : *pieceToSolid(piece),
3166 dest, bwPieceGC, 0, 0,
3167 squareSize, squareSize, x, y);
3170 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3171 ? *pieceToSolid(piece)
3172 : *pieceToOutline(piece),
3173 dest, wbPieceGC, 0, 0,
3174 squareSize, squareSize, x, y);
3180 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3182 switch (square_color) {
3184 case 2: /* neutral */
3186 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3187 ? *pieceToOutline(piece)
3188 : *pieceToSolid(piece),
3189 dest, bwPieceGC, 0, 0,
3190 squareSize, squareSize, x, y, 1);
3193 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3194 ? *pieceToSolid(piece)
3195 : *pieceToOutline(piece),
3196 dest, wbPieceGC, 0, 0,
3197 squareSize, squareSize, x, y, 1);
3203 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3205 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
3206 switch (square_color) {
3208 XCopyPlane(xDisplay, *pieceToSolid(piece),
3209 dest, (int) piece < (int) BlackPawn
3210 ? wlPieceGC : blPieceGC, 0, 0,
3211 squareSize, squareSize, x, y, 1);
3214 XCopyPlane(xDisplay, *pieceToSolid(piece),
3215 dest, (int) piece < (int) BlackPawn
3216 ? wdPieceGC : bdPieceGC, 0, 0,
3217 squareSize, squareSize, x, y, 1);
3219 case 2: /* neutral */
3221 break; // should never contain pieces
3226 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3228 int kind, p = piece;
3230 switch (square_color) {
3232 case 2: /* neutral */
3234 if ((int)piece < (int) BlackPawn) {
3242 if ((int)piece < (int) BlackPawn) {
3250 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
3251 if(useTexture & square_color+1) {
3252 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
3253 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
3254 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
3255 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
3256 XSetClipMask(xDisplay, wlPieceGC, None);
3257 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
3259 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3260 dest, wlPieceGC, 0, 0,
3261 squareSize, squareSize, x, y);
3264 typedef void (*DrawFunc)();
3269 if (appData.monoMode) {
3270 if (DefaultDepth(xDisplay, xScreen) == 1) {
3271 return monoDrawPiece_1bit;
3273 return monoDrawPiece;
3277 return colorDrawPieceImage;
3279 return colorDrawPiece;
3284 DrawDot (int marker, int x, int y, int r)
3286 if(appData.monoMode) {
3287 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
3288 x, y, r, r, 0, 64*360);
3289 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
3290 x, y, r, r, 0, 64*360);
3292 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
3293 x, y, r, r, 0, 64*360);
3297 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
3298 { // basic front-end board-draw function: takes care of everything that can be in square:
3299 // piece, background, coordinate/count, marker dot
3300 int direction, font_ascent, font_descent;
3301 XCharStruct overall;
3304 if (piece == EmptySquare) {
3305 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
3307 drawfunc = ChooseDrawFunc();
3308 drawfunc(piece, square_color, x, y, xBoardWindow);
3311 if(align) { // square carries inscription (coord or piece count)
3313 GC hGC = align < 3 ? coordGC : countGC;
3314 // first calculate where it goes
3315 XTextExtents(countFontStruct, string, 1, &direction,
3316 &font_ascent, &font_descent, &overall);
3318 xx += squareSize - overall.width - 2;
3319 yy += squareSize - font_descent - 1;
3320 } else if (align == 2) {
3321 xx += 2, yy += font_ascent + 1;
3322 } else if (align == 3) {
3323 xx += squareSize - overall.width - 2;
3324 yy += font_ascent + 1;
3325 } else if (align == 4) {
3326 xx += 2, yy += font_ascent + 1;
3329 if (appData.monoMode) {
3330 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3332 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3336 if(marker) { // print fat marker dot, if requested
3337 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
3342 FlashDelay (int flash_delay)
3344 XSync(xDisplay, False);
3345 if(flash_delay) do_flash_delay(flash_delay);
3349 Fraction (int x, int start, int stop)
3351 double f = ((double) x - start)/(stop - start);
3352 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
3356 static WindowPlacement wpNew;
3359 CoDrag (Widget sh, WindowPlacement *wp)
3362 int j=0, touch=0, fudge = 2;
3363 GetActualPlacement(sh, wp);
3364 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
3365 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
3366 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
3367 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
3368 if(!touch ) return; // only windows that touch co-move
3369 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
3370 int heightInc = wpNew.height - wpMain.height;
3371 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3372 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3373 wp->y += fracTop * heightInc;
3374 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
3375 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
3376 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
3377 int widthInc = wpNew.width - wpMain.width;
3378 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3379 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3380 wp->y += fracLeft * widthInc;
3381 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
3382 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
3384 wp->x += wpNew.x - wpMain.x;
3385 wp->y += wpNew.y - wpMain.y;
3386 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
3387 if(touch == 3) wp->y += wpNew.height - wpMain.height;
3388 XtSetArg(args[j], XtNx, wp->x); j++;
3389 XtSetArg(args[j], XtNy, wp->y); j++;
3390 XtSetValues(sh, args, j);
3393 static XtIntervalId delayedDragID = 0;
3398 GetActualPlacement(shellWidget, &wpNew);
3399 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
3400 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
3401 return; // false alarm
3402 if(EngineOutputIsUp()) CoDrag(engineOutputShell, &wpEngineOutput);
3403 if(MoveHistoryIsUp()) CoDrag(shells[HistoryDlg], &wpMoveHistory);
3404 if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
3405 if(GameListIsUp()) CoDrag(gameListShell, &wpGameList);
3407 DrawPosition(True, NULL);
3408 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
3415 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
3417 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
3420 /* Why is this needed on some versions of X? */
3422 EventProc (Widget widget, caddr_t unused, XEvent *event)
3424 if (!XtIsRealized(widget))
3426 switch (event->type) {
3427 case ConfigureNotify: // main window is being dragged: drag attached windows with it
3428 if(appData.useStickyWindows)
3429 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
3432 if (event->xexpose.count > 0) return; /* no clipping is done */
3433 DrawPosition(True, NULL);
3434 if(twoBoards) { // [HGM] dual: draw other board in other orientation
3435 flipView = !flipView; partnerUp = !partnerUp;
3436 DrawPosition(True, NULL);
3437 flipView = !flipView; partnerUp = !partnerUp;
3441 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
3448 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
3450 DrawSeekAxis (int x, int y, int xTo, int yTo)
3452 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
3456 DrawSeekBackground (int left, int top, int right, int bottom)
3458 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
3462 DrawSeekText (char *buf, int x, int y)
3464 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
3468 DrawSeekDot (int x, int y, int colorNr)
3470 int square = colorNr & 0x80;
3473 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
3475 XFillRectangle(xDisplay, xBoardWindow, color,
3476 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3478 XFillArc(xDisplay, xBoardWindow, color,
3479 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
3483 DrawGrid (int second)
3485 XDrawSegments(xDisplay, xBoardWindow, lineGC,
3486 second ? secondSegments : // [HGM] dual
3487 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
3492 * event handler for redrawing the board
3495 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3497 DrawPosition(True, NULL);
3502 * event handler for parsing user moves
3504 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
3505 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
3506 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
3507 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
3508 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
3509 // and at the end FinishMove() to perform the move after optional promotion popups.
3510 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
3512 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3514 if (w != boardWidget || errorExitStatus != -1) return;
3515 if(nprms) shiftKey = !strcmp(prms[0], "1");
3518 if (event->type == ButtonPress) {
3519 XtPopdown(promotionShell);
3520 XtDestroyWidget(promotionShell);
3521 promotionUp = False;
3529 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
3530 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
3531 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
3535 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
3537 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
3538 DragPieceMove(event->xmotion.x, event->xmotion.y);
3542 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3543 { // [HGM] pv: walk PV
3544 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3547 static int savedIndex; /* gross that this is global */
3550 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3553 XawTextPosition index, dummy;
3556 XawTextGetSelectionPos(w, &index, &dummy);
3557 XtSetArg(arg, XtNstring, &val);
3558 XtGetValues(w, &arg, 1);
3559 ReplaceComment(savedIndex, val);
3560 if(savedIndex != currentMove) ToNrEvent(savedIndex);
3561 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3565 EditCommentPopUp (int index, char *title, char *text)
3568 if (text == NULL) text = "";
3569 NewCommentPopup(title, text, index);
3573 CommentPopUp (char *title, char *text)
3575 savedIndex = currentMove; // [HGM] vari
3576 NewCommentPopup(title, text, currentMove);
3582 PopDown(CommentDlg);
3585 static char *openName;
3591 (void) (*fileProc)(openFP, 0, openName);
3595 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
3597 fileProc = proc; /* I can't see a way not */
3598 fileOpenMode = openMode; /* to use globals here */
3599 { // [HGM] use file-selector dialog stolen from Ghostview
3600 int index; // this is not supported yet
3601 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
3602 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
3603 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
3604 ScheduleDelayedEvent(&DelayedLoad, 50);
3612 Widget dialog, layout;
3614 Dimension bw_width, pw_width;
3616 char *PromoChars = "wglcqrbnkac+=\0";
3619 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3620 XtGetValues(boardWidget, args, j);
3623 XtSetArg(args[j], XtNresizable, True); j++;
3624 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
3626 XtCreatePopupShell("Promotion", transientShellWidgetClass,
3627 shellWidget, args, j);
3629 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
3630 layoutArgs, XtNumber(layoutArgs));
3633 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
3634 XtSetArg(args[j], XtNborderWidth, 0); j++;
3635 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
3638 if(gameInfo.variant != VariantShogi) {
3639 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
3640 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
3641 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
3642 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
3643 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
3645 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
3646 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
3647 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
3648 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
3650 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
3651 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
3652 gameInfo.variant == VariantGiveaway) {
3653 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
3655 if(gameInfo.variant == VariantCapablanca ||
3656 gameInfo.variant == VariantGothic ||
3657 gameInfo.variant == VariantCapaRandom) {
3658 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
3659 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
3661 } else // [HGM] shogi
3663 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
3664 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
3666 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
3668 XtRealizeWidget(promotionShell);
3669 CatchDeleteWindow(promotionShell, "PromotionPopDown");
3672 XtSetArg(args[j], XtNwidth, &pw_width); j++;
3673 XtGetValues(promotionShell, args, j);
3675 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3676 lineGap + squareSize/3 +
3677 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
3678 0 : 6*(squareSize + lineGap)), &x, &y);
3681 XtSetArg(args[j], XtNx, x); j++;
3682 XtSetArg(args[j], XtNy, y); j++;
3683 XtSetValues(promotionShell, args, j);
3685 XtPopup(promotionShell, XtGrabNone);
3693 if (!promotionUp) return;
3694 XtPopdown(promotionShell);
3695 XtDestroyWidget(promotionShell);
3696 promotionUp = False;
3700 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
3702 int promoChar = * (const char *) client_data;
3706 if (fromX == -1) return;
3713 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
3715 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
3716 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3722 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
3724 dialogError = errorUp = False;
3725 XtPopdown(w = XtParent(XtParent(XtParent(w))));
3727 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3734 if (!errorUp) return;
3735 dialogError = errorUp = False;
3736 XtPopdown(errorShell);
3737 XtDestroyWidget(errorShell);
3738 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3742 ErrorPopUp (char *title, char *label, int modal)
3745 Widget dialog, layout;
3749 Dimension bw_width, pw_width;
3750 Dimension pw_height;
3754 XtSetArg(args[i], XtNresizable, True); i++;
3755 XtSetArg(args[i], XtNtitle, title); i++;
3757 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
3758 shellUp[TransientDlg] ? (dialogError = modal = TRUE, shells[TransientDlg]) : shellWidget, args, i);
3760 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
3761 layoutArgs, XtNumber(layoutArgs));
3764 XtSetArg(args[i], XtNlabel, label); i++;
3765 XtSetArg(args[i], XtNborderWidth, 0); i++;
3766 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
3769 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
3771 XtRealizeWidget(errorShell);
3772 CatchDeleteWindow(errorShell, "ErrorPopDown");
3775 XtSetArg(args[i], XtNwidth, &bw_width); i++;
3776 XtGetValues(boardWidget, args, i);
3778 XtSetArg(args[i], XtNwidth, &pw_width); i++;
3779 XtSetArg(args[i], XtNheight, &pw_height); i++;
3780 XtGetValues(errorShell, args, i);
3783 /* This code seems to tickle an X bug if it is executed too soon
3784 after xboard starts up. The coordinates get transformed as if
3785 the main window was positioned at (0, 0).
3787 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3788 0 - pw_height + squareSize / 3, &x, &y);
3790 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
3791 RootWindowOfScreen(XtScreen(boardWidget)),
3792 (bw_width - pw_width) / 2,
3793 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
3797 if (y < 0) y = 0; /*avoid positioning top offscreen*/
3800 XtSetArg(args[i], XtNx, x); i++;
3801 XtSetArg(args[i], XtNy, y); i++;
3802 XtSetValues(errorShell, args, i);
3805 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
3808 /* Disable all user input other than deleting the window */
3809 static int frozen = 0;
3815 /* Grab by a widget that doesn't accept input */
3816 XtAddGrab(messageWidget, TRUE, FALSE);
3820 /* Undo a FreezeUI */
3824 if (!frozen) return;
3825 XtRemoveGrab(messageWidget);
3833 static int oldPausing = FALSE;
3834 static GameMode oldmode = (GameMode) -1;
3837 if (!boardWidget || !XtIsRealized(boardWidget)) return;
3839 if (pausing != oldPausing) {
3840 oldPausing = pausing;
3841 MarkMenuItem("Pause", pausing);
3843 if (appData.showButtonBar) {
3844 /* Always toggle, don't set. Previous code messes up when
3845 invoked while the button is pressed, as releasing it
3846 toggles the state again. */
3849 XtSetArg(args[0], XtNbackground, &oldbg);
3850 XtSetArg(args[1], XtNforeground, &oldfg);
3851 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
3853 XtSetArg(args[0], XtNbackground, oldfg);
3854 XtSetArg(args[1], XtNforeground, oldbg);
3856 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
3860 wname = ModeToWidgetName(oldmode);
3861 if (wname != NULL) {
3862 MarkMenuItem(wname, False);
3864 wname = ModeToWidgetName(gameMode);
3865 if (wname != NULL) {
3866 MarkMenuItem(wname, True);
3869 MarkMenuItem("Machine Match", matchMode && matchGame < appData.matchGames);
3871 /* Maybe all the enables should be handled here, not just this one */
3872 EnableMenuItem("Training", gameMode == Training || gameMode == PlayFromGameFile);
3877 * Button/menu procedures
3880 LoadGamePopUp (FILE *f, int gameNumber, char *title)
3882 cmailMsgLoaded = FALSE;
3883 if (gameNumber == 0) {
3884 int error = GameListBuild(f);
3886 DisplayError(_("Cannot build game list"), error);
3887 } else if (!ListEmpty(&gameList) &&
3888 ((ListGame *) gameList.tailPred)->number > 1) {
3889 GameListPopUp(f, title);
3895 return LoadGame(f, gameNumber, title, FALSE);
3898 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3899 char *selected_fen_position=NULL;
3902 SendPositionSelection (Widget w, Atom *selection, Atom *target,
3903 Atom *type_return, XtPointer *value_return,
3904 unsigned long *length_return, int *format_return)
3906 char *selection_tmp;
3908 // if (!selected_fen_position) return False; /* should never happen */
3909 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3910 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
3911 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
3914 if (f == NULL) return False;
3918 selection_tmp = XtMalloc(len + 1);
3919 count = fread(selection_tmp, 1, len, f);
3922 XtFree(selection_tmp);
3925 selection_tmp[len] = NULLCHAR;
3927 /* note: since no XtSelectionDoneProc was registered, Xt will
3928 * automatically call XtFree on the value returned. So have to
3929 * make a copy of it allocated with XtMalloc */
3930 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3931 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
3934 *value_return=selection_tmp;
3935 *length_return=strlen(selection_tmp);
3936 *type_return=*target;
3937 *format_return = 8; /* bits per byte */
3939 } else if (*target == XA_TARGETS(xDisplay)) {
3940 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3941 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3942 targets_tmp[1] = XA_STRING;
3943 *value_return = targets_tmp;
3944 *type_return = XA_ATOM;
3947 // This code leads to a read of value_return out of bounds on 64-bit systems.
3948 // Other code which I have seen always sets *format_return to 32 independent of
3949 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
3950 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
3951 *format_return = 8 * sizeof(Atom);
3952 if (*format_return > 32) {
3953 *length_return *= *format_return / 32;
3954 *format_return = 32;
3957 *format_return = 32;
3965 /* note: when called from menu all parameters are NULL, so no clue what the
3966 * Widget which was clicked on was, or what the click event was
3969 CopySomething (char *src)
3971 selected_fen_position = src;
3973 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3974 * have a notion of a position that is selected but not copied.
3975 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3977 XtOwnSelection(menuBarWidget, XA_PRIMARY,
3979 SendPositionSelection,
3980 NULL/* lose_ownership_proc */ ,
3981 NULL/* transfer_done_proc */);
3982 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3984 SendPositionSelection,
3985 NULL/* lose_ownership_proc */ ,
3986 NULL/* transfer_done_proc */);
3989 /* function called when the data to Paste is ready */
3991 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
3992 Atom *type, XtPointer value, unsigned long *len, int *format)
3995 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
3996 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
3997 EditPositionPasteFEN(fenstr);
4001 /* called when Paste Position button is pressed,
4002 * all parameters will be NULL */
4004 PastePositionProc ()
4006 XtGetSelectionValue(menuBarWidget,
4007 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4008 /* (XtSelectionCallbackProc) */ PastePositionCB,
4009 NULL, /* client_data passed to PastePositionCB */
4011 /* better to use the time field from the event that triggered the
4012 * call to this function, but that isn't trivial to get
4019 /* note: when called from menu all parameters are NULL, so no clue what the
4020 * Widget which was clicked on was, or what the click event was
4022 /* function called when the data to Paste is ready */
4024 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
4025 Atom *type, XtPointer value, unsigned long *len, int *format)
4028 if (value == NULL || *len == 0) {
4029 return; /* nothing had been selected to copy */
4031 f = fopen(gamePasteFilename, "w");
4033 DisplayError(_("Can't open temp file"), errno);
4036 fwrite(value, 1, *len, f);
4039 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
4042 /* called when Paste Game button is pressed,
4043 * all parameters will be NULL */
4047 XtGetSelectionValue(menuBarWidget,
4048 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4049 /* (XtSelectionCallbackProc) */ PasteGameCB,
4050 NULL, /* client_data passed to PasteGameCB */
4052 /* better to use the time field from the event that triggered the
4053 * call to this function, but that isn't trivial to get
4062 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4069 { // bassic primitive for determining if modifier keys are pressed
4070 long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
4073 XQueryKeymap(xDisplay,keys);
4074 for(i=0; i<6; i++) {
4076 j = XKeysymToKeycode(xDisplay, codes[i]);
4077 k += ( (keys[j>>3]&1<<(j&7)) != 0 );
4083 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
4087 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
4088 if ( n == 1 && *buf >= 32 // printable
4089 && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
4090 ) BoxAutoPopUp (buf);
4094 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4095 { // [HGM] input: let up-arrow recall previous line from history
4100 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4101 { // [HGM] input: let down-arrow recall next line from history
4106 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4112 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4114 if (!TempBackwardActive) {
4115 TempBackwardActive = True;
4121 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4123 /* Check to see if triggered by a key release event for a repeating key.
4124 * If so the next queued event will be a key press of the same key at the same time */
4125 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
4127 XPeekEvent(xDisplay, &next);
4128 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
4129 next.xkey.keycode == event->xkey.keycode)
4133 TempBackwardActive = False;
4137 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4138 { // called as key binding
4141 if (nprms && *nprms > 0)
4145 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
4150 DisplayMessage (char *message, char *extMessage)
4152 /* display a message in the message widget */
4161 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
4166 message = extMessage;
4170 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
4172 /* need to test if messageWidget already exists, since this function
4173 can also be called during the startup, if for example a Xresource
4174 is not set up correctly */
4177 XtSetArg(arg, XtNlabel, message);
4178 XtSetValues(messageWidget, &arg, 1);
4185 SetWindowTitle (char *text, char *title, char *icon)
4189 if (appData.titleInWindow) {
4191 XtSetArg(args[i], XtNlabel, text); i++;
4192 XtSetValues(titleWidget, args, i);
4195 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
4196 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
4197 XtSetValues(shellWidget, args, i);
4198 XSync(xDisplay, False);
4203 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
4209 DisplayIcsInteractionTitle (String message)
4211 if (oldICSInteractionTitle == NULL) {
4212 /* Magic to find the old window title, adapted from vim */
4213 char *wina = getenv("WINDOWID");
4215 Window win = (Window) atoi(wina);
4216 Window root, parent, *children;
4217 unsigned int nchildren;
4218 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4220 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4221 if (!XQueryTree(xDisplay, win, &root, &parent,
4222 &children, &nchildren)) break;
4223 if (children) XFree((void *)children);
4224 if (parent == root || parent == 0) break;
4227 XSetErrorHandler(oldHandler);
4229 if (oldICSInteractionTitle == NULL) {
4230 oldICSInteractionTitle = "xterm";
4233 printf("\033]0;%s\007", message);
4237 char pendingReplyPrefix[MSG_SIZ];
4238 ProcRef pendingReplyPR;
4241 AskQuestionPopDown ()
4243 if (!askQuestionUp) return;
4244 XtPopdown(askQuestionShell);
4245 XtDestroyWidget(askQuestionShell);
4246 askQuestionUp = False;
4250 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4256 reply = XawDialogGetValueString(w = XtParent(w));
4257 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
4258 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
4259 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
4260 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
4261 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
4262 AskQuestionPopDown();
4264 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
4268 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
4273 XtSetArg(args[0], XtNlabel, &name);
4274 XtGetValues(w, args, 1);
4276 if (strcmp(name, _("cancel")) == 0) {
4277 AskQuestionPopDown();
4279 AskQuestionReplyAction(w, NULL, NULL, NULL);
4284 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
4287 Widget popup, layout, dialog, edit;
4293 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
4294 pendingReplyPR = pr;
4297 XtSetArg(args[i], XtNresizable, True); i++;
4298 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
4299 askQuestionShell = popup =
4300 XtCreatePopupShell(title, transientShellWidgetClass,
4301 shellWidget, args, i);
4304 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
4305 layoutArgs, XtNumber(layoutArgs));
4308 XtSetArg(args[i], XtNlabel, question); i++;
4309 XtSetArg(args[i], XtNvalue, ""); i++;
4310 XtSetArg(args[i], XtNborderWidth, 0); i++;
4311 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
4314 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
4315 (XtPointer) dialog);
4316 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
4317 (XtPointer) dialog);
4319 XtRealizeWidget(popup);
4320 CatchDeleteWindow(popup, "AskQuestionPopDown");
4322 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
4323 &x, &y, &win_x, &win_y, &mask);
4325 XtSetArg(args[0], XtNx, x - 10);
4326 XtSetArg(args[1], XtNy, y - 30);
4327 XtSetValues(popup, args, 2);
4329 XtPopup(popup, XtGrabExclusive);
4330 askQuestionUp = True;
4332 edit = XtNameToWidget(dialog, "*value");
4333 XtSetKeyboardFocus(popup, edit);
4337 XtIntervalId delayedEventTimerXID = 0;
4338 DelayedEventCallback delayedEventCallback = 0;
4343 delayedEventTimerXID = 0;
4344 delayedEventCallback();
4348 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
4350 if(delayedEventTimerXID && delayedEventCallback == cb)
4351 // [HGM] alive: replace, rather than add or flush identical event
4352 XtRemoveTimeOut(delayedEventTimerXID);
4353 delayedEventCallback = cb;
4354 delayedEventTimerXID =
4355 XtAppAddTimeOut(appContext, millisec,
4356 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);