2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
66 # if HAVE_SYS_SOCKET_H
67 # include <sys/socket.h>
68 # include <netinet/in.h>
70 # else /* not HAVE_SYS_SOCKET_H */
71 # if HAVE_LAN_SOCKET_H
72 # include <lan/socket.h>
74 # include <lan/netdb.h>
75 # else /* not HAVE_LAN_SOCKET_H */
76 # define OMIT_SOCKETS 1
77 # endif /* not HAVE_LAN_SOCKET_H */
78 # endif /* not HAVE_SYS_SOCKET_H */
79 #endif /* !OMIT_SOCKETS */
84 #else /* not STDC_HEADERS */
85 extern char *getenv();
88 # else /* not HAVE_STRING_H */
90 # endif /* not HAVE_STRING_H */
91 #endif /* not STDC_HEADERS */
94 # include <sys/fcntl.h>
95 #else /* not HAVE_SYS_FCNTL_H */
98 # endif /* HAVE_FCNTL_H */
99 #endif /* not HAVE_SYS_FCNTL_H */
101 #if HAVE_SYS_SYSTEMINFO_H
102 # include <sys/systeminfo.h>
103 #endif /* HAVE_SYS_SYSTEMINFO_H */
105 #if TIME_WITH_SYS_TIME
106 # include <sys/time.h>
110 # include <sys/time.h>
121 # include <sys/wait.h>
126 # define NAMLEN(dirent) strlen((dirent)->d_name)
127 # define HAVE_DIR_STRUCT
129 # define dirent direct
130 # define NAMLEN(dirent) (dirent)->d_namlen
132 # include <sys/ndir.h>
133 # define HAVE_DIR_STRUCT
136 # include <sys/dir.h>
137 # define HAVE_DIR_STRUCT
141 # define HAVE_DIR_STRUCT
149 #include <X11/Intrinsic.h>
150 #include <X11/StringDefs.h>
151 #include <X11/Shell.h>
152 #include <X11/cursorfont.h>
153 #include <X11/Xatom.h>
154 #include <X11/Xmu/Atoms.h>
156 #include <X11/Xaw3d/Dialog.h>
157 #include <X11/Xaw3d/Form.h>
158 #include <X11/Xaw3d/List.h>
159 #include <X11/Xaw3d/Label.h>
160 #include <X11/Xaw3d/SimpleMenu.h>
161 #include <X11/Xaw3d/SmeBSB.h>
162 #include <X11/Xaw3d/SmeLine.h>
163 #include <X11/Xaw3d/Box.h>
164 #include <X11/Xaw3d/MenuButton.h>
165 #include <X11/Xaw3d/Text.h>
166 #include <X11/Xaw3d/AsciiText.h>
168 #include <X11/Xaw/Dialog.h>
169 #include <X11/Xaw/Form.h>
170 #include <X11/Xaw/List.h>
171 #include <X11/Xaw/Label.h>
172 #include <X11/Xaw/SimpleMenu.h>
173 #include <X11/Xaw/SmeBSB.h>
174 #include <X11/Xaw/SmeLine.h>
175 #include <X11/Xaw/Box.h>
176 #include <X11/Xaw/MenuButton.h>
177 #include <X11/Xaw/Text.h>
178 #include <X11/Xaw/AsciiText.h>
181 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
186 #include "pixmaps/pixmaps.h"
187 #define IMAGE_EXT "xpm"
189 #define IMAGE_EXT "xim"
190 #include "bitmaps/bitmaps.h"
193 #include "bitmaps/icon_white.bm"
194 #include "bitmaps/icon_black.bm"
195 #include "bitmaps/checkmark.bm"
197 #include "frontend.h"
199 #include "backendz.h"
203 #include "xgamelist.h"
204 #include "xhistory.h"
205 #include "xedittags.h"
217 #define usleep(t) _sleep2(((t)+500)/1000)
221 # define _(s) gettext (s)
222 # define N_(s) gettext_noop (s)
228 int main P((int argc, char **argv));
229 RETSIGTYPE CmailSigHandler P((int sig));
230 RETSIGTYPE IntSigHandler P((int sig));
231 RETSIGTYPE TermSizeSigHandler P((int sig));
232 static void CreateGCs P((int redo));
233 static void CreateAnyPieces P((void));
234 void CreateXIMPieces P((void));
235 void CreateXPMPieces P((void));
236 void CreateXPMBoard P((char *s, int n));
237 void CreatePieces P((void));
238 void CreatePieceMenus P((void));
239 Widget CreateMenuBar P((Menu *mb, int boardWidth));
240 Widget CreateButtonBar P ((MenuItem *mi));
242 char *InsertPxlSize P((char *pattern, int targetPxlSize));
243 XFontSet CreateFontSet P((char *base_fnt_lst));
245 char *FindFont P((char *pattern, int targetPxlSize));
247 void PieceMenuPopup P((Widget w, XEvent *event,
248 String *params, Cardinal *num_params));
249 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
250 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
251 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
252 u_int wreq, u_int hreq));
253 void CreateGrid P((void));
254 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
255 void DelayedDrag P((void));
256 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
257 void HandleUserMove P((Widget w, XEvent *event,
258 String *prms, Cardinal *nprms));
259 void AnimateUserMove P((Widget w, XEvent * event,
260 String * params, Cardinal * nParams));
261 void HandlePV P((Widget w, XEvent * event,
262 String * params, Cardinal * nParams));
263 void SelectPV P((Widget w, XEvent * event,
264 String * params, Cardinal * nParams));
265 void StopPV P((Widget w, XEvent * event,
266 String * params, Cardinal * nParams));
267 void WhiteClock P((Widget w, XEvent *event,
268 String *prms, Cardinal *nprms));
269 void BlackClock P((Widget w, XEvent *event,
270 String *prms, Cardinal *nprms));
271 void DrawPositionProc P((Widget w, XEvent *event,
272 String *prms, Cardinal *nprms));
273 void CommentClick P((Widget w, XEvent * event,
274 String * params, Cardinal * nParams));
275 void ICSInputBoxPopUp P((void));
276 void FileNamePopUp P((char *label, char *def, char *filter,
277 FileProc proc, char *openMode));
278 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
279 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
280 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
281 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
282 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
283 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
284 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
285 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
286 Boolean TempBackwardActive = False;
287 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
288 void DisplayMove P((int moveNumber));
289 void ICSInitScript P((void));
290 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
291 void update_ics_width P(());
292 int get_term_width P(());
293 int CopyMemoProc P(());
296 * XBoard depends on Xt R4 or higher
298 int xtVersion = XtSpecificationRelease;
303 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
304 highlightSquareColor, premoveHighlightColor;
305 Pixel lowTimeWarningColor;
306 GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
307 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
309 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
310 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
311 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
312 commentShell, whitePieceMenu, blackPieceMenu, dropMenu,
313 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
314 ICSInputShell, fileNameShell;
315 Widget historyShell, evalGraphShell, gameListShell;
316 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
317 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
319 XFontSet fontSet, clockFontSet;
322 XFontStruct *clockFontStruct;
324 Font coordFontID, countFontID;
325 XFontStruct *coordFontStruct, *countFontStruct;
326 XtAppContext appContext;
331 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
333 Position commentX = -1, commentY = -1;
334 Dimension commentW, commentH;
335 typedef unsigned int BoardSize;
337 Boolean chessProgram;
339 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
340 int smallLayout = 0, tinyLayout = 0,
341 marginW, marginH, // [HGM] for run-time resizing
342 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
343 ICSInputBoxUp = False,
344 filenameUp = False, pmFromX = -1, pmFromY = -1,
345 errorUp = False, errorExitStatus = -1, defaultLineGap;
346 Dimension textHeight;
347 Pixel timerForegroundPixel, timerBackgroundPixel;
348 Pixel buttonForegroundPixel, buttonBackgroundPixel;
349 char *chessDir, *programName, *programVersion;
350 Boolean alwaysOnTop = False;
351 char *icsTextMenuString;
353 char *firstChessProgramNames;
354 char *secondChessProgramNames;
356 WindowPlacement wpMain;
357 WindowPlacement wpConsole;
358 WindowPlacement wpComment;
359 WindowPlacement wpMoveHistory;
360 WindowPlacement wpEvalGraph;
361 WindowPlacement wpEngineOutput;
362 WindowPlacement wpGameList;
363 WindowPlacement wpTags;
368 Pixmap pieceBitmap[2][(int)BlackPawn];
369 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
370 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
371 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
372 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
373 Pixmap xpmBoardBitmap[2];
374 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
375 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
376 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
377 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
378 XImage *ximLightSquare, *ximDarkSquare;
381 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
382 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
384 #define White(piece) ((int)(piece) < (int)BlackPawn)
386 /* Bitmaps for use as masks when drawing XPM pieces.
387 Need one for each black and white piece. */
388 static Pixmap xpmMask[BlackKing + 1];
390 /* This magic number is the number of intermediate frames used
391 in each half of the animation. For short moves it's reduced
392 by 1. The total number of frames will be factor * 2 + 1. */
395 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
397 #define PAUSE_BUTTON "P"
398 MenuItem buttonBar[] = {
399 {"<<", "<<", ToStartEvent},
400 {"<", "<", BackwardEvent},
401 {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseEvent},
402 {">", ">", ForwardEvent},
403 {">>", ">>", ToEndEvent},
407 #define PIECE_MENU_SIZE 18
408 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
409 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
410 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
411 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
412 N_("Empty square"), N_("Clear board") },
413 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
414 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
415 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
416 N_("Empty square"), N_("Clear board") }
418 /* must be in same order as pieceMenuStrings! */
419 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
420 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
421 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
422 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
423 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
424 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
425 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
426 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
427 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
430 #define DROP_MENU_SIZE 6
431 String dropMenuStrings[DROP_MENU_SIZE] = {
432 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
434 /* must be in same order as dropMenuStrings! */
435 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
436 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
437 WhiteRook, WhiteQueen
445 DropMenuEnables dmEnables[] = {
463 { XtNborderWidth, 0 },
464 { XtNdefaultDistance, 0 },
468 { XtNborderWidth, 0 },
469 { XtNresizable, (XtArgVal) True },
473 { XtNborderWidth, 0 },
479 { XtNjustify, (XtArgVal) XtJustifyRight },
480 { XtNlabel, (XtArgVal) "..." },
481 { XtNresizable, (XtArgVal) True },
482 { XtNresize, (XtArgVal) False }
485 Arg messageArgs[] = {
486 { XtNjustify, (XtArgVal) XtJustifyLeft },
487 { XtNlabel, (XtArgVal) "..." },
488 { XtNresizable, (XtArgVal) True },
489 { XtNresize, (XtArgVal) False }
493 { XtNborderWidth, 0 },
494 { XtNjustify, (XtArgVal) XtJustifyLeft }
497 XtResource clientResources[] = {
498 { "flashCount", "flashCount", XtRInt, sizeof(int),
499 XtOffset(AppDataPtr, flashCount), XtRImmediate,
500 (XtPointer) FLASH_COUNT },
503 XrmOptionDescRec shellOptions[] = {
504 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
505 { "-flash", "flashCount", XrmoptionNoArg, "3" },
506 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
509 XtActionsRec boardActions[] = {
510 { "DrawPosition", DrawPositionProc },
511 { "HandleUserMove", HandleUserMove },
512 { "AnimateUserMove", AnimateUserMove },
513 { "HandlePV", HandlePV },
514 { "SelectPV", SelectPV },
515 { "StopPV", StopPV },
516 { "PieceMenuPopup", PieceMenuPopup },
517 { "WhiteClock", WhiteClock },
518 { "BlackClock", BlackClock },
519 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
520 { "QuitProc", QuitWrapper },
521 { "ManProc", ManInner },
522 { "TempBackwardProc", TempBackwardProc },
523 { "TempForwardProc", TempForwardProc },
524 { "CommentClick", (XtActionProc) CommentClick },
525 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
526 { "GameListPopDown", (XtActionProc) GameListPopDown },
527 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
528 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
529 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
530 { "GenericPopDown", (XtActionProc) GenericPopDown },
531 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
532 { "SelectMove", (XtActionProc) SelectMove },
533 { "LoadSelectedProc", LoadSelectedProc },
534 { "SetFilterProc", SetFilterProc },
535 { "TypeInProc", TypeInProc },
536 { "EnterKeyProc", EnterKeyProc },
537 { "UpKeyProc", UpKeyProc },
538 { "DownKeyProc", DownKeyProc },
539 { "WheelProc", WheelProc },
540 { "TabProc", TabProc },
543 char globalTranslations[] =
544 ":<Key>F9: MenuItem(ResignProc) \n \
545 :Ctrl<Key>n: MenuItem(NewGame) \n \
546 :Meta<Key>V: MenuItem(NewVariant) \n \
547 :Ctrl<Key>o: MenuItem(LoadGame) \n \
548 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
549 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
550 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
551 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
552 :Ctrl<Key>s: MenuItem(SaveGame) \n \
553 :Ctrl<Key>c: MenuItem(CopyGame) \n \
554 :Ctrl<Key>v: MenuItem(PasteGame) \n \
555 :Ctrl<Key>O: MenuItem(LoadPosition) \n \
556 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
557 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
558 :Ctrl<Key>S: MenuItem(SavePosition) \n \
559 :Ctrl<Key>C: MenuItem(CopyPosition) \n \
560 :Ctrl<Key>V: MenuItem(PastePosition) \n \
561 :Ctrl<Key>q: MenuItem(Exit) \n \
562 :Ctrl<Key>w: MenuItem(MachineWhite) \n \
563 :Ctrl<Key>b: MenuItem(MachineBlack) \n \
564 :Ctrl<Key>t: MenuItem(TwoMachines) \n \
565 :Ctrl<Key>a: MenuItem(AnalysisMode) \n \
566 :Ctrl<Key>g: MenuItem(AnalyzeFile) \n \
567 :Ctrl<Key>e: MenuItem(EditGame) \n \
568 :Ctrl<Key>E: MenuItem(EditPosition) \n \
569 :Meta<Key>O: MenuItem(ShowEngineOutput) \n \
570 :Meta<Key>E: MenuItem(ShowEvaluationGraph) \n \
571 :Meta<Key>G: MenuItem(ShowGameList) \n \
572 :Meta<Key>H: MenuItem(ShowMoveHistory) \n \
573 :<Key>Pause: MenuItem(Pause) \n \
574 :<Key>F3: MenuItem(Accept) \n \
575 :<Key>F4: MenuItem(Decline) \n \
576 :<Key>F12: MenuItem(Rematch) \n \
577 :<Key>F5: MenuItem(CallFlag) \n \
578 :<Key>F6: MenuItem(Draw) \n \
579 :<Key>F7: MenuItem(Adjourn) \n \
580 :<Key>F8: MenuItem(Abort) \n \
581 :<Key>F10: MenuItem(StopObserving) \n \
582 :<Key>F11: MenuItem(StopExamining) \n \
583 :Ctrl<Key>d: MenuItem(DebugProc) \n \
584 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
585 :Meta<Key>End: MenuItem(ToEnd) \n \
586 :Meta<Key>Right: MenuItem(Forward) \n \
587 :Meta<Key>Home: MenuItem(ToStart) \n \
588 :Meta<Key>Left: MenuItem(Backward) \n \
589 :<Key>Left: MenuItem(Backward) \n \
590 :<Key>Right: MenuItem(Forward) \n \
591 :<Key>Home: MenuItem(Revert) \n \
592 :<Key>End: MenuItem(TruncateGame) \n \
593 :Ctrl<Key>m: MenuItem(MoveNow) \n \
594 :Ctrl<Key>x: MenuItem(RetractMove) \n \
595 :Meta<Key>J: MenuItem(Adjudications) \n \
596 :Meta<Key>U: MenuItem(CommonEngine) \n \
597 :Meta<Key>T: MenuItem(TimeControl) \n \
598 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
599 #ifndef OPTIONSDIALOG
601 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
602 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
603 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
604 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
605 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
608 :<Key>F1: MenuItem(Manual) \n \
609 :<Key>F2: MenuItem(FlipView) \n \
610 :<KeyDown>Return: TempBackwardProc() \n \
611 :<KeyUp>Return: TempForwardProc() \n";
613 char boardTranslations[] =
614 "<Btn1Down>: HandleUserMove(0) \n \
615 Shift<Btn1Up>: HandleUserMove(1) \n \
616 <Btn1Up>: HandleUserMove(0) \n \
617 <Btn1Motion>: AnimateUserMove() \n \
618 <Btn3Motion>: HandlePV() \n \
619 <Btn2Motion>: HandlePV() \n \
620 <Btn3Up>: PieceMenuPopup(menuB) \n \
621 <Btn2Up>: PieceMenuPopup(menuB) \n \
622 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
623 PieceMenuPopup(menuB) \n \
624 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
625 PieceMenuPopup(menuW) \n \
626 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
627 PieceMenuPopup(menuW) \n \
628 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
629 PieceMenuPopup(menuB) \n";
631 char whiteTranslations[] =
632 "Shift<BtnDown>: WhiteClock(1)\n \
633 <BtnDown>: WhiteClock(0)\n";
634 char blackTranslations[] =
635 "Shift<BtnDown>: BlackClock(1)\n \
636 <BtnDown>: BlackClock(0)\n";
638 char ICSInputTranslations[] =
639 "<Key>Up: UpKeyProc() \n "
640 "<Key>Down: DownKeyProc() \n "
641 "<Key>Return: EnterKeyProc() \n";
643 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
644 // as the widget is destroyed before the up-click can call extend-end
645 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
647 String xboardResources[] = {
648 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
653 /* Max possible square size */
654 #define MAXSQSIZE 256
656 static int xpm_avail[MAXSQSIZE];
658 #ifdef HAVE_DIR_STRUCT
660 /* Extract piece size from filename */
662 xpm_getsize (char *name, int len, char *ext)
670 if ((p=strchr(name, '.')) == NULL ||
671 StrCaseCmp(p+1, ext) != 0)
677 while (*p && isdigit(*p))
684 /* Setup xpm_avail */
686 xpm_getavail (char *dirname, char *ext)
692 for (i=0; i<MAXSQSIZE; ++i)
695 if (appData.debugMode)
696 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
698 dir = opendir(dirname);
701 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
702 programName, dirname);
706 while ((ent=readdir(dir)) != NULL) {
707 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
708 if (i > 0 && i < MAXSQSIZE)
718 xpm_print_avail (FILE *fp, char *ext)
722 fprintf(fp, _("Available `%s' sizes:\n"), ext);
723 for (i=1; i<MAXSQSIZE; ++i) {
729 /* Return XPM piecesize closest to size */
731 xpm_closest_to (char *dirname, int size, char *ext)
734 int sm_diff = MAXSQSIZE;
738 xpm_getavail(dirname, ext);
740 if (appData.debugMode)
741 xpm_print_avail(stderr, ext);
743 for (i=1; i<MAXSQSIZE; ++i) {
746 diff = (diff<0) ? -diff : diff;
747 if (diff < sm_diff) {
755 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
761 #else /* !HAVE_DIR_STRUCT */
762 /* If we are on a system without a DIR struct, we can't
763 read the directory, so we can't collect a list of
764 filenames, etc., so we can't do any size-fitting. */
766 xpm_closest_to (char *dirname, int size, char *ext)
769 Warning: No DIR structure found on this system --\n\
770 Unable to autosize for XPM/XIM pieces.\n\
771 Please report this error to %s.\n\
772 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
775 #endif /* HAVE_DIR_STRUCT */
778 /* Arrange to catch delete-window events */
779 Atom wm_delete_window;
781 CatchDeleteWindow (Widget w, String procname)
784 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
785 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
786 XtAugmentTranslations(w, XtParseTranslationTable(buf));
793 XtSetArg(args[0], XtNiconic, False);
794 XtSetValues(shellWidget, args, 1);
796 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
799 //---------------------------------------------------------------------------------------------------------
800 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
803 #define CW_USEDEFAULT (1<<31)
804 #define ICS_TEXT_MENU_SIZE 90
805 #define DEBUG_FILE "xboard.debug"
806 #define SetCurrentDirectory chdir
807 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
811 // these two must some day move to frontend.h, when they are implemented
812 Boolean GameListIsUp();
814 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
817 // front-end part of option handling
819 // [HGM] This platform-dependent table provides the location for storing the color info
820 extern char *crWhite, * crBlack;
824 &appData.whitePieceColor,
825 &appData.blackPieceColor,
826 &appData.lightSquareColor,
827 &appData.darkSquareColor,
828 &appData.highlightSquareColor,
829 &appData.premoveHighlightColor,
830 &appData.lowTimeWarningColor,
841 // [HGM] font: keep a font for each square size, even non-stndard ones
844 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
845 char *fontTable[NUM_FONTS][MAX_SIZE];
848 ParseFont (char *name, int number)
849 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
851 if(sscanf(name, "size%d:", &size)) {
852 // [HGM] font: font is meant for specific boardSize (likely from settings file);
853 // defer processing it until we know if it matches our board size
854 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
855 fontTable[number][size] = strdup(strchr(name, ':')+1);
856 fontValid[number][size] = True;
861 case 0: // CLOCK_FONT
862 appData.clockFont = strdup(name);
864 case 1: // MESSAGE_FONT
865 appData.font = strdup(name);
867 case 2: // COORD_FONT
868 appData.coordFont = strdup(name);
873 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
878 { // only 2 fonts currently
879 appData.clockFont = CLOCK_FONT_NAME;
880 appData.coordFont = COORD_FONT_NAME;
881 appData.font = DEFAULT_FONT_NAME;
886 { // no-op, until we identify the code for this already in XBoard and move it here
890 ParseColor (int n, char *name)
891 { // in XBoard, just copy the color-name string
892 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
896 ParseTextAttribs (ColorClass cc, char *s)
898 (&appData.colorShout)[cc] = strdup(s);
902 ParseBoardSize (void *addr, char *name)
904 appData.boardSize = strdup(name);
909 { // In XBoard the sound-playing program takes care of obtaining the actual sound
913 SetCommPortDefaults ()
914 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
917 // [HGM] args: these three cases taken out to stay in front-end
919 SaveFontArg (FILE *f, ArgDescriptor *ad)
922 int i, n = (int)(intptr_t)ad->argLoc;
924 case 0: // CLOCK_FONT
925 name = appData.clockFont;
927 case 1: // MESSAGE_FONT
930 case 2: // COORD_FONT
931 name = appData.coordFont;
936 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
937 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
938 fontTable[n][squareSize] = strdup(name);
939 fontValid[n][squareSize] = True;
942 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
943 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
948 { // nothing to do, as the sounds are at all times represented by their text-string names already
952 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
953 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
954 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
958 SaveColor (FILE *f, ArgDescriptor *ad)
959 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
960 if(colorVariable[(int)(intptr_t)ad->argLoc])
961 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
965 SaveBoardSize (FILE *f, char *name, void *addr)
966 { // wrapper to shield back-end from BoardSize & sizeInfo
967 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
971 ParseCommPortSettings (char *s)
972 { // no such option in XBoard (yet)
975 extern Widget engineOutputShell;
979 GetActualPlacement (Widget wg, WindowPlacement *wp)
984 XWindowAttributes winAt;
991 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
992 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
993 wp->x = rx - winAt.x;
994 wp->y = ry - winAt.y;
995 wp->height = winAt.height;
996 wp->width = winAt.width;
997 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
1002 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1003 // In XBoard this will have to wait until awareness of window parameters is implemented
1004 GetActualPlacement(shellWidget, &wpMain);
1005 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1006 if(MoveHistoryIsUp()) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
1007 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1008 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1009 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
1010 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
1014 PrintCommPortSettings (FILE *f, char *name)
1015 { // This option does not exist in XBoard
1019 EnsureOnScreen (int *x, int *y, int minX, int minY)
1026 { // [HGM] args: allows testing if main window is realized from back-end
1027 return xBoardWindow != 0;
1031 PopUpStartupDialog ()
1032 { // start menu not implemented in XBoard
1036 ConvertToLine (int argc, char **argv)
1038 static char line[128*1024], buf[1024];
1042 for(i=1; i<argc; i++)
1044 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1045 && argv[i][0] != '{' )
1046 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1048 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1049 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1052 line[strlen(line)-1] = NULLCHAR;
1056 //--------------------------------------------------------------------------------------------
1059 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1061 #define BoardSize int
1063 InitDrawingSizes (BoardSize boardSize, int flags)
1064 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1065 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1067 XtGeometryResult gres;
1069 static Dimension oldWidth, oldHeight;
1070 static VariantClass oldVariant;
1071 static int oldDual = -1, oldMono = -1;
1073 if(!formWidget) return;
1075 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1076 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1077 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1079 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1081 * Enable shell resizing.
1083 shellArgs[0].value = (XtArgVal) &w;
1084 shellArgs[1].value = (XtArgVal) &h;
1085 XtGetValues(shellWidget, shellArgs, 2);
1087 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1088 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1089 XtSetValues(shellWidget, &shellArgs[2], 4);
1091 XtSetArg(args[0], XtNdefaultDistance, &sep);
1092 XtGetValues(formWidget, args, 1);
1094 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1096 hOffset = boardWidth + 10;
1097 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1098 secondSegments[i] = gridSegments[i];
1099 secondSegments[i].x1 += hOffset;
1100 secondSegments[i].x2 += hOffset;
1103 XtSetArg(args[0], XtNwidth, boardWidth);
1104 XtSetArg(args[1], XtNheight, boardHeight);
1105 XtSetValues(boardWidget, args, 2);
1107 timerWidth = (boardWidth - sep) / 2;
1108 XtSetArg(args[0], XtNwidth, timerWidth);
1109 XtSetValues(whiteTimerWidget, args, 1);
1110 XtSetValues(blackTimerWidget, args, 1);
1112 XawFormDoLayout(formWidget, False);
1114 if (appData.titleInWindow) {
1116 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1117 XtSetArg(args[i], XtNheight, &h); i++;
1118 XtGetValues(titleWidget, args, i);
1120 w = boardWidth - 2*bor;
1122 XtSetArg(args[0], XtNwidth, &w);
1123 XtGetValues(menuBarWidget, args, 1);
1124 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1127 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1128 if (gres != XtGeometryYes && appData.debugMode) {
1130 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1131 programName, gres, w, h, wr, hr);
1135 XawFormDoLayout(formWidget, True);
1138 * Inhibit shell resizing.
1140 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1141 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1142 shellArgs[4].value = shellArgs[2].value = w;
1143 shellArgs[5].value = shellArgs[3].value = h;
1144 XtSetValues(shellWidget, &shellArgs[0], 6);
1146 XSync(xDisplay, False);
1150 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1153 if(gameInfo.variant != oldVariant) { // and only if variant changed
1156 for(i=0; i<4; i++) {
1158 for(p=0; p<=(int)WhiteKing; p++)
1159 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1160 if(gameInfo.variant == VariantShogi) {
1161 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1162 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1163 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1164 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1165 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1168 if(gameInfo.variant == VariantGothic) {
1169 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1172 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1173 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1174 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1177 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1178 for(p=0; p<=(int)WhiteKing; p++)
1179 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1180 if(gameInfo.variant == VariantShogi) {
1181 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1182 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1183 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1184 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1185 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1188 if(gameInfo.variant == VariantGothic) {
1189 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1192 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1193 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1194 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1199 for(i=0; i<2; i++) {
1201 for(p=0; p<=(int)WhiteKing; p++)
1202 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1203 if(gameInfo.variant == VariantShogi) {
1204 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1205 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1206 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1207 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1208 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1211 if(gameInfo.variant == VariantGothic) {
1212 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1215 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1216 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1217 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1221 oldMono = -10; // kludge to force recreation of animation masks
1222 oldVariant = gameInfo.variant;
1225 if(appData.monoMode != oldMono)
1228 oldMono = appData.monoMode;
1233 MakeOneColor (char *name, Pixel *color)
1235 XrmValue vFrom, vTo;
1236 if (!appData.monoMode) {
1237 vFrom.addr = (caddr_t) name;
1238 vFrom.size = strlen(name);
1239 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1240 if (vTo.addr == NULL) {
1241 appData.monoMode = True;
1244 *color = *(Pixel *) vTo.addr;
1252 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1253 int forceMono = False;
1255 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1256 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1257 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1258 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1259 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1260 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1267 { // [HGM] taken out of main
1269 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1270 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1271 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1273 if (appData.bitmapDirectory[0] != NULLCHAR) {
1277 CreateXPMBoard(appData.liteBackTextureFile, 1);
1278 CreateXPMBoard(appData.darkBackTextureFile, 0);
1282 /* Create regular pieces */
1283 if (!useImages) CreatePieces();
1288 InitDrawingParams ()
1290 MakeColors(); CreateGCs(True);
1295 main (int argc, char **argv)
1297 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1298 XSetWindowAttributes window_attributes;
1300 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1301 XrmValue vFrom, vTo;
1302 XtGeometryResult gres;
1305 int forceMono = False;
1307 srandom(time(0)); // [HGM] book: make random truly random
1309 setbuf(stdout, NULL);
1310 setbuf(stderr, NULL);
1313 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1314 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1318 programName = strrchr(argv[0], '/');
1319 if (programName == NULL)
1320 programName = argv[0];
1325 XtSetLanguageProc(NULL, NULL, NULL);
1326 bindtextdomain(PACKAGE, LOCALEDIR);
1327 textdomain(PACKAGE);
1331 XtAppInitialize(&appContext, "XBoard", shellOptions,
1332 XtNumber(shellOptions),
1333 &argc, argv, xboardResources, NULL, 0);
1334 appData.boardSize = "";
1335 InitAppData(ConvertToLine(argc, argv));
1337 if (p == NULL) p = "/tmp";
1338 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1339 gameCopyFilename = (char*) malloc(i);
1340 gamePasteFilename = (char*) malloc(i);
1341 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1342 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1344 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1345 clientResources, XtNumber(clientResources),
1348 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1349 static char buf[MSG_SIZ];
1350 EscapeExpand(buf, appData.firstInitString);
1351 appData.firstInitString = strdup(buf);
1352 EscapeExpand(buf, appData.secondInitString);
1353 appData.secondInitString = strdup(buf);
1354 EscapeExpand(buf, appData.firstComputerString);
1355 appData.firstComputerString = strdup(buf);
1356 EscapeExpand(buf, appData.secondComputerString);
1357 appData.secondComputerString = strdup(buf);
1360 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1363 if (chdir(chessDir) != 0) {
1364 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1370 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1371 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1372 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1373 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1376 setbuf(debugFP, NULL);
1380 if (appData.debugMode) {
1381 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1385 /* [HGM,HR] make sure board size is acceptable */
1386 if(appData.NrFiles > BOARD_FILES ||
1387 appData.NrRanks > BOARD_RANKS )
1388 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1391 /* This feature does not work; animation needs a rewrite */
1392 appData.highlightDragging = FALSE;
1396 xDisplay = XtDisplay(shellWidget);
1397 xScreen = DefaultScreen(xDisplay);
1398 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1400 gameInfo.variant = StringToVariant(appData.variant);
1401 InitPosition(FALSE);
1404 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1406 if (isdigit(appData.boardSize[0])) {
1407 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1408 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1409 &fontPxlSize, &smallLayout, &tinyLayout);
1411 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1412 programName, appData.boardSize);
1416 /* Find some defaults; use the nearest known size */
1417 SizeDefaults *szd, *nearest;
1418 int distance = 99999;
1419 nearest = szd = sizeDefaults;
1420 while (szd->name != NULL) {
1421 if (abs(szd->squareSize - squareSize) < distance) {
1423 distance = abs(szd->squareSize - squareSize);
1424 if (distance == 0) break;
1428 if (i < 2) lineGap = nearest->lineGap;
1429 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1430 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1431 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1432 if (i < 6) smallLayout = nearest->smallLayout;
1433 if (i < 7) tinyLayout = nearest->tinyLayout;
1436 SizeDefaults *szd = sizeDefaults;
1437 if (*appData.boardSize == NULLCHAR) {
1438 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1439 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1442 if (szd->name == NULL) szd--;
1443 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1445 while (szd->name != NULL &&
1446 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1447 if (szd->name == NULL) {
1448 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1449 programName, appData.boardSize);
1453 squareSize = szd->squareSize;
1454 lineGap = szd->lineGap;
1455 clockFontPxlSize = szd->clockFontPxlSize;
1456 coordFontPxlSize = szd->coordFontPxlSize;
1457 fontPxlSize = szd->fontPxlSize;
1458 smallLayout = szd->smallLayout;
1459 tinyLayout = szd->tinyLayout;
1460 // [HGM] font: use defaults from settings file if available and not overruled
1462 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1463 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1464 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1465 appData.font = fontTable[MESSAGE_FONT][squareSize];
1466 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1467 appData.coordFont = fontTable[COORD_FONT][squareSize];
1469 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1470 if (strlen(appData.pixmapDirectory) > 0) {
1471 p = ExpandPathName(appData.pixmapDirectory);
1473 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1474 appData.pixmapDirectory);
1477 if (appData.debugMode) {
1478 fprintf(stderr, _("\
1479 XBoard square size (hint): %d\n\
1480 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1482 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1483 if (appData.debugMode) {
1484 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1487 defaultLineGap = lineGap;
1488 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1490 /* [HR] height treated separately (hacked) */
1491 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1492 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1493 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1494 XtSetArg(boardArgs[2], XtNheight, boardHeight);
1497 * Determine what fonts to use.
1500 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1501 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1502 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1503 fontSet = CreateFontSet(appData.font);
1504 clockFontSet = CreateFontSet(appData.clockFont);
1506 /* For the coordFont, use the 0th font of the fontset. */
1507 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1508 XFontStruct **font_struct_list;
1509 XFontSetExtents *fontSize;
1510 char **font_name_list;
1511 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1512 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1513 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1514 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1515 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1518 appData.font = FindFont(appData.font, fontPxlSize);
1519 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1520 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1521 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1522 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1523 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1524 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1526 countFontID = coordFontID; // [HGM] holdings
1527 countFontStruct = coordFontStruct;
1529 xdb = XtDatabase(xDisplay);
1531 XrmPutLineResource(&xdb, "*international: True");
1532 vTo.size = sizeof(XFontSet);
1533 vTo.addr = (XtPointer) &fontSet;
1534 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1536 XrmPutStringResource(&xdb, "*font", appData.font);
1540 * Detect if there are not enough colors available and adapt.
1542 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1543 appData.monoMode = True;
1546 forceMono = MakeColors();
1549 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1551 appData.monoMode = True;
1554 if (appData.lowTimeWarning && !appData.monoMode) {
1555 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1556 vFrom.size = strlen(appData.lowTimeWarningColor);
1557 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1558 if (vTo.addr == NULL)
1559 appData.monoMode = True;
1561 lowTimeWarningColor = *(Pixel *) vTo.addr;
1564 if (appData.monoMode && appData.debugMode) {
1565 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1566 (unsigned long) XWhitePixel(xDisplay, xScreen),
1567 (unsigned long) XBlackPixel(xDisplay, xScreen));
1570 ParseIcsTextColors();
1572 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1578 layoutName = "tinyLayout";
1579 } else if (smallLayout) {
1580 layoutName = "smallLayout";
1582 layoutName = "normalLayout";
1584 /* Outer layoutWidget is there only to provide a name for use in
1585 resources that depend on the layout style */
1587 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
1588 layoutArgs, XtNumber(layoutArgs));
1590 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
1591 formArgs, XtNumber(formArgs));
1592 XtSetArg(args[0], XtNdefaultDistance, &sep);
1593 XtGetValues(formWidget, args, 1);
1596 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
1597 XtSetArg(args[0], XtNtop, XtChainTop);
1598 XtSetArg(args[1], XtNbottom, XtChainTop);
1599 XtSetArg(args[2], XtNright, XtChainLeft);
1600 XtSetValues(menuBarWidget, args, 3);
1602 widgetList[j++] = whiteTimerWidget =
1603 XtCreateWidget("whiteTime", labelWidgetClass,
1604 formWidget, timerArgs, XtNumber(timerArgs));
1606 XtSetArg(args[0], XtNfontSet, clockFontSet);
1608 XtSetArg(args[0], XtNfont, clockFontStruct);
1610 XtSetArg(args[1], XtNtop, XtChainTop);
1611 XtSetArg(args[2], XtNbottom, XtChainTop);
1612 XtSetValues(whiteTimerWidget, args, 3);
1614 widgetList[j++] = blackTimerWidget =
1615 XtCreateWidget("blackTime", labelWidgetClass,
1616 formWidget, timerArgs, XtNumber(timerArgs));
1618 XtSetArg(args[0], XtNfontSet, clockFontSet);
1620 XtSetArg(args[0], XtNfont, clockFontStruct);
1622 XtSetArg(args[1], XtNtop, XtChainTop);
1623 XtSetArg(args[2], XtNbottom, XtChainTop);
1624 XtSetValues(blackTimerWidget, args, 3);
1626 if (appData.titleInWindow) {
1627 widgetList[j++] = titleWidget =
1628 XtCreateWidget("title", labelWidgetClass, formWidget,
1629 titleArgs, XtNumber(titleArgs));
1630 XtSetArg(args[0], XtNtop, XtChainTop);
1631 XtSetArg(args[1], XtNbottom, XtChainTop);
1632 XtSetValues(titleWidget, args, 2);
1635 if (appData.showButtonBar) {
1636 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
1637 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
1638 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
1639 XtSetArg(args[2], XtNtop, XtChainTop);
1640 XtSetArg(args[3], XtNbottom, XtChainTop);
1641 XtSetValues(buttonBarWidget, args, 4);
1644 widgetList[j++] = messageWidget =
1645 XtCreateWidget("message", labelWidgetClass, formWidget,
1646 messageArgs, XtNumber(messageArgs));
1647 XtSetArg(args[0], XtNtop, XtChainTop);
1648 XtSetArg(args[1], XtNbottom, XtChainTop);
1649 XtSetValues(messageWidget, args, 2);
1651 widgetList[j++] = boardWidget =
1652 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
1653 XtNumber(boardArgs));
1655 XtManageChildren(widgetList, j);
1657 timerWidth = (boardWidth - sep) / 2;
1658 XtSetArg(args[0], XtNwidth, timerWidth);
1659 XtSetValues(whiteTimerWidget, args, 1);
1660 XtSetValues(blackTimerWidget, args, 1);
1662 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1663 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1664 XtGetValues(whiteTimerWidget, args, 2);
1666 if (appData.showButtonBar) {
1667 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1668 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1669 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
1673 * formWidget uses these constraints but they are stored
1677 XtSetArg(args[i], XtNfromHoriz, 0); i++;
1678 XtSetValues(menuBarWidget, args, i);
1679 if (appData.titleInWindow) {
1682 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1683 XtSetValues(whiteTimerWidget, args, i);
1685 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1686 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1687 XtSetValues(blackTimerWidget, args, i);
1689 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1690 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
1691 XtSetValues(titleWidget, args, i);
1693 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1694 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1695 XtSetValues(messageWidget, args, i);
1696 if (appData.showButtonBar) {
1698 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1699 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1700 XtSetValues(buttonBarWidget, args, i);
1704 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1705 XtSetValues(whiteTimerWidget, args, i);
1707 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1708 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1709 XtSetValues(blackTimerWidget, args, i);
1711 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
1712 XtSetValues(titleWidget, args, i);
1714 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1715 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1716 XtSetValues(messageWidget, args, i);
1717 if (appData.showButtonBar) {
1719 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1720 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1721 XtSetValues(buttonBarWidget, args, i);
1726 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1727 XtSetValues(whiteTimerWidget, args, i);
1729 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1730 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1731 XtSetValues(blackTimerWidget, args, i);
1733 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1734 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1735 XtSetValues(messageWidget, args, i);
1736 if (appData.showButtonBar) {
1738 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1739 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1740 XtSetValues(buttonBarWidget, args, i);
1744 XtSetArg(args[0], XtNfromVert, messageWidget);
1745 XtSetArg(args[1], XtNtop, XtChainTop);
1746 XtSetArg(args[2], XtNbottom, XtChainBottom);
1747 XtSetArg(args[3], XtNleft, XtChainLeft);
1748 XtSetArg(args[4], XtNright, XtChainRight);
1749 XtSetValues(boardWidget, args, 5);
1751 XtRealizeWidget(shellWidget);
1754 XtSetArg(args[0], XtNx, wpMain.x);
1755 XtSetArg(args[1], XtNy, wpMain.y);
1756 XtSetValues(shellWidget, args, 2);
1760 * Correct the width of the message and title widgets.
1761 * It is not known why some systems need the extra fudge term.
1762 * The value "2" is probably larger than needed.
1764 XawFormDoLayout(formWidget, False);
1766 #define WIDTH_FUDGE 2
1768 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1769 XtSetArg(args[i], XtNheight, &h); i++;
1770 XtGetValues(messageWidget, args, i);
1771 if (appData.showButtonBar) {
1773 XtSetArg(args[i], XtNwidth, &w); i++;
1774 XtGetValues(buttonBarWidget, args, i);
1775 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1777 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
1780 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1781 if (gres != XtGeometryYes && appData.debugMode) {
1782 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1783 programName, gres, w, h, wr, hr);
1786 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
1787 /* The size used for the child widget in layout lags one resize behind
1788 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
1790 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1791 if (gres != XtGeometryYes && appData.debugMode) {
1792 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1793 programName, gres, w, h, wr, hr);
1796 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
1797 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
1798 XtSetArg(args[1], XtNright, XtChainRight);
1799 XtSetValues(messageWidget, args, 2);
1801 if (appData.titleInWindow) {
1803 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1804 XtSetArg(args[i], XtNheight, &h); i++;
1805 XtGetValues(titleWidget, args, i);
1807 w = boardWidth - 2*bor;
1809 XtSetArg(args[0], XtNwidth, &w);
1810 XtGetValues(menuBarWidget, args, 1);
1811 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1814 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1815 if (gres != XtGeometryYes && appData.debugMode) {
1817 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1818 programName, gres, w, h, wr, hr);
1821 XawFormDoLayout(formWidget, True);
1823 xBoardWindow = XtWindow(boardWidget);
1825 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1826 // not need to go into InitDrawingSizes().
1830 * Create X checkmark bitmap and initialize option menu checks.
1832 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1833 checkmark_bits, checkmark_width, checkmark_height);
1839 ReadBitmap(&wIconPixmap, "icon_white.bm",
1840 icon_white_bits, icon_white_width, icon_white_height);
1841 ReadBitmap(&bIconPixmap, "icon_black.bm",
1842 icon_black_bits, icon_black_width, icon_black_height);
1843 iconPixmap = wIconPixmap;
1845 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1846 XtSetValues(shellWidget, args, i);
1849 * Create a cursor for the board widget.
1851 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1852 XChangeWindowAttributes(xDisplay, xBoardWindow,
1853 CWCursor, &window_attributes);
1856 * Inhibit shell resizing.
1858 shellArgs[0].value = (XtArgVal) &w;
1859 shellArgs[1].value = (XtArgVal) &h;
1860 XtGetValues(shellWidget, shellArgs, 2);
1861 shellArgs[4].value = shellArgs[2].value = w;
1862 shellArgs[5].value = shellArgs[3].value = h;
1863 XtSetValues(shellWidget, &shellArgs[2], 4);
1864 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1865 marginH = h - boardHeight;
1867 CatchDeleteWindow(shellWidget, "QuitProc");
1875 if (appData.animate || appData.animateDragging)
1878 XtAugmentTranslations(formWidget,
1879 XtParseTranslationTable(globalTranslations));
1880 XtAugmentTranslations(boardWidget,
1881 XtParseTranslationTable(boardTranslations));
1882 XtAugmentTranslations(whiteTimerWidget,
1883 XtParseTranslationTable(whiteTranslations));
1884 XtAugmentTranslations(blackTimerWidget,
1885 XtParseTranslationTable(blackTranslations));
1887 /* Why is the following needed on some versions of X instead
1888 * of a translation? */
1889 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
1890 (XtEventHandler) EventProc, NULL);
1892 XtAddEventHandler(formWidget, KeyPressMask, False,
1893 (XtEventHandler) MoveTypeInProc, NULL);
1894 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1895 (XtEventHandler) EventProc, NULL);
1897 /* [AS] Restore layout */
1898 if( wpMoveHistory.visible ) {
1902 if( wpEvalGraph.visible )
1907 if( wpEngineOutput.visible ) {
1908 EngineOutputPopUp();
1913 if (errorExitStatus == -1) {
1914 if (appData.icsActive) {
1915 /* We now wait until we see "login:" from the ICS before
1916 sending the logon script (problems with timestamp otherwise) */
1917 /*ICSInitScript();*/
1918 if (appData.icsInputBox) ICSInputBoxPopUp();
1922 signal(SIGWINCH, TermSizeSigHandler);
1924 signal(SIGINT, IntSigHandler);
1925 signal(SIGTERM, IntSigHandler);
1926 if (*appData.cmailGameName != NULLCHAR) {
1927 signal(SIGUSR1, CmailSigHandler);
1931 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1933 // XtSetKeyboardFocus(shellWidget, formWidget);
1934 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1936 XtAppMainLoop(appContext);
1937 if (appData.debugMode) fclose(debugFP); // [DM] debug
1942 TermSizeSigHandler (int sig)
1948 IntSigHandler (int sig)
1954 CmailSigHandler (int sig)
1959 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1961 /* Activate call-back function CmailSigHandlerCallBack() */
1962 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1964 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1968 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1971 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1973 /**** end signal code ****/
1976 #define Abs(n) ((n)<0 ? -(n) : (n))
1980 InsertPxlSize (char *pattern, int targetPxlSize)
1982 char *base_fnt_lst, strInt[12], *p, *q;
1983 int alternatives, i, len, strIntLen;
1986 * Replace the "*" (if present) in the pixel-size slot of each
1987 * alternative with the targetPxlSize.
1991 while ((p = strchr(p, ',')) != NULL) {
1995 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1996 strIntLen = strlen(strInt);
1997 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
2001 while (alternatives--) {
2002 char *comma = strchr(p, ',');
2003 for (i=0; i<14; i++) {
2004 char *hyphen = strchr(p, '-');
2006 if (comma && hyphen > comma) break;
2007 len = hyphen + 1 - p;
2008 if (i == 7 && *p == '*' && len == 2) {
2010 memcpy(q, strInt, strIntLen);
2020 len = comma + 1 - p;
2027 return base_fnt_lst;
2031 CreateFontSet (char *base_fnt_lst)
2034 char **missing_list;
2038 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
2039 &missing_list, &missing_count, &def_string);
2040 if (appData.debugMode) {
2042 XFontStruct **font_struct_list;
2043 char **font_name_list;
2044 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
2046 fprintf(debugFP, " got list %s, locale %s\n",
2047 XBaseFontNameListOfFontSet(fntSet),
2048 XLocaleOfFontSet(fntSet));
2049 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
2050 for (i = 0; i < count; i++) {
2051 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
2054 for (i = 0; i < missing_count; i++) {
2055 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
2058 if (fntSet == NULL) {
2059 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
2064 #else // not ENABLE_NLS
2066 * Find a font that matches "pattern" that is as close as
2067 * possible to the targetPxlSize. Prefer fonts that are k
2068 * pixels smaller to fonts that are k pixels larger. The
2069 * pattern must be in the X Consortium standard format,
2070 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2071 * The return value should be freed with XtFree when no
2075 FindFont (char *pattern, int targetPxlSize)
2077 char **fonts, *p, *best, *scalable, *scalableTail;
2078 int i, j, nfonts, minerr, err, pxlSize;
2080 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2082 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2083 programName, pattern);
2090 for (i=0; i<nfonts; i++) {
2093 if (*p != '-') continue;
2095 if (*p == NULLCHAR) break;
2096 if (*p++ == '-') j++;
2098 if (j < 7) continue;
2101 scalable = fonts[i];
2104 err = pxlSize - targetPxlSize;
2105 if (Abs(err) < Abs(minerr) ||
2106 (minerr > 0 && err < 0 && -err == minerr)) {
2112 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2113 /* If the error is too big and there is a scalable font,
2114 use the scalable font. */
2115 int headlen = scalableTail - scalable;
2116 p = (char *) XtMalloc(strlen(scalable) + 10);
2117 while (isdigit(*scalableTail)) scalableTail++;
2118 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2120 p = (char *) XtMalloc(strlen(best) + 2);
2121 safeStrCpy(p, best, strlen(best)+1 );
2123 if (appData.debugMode) {
2124 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2125 pattern, targetPxlSize, p);
2127 XFreeFontNames(fonts);
2134 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
2135 // must be called before all non-first callse to CreateGCs()
2136 XtReleaseGC(shellWidget, highlineGC);
2137 XtReleaseGC(shellWidget, lightSquareGC);
2138 XtReleaseGC(shellWidget, darkSquareGC);
2139 XtReleaseGC(shellWidget, lineGC);
2140 if (appData.monoMode) {
2141 if (DefaultDepth(xDisplay, xScreen) == 1) {
2142 XtReleaseGC(shellWidget, wbPieceGC);
2144 XtReleaseGC(shellWidget, bwPieceGC);
2147 XtReleaseGC(shellWidget, prelineGC);
2148 XtReleaseGC(shellWidget, wdPieceGC);
2149 XtReleaseGC(shellWidget, wlPieceGC);
2150 XtReleaseGC(shellWidget, bdPieceGC);
2151 XtReleaseGC(shellWidget, blPieceGC);
2156 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
2158 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2159 | GCBackground | GCFunction | GCPlaneMask;
2160 gc_values->foreground = foreground;
2161 gc_values->background = background;
2162 return XtGetGC(shellWidget, value_mask, gc_values);
2166 CreateGCs (int redo)
2168 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2169 | GCBackground | GCFunction | GCPlaneMask;
2170 XGCValues gc_values;
2172 Pixel white = XWhitePixel(xDisplay, xScreen);
2173 Pixel black = XBlackPixel(xDisplay, xScreen);
2175 gc_values.plane_mask = AllPlanes;
2176 gc_values.line_width = lineGap;
2177 gc_values.line_style = LineSolid;
2178 gc_values.function = GXcopy;
2181 DeleteGCs(); // called a second time; clean up old GCs first
2182 } else { // [HGM] grid and font GCs created on first call only
2183 coordGC = CreateOneGC(&gc_values, black, white);
2184 XSetFont(xDisplay, coordGC, coordFontID);
2186 // [HGM] make font for holdings counts (white on black)
2187 countGC = CreateOneGC(&gc_values, white, black);
2188 XSetFont(xDisplay, countGC, countFontID);
2190 lineGC = CreateOneGC(&gc_values, black, black);
2192 if (appData.monoMode) {
2194 highlineGC = CreateOneGC(&gc_values, white, white);
2195 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
2196 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
2198 if (DefaultDepth(xDisplay, xScreen) == 1) {
2199 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2200 gc_values.function = GXcopyInverted;
2201 copyInvertedGC = CreateOneGC(&gc_values, black, white);
2202 gc_values.function = GXcopy;
2203 if (XBlackPixel(xDisplay, xScreen) == 1) {
2204 bwPieceGC = darkSquareGC;
2205 wbPieceGC = copyInvertedGC;
2207 bwPieceGC = copyInvertedGC;
2208 wbPieceGC = lightSquareGC;
2213 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
2214 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
2215 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
2216 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
2217 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
2218 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
2219 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
2220 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
2225 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
2233 fp = fopen(filename, "rb");
2235 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
2242 for (y=0; y<h; ++y) {
2243 for (x=0; x<h; ++x) {
2248 XPutPixel(xim, x, y, blackPieceColor);
2250 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2253 XPutPixel(xim, x, y, darkSquareColor);
2255 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2258 XPutPixel(xim, x, y, whitePieceColor);
2260 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2263 XPutPixel(xim, x, y, lightSquareColor);
2265 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2273 /* create Pixmap of piece */
2274 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2276 XPutImage(xDisplay, *dest, lightSquareGC, xim,
2279 /* create Pixmap of clipmask
2280 Note: We assume the white/black pieces have the same
2281 outline, so we make only 6 masks. This is okay
2282 since the XPM clipmask routines do the same. */
2284 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2286 XPutImage(xDisplay, temp, lightSquareGC, xmask,
2289 /* now create the 1-bit version */
2290 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2293 values.foreground = 1;
2294 values.background = 0;
2296 /* Don't use XtGetGC, not read only */
2297 maskGC = XCreateGC(xDisplay, *mask,
2298 GCForeground | GCBackground, &values);
2299 XCopyPlane(xDisplay, temp, *mask, maskGC,
2300 0, 0, squareSize, squareSize, 0, 0, 1);
2301 XFreePixmap(xDisplay, temp);
2306 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
2314 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
2319 /* The XSynchronize calls were copied from CreatePieces.
2320 Not sure if needed, but can't hurt */
2321 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2324 /* temp needed by loadXIM() */
2325 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2326 0, 0, ss, ss, AllPlanes, XYPixmap);
2328 if (strlen(appData.pixmapDirectory) == 0) {
2332 if (appData.monoMode) {
2333 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
2337 fprintf(stderr, _("\nLoading XIMs...\n"));
2339 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2340 fprintf(stderr, "%d", piece+1);
2341 for (kind=0; kind<4; kind++) {
2342 fprintf(stderr, ".");
2343 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2344 ExpandPathName(appData.pixmapDirectory),
2345 piece <= (int) WhiteKing ? "" : "w",
2346 pieceBitmapNames[piece],
2348 ximPieceBitmap[kind][piece] =
2349 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2350 0, 0, ss, ss, AllPlanes, XYPixmap);
2351 if (appData.debugMode)
2352 fprintf(stderr, _("(File:%s:) "), buf);
2353 loadXIM(ximPieceBitmap[kind][piece],
2355 &(xpmPieceBitmap2[kind][piece]),
2356 &(ximMaskPm2[piece]));
2357 if(piece <= (int)WhiteKing)
2358 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2360 fprintf(stderr," ");
2362 /* Load light and dark squares */
2363 /* If the LSQ and DSQ pieces don't exist, we will
2364 draw them with solid squares. */
2365 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2366 if (access(buf, 0) != 0) {
2370 fprintf(stderr, _("light square "));
2372 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2373 0, 0, ss, ss, AllPlanes, XYPixmap);
2374 if (appData.debugMode)
2375 fprintf(stderr, _("(File:%s:) "), buf);
2377 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2378 fprintf(stderr, _("dark square "));
2379 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2380 ExpandPathName(appData.pixmapDirectory), ss);
2381 if (appData.debugMode)
2382 fprintf(stderr, _("(File:%s:) "), buf);
2384 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2385 0, 0, ss, ss, AllPlanes, XYPixmap);
2386 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2387 xpmJailSquare = xpmLightSquare;
2389 fprintf(stderr, _("Done.\n"));
2391 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2394 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2398 CreateXPMBoard (char *s, int kind)
2402 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2403 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2404 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2410 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2411 // thisroutine has to be called t free the old piece pixmaps
2413 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2414 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2416 XFreePixmap(xDisplay, xpmLightSquare);
2417 XFreePixmap(xDisplay, xpmDarkSquare);
2426 u_int ss = squareSize;
2428 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2429 XpmColorSymbol symbols[4];
2430 static int redo = False;
2432 if(redo) FreeXPMPieces(); else redo = 1;
2434 /* The XSynchronize calls were copied from CreatePieces.
2435 Not sure if needed, but can't hurt */
2436 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2438 /* Setup translations so piece colors match square colors */
2439 symbols[0].name = "light_piece";
2440 symbols[0].value = appData.whitePieceColor;
2441 symbols[1].name = "dark_piece";
2442 symbols[1].value = appData.blackPieceColor;
2443 symbols[2].name = "light_square";
2444 symbols[2].value = appData.lightSquareColor;
2445 symbols[3].name = "dark_square";
2446 symbols[3].value = appData.darkSquareColor;
2448 attr.valuemask = XpmColorSymbols;
2449 attr.colorsymbols = symbols;
2450 attr.numsymbols = 4;
2452 if (appData.monoMode) {
2453 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2457 if (strlen(appData.pixmapDirectory) == 0) {
2458 XpmPieces* pieces = builtInXpms;
2461 while (pieces->size != squareSize && pieces->size) pieces++;
2462 if (!pieces->size) {
2463 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2466 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2467 for (kind=0; kind<4; kind++) {
2469 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2470 pieces->xpm[piece][kind],
2471 &(xpmPieceBitmap2[kind][piece]),
2472 NULL, &attr)) != 0) {
2473 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2477 if(piece <= (int) WhiteKing)
2478 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2482 xpmJailSquare = xpmLightSquare;
2486 fprintf(stderr, _("\nLoading XPMs...\n"));
2489 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2490 fprintf(stderr, "%d ", piece+1);
2491 for (kind=0; kind<4; kind++) {
2492 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2493 ExpandPathName(appData.pixmapDirectory),
2494 piece > (int) WhiteKing ? "w" : "",
2495 pieceBitmapNames[piece],
2497 if (appData.debugMode) {
2498 fprintf(stderr, _("(File:%s:) "), buf);
2500 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2501 &(xpmPieceBitmap2[kind][piece]),
2502 NULL, &attr)) != 0) {
2503 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2504 // [HGM] missing: read of unorthodox piece failed; substitute King.
2505 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2506 ExpandPathName(appData.pixmapDirectory),
2508 if (appData.debugMode) {
2509 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2511 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2512 &(xpmPieceBitmap2[kind][piece]),
2516 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2521 if(piece <= (int) WhiteKing)
2522 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2525 /* Load light and dark squares */
2526 /* If the LSQ and DSQ pieces don't exist, we will
2527 draw them with solid squares. */
2528 fprintf(stderr, _("light square "));
2529 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2530 if (access(buf, 0) != 0) {
2534 if (appData.debugMode)
2535 fprintf(stderr, _("(File:%s:) "), buf);
2537 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2538 &xpmLightSquare, NULL, &attr)) != 0) {
2539 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2542 fprintf(stderr, _("dark square "));
2543 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2544 ExpandPathName(appData.pixmapDirectory), ss);
2545 if (appData.debugMode) {
2546 fprintf(stderr, _("(File:%s:) "), buf);
2548 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2549 &xpmDarkSquare, NULL, &attr)) != 0) {
2550 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2554 xpmJailSquare = xpmLightSquare;
2555 fprintf(stderr, _("Done.\n"));
2557 oldVariant = -1; // kludge to force re-makig of animation masks
2558 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2561 #endif /* HAVE_LIBXPM */
2564 /* No built-in bitmaps */
2569 u_int ss = squareSize;
2571 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2574 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2575 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2576 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2577 pieceBitmapNames[piece],
2578 ss, kind == SOLID ? 's' : 'o');
2579 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2580 if(piece <= (int)WhiteKing)
2581 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2585 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2589 /* With built-in bitmaps */
2593 BuiltInBits* bib = builtInBits;
2596 u_int ss = squareSize;
2598 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2601 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2603 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2604 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2605 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2606 pieceBitmapNames[piece],
2607 ss, kind == SOLID ? 's' : 'o');
2608 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2609 bib->bits[kind][piece], ss, ss);
2610 if(piece <= (int)WhiteKing)
2611 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2615 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2621 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2626 char msg[MSG_SIZ], fullname[MSG_SIZ];
2628 if (*appData.bitmapDirectory != NULLCHAR) {
2629 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2630 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2631 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2632 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2633 &w, &h, pm, &x_hot, &y_hot);
2634 fprintf(stderr, "load %s\n", name);
2635 if (errcode != BitmapSuccess) {
2637 case BitmapOpenFailed:
2638 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2640 case BitmapFileInvalid:
2641 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2643 case BitmapNoMemory:
2644 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2648 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2652 fprintf(stderr, _("%s: %s...using built-in\n"),
2654 } else if (w != wreq || h != hreq) {
2656 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2657 programName, fullname, w, h, wreq, hreq);
2663 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2673 if (lineGap == 0) return;
2675 /* [HR] Split this into 2 loops for non-square boards. */
2677 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2678 gridSegments[i].x1 = 0;
2679 gridSegments[i].x2 =
2680 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2681 gridSegments[i].y1 = gridSegments[i].y2
2682 = lineGap / 2 + (i * (squareSize + lineGap));
2685 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2686 gridSegments[j + i].y1 = 0;
2687 gridSegments[j + i].y2 =
2688 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2689 gridSegments[j + i].x1 = gridSegments[j + i].x2
2690 = lineGap / 2 + (j * (squareSize + lineGap));
2694 int nrOfMenuItems = 7;
2695 Widget menuWidget[150];
2696 MenuListItem menuItemList[150] = {
2697 { "LoadNextGameProc", LoadNextGameProc },
2698 { "LoadPrevGameProc", LoadPrevGameProc },
2699 { "ReloadGameProc", ReloadGameProc },
2700 { "ReloadPositionProc", ReloadPositionProc },
2701 #ifndef OPTIONSDIALOG
2702 { "AlwaysQueenProc", AlwaysQueenProc },
2703 { "AnimateDraggingProc", AnimateDraggingProc },
2704 { "AnimateMovingProc", AnimateMovingProc },
2705 { "AutoflagProc", AutoflagProc },
2706 { "AutoflipProc", AutoflipProc },
2707 { "BlindfoldProc", BlindfoldProc },
2708 { "FlashMovesProc", FlashMovesProc },
2710 { "HighlightDraggingProc", HighlightDraggingProc },
2712 { "HighlightLastMoveProc", HighlightLastMoveProc },
2713 // { "IcsAlarmProc", IcsAlarmProc },
2714 { "MoveSoundProc", MoveSoundProc },
2715 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
2716 { "PopupExitMessageProc", PopupExitMessageProc },
2717 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
2718 // { "PremoveProc", PremoveProc },
2719 { "ShowCoordsProc", ShowCoordsProc },
2720 { "ShowThinkingProc", ShowThinkingProc },
2721 { "HideThinkingProc", HideThinkingProc },
2722 { "TestLegalityProc", TestLegalityProc },
2724 { "AboutGameProc", AboutGameEvent },
2725 { "DebugProc", DebugProc },
2726 { "NothingProc", NothingProc },
2731 MarkMenuItem (char *menuRef, int state)
2733 int nr = MenuToNumber(menuRef);
2736 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2737 XtSetValues(menuWidget[nr], args, 1);
2742 EnableMenuItem (char *menuRef, int state)
2744 int nr = MenuToNumber(menuRef);
2745 if(nr >= 0) XtSetSensitive(menuWidget[nr], state);
2749 EnableButtonBar (int state)
2751 XtSetSensitive(buttonBarWidget, state);
2756 SetMenuEnables (Enables *enab)
2758 while (enab->name != NULL) {
2759 EnableMenuItem(enab->name, enab->value);
2765 Equal(char *p, char *s)
2766 { // compare strings skipping spaces in second
2768 if(*s == ' ') { s++; continue; }
2769 if(*s++ != *p++) return 0;
2775 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2776 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2778 if(*nprms == 0) return;
2779 for(i=0; menuItemList[i].name; i++) {
2780 if(Equal(prms[0], menuItemList[i].name)) {
2781 (menuItemList[i].proc) ();
2788 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
2790 MenuProc *proc = (MenuProc *) addr;
2796 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2798 RecentEngineEvent((int) (intptr_t) addr);
2801 // some stuff that must remain in front-end
2802 static Widget mainBar, currentMenu;
2803 static int wtot, nr = 0, widths[10];
2806 AppendMenuItem (char *text, char *name, MenuProc *action)
2813 XtSetArg(args[j], XtNleftMargin, 20); j++;
2814 XtSetArg(args[j], XtNrightMargin, 20); j++;
2816 if (strcmp(text, "----") == 0) {
2817 entry = XtCreateManagedWidget(text, smeLineObjectClass,
2818 currentMenu, args, j);
2820 XtSetArg(args[j], XtNlabel, XtNewString(_(text)));
2821 entry = XtCreateManagedWidget(name, smeBSBObjectClass,
2822 currentMenu, args, j+1);
2823 XtAddCallback(entry, XtNcallback,
2824 (XtCallbackProc) (strcmp(name, "recent") ? MenuBarSelect : MenuEngineSelect),
2826 menuWidget[nrOfMenuItems] = entry;
2831 CreateMenuButton (char *name, Menu *mb)
2832 { // create menu button on main bar, and shell for pull-down list
2838 XtSetArg(args[j], XtNmenuName, XtNewString(name)); j++;
2839 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
2840 XtSetArg(args[j], XtNborderWidth, 0); j++;
2841 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
2843 currentMenu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2846 XtSetArg(args[j], XtNwidth, &w); j++;
2847 XtGetValues(mb->subMenu, args, j);
2848 wtot += mb->textWidth = widths[nr++] = w;
2852 CreateMenuBar (Menu *mb, int boardWidth)
2856 char menuName[MSG_SIZ];
2860 // create bar itself
2862 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2863 XtSetArg(args[j], XtNvSpace, 0); j++;
2864 XtSetArg(args[j], XtNborderWidth, 0); j++;
2865 mainBar = XtCreateWidget("menuBar", boxWidgetClass,
2866 formWidget, args, j);
2868 CreateMainMenus(mb); // put menus in bar according to description in back-end
2870 // size buttons to make menu bar fit, clipping menu names where necessary
2871 while(wtot > boardWidth - 40) {
2873 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
2877 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
2879 XtSetArg(args[j], XtNwidth, widths[i]); j++;
2880 XtSetValues(ma[i].subMenu, args, j);
2887 CreateButtonBar (MenuItem *mi)
2890 Widget button, buttonBar;
2894 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2896 XtSetArg(args[j], XtNhSpace, 0); j++;
2898 XtSetArg(args[j], XtNborderWidth, 0); j++;
2899 XtSetArg(args[j], XtNvSpace, 0); j++;
2900 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
2901 formWidget, args, j);
2903 while (mi->string != NULL) {
2906 XtSetArg(args[j], XtNinternalWidth, 2); j++;
2907 XtSetArg(args[j], XtNborderWidth, 0); j++;
2909 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
2910 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
2911 buttonBar, args, j);
2912 XtAddCallback(button, XtNcallback,
2913 (XtCallbackProc) MenuBarSelect,
2914 (caddr_t) mi->proc);
2921 CreatePieceMenu (char *name, int color)
2926 ChessSquare selection;
2928 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2929 boardWidget, args, 0);
2931 for (i = 0; i < PIECE_MENU_SIZE; i++) {
2932 String item = pieceMenuStrings[color][i];
2934 if (strcmp(item, "----") == 0) {
2935 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2938 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2939 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2941 selection = pieceMenuTranslation[color][i];
2942 XtAddCallback(entry, XtNcallback,
2943 (XtCallbackProc) PieceMenuSelect,
2944 (caddr_t) selection);
2945 if (selection == WhitePawn || selection == BlackPawn) {
2946 XtSetArg(args[0], XtNpopupOnEntry, entry);
2947 XtSetValues(menu, args, 1);
2960 ChessSquare selection;
2962 whitePieceMenu = CreatePieceMenu("menuW", 0);
2963 blackPieceMenu = CreatePieceMenu("menuB", 1);
2965 if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
2966 XtRegisterGrabAction(PieceMenuPopup, True,
2967 (unsigned)(ButtonPressMask|ButtonReleaseMask),
2968 GrabModeAsync, GrabModeAsync);
2970 XtSetArg(args[0], XtNlabel, _("Drop"));
2971 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
2972 boardWidget, args, 1);
2973 for (i = 0; i < DROP_MENU_SIZE; i++) {
2974 String item = dropMenuStrings[i];
2976 if (strcmp(item, "----") == 0) {
2977 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2980 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2981 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2983 selection = dropMenuTranslation[i];
2984 XtAddCallback(entry, XtNcallback,
2985 (XtCallbackProc) DropMenuSelect,
2986 (caddr_t) selection);
3000 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3001 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3002 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3003 dmEnables[i].piece);
3004 XtSetSensitive(entry, p != NULL || !appData.testLegality
3005 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3006 && !appData.icsActive));
3008 while (p && *p++ == dmEnables[i].piece) count++;
3009 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3011 XtSetArg(args[j], XtNlabel, label); j++;
3012 XtSetValues(entry, args, j);
3017 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
3019 String whichMenu; int menuNr = -2;
3020 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3021 if (event->type == ButtonRelease)
3022 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3023 else if (event->type == ButtonPress)
3024 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3026 case 0: whichMenu = params[0]; break;
3027 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3029 case -1: if (errorUp) ErrorPopDown();
3032 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3036 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3038 if (pmFromX < 0 || pmFromY < 0) return;
3039 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3043 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3045 if (pmFromX < 0 || pmFromY < 0) return;
3046 DropMenuEvent(piece, pmFromX, pmFromY);
3050 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3052 shiftKey = prms[0][0] & 1;
3057 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3059 shiftKey = prms[0][0] & 1;
3065 do_flash_delay (unsigned long msec)
3071 DrawBorder (int x, int y, int type)
3075 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
3077 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3078 squareSize+lineGap, squareSize+lineGap);
3082 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
3084 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
3085 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
3087 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
3088 if(textureW[kind] < W*squareSize)
3089 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
3091 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
3092 if(textureH[kind] < H*squareSize)
3093 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
3095 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
3100 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
3101 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
3103 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
3104 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
3105 squareSize, squareSize, x*fac, y*fac);
3107 if (useImages && useImageSqs) {
3111 pm = xpmLightSquare;
3116 case 2: /* neutral */
3118 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
3121 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3122 squareSize, squareSize, x*fac, y*fac);
3132 case 2: /* neutral */
3137 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
3142 I split out the routines to draw a piece so that I could
3143 make a generic flash routine.
3146 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3148 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3149 switch (square_color) {
3151 case 2: /* neutral */
3153 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3154 ? *pieceToOutline(piece)
3155 : *pieceToSolid(piece),
3156 dest, bwPieceGC, 0, 0,
3157 squareSize, squareSize, x, y);
3160 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3161 ? *pieceToSolid(piece)
3162 : *pieceToOutline(piece),
3163 dest, wbPieceGC, 0, 0,
3164 squareSize, squareSize, x, y);
3170 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3172 switch (square_color) {
3174 case 2: /* neutral */
3176 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3177 ? *pieceToOutline(piece)
3178 : *pieceToSolid(piece),
3179 dest, bwPieceGC, 0, 0,
3180 squareSize, squareSize, x, y, 1);
3183 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3184 ? *pieceToSolid(piece)
3185 : *pieceToOutline(piece),
3186 dest, wbPieceGC, 0, 0,
3187 squareSize, squareSize, x, y, 1);
3193 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3195 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
3196 switch (square_color) {
3198 XCopyPlane(xDisplay, *pieceToSolid(piece),
3199 dest, (int) piece < (int) BlackPawn
3200 ? wlPieceGC : blPieceGC, 0, 0,
3201 squareSize, squareSize, x, y, 1);
3204 XCopyPlane(xDisplay, *pieceToSolid(piece),
3205 dest, (int) piece < (int) BlackPawn
3206 ? wdPieceGC : bdPieceGC, 0, 0,
3207 squareSize, squareSize, x, y, 1);
3209 case 2: /* neutral */
3211 break; // should never contain pieces
3216 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3218 int kind, p = piece;
3220 switch (square_color) {
3222 case 2: /* neutral */
3224 if ((int)piece < (int) BlackPawn) {
3232 if ((int)piece < (int) BlackPawn) {
3240 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
3241 if(useTexture & square_color+1) {
3242 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
3243 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
3244 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
3245 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
3246 XSetClipMask(xDisplay, wlPieceGC, None);
3247 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
3249 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3250 dest, wlPieceGC, 0, 0,
3251 squareSize, squareSize, x, y);
3254 typedef void (*DrawFunc)();
3259 if (appData.monoMode) {
3260 if (DefaultDepth(xDisplay, xScreen) == 1) {
3261 return monoDrawPiece_1bit;
3263 return monoDrawPiece;
3267 return colorDrawPieceImage;
3269 return colorDrawPiece;
3274 DrawDot (int marker, int x, int y, int r)
3276 if(appData.monoMode) {
3277 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
3278 x, y, r, r, 0, 64*360);
3279 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
3280 x, y, r, r, 0, 64*360);
3282 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
3283 x, y, r, r, 0, 64*360);
3287 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
3288 { // basic front-end board-draw function: takes care of everything that can be in square:
3289 // piece, background, coordinate/count, marker dot
3290 int direction, font_ascent, font_descent;
3291 XCharStruct overall;
3294 if (piece == EmptySquare) {
3295 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
3297 drawfunc = ChooseDrawFunc();
3298 drawfunc(piece, square_color, x, y, xBoardWindow);
3301 if(align) { // square carries inscription (coord or piece count)
3303 GC hGC = align < 3 ? coordGC : countGC;
3304 // first calculate where it goes
3305 XTextExtents(countFontStruct, string, 1, &direction,
3306 &font_ascent, &font_descent, &overall);
3308 xx += squareSize - overall.width - 2;
3309 yy += squareSize - font_descent - 1;
3310 } else if (align == 2) {
3311 xx += 2, yy += font_ascent + 1;
3312 } else if (align == 3) {
3313 xx += squareSize - overall.width - 2;
3314 yy += font_ascent + 1;
3315 } else if (align == 4) {
3316 xx += 2, yy += font_ascent + 1;
3319 if (appData.monoMode) {
3320 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3322 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3326 if(marker) { // print fat marker dot, if requested
3327 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
3332 FlashDelay (int flash_delay)
3334 XSync(xDisplay, False);
3335 if(flash_delay) do_flash_delay(flash_delay);
3339 Fraction (int x, int start, int stop)
3341 double f = ((double) x - start)/(stop - start);
3342 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
3346 static WindowPlacement wpNew;
3349 CoDrag (Widget sh, WindowPlacement *wp)
3352 int j=0, touch=0, fudge = 2;
3353 GetActualPlacement(sh, wp);
3354 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
3355 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
3356 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
3357 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
3358 if(!touch ) return; // only windows that touch co-move
3359 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
3360 int heightInc = wpNew.height - wpMain.height;
3361 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3362 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3363 wp->y += fracTop * heightInc;
3364 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
3365 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
3366 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
3367 int widthInc = wpNew.width - wpMain.width;
3368 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3369 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3370 wp->y += fracLeft * widthInc;
3371 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
3372 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
3374 wp->x += wpNew.x - wpMain.x;
3375 wp->y += wpNew.y - wpMain.y;
3376 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
3377 if(touch == 3) wp->y += wpNew.height - wpMain.height;
3378 XtSetArg(args[j], XtNx, wp->x); j++;
3379 XtSetArg(args[j], XtNy, wp->y); j++;
3380 XtSetValues(sh, args, j);
3383 static XtIntervalId delayedDragID = 0;
3388 GetActualPlacement(shellWidget, &wpNew);
3389 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
3390 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
3391 return; // false alarm
3392 if(EngineOutputIsUp()) CoDrag(engineOutputShell, &wpEngineOutput);
3393 if(MoveHistoryIsUp()) CoDrag(shells[HistoryDlg], &wpMoveHistory);
3394 if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
3395 if(GameListIsUp()) CoDrag(gameListShell, &wpGameList);
3397 DrawPosition(True, NULL);
3398 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
3405 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
3407 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
3410 /* Why is this needed on some versions of X? */
3412 EventProc (Widget widget, caddr_t unused, XEvent *event)
3414 if (!XtIsRealized(widget))
3416 switch (event->type) {
3417 case ConfigureNotify: // main window is being dragged: drag attached windows with it
3418 if(appData.useStickyWindows)
3419 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
3422 if (event->xexpose.count > 0) return; /* no clipping is done */
3423 DrawPosition(True, NULL);
3424 if(twoBoards) { // [HGM] dual: draw other board in other orientation
3425 flipView = !flipView; partnerUp = !partnerUp;
3426 DrawPosition(True, NULL);
3427 flipView = !flipView; partnerUp = !partnerUp;
3431 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
3438 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
3440 DrawSeekAxis (int x, int y, int xTo, int yTo)
3442 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
3446 DrawSeekBackground (int left, int top, int right, int bottom)
3448 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
3452 DrawSeekText (char *buf, int x, int y)
3454 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
3458 DrawSeekDot (int x, int y, int colorNr)
3460 int square = colorNr & 0x80;
3463 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
3465 XFillRectangle(xDisplay, xBoardWindow, color,
3466 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3468 XFillArc(xDisplay, xBoardWindow, color,
3469 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
3473 DrawGrid (int second)
3475 XDrawSegments(xDisplay, xBoardWindow, lineGC,
3476 second ? secondSegments : // [HGM] dual
3477 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
3482 * event handler for redrawing the board
3485 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3487 DrawPosition(True, NULL);
3492 * event handler for parsing user moves
3494 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
3495 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
3496 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
3497 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
3498 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
3499 // and at the end FinishMove() to perform the move after optional promotion popups.
3500 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
3502 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3504 if (w != boardWidget || errorExitStatus != -1) return;
3505 if(nprms) shiftKey = !strcmp(prms[0], "1");
3507 if (shellUp[PromoDlg]) { // [HGM] is this still needed?
3508 if (event->type == ButtonPress) {
3517 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
3518 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
3519 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
3523 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
3525 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
3526 DragPieceMove(event->xmotion.x, event->xmotion.y);
3530 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3531 { // [HGM] pv: walk PV
3532 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3535 static int savedIndex; /* gross that this is global */
3538 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3541 XawTextPosition index, dummy;
3544 XawTextGetSelectionPos(w, &index, &dummy);
3545 XtSetArg(arg, XtNstring, &val);
3546 XtGetValues(w, &arg, 1);
3547 ReplaceComment(savedIndex, val);
3548 if(savedIndex != currentMove) ToNrEvent(savedIndex);
3549 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3553 EditCommentPopUp (int index, char *title, char *text)
3556 if (text == NULL) text = "";
3557 NewCommentPopup(title, text, index);
3561 CommentPopUp (char *title, char *text)
3563 savedIndex = currentMove; // [HGM] vari
3564 NewCommentPopup(title, text, currentMove);
3570 PopDown(CommentDlg);
3573 static char *openName;
3579 (void) (*fileProc)(openFP, 0, openName);
3583 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
3585 fileProc = proc; /* I can't see a way not */
3586 fileOpenMode = openMode; /* to use globals here */
3587 { // [HGM] use file-selector dialog stolen from Ghostview
3588 int index; // this is not supported yet
3589 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
3590 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
3591 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
3592 ScheduleDelayedEvent(&DelayedLoad, 50);
3598 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
3600 dialogError = errorUp = False;
3601 XtPopdown(w = XtParent(XtParent(XtParent(w))));
3603 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3610 if (!errorUp) return;
3611 dialogError = errorUp = False;
3612 XtPopdown(errorShell);
3613 XtDestroyWidget(errorShell);
3614 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3618 ErrorPopUp (char *title, char *label, int modal)
3621 Widget dialog, layout;
3625 Dimension bw_width, pw_width;
3626 Dimension pw_height;
3630 XtSetArg(args[i], XtNresizable, True); i++;
3631 XtSetArg(args[i], XtNtitle, title); i++;
3633 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
3634 shellUp[TransientDlg] ? (dialogError = modal = TRUE, shells[TransientDlg]) : shellWidget, args, i);
3636 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
3637 layoutArgs, XtNumber(layoutArgs));
3640 XtSetArg(args[i], XtNlabel, label); i++;
3641 XtSetArg(args[i], XtNborderWidth, 0); i++;
3642 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
3645 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
3647 XtRealizeWidget(errorShell);
3648 CatchDeleteWindow(errorShell, "ErrorPopDown");
3651 XtSetArg(args[i], XtNwidth, &bw_width); i++;
3652 XtGetValues(boardWidget, args, i);
3654 XtSetArg(args[i], XtNwidth, &pw_width); i++;
3655 XtSetArg(args[i], XtNheight, &pw_height); i++;
3656 XtGetValues(errorShell, args, i);
3659 /* This code seems to tickle an X bug if it is executed too soon