2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
66 # if HAVE_SYS_SOCKET_H
67 # include <sys/socket.h>
68 # include <netinet/in.h>
70 # else /* not HAVE_SYS_SOCKET_H */
71 # if HAVE_LAN_SOCKET_H
72 # include <lan/socket.h>
74 # include <lan/netdb.h>
75 # else /* not HAVE_LAN_SOCKET_H */
76 # define OMIT_SOCKETS 1
77 # endif /* not HAVE_LAN_SOCKET_H */
78 # endif /* not HAVE_SYS_SOCKET_H */
79 #endif /* !OMIT_SOCKETS */
84 #else /* not STDC_HEADERS */
85 extern char *getenv();
88 # else /* not HAVE_STRING_H */
90 # endif /* not HAVE_STRING_H */
91 #endif /* not STDC_HEADERS */
94 # include <sys/fcntl.h>
95 #else /* not HAVE_SYS_FCNTL_H */
98 # endif /* HAVE_FCNTL_H */
99 #endif /* not HAVE_SYS_FCNTL_H */
101 #if HAVE_SYS_SYSTEMINFO_H
102 # include <sys/systeminfo.h>
103 #endif /* HAVE_SYS_SYSTEMINFO_H */
105 #if TIME_WITH_SYS_TIME
106 # include <sys/time.h>
110 # include <sys/time.h>
121 # include <sys/wait.h>
126 # define NAMLEN(dirent) strlen((dirent)->d_name)
127 # define HAVE_DIR_STRUCT
129 # define dirent direct
130 # define NAMLEN(dirent) (dirent)->d_namlen
132 # include <sys/ndir.h>
133 # define HAVE_DIR_STRUCT
136 # include <sys/dir.h>
137 # define HAVE_DIR_STRUCT
141 # define HAVE_DIR_STRUCT
149 #include <X11/Intrinsic.h>
150 #include <X11/StringDefs.h>
151 #include <X11/Shell.h>
152 #include <X11/cursorfont.h>
153 #include <X11/Xatom.h>
154 #include <X11/Xmu/Atoms.h>
156 #include <X11/Xaw3d/Dialog.h>
157 #include <X11/Xaw3d/Form.h>
158 #include <X11/Xaw3d/List.h>
159 #include <X11/Xaw3d/Label.h>
160 #include <X11/Xaw3d/SimpleMenu.h>
161 #include <X11/Xaw3d/SmeBSB.h>
162 #include <X11/Xaw3d/SmeLine.h>
163 #include <X11/Xaw3d/Box.h>
164 #include <X11/Xaw3d/MenuButton.h>
165 #include <X11/Xaw3d/Text.h>
166 #include <X11/Xaw3d/AsciiText.h>
168 #include <X11/Xaw/Dialog.h>
169 #include <X11/Xaw/Form.h>
170 #include <X11/Xaw/List.h>
171 #include <X11/Xaw/Label.h>
172 #include <X11/Xaw/SimpleMenu.h>
173 #include <X11/Xaw/SmeBSB.h>
174 #include <X11/Xaw/SmeLine.h>
175 #include <X11/Xaw/Box.h>
176 #include <X11/Xaw/MenuButton.h>
177 #include <X11/Xaw/Text.h>
178 #include <X11/Xaw/AsciiText.h>
181 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
186 #include "pixmaps/pixmaps.h"
187 #define IMAGE_EXT "xpm"
189 #define IMAGE_EXT "xim"
190 #include "bitmaps/bitmaps.h"
193 #include "bitmaps/icon_white.bm"
194 #include "bitmaps/icon_black.bm"
195 #include "bitmaps/checkmark.bm"
197 #include "frontend.h"
199 #include "backendz.h"
203 #include "xgamelist.h"
204 #include "xhistory.h"
205 #include "xedittags.h"
216 #define usleep(t) _sleep2(((t)+500)/1000)
220 # define _(s) gettext (s)
221 # define N_(s) gettext_noop (s)
227 int main P((int argc, char **argv));
228 RETSIGTYPE CmailSigHandler P((int sig));
229 RETSIGTYPE IntSigHandler P((int sig));
230 RETSIGTYPE TermSizeSigHandler P((int sig));
231 static void CreateGCs P((int redo));
232 static void CreateAnyPieces P((void));
233 void CreateXIMPieces P((void));
234 void CreateXPMPieces P((void));
235 void CreateXPMBoard P((char *s, int n));
236 void CreatePieces P((void));
237 void CreatePieceMenus P((void));
238 Widget CreateMenuBar P((Menu *mb, int boardWidth));
239 Widget CreateButtonBar P ((MenuItem *mi));
241 char *InsertPxlSize P((char *pattern, int targetPxlSize));
242 XFontSet CreateFontSet P((char *base_fnt_lst));
244 char *FindFont P((char *pattern, int targetPxlSize));
246 void PieceMenuPopup P((Widget w, XEvent *event,
247 String *params, Cardinal *num_params));
248 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
249 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
250 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
251 u_int wreq, u_int hreq));
252 void CreateGrid P((void));
253 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
254 void DelayedDrag P((void));
255 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
256 void HandleUserMove P((Widget w, XEvent *event,
257 String *prms, Cardinal *nprms));
258 void AnimateUserMove P((Widget w, XEvent * event,
259 String * params, Cardinal * nParams));
260 void HandlePV P((Widget w, XEvent * event,
261 String * params, Cardinal * nParams));
262 void SelectPV P((Widget w, XEvent * event,
263 String * params, Cardinal * nParams));
264 void StopPV P((Widget w, XEvent * event,
265 String * params, Cardinal * nParams));
266 void WhiteClock P((Widget w, XEvent *event,
267 String *prms, Cardinal *nprms));
268 void BlackClock P((Widget w, XEvent *event,
269 String *prms, Cardinal *nprms));
270 void DrawPositionProc P((Widget w, XEvent *event,
271 String *prms, Cardinal *nprms));
272 void CommentClick P((Widget w, XEvent * event,
273 String * params, Cardinal * nParams));
274 void ICSInputBoxPopUp P((void));
275 void FileNamePopUp P((char *label, char *def, char *filter,
276 FileProc proc, char *openMode));
277 void AskQuestionReplyAction P((Widget w, XEvent *event,
278 String *prms, Cardinal *nprms));
279 void AskQuestionPopDown P((void));
280 void PromotionPopDown P((void));
281 void PromotionCallback P((Widget w, XtPointer client_data,
282 XtPointer call_data));
283 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
284 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
285 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
286 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
287 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
288 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
289 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
290 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
291 Boolean TempBackwardActive = False;
292 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
293 void DisplayMove P((int moveNumber));
294 void ICSInitScript P((void));
295 static char *ExpandPathName P((char *path));
296 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
297 void update_ics_width P(());
298 int get_term_width P(());
299 int CopyMemoProc P(());
302 * XBoard depends on Xt R4 or higher
304 int xtVersion = XtSpecificationRelease;
309 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
310 highlightSquareColor, premoveHighlightColor;
311 Pixel lowTimeWarningColor;
312 GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
313 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
315 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
316 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
317 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
318 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
319 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
320 ICSInputShell, fileNameShell, askQuestionShell;
321 Widget historyShell, evalGraphShell, gameListShell;
322 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
323 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
325 XFontSet fontSet, clockFontSet;
328 XFontStruct *clockFontStruct;
330 Font coordFontID, countFontID;
331 XFontStruct *coordFontStruct, *countFontStruct;
332 XtAppContext appContext;
334 char *oldICSInteractionTitle;
338 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
340 Position commentX = -1, commentY = -1;
341 Dimension commentW, commentH;
342 typedef unsigned int BoardSize;
344 Boolean chessProgram;
346 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
347 int smallLayout = 0, tinyLayout = 0,
348 marginW, marginH, // [HGM] for run-time resizing
349 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
350 ICSInputBoxUp = False, askQuestionUp = False,
351 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
352 errorUp = False, errorExitStatus = -1, defaultLineGap;
353 Dimension textHeight;
354 Pixel timerForegroundPixel, timerBackgroundPixel;
355 Pixel buttonForegroundPixel, buttonBackgroundPixel;
356 char *chessDir, *programName, *programVersion;
357 Boolean alwaysOnTop = False;
358 char *icsTextMenuString;
360 char *firstChessProgramNames;
361 char *secondChessProgramNames;
363 WindowPlacement wpMain;
364 WindowPlacement wpConsole;
365 WindowPlacement wpComment;
366 WindowPlacement wpMoveHistory;
367 WindowPlacement wpEvalGraph;
368 WindowPlacement wpEngineOutput;
369 WindowPlacement wpGameList;
370 WindowPlacement wpTags;
375 Pixmap pieceBitmap[2][(int)BlackPawn];
376 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
377 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
378 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
379 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
380 Pixmap xpmBoardBitmap[2];
381 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
382 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
383 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
384 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
385 XImage *ximLightSquare, *ximDarkSquare;
388 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
389 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
391 #define White(piece) ((int)(piece) < (int)BlackPawn)
393 /* Bitmaps for use as masks when drawing XPM pieces.
394 Need one for each black and white piece. */
395 static Pixmap xpmMask[BlackKing + 1];
397 /* This magic number is the number of intermediate frames used
398 in each half of the animation. For short moves it's reduced
399 by 1. The total number of frames will be factor * 2 + 1. */
402 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
404 #define PAUSE_BUTTON "P"
405 MenuItem buttonBar[] = {
406 {"<<", "<<", ToStartEvent},
407 {"<", "<", BackwardEvent},
408 {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseEvent},
409 {">", ">", ForwardEvent},
410 {">>", ">>", ToEndEvent},
414 #define PIECE_MENU_SIZE 18
415 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
416 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
417 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
418 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
419 N_("Empty square"), N_("Clear board") },
420 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
421 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
422 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
423 N_("Empty square"), N_("Clear board") }
425 /* must be in same order as pieceMenuStrings! */
426 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
427 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
428 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
429 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
430 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
431 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
432 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
433 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
434 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
437 #define DROP_MENU_SIZE 6
438 String dropMenuStrings[DROP_MENU_SIZE] = {
439 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
441 /* must be in same order as dropMenuStrings! */
442 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
443 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
444 WhiteRook, WhiteQueen
452 DropMenuEnables dmEnables[] = {
470 { XtNborderWidth, 0 },
471 { XtNdefaultDistance, 0 },
475 { XtNborderWidth, 0 },
476 { XtNresizable, (XtArgVal) True },
480 { XtNborderWidth, 0 },
486 { XtNjustify, (XtArgVal) XtJustifyRight },
487 { XtNlabel, (XtArgVal) "..." },
488 { XtNresizable, (XtArgVal) True },
489 { XtNresize, (XtArgVal) False }
492 Arg messageArgs[] = {
493 { XtNjustify, (XtArgVal) XtJustifyLeft },
494 { XtNlabel, (XtArgVal) "..." },
495 { XtNresizable, (XtArgVal) True },
496 { XtNresize, (XtArgVal) False }
500 { XtNborderWidth, 0 },
501 { XtNjustify, (XtArgVal) XtJustifyLeft }
504 XtResource clientResources[] = {
505 { "flashCount", "flashCount", XtRInt, sizeof(int),
506 XtOffset(AppDataPtr, flashCount), XtRImmediate,
507 (XtPointer) FLASH_COUNT },
510 XrmOptionDescRec shellOptions[] = {
511 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
512 { "-flash", "flashCount", XrmoptionNoArg, "3" },
513 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
516 XtActionsRec boardActions[] = {
517 { "DrawPosition", DrawPositionProc },
518 { "HandleUserMove", HandleUserMove },
519 { "AnimateUserMove", AnimateUserMove },
520 { "HandlePV", HandlePV },
521 { "SelectPV", SelectPV },
522 { "StopPV", StopPV },
523 { "AskQuestionReplyAction", AskQuestionReplyAction },
524 { "PieceMenuPopup", PieceMenuPopup },
525 { "WhiteClock", WhiteClock },
526 { "BlackClock", BlackClock },
527 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
528 { "QuitProc", QuitWrapper },
529 { "ManProc", ManInner },
530 { "TempBackwardProc", TempBackwardProc },
531 { "TempForwardProc", TempForwardProc },
532 { "CommentClick", (XtActionProc) CommentClick },
533 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
534 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
535 { "GameListPopDown", (XtActionProc) GameListPopDown },
536 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
537 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
538 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
539 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
540 { "GenericPopDown", (XtActionProc) GenericPopDown },
541 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
542 { "SelectMove", (XtActionProc) SelectMove },
543 { "LoadSelectedProc", LoadSelectedProc },
544 { "SetFilterProc", SetFilterProc },
545 { "TypeInProc", TypeInProc },
546 { "EnterKeyProc", EnterKeyProc },
547 { "UpKeyProc", UpKeyProc },
548 { "DownKeyProc", DownKeyProc },
551 char globalTranslations[] =
552 ":<Key>F9: MenuItem(ResignProc) \n \
553 :Ctrl<Key>n: MenuItem(NewGame) \n \
554 :Meta<Key>V: MenuItem(NewVariant) \n \
555 :Ctrl<Key>o: MenuItem(LoadGame) \n \
556 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
557 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
558 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
559 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
560 :Ctrl<Key>s: MenuItem(SaveGame) \n \
561 :Ctrl<Key>c: MenuItem(CopyGame) \n \
562 :Ctrl<Key>v: MenuItem(PasteGame) \n \
563 :Ctrl<Key>O: MenuItem(LoadPosition) \n \
564 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
565 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
566 :Ctrl<Key>S: MenuItem(SavePosition) \n \
567 :Ctrl<Key>C: MenuItem(CopyPosition) \n \
568 :Ctrl<Key>V: MenuItem(PastePosition) \n \
569 :Ctrl<Key>q: MenuItem(Exit) \n \
570 :Ctrl<Key>w: MenuItem(MachineWhite) \n \
571 :Ctrl<Key>b: MenuItem(MachineBlack) \n \
572 :Ctrl<Key>t: MenuItem(TwoMachines) \n \
573 :Ctrl<Key>a: MenuItem(AnalysisMode) \n \
574 :Ctrl<Key>g: MenuItem(AnalyzeFile) \n \
575 :Ctrl<Key>e: MenuItem(EditGame) \n \
576 :Ctrl<Key>E: MenuItem(EditPosition) \n \
577 :Meta<Key>O: MenuItem(ShowEngineOutput) \n \
578 :Meta<Key>E: MenuItem(ShowEvaluationGraph) \n \
579 :Meta<Key>G: MenuItem(ShowGameList) \n \
580 :Meta<Key>H: MenuItem(ShowMoveHistory) \n \
581 :<Key>Pause: MenuItem(Pause) \n \
582 :<Key>F3: MenuItem(Accept) \n \
583 :<Key>F4: MenuItem(Decline) \n \
584 :<Key>F12: MenuItem(Rematch) \n \
585 :<Key>F5: MenuItem(CallFlag) \n \
586 :<Key>F6: MenuItem(Draw) \n \
587 :<Key>F7: MenuItem(Adjourn) \n \
588 :<Key>F8: MenuItem(Abort) \n \
589 :<Key>F10: MenuItem(StopObserving) \n \
590 :<Key>F11: MenuItem(StopExamining) \n \
591 :Ctrl<Key>d: MenuItem(DebugProc) \n \
592 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
593 :Meta<Key>End: MenuItem(ToEnd) \n \
594 :Meta<Key>Right: MenuItem(Forward) \n \
595 :Meta<Key>Home: MenuItem(ToStart) \n \
596 :Meta<Key>Left: MenuItem(Backward) \n \
597 :<Key>Left: MenuItem(Backward) \n \
598 :<Key>Right: MenuItem(Forward) \n \
599 :<Key>Home: MenuItem(Revert) \n \
600 :<Key>End: MenuItem(TruncateGame) \n \
601 :Ctrl<Key>m: MenuItem(MoveNow) \n \
602 :Ctrl<Key>x: MenuItem(RetractMove) \n \
603 :Meta<Key>J: MenuItem(Adjudications) \n \
604 :Meta<Key>U: MenuItem(CommonEngine) \n \
605 :Meta<Key>T: MenuItem(TimeControl) \n \
606 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
607 #ifndef OPTIONSDIALOG
609 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
610 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
611 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
612 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
613 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
616 :<Key>F1: MenuItem(Manual) \n \
617 :<Key>F2: MenuItem(FlipView) \n \
618 :<KeyDown>Return: TempBackwardProc() \n \
619 :<KeyUp>Return: TempForwardProc() \n";
621 char boardTranslations[] =
622 "<Btn1Down>: HandleUserMove(0) \n \
623 Shift<Btn1Up>: HandleUserMove(1) \n \
624 <Btn1Up>: HandleUserMove(0) \n \
625 <Btn1Motion>: AnimateUserMove() \n \
626 <Btn3Motion>: HandlePV() \n \
627 <Btn2Motion>: HandlePV() \n \
628 <Btn3Up>: PieceMenuPopup(menuB) \n \
629 <Btn2Up>: PieceMenuPopup(menuB) \n \
630 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
631 PieceMenuPopup(menuB) \n \
632 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
633 PieceMenuPopup(menuW) \n \
634 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
635 PieceMenuPopup(menuW) \n \
636 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
637 PieceMenuPopup(menuB) \n";
639 char whiteTranslations[] =
640 "Shift<BtnDown>: WhiteClock(1)\n \
641 <BtnDown>: WhiteClock(0)\n";
642 char blackTranslations[] =
643 "Shift<BtnDown>: BlackClock(1)\n \
644 <BtnDown>: BlackClock(0)\n";
646 char ICSInputTranslations[] =
647 "<Key>Up: UpKeyProc() \n "
648 "<Key>Down: DownKeyProc() \n "
649 "<Key>Return: EnterKeyProc() \n";
651 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
652 // as the widget is destroyed before the up-click can call extend-end
653 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
655 String xboardResources[] = {
656 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
657 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
662 /* Max possible square size */
663 #define MAXSQSIZE 256
665 static int xpm_avail[MAXSQSIZE];
667 #ifdef HAVE_DIR_STRUCT
669 /* Extract piece size from filename */
671 xpm_getsize (char *name, int len, char *ext)
679 if ((p=strchr(name, '.')) == NULL ||
680 StrCaseCmp(p+1, ext) != 0)
686 while (*p && isdigit(*p))
693 /* Setup xpm_avail */
695 xpm_getavail (char *dirname, char *ext)
701 for (i=0; i<MAXSQSIZE; ++i)
704 if (appData.debugMode)
705 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
707 dir = opendir(dirname);
710 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
711 programName, dirname);
715 while ((ent=readdir(dir)) != NULL) {
716 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
717 if (i > 0 && i < MAXSQSIZE)
727 xpm_print_avail (FILE *fp, char *ext)
731 fprintf(fp, _("Available `%s' sizes:\n"), ext);
732 for (i=1; i<MAXSQSIZE; ++i) {
738 /* Return XPM piecesize closest to size */
740 xpm_closest_to (char *dirname, int size, char *ext)
743 int sm_diff = MAXSQSIZE;
747 xpm_getavail(dirname, ext);
749 if (appData.debugMode)
750 xpm_print_avail(stderr, ext);
752 for (i=1; i<MAXSQSIZE; ++i) {
755 diff = (diff<0) ? -diff : diff;
756 if (diff < sm_diff) {
764 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
770 #else /* !HAVE_DIR_STRUCT */
771 /* If we are on a system without a DIR struct, we can't
772 read the directory, so we can't collect a list of
773 filenames, etc., so we can't do any size-fitting. */
775 xpm_closest_to (char *dirname, int size, char *ext)
778 Warning: No DIR structure found on this system --\n\
779 Unable to autosize for XPM/XIM pieces.\n\
780 Please report this error to %s.\n\
781 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
784 #endif /* HAVE_DIR_STRUCT */
786 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
787 "magenta", "cyan", "white" };
791 TextColors textColors[(int)NColorClasses];
793 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
795 parse_color (char *str, int which)
797 char *p, buf[100], *d;
800 if (strlen(str) > 99) /* watch bounds on buf */
805 for (i=0; i<which; ++i) {
812 /* Could be looking at something like:
814 .. in which case we want to stop on a comma also */
815 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
819 return -1; /* Use default for empty field */
822 if (which == 2 || isdigit(*p))
825 while (*p && isalpha(*p))
830 for (i=0; i<8; ++i) {
831 if (!StrCaseCmp(buf, cnames[i]))
832 return which? (i+40) : (i+30);
834 if (!StrCaseCmp(buf, "default")) return -1;
836 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
841 parse_cpair (ColorClass cc, char *str)
843 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
844 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
849 /* bg and attr are optional */
850 textColors[(int)cc].bg = parse_color(str, 1);
851 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
852 textColors[(int)cc].attr = 0;
858 /* Arrange to catch delete-window events */
859 Atom wm_delete_window;
861 CatchDeleteWindow (Widget w, String procname)
864 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
865 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
866 XtAugmentTranslations(w, XtParseTranslationTable(buf));
873 XtSetArg(args[0], XtNiconic, False);
874 XtSetValues(shellWidget, args, 1);
876 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
879 //---------------------------------------------------------------------------------------------------------
880 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
883 #define CW_USEDEFAULT (1<<31)
884 #define ICS_TEXT_MENU_SIZE 90
885 #define DEBUG_FILE "xboard.debug"
886 #define SetCurrentDirectory chdir
887 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
891 // these two must some day move to frontend.h, when they are implemented
892 Boolean GameListIsUp();
894 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
897 // front-end part of option handling
899 // [HGM] This platform-dependent table provides the location for storing the color info
900 extern char *crWhite, * crBlack;
904 &appData.whitePieceColor,
905 &appData.blackPieceColor,
906 &appData.lightSquareColor,
907 &appData.darkSquareColor,
908 &appData.highlightSquareColor,
909 &appData.premoveHighlightColor,
910 &appData.lowTimeWarningColor,
921 // [HGM] font: keep a font for each square size, even non-stndard ones
924 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
925 char *fontTable[NUM_FONTS][MAX_SIZE];
928 ParseFont (char *name, int number)
929 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
931 if(sscanf(name, "size%d:", &size)) {
932 // [HGM] font: font is meant for specific boardSize (likely from settings file);
933 // defer processing it until we know if it matches our board size
934 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
935 fontTable[number][size] = strdup(strchr(name, ':')+1);
936 fontValid[number][size] = True;
941 case 0: // CLOCK_FONT
942 appData.clockFont = strdup(name);
944 case 1: // MESSAGE_FONT
945 appData.font = strdup(name);
947 case 2: // COORD_FONT
948 appData.coordFont = strdup(name);
953 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
958 { // only 2 fonts currently
959 appData.clockFont = CLOCK_FONT_NAME;
960 appData.coordFont = COORD_FONT_NAME;
961 appData.font = DEFAULT_FONT_NAME;
966 { // no-op, until we identify the code for this already in XBoard and move it here
970 ParseColor (int n, char *name)
971 { // in XBoard, just copy the color-name string
972 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
976 ParseTextAttribs (ColorClass cc, char *s)
978 (&appData.colorShout)[cc] = strdup(s);
982 ParseBoardSize (void *addr, char *name)
984 appData.boardSize = strdup(name);
989 { // In XBoard the sound-playing program takes care of obtaining the actual sound
993 SetCommPortDefaults ()
994 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
997 // [HGM] args: these three cases taken out to stay in front-end
999 SaveFontArg (FILE *f, ArgDescriptor *ad)
1002 int i, n = (int)(intptr_t)ad->argLoc;
1004 case 0: // CLOCK_FONT
1005 name = appData.clockFont;
1007 case 1: // MESSAGE_FONT
1008 name = appData.font;
1010 case 2: // COORD_FONT
1011 name = appData.coordFont;
1016 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1017 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1018 fontTable[n][squareSize] = strdup(name);
1019 fontValid[n][squareSize] = True;
1022 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1023 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1028 { // nothing to do, as the sounds are at all times represented by their text-string names already
1032 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
1033 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1034 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1038 SaveColor (FILE *f, ArgDescriptor *ad)
1039 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1040 if(colorVariable[(int)(intptr_t)ad->argLoc])
1041 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1045 SaveBoardSize (FILE *f, char *name, void *addr)
1046 { // wrapper to shield back-end from BoardSize & sizeInfo
1047 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1051 ParseCommPortSettings (char *s)
1052 { // no such option in XBoard (yet)
1055 extern Widget engineOutputShell;
1059 GetActualPlacement (Widget wg, WindowPlacement *wp)
1064 XWindowAttributes winAt;
1071 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
1072 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
1073 wp->x = rx - winAt.x;
1074 wp->y = ry - winAt.y;
1075 wp->height = winAt.height;
1076 wp->width = winAt.width;
1077 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
1082 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1083 // In XBoard this will have to wait until awareness of window parameters is implemented
1084 GetActualPlacement(shellWidget, &wpMain);
1085 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1086 if(MoveHistoryIsUp()) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
1087 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1088 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1089 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
1090 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
1094 PrintCommPortSettings (FILE *f, char *name)
1095 { // This option does not exist in XBoard
1099 MySearchPath (char *installDir, char *name, char *fullname)
1100 { // just append installDir and name. Perhaps ExpandPath should be used here?
1101 name = ExpandPathName(name);
1102 if(name && name[0] == '/')
1103 safeStrCpy(fullname, name, MSG_SIZ );
1105 sprintf(fullname, "%s%c%s", installDir, '/', name);
1111 MyGetFullPathName (char *name, char *fullname)
1112 { // should use ExpandPath?
1113 name = ExpandPathName(name);
1114 safeStrCpy(fullname, name, MSG_SIZ );
1119 EnsureOnScreen (int *x, int *y, int minX, int minY)
1126 { // [HGM] args: allows testing if main window is realized from back-end
1127 return xBoardWindow != 0;
1131 PopUpStartupDialog ()
1132 { // start menu not implemented in XBoard
1136 ConvertToLine (int argc, char **argv)
1138 static char line[128*1024], buf[1024];
1142 for(i=1; i<argc; i++)
1144 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1145 && argv[i][0] != '{' )
1146 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1148 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1149 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1152 line[strlen(line)-1] = NULLCHAR;
1156 //--------------------------------------------------------------------------------------------
1159 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1161 #define BoardSize int
1163 InitDrawingSizes (BoardSize boardSize, int flags)
1164 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1165 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1167 XtGeometryResult gres;
1169 static Dimension oldWidth, oldHeight;
1170 static VariantClass oldVariant;
1171 static int oldDual = -1, oldMono = -1;
1173 if(!formWidget) return;
1175 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1176 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1177 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1179 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1181 * Enable shell resizing.
1183 shellArgs[0].value = (XtArgVal) &w;
1184 shellArgs[1].value = (XtArgVal) &h;
1185 XtGetValues(shellWidget, shellArgs, 2);
1187 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1188 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1189 XtSetValues(shellWidget, &shellArgs[2], 4);
1191 XtSetArg(args[0], XtNdefaultDistance, &sep);
1192 XtGetValues(formWidget, args, 1);
1194 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1196 hOffset = boardWidth + 10;
1197 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1198 secondSegments[i] = gridSegments[i];
1199 secondSegments[i].x1 += hOffset;
1200 secondSegments[i].x2 += hOffset;
1203 XtSetArg(args[0], XtNwidth, boardWidth);
1204 XtSetArg(args[1], XtNheight, boardHeight);
1205 XtSetValues(boardWidget, args, 2);
1207 timerWidth = (boardWidth - sep) / 2;
1208 XtSetArg(args[0], XtNwidth, timerWidth);
1209 XtSetValues(whiteTimerWidget, args, 1);
1210 XtSetValues(blackTimerWidget, args, 1);
1212 XawFormDoLayout(formWidget, False);
1214 if (appData.titleInWindow) {
1216 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1217 XtSetArg(args[i], XtNheight, &h); i++;
1218 XtGetValues(titleWidget, args, i);
1220 w = boardWidth - 2*bor;
1222 XtSetArg(args[0], XtNwidth, &w);
1223 XtGetValues(menuBarWidget, args, 1);
1224 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1227 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1228 if (gres != XtGeometryYes && appData.debugMode) {
1230 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1231 programName, gres, w, h, wr, hr);
1235 XawFormDoLayout(formWidget, True);
1238 * Inhibit shell resizing.
1240 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1241 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1242 shellArgs[4].value = shellArgs[2].value = w;
1243 shellArgs[5].value = shellArgs[3].value = h;
1244 XtSetValues(shellWidget, &shellArgs[0], 6);
1246 XSync(xDisplay, False);
1250 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1253 if(gameInfo.variant != oldVariant) { // and only if variant changed
1256 for(i=0; i<4; i++) {
1258 for(p=0; p<=(int)WhiteKing; p++)
1259 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1260 if(gameInfo.variant == VariantShogi) {
1261 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1262 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1263 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1264 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1265 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1268 if(gameInfo.variant == VariantGothic) {
1269 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1272 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1273 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1274 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1277 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1278 for(p=0; p<=(int)WhiteKing; p++)
1279 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1280 if(gameInfo.variant == VariantShogi) {
1281 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1282 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1283 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1284 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1285 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1288 if(gameInfo.variant == VariantGothic) {
1289 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1292 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1293 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1294 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1299 for(i=0; i<2; i++) {
1301 for(p=0; p<=(int)WhiteKing; p++)
1302 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1303 if(gameInfo.variant == VariantShogi) {
1304 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1305 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1306 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1307 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1308 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1311 if(gameInfo.variant == VariantGothic) {
1312 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1315 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1316 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1317 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1321 oldMono = -10; // kludge to force recreation of animation masks
1322 oldVariant = gameInfo.variant;
1325 if(appData.monoMode != oldMono)
1328 oldMono = appData.monoMode;
1333 ParseIcsTextColors ()
1334 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1335 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1336 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1337 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1338 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1339 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1340 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1341 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1342 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1343 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1344 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1346 if (appData.colorize) {
1348 _("%s: can't parse color names; disabling colorization\n"),
1351 appData.colorize = FALSE;
1356 MakeOneColor (char *name, Pixel *color)
1358 XrmValue vFrom, vTo;
1359 if (!appData.monoMode) {
1360 vFrom.addr = (caddr_t) name;
1361 vFrom.size = strlen(name);
1362 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1363 if (vTo.addr == NULL) {
1364 appData.monoMode = True;
1367 *color = *(Pixel *) vTo.addr;
1375 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1376 int forceMono = False;
1378 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1379 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1380 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1381 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1382 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1383 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1390 { // [HGM] taken out of main
1392 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1393 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1394 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1396 if (appData.bitmapDirectory[0] != NULLCHAR) {
1400 CreateXPMBoard(appData.liteBackTextureFile, 1);
1401 CreateXPMBoard(appData.darkBackTextureFile, 0);
1405 /* Create regular pieces */
1406 if (!useImages) CreatePieces();
1411 InitDrawingParams ()
1413 MakeColors(); CreateGCs(True);
1418 main (int argc, char **argv)
1420 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1421 XSetWindowAttributes window_attributes;
1423 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1424 XrmValue vFrom, vTo;
1425 XtGeometryResult gres;
1428 int forceMono = False;
1430 srandom(time(0)); // [HGM] book: make random truly random
1432 setbuf(stdout, NULL);
1433 setbuf(stderr, NULL);
1436 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1437 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1441 programName = strrchr(argv[0], '/');
1442 if (programName == NULL)
1443 programName = argv[0];
1448 XtSetLanguageProc(NULL, NULL, NULL);
1449 bindtextdomain(PACKAGE, LOCALEDIR);
1450 textdomain(PACKAGE);
1454 XtAppInitialize(&appContext, "XBoard", shellOptions,
1455 XtNumber(shellOptions),
1456 &argc, argv, xboardResources, NULL, 0);
1457 appData.boardSize = "";
1458 InitAppData(ConvertToLine(argc, argv));
1460 if (p == NULL) p = "/tmp";
1461 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1462 gameCopyFilename = (char*) malloc(i);
1463 gamePasteFilename = (char*) malloc(i);
1464 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1465 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1467 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1468 clientResources, XtNumber(clientResources),
1471 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1472 static char buf[MSG_SIZ];
1473 EscapeExpand(buf, appData.firstInitString);
1474 appData.firstInitString = strdup(buf);
1475 EscapeExpand(buf, appData.secondInitString);
1476 appData.secondInitString = strdup(buf);
1477 EscapeExpand(buf, appData.firstComputerString);
1478 appData.firstComputerString = strdup(buf);
1479 EscapeExpand(buf, appData.secondComputerString);
1480 appData.secondComputerString = strdup(buf);
1483 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1486 if (chdir(chessDir) != 0) {
1487 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1493 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1494 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1495 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1496 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1499 setbuf(debugFP, NULL);
1503 if (appData.debugMode) {
1504 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1508 /* [HGM,HR] make sure board size is acceptable */
1509 if(appData.NrFiles > BOARD_FILES ||
1510 appData.NrRanks > BOARD_RANKS )
1511 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1514 /* This feature does not work; animation needs a rewrite */
1515 appData.highlightDragging = FALSE;
1519 xDisplay = XtDisplay(shellWidget);
1520 xScreen = DefaultScreen(xDisplay);
1521 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1523 gameInfo.variant = StringToVariant(appData.variant);
1524 InitPosition(FALSE);
1527 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1529 if (isdigit(appData.boardSize[0])) {
1530 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1531 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1532 &fontPxlSize, &smallLayout, &tinyLayout);
1534 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1535 programName, appData.boardSize);
1539 /* Find some defaults; use the nearest known size */
1540 SizeDefaults *szd, *nearest;
1541 int distance = 99999;
1542 nearest = szd = sizeDefaults;
1543 while (szd->name != NULL) {
1544 if (abs(szd->squareSize - squareSize) < distance) {
1546 distance = abs(szd->squareSize - squareSize);
1547 if (distance == 0) break;
1551 if (i < 2) lineGap = nearest->lineGap;
1552 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1553 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1554 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1555 if (i < 6) smallLayout = nearest->smallLayout;
1556 if (i < 7) tinyLayout = nearest->tinyLayout;
1559 SizeDefaults *szd = sizeDefaults;
1560 if (*appData.boardSize == NULLCHAR) {
1561 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1562 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1565 if (szd->name == NULL) szd--;
1566 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1568 while (szd->name != NULL &&
1569 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1570 if (szd->name == NULL) {
1571 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1572 programName, appData.boardSize);
1576 squareSize = szd->squareSize;
1577 lineGap = szd->lineGap;
1578 clockFontPxlSize = szd->clockFontPxlSize;
1579 coordFontPxlSize = szd->coordFontPxlSize;
1580 fontPxlSize = szd->fontPxlSize;
1581 smallLayout = szd->smallLayout;
1582 tinyLayout = szd->tinyLayout;
1583 // [HGM] font: use defaults from settings file if available and not overruled
1585 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1586 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1587 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1588 appData.font = fontTable[MESSAGE_FONT][squareSize];
1589 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1590 appData.coordFont = fontTable[COORD_FONT][squareSize];
1592 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1593 if (strlen(appData.pixmapDirectory) > 0) {
1594 p = ExpandPathName(appData.pixmapDirectory);
1596 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1597 appData.pixmapDirectory);
1600 if (appData.debugMode) {
1601 fprintf(stderr, _("\
1602 XBoard square size (hint): %d\n\
1603 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1605 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1606 if (appData.debugMode) {
1607 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1610 defaultLineGap = lineGap;
1611 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1613 /* [HR] height treated separately (hacked) */
1614 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1615 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1616 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1617 XtSetArg(boardArgs[2], XtNheight, boardHeight);
1620 * Determine what fonts to use.
1623 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1624 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1625 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1626 fontSet = CreateFontSet(appData.font);
1627 clockFontSet = CreateFontSet(appData.clockFont);
1629 /* For the coordFont, use the 0th font of the fontset. */
1630 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1631 XFontStruct **font_struct_list;
1632 XFontSetExtents *fontSize;
1633 char **font_name_list;
1634 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1635 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1636 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1637 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1638 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1641 appData.font = FindFont(appData.font, fontPxlSize);
1642 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1643 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1644 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1645 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1646 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1647 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1649 countFontID = coordFontID; // [HGM] holdings
1650 countFontStruct = coordFontStruct;
1652 xdb = XtDatabase(xDisplay);
1654 XrmPutLineResource(&xdb, "*international: True");
1655 vTo.size = sizeof(XFontSet);
1656 vTo.addr = (XtPointer) &fontSet;
1657 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1659 XrmPutStringResource(&xdb, "*font", appData.font);
1663 * Detect if there are not enough colors available and adapt.
1665 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1666 appData.monoMode = True;
1669 forceMono = MakeColors();
1672 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1674 appData.monoMode = True;
1677 if (appData.lowTimeWarning && !appData.monoMode) {
1678 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1679 vFrom.size = strlen(appData.lowTimeWarningColor);
1680 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1681 if (vTo.addr == NULL)
1682 appData.monoMode = True;
1684 lowTimeWarningColor = *(Pixel *) vTo.addr;
1687 if (appData.monoMode && appData.debugMode) {
1688 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1689 (unsigned long) XWhitePixel(xDisplay, xScreen),
1690 (unsigned long) XBlackPixel(xDisplay, xScreen));
1693 ParseIcsTextColors();
1694 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
1695 textColors[ColorNone].attr = 0;
1697 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1703 layoutName = "tinyLayout";
1704 } else if (smallLayout) {
1705 layoutName = "smallLayout";
1707 layoutName = "normalLayout";
1709 /* Outer layoutWidget is there only to provide a name for use in
1710 resources that depend on the layout style */
1712 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
1713 layoutArgs, XtNumber(layoutArgs));
1715 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
1716 formArgs, XtNumber(formArgs));
1717 XtSetArg(args[0], XtNdefaultDistance, &sep);
1718 XtGetValues(formWidget, args, 1);
1721 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
1722 XtSetArg(args[0], XtNtop, XtChainTop);
1723 XtSetArg(args[1], XtNbottom, XtChainTop);
1724 XtSetArg(args[2], XtNright, XtChainLeft);
1725 XtSetValues(menuBarWidget, args, 3);
1727 widgetList[j++] = whiteTimerWidget =
1728 XtCreateWidget("whiteTime", labelWidgetClass,
1729 formWidget, timerArgs, XtNumber(timerArgs));
1731 XtSetArg(args[0], XtNfontSet, clockFontSet);
1733 XtSetArg(args[0], XtNfont, clockFontStruct);
1735 XtSetArg(args[1], XtNtop, XtChainTop);
1736 XtSetArg(args[2], XtNbottom, XtChainTop);
1737 XtSetValues(whiteTimerWidget, args, 3);
1739 widgetList[j++] = blackTimerWidget =
1740 XtCreateWidget("blackTime", labelWidgetClass,
1741 formWidget, timerArgs, XtNumber(timerArgs));
1743 XtSetArg(args[0], XtNfontSet, clockFontSet);
1745 XtSetArg(args[0], XtNfont, clockFontStruct);
1747 XtSetArg(args[1], XtNtop, XtChainTop);
1748 XtSetArg(args[2], XtNbottom, XtChainTop);
1749 XtSetValues(blackTimerWidget, args, 3);
1751 if (appData.titleInWindow) {
1752 widgetList[j++] = titleWidget =
1753 XtCreateWidget("title", labelWidgetClass, formWidget,
1754 titleArgs, XtNumber(titleArgs));
1755 XtSetArg(args[0], XtNtop, XtChainTop);
1756 XtSetArg(args[1], XtNbottom, XtChainTop);
1757 XtSetValues(titleWidget, args, 2);
1760 if (appData.showButtonBar) {
1761 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
1762 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
1763 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
1764 XtSetArg(args[2], XtNtop, XtChainTop);
1765 XtSetArg(args[3], XtNbottom, XtChainTop);
1766 XtSetValues(buttonBarWidget, args, 4);
1769 widgetList[j++] = messageWidget =
1770 XtCreateWidget("message", labelWidgetClass, formWidget,
1771 messageArgs, XtNumber(messageArgs));
1772 XtSetArg(args[0], XtNtop, XtChainTop);
1773 XtSetArg(args[1], XtNbottom, XtChainTop);
1774 XtSetValues(messageWidget, args, 2);
1776 widgetList[j++] = boardWidget =
1777 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
1778 XtNumber(boardArgs));
1780 XtManageChildren(widgetList, j);
1782 timerWidth = (boardWidth - sep) / 2;
1783 XtSetArg(args[0], XtNwidth, timerWidth);
1784 XtSetValues(whiteTimerWidget, args, 1);
1785 XtSetValues(blackTimerWidget, args, 1);
1787 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1788 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1789 XtGetValues(whiteTimerWidget, args, 2);
1791 if (appData.showButtonBar) {
1792 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1793 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1794 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
1798 * formWidget uses these constraints but they are stored
1802 XtSetArg(args[i], XtNfromHoriz, 0); i++;
1803 XtSetValues(menuBarWidget, args, i);
1804 if (appData.titleInWindow) {
1807 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1808 XtSetValues(whiteTimerWidget, args, i);
1810 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1811 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1812 XtSetValues(blackTimerWidget, args, i);
1814 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1815 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
1816 XtSetValues(titleWidget, args, i);
1818 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1819 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1820 XtSetValues(messageWidget, args, i);
1821 if (appData.showButtonBar) {
1823 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1824 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1825 XtSetValues(buttonBarWidget, args, i);
1829 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1830 XtSetValues(whiteTimerWidget, args, i);
1832 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1833 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1834 XtSetValues(blackTimerWidget, args, i);
1836 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
1837 XtSetValues(titleWidget, args, i);
1839 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1840 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1841 XtSetValues(messageWidget, args, i);
1842 if (appData.showButtonBar) {
1844 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1845 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1846 XtSetValues(buttonBarWidget, args, i);
1851 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1852 XtSetValues(whiteTimerWidget, args, i);
1854 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1855 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1856 XtSetValues(blackTimerWidget, args, i);
1858 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1859 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1860 XtSetValues(messageWidget, args, i);
1861 if (appData.showButtonBar) {
1863 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1864 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1865 XtSetValues(buttonBarWidget, args, i);
1869 XtSetArg(args[0], XtNfromVert, messageWidget);
1870 XtSetArg(args[1], XtNtop, XtChainTop);
1871 XtSetArg(args[2], XtNbottom, XtChainBottom);
1872 XtSetArg(args[3], XtNleft, XtChainLeft);
1873 XtSetArg(args[4], XtNright, XtChainRight);
1874 XtSetValues(boardWidget, args, 5);
1876 XtRealizeWidget(shellWidget);
1879 XtSetArg(args[0], XtNx, wpMain.x);
1880 XtSetArg(args[1], XtNy, wpMain.y);
1881 XtSetValues(shellWidget, args, 2);
1885 * Correct the width of the message and title widgets.
1886 * It is not known why some systems need the extra fudge term.
1887 * The value "2" is probably larger than needed.
1889 XawFormDoLayout(formWidget, False);
1891 #define WIDTH_FUDGE 2
1893 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1894 XtSetArg(args[i], XtNheight, &h); i++;
1895 XtGetValues(messageWidget, args, i);
1896 if (appData.showButtonBar) {
1898 XtSetArg(args[i], XtNwidth, &w); i++;
1899 XtGetValues(buttonBarWidget, args, i);
1900 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1902 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
1905 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1906 if (gres != XtGeometryYes && appData.debugMode) {
1907 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1908 programName, gres, w, h, wr, hr);
1911 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
1912 /* The size used for the child widget in layout lags one resize behind
1913 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
1915 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1916 if (gres != XtGeometryYes && appData.debugMode) {
1917 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1918 programName, gres, w, h, wr, hr);
1921 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
1922 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
1923 XtSetArg(args[1], XtNright, XtChainRight);
1924 XtSetValues(messageWidget, args, 2);
1926 if (appData.titleInWindow) {
1928 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1929 XtSetArg(args[i], XtNheight, &h); i++;
1930 XtGetValues(titleWidget, args, i);
1932 w = boardWidth - 2*bor;
1934 XtSetArg(args[0], XtNwidth, &w);
1935 XtGetValues(menuBarWidget, args, 1);
1936 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1939 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1940 if (gres != XtGeometryYes && appData.debugMode) {
1942 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1943 programName, gres, w, h, wr, hr);
1946 XawFormDoLayout(formWidget, True);
1948 xBoardWindow = XtWindow(boardWidget);
1950 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1951 // not need to go into InitDrawingSizes().
1955 * Create X checkmark bitmap and initialize option menu checks.
1957 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1958 checkmark_bits, checkmark_width, checkmark_height);
1964 ReadBitmap(&wIconPixmap, "icon_white.bm",
1965 icon_white_bits, icon_white_width, icon_white_height);
1966 ReadBitmap(&bIconPixmap, "icon_black.bm",
1967 icon_black_bits, icon_black_width, icon_black_height);
1968 iconPixmap = wIconPixmap;
1970 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1971 XtSetValues(shellWidget, args, i);
1974 * Create a cursor for the board widget.
1976 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1977 XChangeWindowAttributes(xDisplay, xBoardWindow,
1978 CWCursor, &window_attributes);
1981 * Inhibit shell resizing.
1983 shellArgs[0].value = (XtArgVal) &w;
1984 shellArgs[1].value = (XtArgVal) &h;
1985 XtGetValues(shellWidget, shellArgs, 2);
1986 shellArgs[4].value = shellArgs[2].value = w;
1987 shellArgs[5].value = shellArgs[3].value = h;
1988 XtSetValues(shellWidget, &shellArgs[2], 4);
1989 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1990 marginH = h - boardHeight;
1992 CatchDeleteWindow(shellWidget, "QuitProc");
2000 if (appData.animate || appData.animateDragging)
2003 XtAugmentTranslations(formWidget,
2004 XtParseTranslationTable(globalTranslations));
2005 XtAugmentTranslations(boardWidget,
2006 XtParseTranslationTable(boardTranslations));
2007 XtAugmentTranslations(whiteTimerWidget,
2008 XtParseTranslationTable(whiteTranslations));
2009 XtAugmentTranslations(blackTimerWidget,
2010 XtParseTranslationTable(blackTranslations));
2012 /* Why is the following needed on some versions of X instead
2013 * of a translation? */
2014 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2015 (XtEventHandler) EventProc, NULL);
2017 XtAddEventHandler(formWidget, KeyPressMask, False,
2018 (XtEventHandler) MoveTypeInProc, NULL);
2019 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
2020 (XtEventHandler) EventProc, NULL);
2022 /* [AS] Restore layout */
2023 if( wpMoveHistory.visible ) {
2027 if( wpEvalGraph.visible )
2032 if( wpEngineOutput.visible ) {
2033 EngineOutputPopUp();
2038 if (errorExitStatus == -1) {
2039 if (appData.icsActive) {
2040 /* We now wait until we see "login:" from the ICS before
2041 sending the logon script (problems with timestamp otherwise) */
2042 /*ICSInitScript();*/
2043 if (appData.icsInputBox) ICSInputBoxPopUp();
2047 signal(SIGWINCH, TermSizeSigHandler);
2049 signal(SIGINT, IntSigHandler);
2050 signal(SIGTERM, IntSigHandler);
2051 if (*appData.cmailGameName != NULLCHAR) {
2052 signal(SIGUSR1, CmailSigHandler);
2056 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2058 // XtSetKeyboardFocus(shellWidget, formWidget);
2059 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2061 XtAppMainLoop(appContext);
2062 if (appData.debugMode) fclose(debugFP); // [DM] debug
2066 static Boolean noEcho;
2071 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2072 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2074 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2075 unlink(gameCopyFilename);
2076 unlink(gamePasteFilename);
2077 if(noEcho) EchoOn();
2081 TermSizeSigHandler (int sig)
2087 IntSigHandler (int sig)
2093 CmailSigHandler (int sig)
2098 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2100 /* Activate call-back function CmailSigHandlerCallBack() */
2101 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2103 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2107 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
2110 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2112 /**** end signal code ****/
2118 /* try to open the icsLogon script, either in the location given
2119 * or in the users HOME directory
2126 f = fopen(appData.icsLogon, "r");
2129 homedir = getenv("HOME");
2130 if (homedir != NULL)
2132 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2133 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2134 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2135 f = fopen(buf, "r");
2140 ProcessICSInitScript(f);
2142 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2155 #define Abs(n) ((n)<0 ? -(n) : (n))
2159 InsertPxlSize (char *pattern, int targetPxlSize)
2161 char *base_fnt_lst, strInt[12], *p, *q;
2162 int alternatives, i, len, strIntLen;
2165 * Replace the "*" (if present) in the pixel-size slot of each
2166 * alternative with the targetPxlSize.
2170 while ((p = strchr(p, ',')) != NULL) {
2174 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
2175 strIntLen = strlen(strInt);
2176 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
2180 while (alternatives--) {
2181 char *comma = strchr(p, ',');
2182 for (i=0; i<14; i++) {
2183 char *hyphen = strchr(p, '-');
2185 if (comma && hyphen > comma) break;
2186 len = hyphen + 1 - p;
2187 if (i == 7 && *p == '*' && len == 2) {
2189 memcpy(q, strInt, strIntLen);
2199 len = comma + 1 - p;
2206 return base_fnt_lst;
2210 CreateFontSet (char *base_fnt_lst)
2213 char **missing_list;
2217 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
2218 &missing_list, &missing_count, &def_string);
2219 if (appData.debugMode) {
2221 XFontStruct **font_struct_list;
2222 char **font_name_list;
2223 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
2225 fprintf(debugFP, " got list %s, locale %s\n",
2226 XBaseFontNameListOfFontSet(fntSet),
2227 XLocaleOfFontSet(fntSet));
2228 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
2229 for (i = 0; i < count; i++) {
2230 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
2233 for (i = 0; i < missing_count; i++) {
2234 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
2237 if (fntSet == NULL) {
2238 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
2243 #else // not ENABLE_NLS
2245 * Find a font that matches "pattern" that is as close as
2246 * possible to the targetPxlSize. Prefer fonts that are k
2247 * pixels smaller to fonts that are k pixels larger. The
2248 * pattern must be in the X Consortium standard format,
2249 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2250 * The return value should be freed with XtFree when no
2254 FindFont (char *pattern, int targetPxlSize)
2256 char **fonts, *p, *best, *scalable, *scalableTail;
2257 int i, j, nfonts, minerr, err, pxlSize;
2259 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2261 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2262 programName, pattern);
2269 for (i=0; i<nfonts; i++) {
2272 if (*p != '-') continue;
2274 if (*p == NULLCHAR) break;
2275 if (*p++ == '-') j++;
2277 if (j < 7) continue;
2280 scalable = fonts[i];
2283 err = pxlSize - targetPxlSize;
2284 if (Abs(err) < Abs(minerr) ||
2285 (minerr > 0 && err < 0 && -err == minerr)) {
2291 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2292 /* If the error is too big and there is a scalable font,
2293 use the scalable font. */
2294 int headlen = scalableTail - scalable;
2295 p = (char *) XtMalloc(strlen(scalable) + 10);
2296 while (isdigit(*scalableTail)) scalableTail++;
2297 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2299 p = (char *) XtMalloc(strlen(best) + 2);
2300 safeStrCpy(p, best, strlen(best)+1 );
2302 if (appData.debugMode) {
2303 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2304 pattern, targetPxlSize, p);
2306 XFreeFontNames(fonts);
2313 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
2314 // must be called before all non-first callse to CreateGCs()
2315 XtReleaseGC(shellWidget, highlineGC);
2316 XtReleaseGC(shellWidget, lightSquareGC);
2317 XtReleaseGC(shellWidget, darkSquareGC);
2318 XtReleaseGC(shellWidget, lineGC);
2319 if (appData.monoMode) {
2320 if (DefaultDepth(xDisplay, xScreen) == 1) {
2321 XtReleaseGC(shellWidget, wbPieceGC);
2323 XtReleaseGC(shellWidget, bwPieceGC);
2326 XtReleaseGC(shellWidget, prelineGC);
2327 XtReleaseGC(shellWidget, wdPieceGC);
2328 XtReleaseGC(shellWidget, wlPieceGC);
2329 XtReleaseGC(shellWidget, bdPieceGC);
2330 XtReleaseGC(shellWidget, blPieceGC);
2335 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
2337 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2338 | GCBackground | GCFunction | GCPlaneMask;
2339 gc_values->foreground = foreground;
2340 gc_values->background = background;
2341 return XtGetGC(shellWidget, value_mask, gc_values);
2345 CreateGCs (int redo)
2347 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2348 | GCBackground | GCFunction | GCPlaneMask;
2349 XGCValues gc_values;
2351 Pixel white = XWhitePixel(xDisplay, xScreen);
2352 Pixel black = XBlackPixel(xDisplay, xScreen);
2354 gc_values.plane_mask = AllPlanes;
2355 gc_values.line_width = lineGap;
2356 gc_values.line_style = LineSolid;
2357 gc_values.function = GXcopy;
2360 DeleteGCs(); // called a second time; clean up old GCs first
2361 } else { // [HGM] grid and font GCs created on first call only
2362 coordGC = CreateOneGC(&gc_values, black, white);
2363 XSetFont(xDisplay, coordGC, coordFontID);
2365 // [HGM] make font for holdings counts (white on black)
2366 countGC = CreateOneGC(&gc_values, white, black);
2367 XSetFont(xDisplay, countGC, countFontID);
2369 lineGC = CreateOneGC(&gc_values, black, black);
2371 if (appData.monoMode) {
2373 highlineGC = CreateOneGC(&gc_values, white, white);
2374 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
2375 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
2377 if (DefaultDepth(xDisplay, xScreen) == 1) {
2378 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2379 gc_values.function = GXcopyInverted;
2380 copyInvertedGC = CreateOneGC(&gc_values, black, white);
2381 gc_values.function = GXcopy;
2382 if (XBlackPixel(xDisplay, xScreen) == 1) {
2383 bwPieceGC = darkSquareGC;
2384 wbPieceGC = copyInvertedGC;
2386 bwPieceGC = copyInvertedGC;
2387 wbPieceGC = lightSquareGC;
2392 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
2393 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
2394 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
2395 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
2396 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
2397 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
2398 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
2399 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
2404 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
2412 fp = fopen(filename, "rb");
2414 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
2421 for (y=0; y<h; ++y) {
2422 for (x=0; x<h; ++x) {
2427 XPutPixel(xim, x, y, blackPieceColor);
2429 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2432 XPutPixel(xim, x, y, darkSquareColor);
2434 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2437 XPutPixel(xim, x, y, whitePieceColor);
2439 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2442 XPutPixel(xim, x, y, lightSquareColor);
2444 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2452 /* create Pixmap of piece */
2453 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2455 XPutImage(xDisplay, *dest, lightSquareGC, xim,
2458 /* create Pixmap of clipmask
2459 Note: We assume the white/black pieces have the same
2460 outline, so we make only 6 masks. This is okay
2461 since the XPM clipmask routines do the same. */
2463 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2465 XPutImage(xDisplay, temp, lightSquareGC, xmask,
2468 /* now create the 1-bit version */
2469 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2472 values.foreground = 1;
2473 values.background = 0;
2475 /* Don't use XtGetGC, not read only */
2476 maskGC = XCreateGC(xDisplay, *mask,
2477 GCForeground | GCBackground, &values);
2478 XCopyPlane(xDisplay, temp, *mask, maskGC,
2479 0, 0, squareSize, squareSize, 0, 0, 1);
2480 XFreePixmap(xDisplay, temp);
2485 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
2493 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
2498 /* The XSynchronize calls were copied from CreatePieces.
2499 Not sure if needed, but can't hurt */
2500 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2503 /* temp needed by loadXIM() */
2504 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2505 0, 0, ss, ss, AllPlanes, XYPixmap);
2507 if (strlen(appData.pixmapDirectory) == 0) {
2511 if (appData.monoMode) {
2512 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
2516 fprintf(stderr, _("\nLoading XIMs...\n"));
2518 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2519 fprintf(stderr, "%d", piece+1);
2520 for (kind=0; kind<4; kind++) {
2521 fprintf(stderr, ".");
2522 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2523 ExpandPathName(appData.pixmapDirectory),
2524 piece <= (int) WhiteKing ? "" : "w",
2525 pieceBitmapNames[piece],
2527 ximPieceBitmap[kind][piece] =
2528 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2529 0, 0, ss, ss, AllPlanes, XYPixmap);
2530 if (appData.debugMode)
2531 fprintf(stderr, _("(File:%s:) "), buf);
2532 loadXIM(ximPieceBitmap[kind][piece],
2534 &(xpmPieceBitmap2[kind][piece]),
2535 &(ximMaskPm2[piece]));
2536 if(piece <= (int)WhiteKing)
2537 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2539 fprintf(stderr," ");
2541 /* Load light and dark squares */
2542 /* If the LSQ and DSQ pieces don't exist, we will
2543 draw them with solid squares. */
2544 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2545 if (access(buf, 0) != 0) {
2549 fprintf(stderr, _("light square "));
2551 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2552 0, 0, ss, ss, AllPlanes, XYPixmap);
2553 if (appData.debugMode)
2554 fprintf(stderr, _("(File:%s:) "), buf);
2556 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2557 fprintf(stderr, _("dark square "));
2558 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2559 ExpandPathName(appData.pixmapDirectory), ss);
2560 if (appData.debugMode)
2561 fprintf(stderr, _("(File:%s:) "), buf);
2563 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2564 0, 0, ss, ss, AllPlanes, XYPixmap);
2565 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2566 xpmJailSquare = xpmLightSquare;
2568 fprintf(stderr, _("Done.\n"));
2570 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2573 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2577 CreateXPMBoard (char *s, int kind)
2581 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2582 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2583 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2589 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2590 // thisroutine has to be called t free the old piece pixmaps
2592 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2593 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2595 XFreePixmap(xDisplay, xpmLightSquare);
2596 XFreePixmap(xDisplay, xpmDarkSquare);
2605 u_int ss = squareSize;
2607 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2608 XpmColorSymbol symbols[4];
2609 static int redo = False;
2611 if(redo) FreeXPMPieces(); else redo = 1;
2613 /* The XSynchronize calls were copied from CreatePieces.
2614 Not sure if needed, but can't hurt */
2615 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2617 /* Setup translations so piece colors match square colors */
2618 symbols[0].name = "light_piece";
2619 symbols[0].value = appData.whitePieceColor;
2620 symbols[1].name = "dark_piece";
2621 symbols[1].value = appData.blackPieceColor;
2622 symbols[2].name = "light_square";
2623 symbols[2].value = appData.lightSquareColor;
2624 symbols[3].name = "dark_square";
2625 symbols[3].value = appData.darkSquareColor;
2627 attr.valuemask = XpmColorSymbols;
2628 attr.colorsymbols = symbols;
2629 attr.numsymbols = 4;
2631 if (appData.monoMode) {
2632 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2636 if (strlen(appData.pixmapDirectory) == 0) {
2637 XpmPieces* pieces = builtInXpms;
2640 while (pieces->size != squareSize && pieces->size) pieces++;
2641 if (!pieces->size) {
2642 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2645 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2646 for (kind=0; kind<4; kind++) {
2648 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2649 pieces->xpm[piece][kind],
2650 &(xpmPieceBitmap2[kind][piece]),
2651 NULL, &attr)) != 0) {
2652 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2656 if(piece <= (int) WhiteKing)
2657 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2661 xpmJailSquare = xpmLightSquare;
2665 fprintf(stderr, _("\nLoading XPMs...\n"));
2668 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2669 fprintf(stderr, "%d ", piece+1);
2670 for (kind=0; kind<4; kind++) {
2671 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2672 ExpandPathName(appData.pixmapDirectory),
2673 piece > (int) WhiteKing ? "w" : "",
2674 pieceBitmapNames[piece],
2676 if (appData.debugMode) {
2677 fprintf(stderr, _("(File:%s:) "), buf);
2679 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2680 &(xpmPieceBitmap2[kind][piece]),
2681 NULL, &attr)) != 0) {
2682 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2683 // [HGM] missing: read of unorthodox piece failed; substitute King.
2684 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2685 ExpandPathName(appData.pixmapDirectory),
2687 if (appData.debugMode) {
2688 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2690 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2691 &(xpmPieceBitmap2[kind][piece]),
2695 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2700 if(piece <= (int) WhiteKing)
2701 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2704 /* Load light and dark squares */
2705 /* If the LSQ and DSQ pieces don't exist, we will
2706 draw them with solid squares. */
2707 fprintf(stderr, _("light square "));
2708 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2709 if (access(buf, 0) != 0) {
2713 if (appData.debugMode)
2714 fprintf(stderr, _("(File:%s:) "), buf);
2716 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2717 &xpmLightSquare, NULL, &attr)) != 0) {
2718 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2721 fprintf(stderr, _("dark square "));
2722 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2723 ExpandPathName(appData.pixmapDirectory), ss);
2724 if (appData.debugMode) {
2725 fprintf(stderr, _("(File:%s:) "), buf);
2727 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2728 &xpmDarkSquare, NULL, &attr)) != 0) {
2729 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2733 xpmJailSquare = xpmLightSquare;
2734 fprintf(stderr, _("Done.\n"));
2736 oldVariant = -1; // kludge to force re-makig of animation masks
2737 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2740 #endif /* HAVE_LIBXPM */
2743 /* No built-in bitmaps */
2748 u_int ss = squareSize;
2750 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2753 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2754 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2755 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2756 pieceBitmapNames[piece],
2757 ss, kind == SOLID ? 's' : 'o');
2758 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2759 if(piece <= (int)WhiteKing)
2760 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2764 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2768 /* With built-in bitmaps */
2772 BuiltInBits* bib = builtInBits;
2775 u_int ss = squareSize;
2777 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2780 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2782 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2783 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2784 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2785 pieceBitmapNames[piece],
2786 ss, kind == SOLID ? 's' : 'o');
2787 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2788 bib->bits[kind][piece], ss, ss);
2789 if(piece <= (int)WhiteKing)
2790 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2794 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2800 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2805 char msg[MSG_SIZ], fullname[MSG_SIZ];
2807 if (*appData.bitmapDirectory != NULLCHAR) {
2808 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2809 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2810 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2811 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2812 &w, &h, pm, &x_hot, &y_hot);
2813 fprintf(stderr, "load %s\n", name);
2814 if (errcode != BitmapSuccess) {
2816 case BitmapOpenFailed:
2817 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2819 case BitmapFileInvalid:
2820 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2822 case BitmapNoMemory:
2823 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2827 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2831 fprintf(stderr, _("%s: %s...using built-in\n"),
2833 } else if (w != wreq || h != hreq) {
2835 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2836 programName, fullname, w, h, wreq, hreq);
2842 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2852 if (lineGap == 0) return;
2854 /* [HR] Split this into 2 loops for non-square boards. */
2856 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2857 gridSegments[i].x1 = 0;
2858 gridSegments[i].x2 =
2859 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2860 gridSegments[i].y1 = gridSegments[i].y2
2861 = lineGap / 2 + (i * (squareSize + lineGap));
2864 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2865 gridSegments[j + i].y1 = 0;
2866 gridSegments[j + i].y2 =
2867 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2868 gridSegments[j + i].x1 = gridSegments[j + i].x2
2869 = lineGap / 2 + (j * (squareSize + lineGap));
2873 int nrOfMenuItems = 7;
2874 Widget menuWidget[150];
2875 MenuListItem menuItemList[150] = {
2876 { "LoadNextGameProc", LoadNextGameProc },
2877 { "LoadPrevGameProc", LoadPrevGameProc },
2878 { "ReloadGameProc", ReloadGameProc },
2879 { "ReloadPositionProc", ReloadPositionProc },
2880 #ifndef OPTIONSDIALOG
2881 { "AlwaysQueenProc", AlwaysQueenProc },
2882 { "AnimateDraggingProc", AnimateDraggingProc },
2883 { "AnimateMovingProc", AnimateMovingProc },
2884 { "AutoflagProc", AutoflagProc },
2885 { "AutoflipProc", AutoflipProc },
2886 { "BlindfoldProc", BlindfoldProc },
2887 { "FlashMovesProc", FlashMovesProc },
2889 { "HighlightDraggingProc", HighlightDraggingProc },
2891 { "HighlightLastMoveProc", HighlightLastMoveProc },
2892 // { "IcsAlarmProc", IcsAlarmProc },
2893 { "MoveSoundProc", MoveSoundProc },
2894 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
2895 { "PopupExitMessageProc", PopupExitMessageProc },
2896 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
2897 // { "PremoveProc", PremoveProc },
2898 { "ShowCoordsProc", ShowCoordsProc },
2899 { "ShowThinkingProc", ShowThinkingProc },
2900 { "HideThinkingProc", HideThinkingProc },
2901 { "TestLegalityProc", TestLegalityProc },
2903 { "AboutGameProc", AboutGameEvent },
2904 { "DebugProc", DebugProc },
2905 { "NothingProc", NothingProc },
2910 MarkMenuItem (char *menuRef, int state)
2912 int nr = MenuToNumber(menuRef);
2915 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2916 XtSetValues(menuWidget[nr], args, 1);
2921 EnableMenuItem (char *menuRef, int state)
2923 int nr = MenuToNumber(menuRef);
2924 if(nr >= 0) XtSetSensitive(menuWidget[nr], state);
2928 EnableButtonBar (int state)
2930 XtSetSensitive(buttonBarWidget, state);
2935 SetMenuEnables (Enables *enab)
2937 while (enab->name != NULL) {
2938 EnableMenuItem(enab->name, enab->value);
2944 Equal(char *p, char *s)
2945 { // compare strings skipping spaces in second
2947 if(*s == ' ') { s++; continue; }
2948 if(*s++ != *p++) return 0;
2954 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2955 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2957 if(*nprms == 0) return;
2958 for(i=0; menuItemList[i].name; i++) {
2959 if(Equal(prms[0], menuItemList[i].name)) {
2960 (menuItemList[i].proc) ();
2967 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
2969 MenuProc *proc = (MenuProc *) addr;
2975 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2977 RecentEngineEvent((int) (intptr_t) addr);
2980 // some stuff that must remain in front-end
2981 static Widget mainBar, currentMenu;
2982 static int wtot, nr = 0, widths[10];
2985 AppendMenuItem (char *text, char *name, MenuProc *action)
2992 XtSetArg(args[j], XtNleftMargin, 20); j++;
2993 XtSetArg(args[j], XtNrightMargin, 20); j++;
2995 if (strcmp(text, "----") == 0) {
2996 entry = XtCreateManagedWidget(text, smeLineObjectClass,
2997 currentMenu, args, j);
2999 XtSetArg(args[j], XtNlabel, XtNewString(_(text)));
3000 entry = XtCreateManagedWidget(name, smeBSBObjectClass,
3001 currentMenu, args, j+1);
3002 XtAddCallback(entry, XtNcallback,
3003 (XtCallbackProc) (strcmp(name, "recent") ? MenuBarSelect : MenuEngineSelect),
3005 menuWidget[nrOfMenuItems] = entry;
3010 CreateMenuButton (char *name, Menu *mb)
3011 { // create menu button on main bar, and shell for pull-down list
3017 XtSetArg(args[j], XtNmenuName, XtNewString(name)); j++;
3018 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3019 XtSetArg(args[j], XtNborderWidth, 0); j++;
3020 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3022 currentMenu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3025 XtSetArg(args[j], XtNwidth, &w); j++;
3026 XtGetValues(mb->subMenu, args, j);
3027 wtot += mb->textWidth = widths[nr++] = w;
3031 CreateMenuBar (Menu *mb, int boardWidth)
3035 char menuName[MSG_SIZ];
3039 // create bar itself
3041 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3042 XtSetArg(args[j], XtNvSpace, 0); j++;
3043 XtSetArg(args[j], XtNborderWidth, 0); j++;
3044 mainBar = XtCreateWidget("menuBar", boxWidgetClass,
3045 formWidget, args, j);
3047 CreateMainMenus(mb); // put menus in bar according to description in back-end
3049 // size buttons to make menu bar fit, clipping menu names where necessary
3050 while(wtot > boardWidth - 40) {
3052 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
3056 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
3058 XtSetArg(args[j], XtNwidth, widths[i]); j++;
3059 XtSetValues(ma[i].subMenu, args, j);
3066 CreateButtonBar (MenuItem *mi)
3069 Widget button, buttonBar;
3073 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3075 XtSetArg(args[j], XtNhSpace, 0); j++;
3077 XtSetArg(args[j], XtNborderWidth, 0); j++;
3078 XtSetArg(args[j], XtNvSpace, 0); j++;
3079 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3080 formWidget, args, j);
3082 while (mi->string != NULL) {
3085 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3086 XtSetArg(args[j], XtNborderWidth, 0); j++;
3088 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3089 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3090 buttonBar, args, j);
3091 XtAddCallback(button, XtNcallback,
3092 (XtCallbackProc) MenuBarSelect,
3093 (caddr_t) mi->proc);
3100 CreatePieceMenu (char *name, int color)
3105 ChessSquare selection;
3107 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3108 boardWidget, args, 0);
3110 for (i = 0; i < PIECE_MENU_SIZE; i++) {
3111 String item = pieceMenuStrings[color][i];
3113 if (strcmp(item, "----") == 0) {
3114 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3117 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3118 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3120 selection = pieceMenuTranslation[color][i];
3121 XtAddCallback(entry, XtNcallback,
3122 (XtCallbackProc) PieceMenuSelect,
3123 (caddr_t) selection);
3124 if (selection == WhitePawn || selection == BlackPawn) {
3125 XtSetArg(args[0], XtNpopupOnEntry, entry);
3126 XtSetValues(menu, args, 1);
3139 ChessSquare selection;
3141 whitePieceMenu = CreatePieceMenu("menuW", 0);
3142 blackPieceMenu = CreatePieceMenu("menuB", 1);
3144 if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
3145 XtRegisterGrabAction(PieceMenuPopup, True,
3146 (unsigned)(ButtonPressMask|ButtonReleaseMask),
3147 GrabModeAsync, GrabModeAsync);
3149 XtSetArg(args[0], XtNlabel, _("Drop"));
3150 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3151 boardWidget, args, 1);
3152 for (i = 0; i < DROP_MENU_SIZE; i++) {
3153 String item = dropMenuStrings[i];
3155 if (strcmp(item, "----") == 0) {
3156 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3159 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3160 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3162 selection = dropMenuTranslation[i];
3163 XtAddCallback(entry, XtNcallback,
3164 (XtCallbackProc) DropMenuSelect,
3165 (caddr_t) selection);
3179 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3180 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3181 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3182 dmEnables[i].piece);
3183 XtSetSensitive(entry, p != NULL || !appData.testLegality
3184 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3185 && !appData.icsActive));
3187 while (p && *p++ == dmEnables[i].piece) count++;
3188 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3190 XtSetArg(args[j], XtNlabel, label); j++;
3191 XtSetValues(entry, args, j);
3196 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
3198 String whichMenu; int menuNr = -2;
3199 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3200 if (event->type == ButtonRelease)
3201 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3202 else if (event->type == ButtonPress)
3203 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3205 case 0: whichMenu = params[0]; break;
3206 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3208 case -1: if (errorUp) ErrorPopDown();
3211 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3215 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3217 if (pmFromX < 0 || pmFromY < 0) return;
3218 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3222 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3224 if (pmFromX < 0 || pmFromY < 0) return;
3225 DropMenuEvent(piece, pmFromX, pmFromY);
3229 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3231 shiftKey = prms[0][0] & 1;
3236 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3238 shiftKey = prms[0][0] & 1;
3244 do_flash_delay (unsigned long msec)
3250 DrawBorder (int x, int y, int type)
3254 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
3256 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3257 squareSize+lineGap, squareSize+lineGap);
3261 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
3263 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
3264 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
3266 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
3267 if(textureW[kind] < W*squareSize)
3268 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
3270 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
3271 if(textureH[kind] < H*squareSize)
3272 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
3274 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
3279 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
3280 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
3282 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
3283 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
3284 squareSize, squareSize, x*fac, y*fac);
3286 if (useImages && useImageSqs) {
3290 pm = xpmLightSquare;
3295 case 2: /* neutral */
3297 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
3300 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3301 squareSize, squareSize, x*fac, y*fac);
3311 case 2: /* neutral */
3316 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
3321 I split out the routines to draw a piece so that I could
3322 make a generic flash routine.
3325 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3327 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3328 switch (square_color) {
3330 case 2: /* neutral */
3332 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3333 ? *pieceToOutline(piece)
3334 : *pieceToSolid(piece),
3335 dest, bwPieceGC, 0, 0,
3336 squareSize, squareSize, x, y);
3339 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3340 ? *pieceToSolid(piece)
3341 : *pieceToOutline(piece),
3342 dest, wbPieceGC, 0, 0,
3343 squareSize, squareSize, x, y);
3349 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3351 switch (square_color) {
3353 case 2: /* neutral */
3355 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3356 ? *pieceToOutline(piece)
3357 : *pieceToSolid(piece),
3358 dest, bwPieceGC, 0, 0,
3359 squareSize, squareSize, x, y, 1);
3362 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3363 ? *pieceToSolid(piece)
3364 : *pieceToOutline(piece),
3365 dest, wbPieceGC, 0, 0,
3366 squareSize, squareSize, x, y, 1);
3372 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3374 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
3375 switch (square_color) {
3377 XCopyPlane(xDisplay, *pieceToSolid(piece),
3378 dest, (int) piece < (int) BlackPawn
3379 ? wlPieceGC : blPieceGC, 0, 0,
3380 squareSize, squareSize, x, y, 1);
3383 XCopyPlane(xDisplay, *pieceToSolid(piece),
3384 dest, (int) piece < (int) BlackPawn
3385 ? wdPieceGC : bdPieceGC, 0, 0,
3386 squareSize, squareSize, x, y, 1);
3388 case 2: /* neutral */
3390 break; // should never contain pieces
3395 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3397 int kind, p = piece;
3399 switch (square_color) {
3401 case 2: /* neutral */
3403 if ((int)piece < (int) BlackPawn) {
3411 if ((int)piece < (int) BlackPawn) {
3419 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
3420 if(useTexture & square_color+1) {
3421 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
3422 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
3423 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
3424 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
3425 XSetClipMask(xDisplay, wlPieceGC, None);
3426 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
3428 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3429 dest, wlPieceGC, 0, 0,
3430 squareSize, squareSize, x, y);
3433 typedef void (*DrawFunc)();
3438 if (appData.monoMode) {
3439 if (DefaultDepth(xDisplay, xScreen) == 1) {
3440 return monoDrawPiece_1bit;
3442 return monoDrawPiece;
3446 return colorDrawPieceImage;
3448 return colorDrawPiece;
3453 DrawDot (int marker, int x, int y, int r)
3455 if(appData.monoMode) {
3456 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
3457 x, y, r, r, 0, 64*360);
3458 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
3459 x, y, r, r, 0, 64*360);
3461 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
3462 x, y, r, r, 0, 64*360);
3466 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
3467 { // basic front-end board-draw function: takes care of everything that can be in square:
3468 // piece, background, coordinate/count, marker dot
3469 int direction, font_ascent, font_descent;
3470 XCharStruct overall;
3473 if (piece == EmptySquare) {
3474 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
3476 drawfunc = ChooseDrawFunc();
3477 drawfunc(piece, square_color, x, y, xBoardWindow);
3480 if(align) { // square carries inscription (coord or piece count)
3482 GC hGC = align < 3 ? coordGC : countGC;
3483 // first calculate where it goes
3484 XTextExtents(countFontStruct, string, 1, &direction,
3485 &font_ascent, &font_descent, &overall);
3487 xx += squareSize - overall.width - 2;
3488 yy += squareSize - font_descent - 1;
3489 } else if (align == 2) {
3490 xx += 2, yy += font_ascent + 1;
3491 } else if (align == 3) {
3492 xx += squareSize - overall.width - 2;
3493 yy += font_ascent + 1;
3494 } else if (align == 4) {
3495 xx += 2, yy += font_ascent + 1;
3498 if (appData.monoMode) {
3499 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3501 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3505 if(marker) { // print fat marker dot, if requested
3506 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
3511 FlashDelay (int flash_delay)
3513 XSync(xDisplay, False);
3514 if(flash_delay) do_flash_delay(flash_delay);
3518 Fraction (int x, int start, int stop)
3520 double f = ((double) x - start)/(stop - start);
3521 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
3525 static WindowPlacement wpNew;
3528 CoDrag (Widget sh, WindowPlacement *wp)
3531 int j=0, touch=0, fudge = 2;
3532 GetActualPlacement(sh, wp);
3533 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
3534 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
3535 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
3536 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
3537 if(!touch ) return; // only windows that touch co-move
3538 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
3539 int heightInc = wpNew.height - wpMain.height;
3540 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3541 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3542 wp->y += fracTop * heightInc;
3543 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
3544 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
3545 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
3546 int widthInc = wpNew.width - wpMain.width;
3547 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3548 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3549 wp->y += fracLeft * widthInc;
3550 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
3551 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
3553 wp->x += wpNew.x - wpMain.x;
3554 wp->y += wpNew.y - wpMain.y;
3555 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
3556 if(touch == 3) wp->y += wpNew.height - wpMain.height;
3557 XtSetArg(args[j], XtNx, wp->x); j++;
3558 XtSetArg(args[j], XtNy, wp->y); j++;
3559 XtSetValues(sh, args, j);
3562 static XtIntervalId delayedDragID = 0;
3567 GetActualPlacement(shellWidget, &wpNew);
3568 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
3569 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
3570 return; // false alarm
3571 if(EngineOutputIsUp()) CoDrag(engineOutputShell, &wpEngineOutput);
3572 if(MoveHistoryIsUp()) CoDrag(shells[HistoryDlg], &wpMoveHistory);
3573 if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
3574 if(GameListIsUp()) CoDrag(gameListShell, &wpGameList);
3576 DrawPosition(True, NULL);
3577 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
3584 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
3586 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
3589 /* Why is this needed on some versions of X? */
3591 EventProc (Widget widget, caddr_t unused, XEvent *event)
3593 if (!XtIsRealized(widget))
3595 switch (event->type) {
3596 case ConfigureNotify: // main window is being dragged: drag attached windows with it
3597 if(appData.useStickyWindows)
3598 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
3601 if (event->xexpose.count > 0) return; /* no clipping is done */
3602 DrawPosition(True, NULL);
3603 if(twoBoards) { // [HGM] dual: draw other board in other orientation
3604 flipView = !flipView; partnerUp = !partnerUp;
3605 DrawPosition(True, NULL);
3606 flipView = !flipView; partnerUp = !partnerUp;
3610 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
3617 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
3619 DrawSeekAxis (int x, int y, int xTo, int yTo)
3621 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
3625 DrawSeekBackground (int left, int top, int right, int bottom)
3627 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
3631 DrawSeekText (char *buf, int x, int y)
3633 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
3637 DrawSeekDot (int x, int y, int colorNr)
3639 int square = colorNr & 0x80;
3642 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
3644 XFillRectangle(xDisplay, xBoardWindow, color,
3645 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3647 XFillArc(xDisplay, xBoardWindow, color,
3648 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
3652 DrawGrid (int second)
3654 XDrawSegments(xDisplay, xBoardWindow, lineGC,
3655 second ? secondSegments : // [HGM] dual
3656 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
3661 * event handler for redrawing the board
3664 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3666 DrawPosition(True, NULL);
3671 * event handler for parsing user moves
3673 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
3674 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
3675 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
3676 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
3677 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
3678 // and at the end FinishMove() to perform the move after optional promotion popups.
3679 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
3681 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3683 if (w != boardWidget || errorExitStatus != -1) return;
3684 if(nprms) shiftKey = !strcmp(prms[0], "1");
3687 if (event->type == ButtonPress) {
3688 XtPopdown(promotionShell);
3689 XtDestroyWidget(promotionShell);
3690 promotionUp = False;
3698 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
3699 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
3700 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
3704 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
3706 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
3707 DragPieceMove(event->xmotion.x, event->xmotion.y);
3711 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3712 { // [HGM] pv: walk PV
3713 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3716 static int savedIndex; /* gross that this is global */
3719 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3722 XawTextPosition index, dummy;
3725 XawTextGetSelectionPos(w, &index, &dummy);
3726 XtSetArg(arg, XtNstring, &val);
3727 XtGetValues(w, &arg, 1);
3728 ReplaceComment(savedIndex, val);
3729 if(savedIndex != currentMove) ToNrEvent(savedIndex);
3730 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3734 EditCommentPopUp (int index, char *title, char *text)
3737 if (text == NULL) text = "";
3738 NewCommentPopup(title, text, index);
3742 CommentPopUp (char *title, char *text)
3744 savedIndex = currentMove; // [HGM] vari
3745 NewCommentPopup(title, text, currentMove);
3751 PopDown(CommentDlg);
3754 static char *openName;
3760 (void) (*fileProc)(openFP, 0, openName);
3764 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
3766 fileProc = proc; /* I can't see a way not */
3767 fileOpenMode = openMode; /* to use globals here */
3768 { // [HGM] use file-selector dialog stolen from Ghostview
3769 int index; // this is not supported yet
3770 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
3771 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
3772 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
3773 ScheduleDelayedEvent(&DelayedLoad, 50);
3781 Widget dialog, layout;
3783 Dimension bw_width, pw_width;
3785 char *PromoChars = "wglcqrbnkac+=\0";
3788 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3789 XtGetValues(boardWidget, args, j);
3792 XtSetArg(args[j], XtNresizable, True); j++;
3793 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
3795 XtCreatePopupShell("Promotion", transientShellWidgetClass,
3796 shellWidget, args, j);
3798 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
3799 layoutArgs, XtNumber(layoutArgs));
3802 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
3803 XtSetArg(args[j], XtNborderWidth, 0); j++;
3804 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
3807 if(gameInfo.variant != VariantShogi) {
3808 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
3809 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
3810 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
3811 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
3812 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
3814 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
3815 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
3816 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
3817 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
3819 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
3820 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
3821 gameInfo.variant == VariantGiveaway) {
3822 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
3824 if(gameInfo.variant == VariantCapablanca ||
3825 gameInfo.variant == VariantGothic ||
3826 gameInfo.variant == VariantCapaRandom) {
3827 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
3828 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
3830 } else // [HGM] shogi
3832 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
3833 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
3835 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
3837 XtRealizeWidget(promotionShell);
3838 CatchDeleteWindow(promotionShell, "PromotionPopDown");
3841 XtSetArg(args[j], XtNwidth, &pw_width); j++;
3842 XtGetValues(promotionShell, args, j);
3844 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3845 lineGap + squareSize/3 +
3846 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
3847 0 : 6*(squareSize + lineGap)), &x, &y);
3850 XtSetArg(args[j], XtNx, x); j++;
3851 XtSetArg(args[j], XtNy, y); j++;
3852 XtSetValues(promotionShell, args, j);
3854 XtPopup(promotionShell, XtGrabNone);
3862 if (!promotionUp) return;
3863 XtPopdown(promotionShell);
3864 XtDestroyWidget(promotionShell);
3865 promotionUp = False;
3869 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
3871 int promoChar = * (const char *) client_data;
3875 if (fromX == -1) return;
3882 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
3884 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
3885 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3891 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
3893 dialogError = errorUp = False;
3894 XtPopdown(w = XtParent(XtParent(XtParent(w))));
3896 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3903 if (!errorUp) return;
3904 dialogError = errorUp = False;
3905 XtPopdown(errorShell);
3906 XtDestroyWidget(errorShell);
3907 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3911 ErrorPopUp (char *title, char *label, int modal)
3914 Widget dialog, layout;
3918 Dimension bw_width, pw_width;
3919 Dimension pw_height;
3923 XtSetArg(args[i], XtNresizable, True); i++;
3924 XtSetArg(args[i], XtNtitle, title); i++;
3926 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
3927 shellUp[TransientDlg] ? (dialogError = modal = TRUE, shells[TransientDlg]) : shellWidget, args, i);
3929 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
3930 layoutArgs, XtNumber(layoutArgs));
3933 XtSetArg(args[i], XtNlabel, label); i++;
3934 XtSetArg(args[i], XtNborderWidth, 0); i++;
3935 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
3938 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
3940 XtRealizeWidget(errorShell);
3941 CatchDeleteWindow(errorShell, "ErrorPopDown");
3944 XtSetArg(args[i], XtNwidth, &bw_width); i++;
3945 XtGetValues(boardWidget, args, i);
3947 XtSetArg(args[i], XtNwidth, &pw_width); i++;
3948 XtSetArg(args[i], XtNheight, &pw_height); i++;
3949 XtGetValues(errorShell, args, i);
3952 /* This code seems to tickle an X bug if it is executed too soon
3953 after xboard starts up. The coordinates get transformed as if
3954 the main window was positioned at (0, 0).
3956 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3957 0 - pw_height + squareSize / 3, &x, &y);
3959 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
3960 RootWindowOfScreen(XtScreen(boardWidget)),
3961 (bw_width - pw_width) / 2,
3962 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
3966 if (y < 0) y = 0; /*avoid positioning top offscreen*/
3969 XtSetArg(args[i], XtNx, x); i++;
3970 XtSetArg(args[i], XtNy, y); i++;
3971 XtSetValues(errorShell, args, i);
3974 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
3977 /* Disable all user input other than deleting the window */
3978 static int frozen = 0;
3984 /* Grab by a widget that doesn't accept input */
3985 XtAddGrab(messageWidget, TRUE, FALSE);
3989 /* Undo a FreezeUI */
3993 if (!frozen) return;
3994 XtRemoveGrab(messageWidget);
4002 static int oldPausing = FALSE;
4003 static GameMode oldmode = (GameMode) -1;
4006 if (!boardWidget || !XtIsRealized(boardWidget)) return;
4008 if (pausing != oldPausing) {
4009 oldPausing = pausing;
4010 MarkMenuItem("Pause", pausing);
4012 if (appData.showButtonBar) {
4013 /* Always toggle, don't set. Previous code messes up when
4014 invoked while the button is pressed, as releasing it
4015 toggles the state again. */
4018 XtSetArg(args[0], XtNbackground, &oldbg);
4019 XtSetArg(args[1], XtNforeground, &oldfg);
4020 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
4022 XtSetArg(args[0], XtNbackground, oldfg);
4023 XtSetArg(args[1], XtNforeground, oldbg);
4025 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
4029 wname = ModeToWidgetName(oldmode);
4030 if (wname != NULL) {
4031 MarkMenuItem(wname, False);
4033 wname = ModeToWidgetName(gameMode);
4034 if (wname != NULL) {
4035 MarkMenuItem(wname, True);
4038 MarkMenuItem("Machine Match", matchMode && matchGame < appData.matchGames);
4040 /* Maybe all the enables should be handled here, not just this one */
4041 EnableMenuItem("Training", gameMode == Training || gameMode == PlayFromGameFile);
4046 * Button/menu procedures
4049 LoadGamePopUp (FILE *f, int gameNumber, char *title)
4051 cmailMsgLoaded = FALSE;
4052 if (gameNumber == 0) {
4053 int error = GameListBuild(f);
4055 DisplayError(_("Cannot build game list"), error);
4056 } else if (!ListEmpty(&gameList) &&
4057 ((ListGame *) gameList.tailPred)->number > 1) {
4058 GameListPopUp(f, title);
4064 return LoadGame(f, gameNumber, title, FALSE);
4067 /* this variable is shared between CopyPositionProc and SendPositionSelection */
4068 char *selected_fen_position=NULL;
4071 SendPositionSelection (Widget w, Atom *selection, Atom *target,
4072 Atom *type_return, XtPointer *value_return,
4073 unsigned long *length_return, int *format_return)
4075 char *selection_tmp;
4077 // if (!selected_fen_position) return False; /* should never happen */
4078 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4079 if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
4080 FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
4083 if (f == NULL) return False;
4087 selection_tmp = XtMalloc(len + 1);
4088 count = fread(selection_tmp, 1, len, f);
4091 XtFree(selection_tmp);
4094 selection_tmp[len] = NULLCHAR;
4096 /* note: since no XtSelectionDoneProc was registered, Xt will
4097 * automatically call XtFree on the value returned. So have to
4098 * make a copy of it allocated with XtMalloc */
4099 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
4100 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
4103 *value_return=selection_tmp;
4104 *length_return=strlen(selection_tmp);
4105 *type_return=*target;
4106 *format_return = 8; /* bits per byte */
4108 } else if (*target == XA_TARGETS(xDisplay)) {
4109 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4110 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4111 targets_tmp[1] = XA_STRING;
4112 *value_return = targets_tmp;
4113 *type_return = XA_ATOM;
4116 // This code leads to a read of value_return out of bounds on 64-bit systems.
4117 // Other code which I have seen always sets *format_return to 32 independent of
4118 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
4119 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
4120 *format_return = 8 * sizeof(Atom);
4121 if (*format_return > 32) {
4122 *length_return *= *format_return / 32;
4123 *format_return = 32;
4126 *format_return = 32;
4134 /* note: when called from menu all parameters are NULL, so no clue what the
4135 * Widget which was clicked on was, or what the click event was
4138 CopySomething (char *src)
4140 selected_fen_position = src;
4142 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4143 * have a notion of a position that is selected but not copied.
4144 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4146 XtOwnSelection(menuBarWidget, XA_PRIMARY,
4148 SendPositionSelection,
4149 NULL/* lose_ownership_proc */ ,
4150 NULL/* transfer_done_proc */);
4151 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4153 SendPositionSelection,
4154 NULL/* lose_ownership_proc */ ,
4155 NULL/* transfer_done_proc */);
4158 /* function called when the data to Paste is ready */
4160 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
4161 Atom *type, XtPointer value, unsigned long *len, int *format)
4164 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
4165 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
4166 EditPositionPasteFEN(fenstr);
4170 /* called when Paste Position button is pressed,
4171 * all parameters will be NULL */
4173 PastePositionProc ()
4175 XtGetSelectionValue(menuBarWidget,
4176 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4177 /* (XtSelectionCallbackProc) */ PastePositionCB,
4178 NULL, /* client_data passed to PastePositionCB */
4180 /* better to use the time field from the event that triggered the
4181 * call to this function, but that isn't trivial to get
4188 /* note: when called from menu all parameters are NULL, so no clue what the
4189 * Widget which was clicked on was, or what the click event was
4191 /* function called when the data to Paste is ready */
4193 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
4194 Atom *type, XtPointer value, unsigned long *len, int *format)
4197 if (value == NULL || *len == 0) {
4198 return; /* nothing had been selected to copy */
4200 f = fopen(gamePasteFilename, "w");
4202 DisplayError(_("Can't open temp file"), errno);
4205 fwrite(value, 1, *len, f);
4208 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
4211 /* called when Paste Game button is pressed,
4212 * all parameters will be NULL */
4216 XtGetSelectionValue(menuBarWidget,
4217 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4218 /* (XtSelectionCallbackProc) */ PasteGameCB,
4219 NULL, /* client_data passed to PasteGameCB */
4221 /* better to use the time field from the event that triggered the
4222 * call to this function, but that isn't trivial to get
4231 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4237 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
4239 char buf[10], keys[32];
4241 KeyCode metaL, metaR; //, ctrlL, ctrlR;
4242 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
4243 XQueryKeymap(xDisplay,keys);
4244 metaL = XKeysymToKeycode(xDisplay, XK_Meta_L);
4245 metaR = XKeysymToKeycode(xDisplay, XK_Meta_R);
4246 // ctrlL = XKeysymToKeycode(xDisplay, XK_Control_L);
4247 // ctrlR = XKeysymToKeycode(xDisplay, XK_Control_R);
4248 if ( n == 1 && *buf >= 32 // printable
4249 && !(keys[metaL>>3]&1<<(metaL&7)) && !(keys[metaR>>3]&1<<(metaR&7)) // no alt key pressed
4250 // && !(keys[ctrlL>>3]&1<<(ctrlL&7)) && !(keys[ctrlR>>3]&1<<(ctrlR&7)) // no ctrl key pressed
4251 ) BoxAutoPopUp (buf);
4255 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4256 { // [HGM] input: let up-arrow recall previous line from history
4261 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4262 { // [HGM] input: let down-arrow recall next line from history
4267 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4273 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4275 if (!TempBackwardActive) {
4276 TempBackwardActive = True;
4282 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4284 /* Check to see if triggered by a key release event for a repeating key.
4285 * If so the next queued event will be a key press of the same key at the same time */
4286 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
4288 XPeekEvent(xDisplay, &next);
4289 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
4290 next.xkey.keycode == event->xkey.keycode)
4294 TempBackwardActive = False;
4298 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4299 { // called as key binding
4302 if (nprms && *nprms > 0)
4306 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
4311 DisplayMessage (char *message, char *extMessage)
4313 /* display a message in the message widget */
4322 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
4327 message = extMessage;
4331 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
4333 /* need to test if messageWidget already exists, since this function
4334 can also be called during the startup, if for example a Xresource
4335 is not set up correctly */
4338 XtSetArg(arg, XtNlabel, message);
4339 XtSetValues(messageWidget, &arg, 1);
4346 SetWindowTitle (char *text, char *title, char *icon)
4350 if (appData.titleInWindow) {
4352 XtSetArg(args[i], XtNlabel, text); i++;
4353 XtSetValues(titleWidget, args, i);