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 PromotionPopDown P((void));
279 void PromotionCallback P((Widget w, XtPointer client_data,
280 XtPointer call_data));
281 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
282 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
283 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
284 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
285 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
286 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
287 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
288 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
289 Boolean TempBackwardActive = False;
290 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
291 void DisplayMove P((int moveNumber));
292 void ICSInitScript P((void));
293 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
294 void update_ics_width P(());
295 int get_term_width P(());
296 int CopyMemoProc P(());
299 * XBoard depends on Xt R4 or higher
301 int xtVersion = XtSpecificationRelease;
306 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
307 highlightSquareColor, premoveHighlightColor;
308 Pixel lowTimeWarningColor;
309 GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
310 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
312 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
313 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
314 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
315 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
316 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
317 ICSInputShell, fileNameShell;
318 Widget historyShell, evalGraphShell, gameListShell;
319 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
320 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
322 XFontSet fontSet, clockFontSet;
325 XFontStruct *clockFontStruct;
327 Font coordFontID, countFontID;
328 XFontStruct *coordFontStruct, *countFontStruct;
329 XtAppContext appContext;
334 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
336 Position commentX = -1, commentY = -1;
337 Dimension commentW, commentH;
338 typedef unsigned int BoardSize;
340 Boolean chessProgram;
342 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
343 int smallLayout = 0, tinyLayout = 0,
344 marginW, marginH, // [HGM] for run-time resizing
345 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
346 ICSInputBoxUp = False,
347 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
348 errorUp = False, errorExitStatus = -1, defaultLineGap;
349 Dimension textHeight;
350 Pixel timerForegroundPixel, timerBackgroundPixel;
351 Pixel buttonForegroundPixel, buttonBackgroundPixel;
352 char *chessDir, *programName, *programVersion;
353 Boolean alwaysOnTop = False;
354 char *icsTextMenuString;
356 char *firstChessProgramNames;
357 char *secondChessProgramNames;
359 WindowPlacement wpMain;
360 WindowPlacement wpConsole;
361 WindowPlacement wpComment;
362 WindowPlacement wpMoveHistory;
363 WindowPlacement wpEvalGraph;
364 WindowPlacement wpEngineOutput;
365 WindowPlacement wpGameList;
366 WindowPlacement wpTags;
371 Pixmap pieceBitmap[2][(int)BlackPawn];
372 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
373 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
374 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
375 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
376 Pixmap xpmBoardBitmap[2];
377 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
378 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
379 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
380 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
381 XImage *ximLightSquare, *ximDarkSquare;
384 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
385 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
387 #define White(piece) ((int)(piece) < (int)BlackPawn)
389 /* Bitmaps for use as masks when drawing XPM pieces.
390 Need one for each black and white piece. */
391 static Pixmap xpmMask[BlackKing + 1];
393 /* This magic number is the number of intermediate frames used
394 in each half of the animation. For short moves it's reduced
395 by 1. The total number of frames will be factor * 2 + 1. */
398 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
400 #define PAUSE_BUTTON "P"
401 MenuItem buttonBar[] = {
402 {"<<", "<<", ToStartEvent},
403 {"<", "<", BackwardEvent},
404 {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseEvent},
405 {">", ">", ForwardEvent},
406 {">>", ">>", ToEndEvent},
410 #define PIECE_MENU_SIZE 18
411 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
412 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
413 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
414 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
415 N_("Empty square"), N_("Clear board") },
416 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
417 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
418 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
419 N_("Empty square"), N_("Clear board") }
421 /* must be in same order as pieceMenuStrings! */
422 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
423 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
424 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
425 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
426 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
427 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
428 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
429 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
430 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
433 #define DROP_MENU_SIZE 6
434 String dropMenuStrings[DROP_MENU_SIZE] = {
435 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
437 /* must be in same order as dropMenuStrings! */
438 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
439 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
440 WhiteRook, WhiteQueen
448 DropMenuEnables dmEnables[] = {
466 { XtNborderWidth, 0 },
467 { XtNdefaultDistance, 0 },
471 { XtNborderWidth, 0 },
472 { XtNresizable, (XtArgVal) True },
476 { XtNborderWidth, 0 },
482 { XtNjustify, (XtArgVal) XtJustifyRight },
483 { XtNlabel, (XtArgVal) "..." },
484 { XtNresizable, (XtArgVal) True },
485 { XtNresize, (XtArgVal) False }
488 Arg messageArgs[] = {
489 { XtNjustify, (XtArgVal) XtJustifyLeft },
490 { XtNlabel, (XtArgVal) "..." },
491 { XtNresizable, (XtArgVal) True },
492 { XtNresize, (XtArgVal) False }
496 { XtNborderWidth, 0 },
497 { XtNjustify, (XtArgVal) XtJustifyLeft }
500 XtResource clientResources[] = {
501 { "flashCount", "flashCount", XtRInt, sizeof(int),
502 XtOffset(AppDataPtr, flashCount), XtRImmediate,
503 (XtPointer) FLASH_COUNT },
506 XrmOptionDescRec shellOptions[] = {
507 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
508 { "-flash", "flashCount", XrmoptionNoArg, "3" },
509 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
512 XtActionsRec boardActions[] = {
513 { "DrawPosition", DrawPositionProc },
514 { "HandleUserMove", HandleUserMove },
515 { "AnimateUserMove", AnimateUserMove },
516 { "HandlePV", HandlePV },
517 { "SelectPV", SelectPV },
518 { "StopPV", StopPV },
519 { "PieceMenuPopup", PieceMenuPopup },
520 { "WhiteClock", WhiteClock },
521 { "BlackClock", BlackClock },
522 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
523 { "QuitProc", QuitWrapper },
524 { "ManProc", ManInner },
525 { "TempBackwardProc", TempBackwardProc },
526 { "TempForwardProc", TempForwardProc },
527 { "CommentClick", (XtActionProc) CommentClick },
528 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
529 { "GameListPopDown", (XtActionProc) GameListPopDown },
530 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
531 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
532 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
533 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
534 { "GenericPopDown", (XtActionProc) GenericPopDown },
535 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
536 { "SelectMove", (XtActionProc) SelectMove },
537 { "LoadSelectedProc", LoadSelectedProc },
538 { "SetFilterProc", SetFilterProc },
539 { "TypeInProc", TypeInProc },
540 { "EnterKeyProc", EnterKeyProc },
541 { "UpKeyProc", UpKeyProc },
542 { "DownKeyProc", DownKeyProc },
543 { "WheelProc", WheelProc },
544 { "TabProc", TabProc },
547 char globalTranslations[] =
548 ":<Key>F9: MenuItem(ResignProc) \n \
549 :Ctrl<Key>n: MenuItem(NewGame) \n \
550 :Meta<Key>V: MenuItem(NewVariant) \n \
551 :Ctrl<Key>o: MenuItem(LoadGame) \n \
552 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
553 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
554 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
555 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
556 :Ctrl<Key>s: MenuItem(SaveGame) \n \
557 :Ctrl<Key>c: MenuItem(CopyGame) \n \
558 :Ctrl<Key>v: MenuItem(PasteGame) \n \
559 :Ctrl<Key>O: MenuItem(LoadPosition) \n \
560 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
561 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
562 :Ctrl<Key>S: MenuItem(SavePosition) \n \
563 :Ctrl<Key>C: MenuItem(CopyPosition) \n \
564 :Ctrl<Key>V: MenuItem(PastePosition) \n \
565 :Ctrl<Key>q: MenuItem(Exit) \n \
566 :Ctrl<Key>w: MenuItem(MachineWhite) \n \
567 :Ctrl<Key>b: MenuItem(MachineBlack) \n \
568 :Ctrl<Key>t: MenuItem(TwoMachines) \n \
569 :Ctrl<Key>a: MenuItem(AnalysisMode) \n \
570 :Ctrl<Key>g: MenuItem(AnalyzeFile) \n \
571 :Ctrl<Key>e: MenuItem(EditGame) \n \
572 :Ctrl<Key>E: MenuItem(EditPosition) \n \
573 :Meta<Key>O: MenuItem(ShowEngineOutput) \n \
574 :Meta<Key>E: MenuItem(ShowEvaluationGraph) \n \
575 :Meta<Key>G: MenuItem(ShowGameList) \n \
576 :Meta<Key>H: MenuItem(ShowMoveHistory) \n \
577 :<Key>Pause: MenuItem(Pause) \n \
578 :<Key>F3: MenuItem(Accept) \n \
579 :<Key>F4: MenuItem(Decline) \n \
580 :<Key>F12: MenuItem(Rematch) \n \
581 :<Key>F5: MenuItem(CallFlag) \n \
582 :<Key>F6: MenuItem(Draw) \n \
583 :<Key>F7: MenuItem(Adjourn) \n \
584 :<Key>F8: MenuItem(Abort) \n \
585 :<Key>F10: MenuItem(StopObserving) \n \
586 :<Key>F11: MenuItem(StopExamining) \n \
587 :Ctrl<Key>d: MenuItem(DebugProc) \n \
588 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
589 :Meta<Key>End: MenuItem(ToEnd) \n \
590 :Meta<Key>Right: MenuItem(Forward) \n \
591 :Meta<Key>Home: MenuItem(ToStart) \n \
592 :Meta<Key>Left: MenuItem(Backward) \n \
593 :<Key>Left: MenuItem(Backward) \n \
594 :<Key>Right: MenuItem(Forward) \n \
595 :<Key>Home: MenuItem(Revert) \n \
596 :<Key>End: MenuItem(TruncateGame) \n \
597 :Ctrl<Key>m: MenuItem(MoveNow) \n \
598 :Ctrl<Key>x: MenuItem(RetractMove) \n \
599 :Meta<Key>J: MenuItem(Adjudications) \n \
600 :Meta<Key>U: MenuItem(CommonEngine) \n \
601 :Meta<Key>T: MenuItem(TimeControl) \n \
602 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
603 #ifndef OPTIONSDIALOG
605 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
606 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
607 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
608 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
609 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
612 :<Key>F1: MenuItem(Manual) \n \
613 :<Key>F2: MenuItem(FlipView) \n \
614 :<KeyDown>Return: TempBackwardProc() \n \
615 :<KeyUp>Return: TempForwardProc() \n";
617 char boardTranslations[] =
618 "<Btn1Down>: HandleUserMove(0) \n \
619 Shift<Btn1Up>: HandleUserMove(1) \n \
620 <Btn1Up>: HandleUserMove(0) \n \
621 <Btn1Motion>: AnimateUserMove() \n \
622 <Btn3Motion>: HandlePV() \n \
623 <Btn2Motion>: HandlePV() \n \
624 <Btn3Up>: PieceMenuPopup(menuB) \n \
625 <Btn2Up>: PieceMenuPopup(menuB) \n \
626 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
627 PieceMenuPopup(menuB) \n \
628 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
629 PieceMenuPopup(menuW) \n \
630 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
631 PieceMenuPopup(menuW) \n \
632 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
633 PieceMenuPopup(menuB) \n";
635 char whiteTranslations[] =
636 "Shift<BtnDown>: WhiteClock(1)\n \
637 <BtnDown>: WhiteClock(0)\n";
638 char blackTranslations[] =
639 "Shift<BtnDown>: BlackClock(1)\n \
640 <BtnDown>: BlackClock(0)\n";
642 char ICSInputTranslations[] =
643 "<Key>Up: UpKeyProc() \n "
644 "<Key>Down: DownKeyProc() \n "
645 "<Key>Return: EnterKeyProc() \n";
647 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
648 // as the widget is destroyed before the up-click can call extend-end
649 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
651 String xboardResources[] = {
652 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
657 /* Max possible square size */
658 #define MAXSQSIZE 256
660 static int xpm_avail[MAXSQSIZE];
662 #ifdef HAVE_DIR_STRUCT
664 /* Extract piece size from filename */
666 xpm_getsize (char *name, int len, char *ext)
674 if ((p=strchr(name, '.')) == NULL ||
675 StrCaseCmp(p+1, ext) != 0)
681 while (*p && isdigit(*p))
688 /* Setup xpm_avail */
690 xpm_getavail (char *dirname, char *ext)
696 for (i=0; i<MAXSQSIZE; ++i)
699 if (appData.debugMode)
700 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
702 dir = opendir(dirname);
705 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
706 programName, dirname);
710 while ((ent=readdir(dir)) != NULL) {
711 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
712 if (i > 0 && i < MAXSQSIZE)
722 xpm_print_avail (FILE *fp, char *ext)
726 fprintf(fp, _("Available `%s' sizes:\n"), ext);
727 for (i=1; i<MAXSQSIZE; ++i) {
733 /* Return XPM piecesize closest to size */
735 xpm_closest_to (char *dirname, int size, char *ext)
738 int sm_diff = MAXSQSIZE;
742 xpm_getavail(dirname, ext);
744 if (appData.debugMode)
745 xpm_print_avail(stderr, ext);
747 for (i=1; i<MAXSQSIZE; ++i) {
750 diff = (diff<0) ? -diff : diff;
751 if (diff < sm_diff) {
759 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
765 #else /* !HAVE_DIR_STRUCT */
766 /* If we are on a system without a DIR struct, we can't
767 read the directory, so we can't collect a list of
768 filenames, etc., so we can't do any size-fitting. */
770 xpm_closest_to (char *dirname, int size, char *ext)
773 Warning: No DIR structure found on this system --\n\
774 Unable to autosize for XPM/XIM pieces.\n\
775 Please report this error to %s.\n\
776 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
779 #endif /* HAVE_DIR_STRUCT */
782 /* Arrange to catch delete-window events */
783 Atom wm_delete_window;
785 CatchDeleteWindow (Widget w, String procname)
788 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
789 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
790 XtAugmentTranslations(w, XtParseTranslationTable(buf));
797 XtSetArg(args[0], XtNiconic, False);
798 XtSetValues(shellWidget, args, 1);
800 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
803 //---------------------------------------------------------------------------------------------------------
804 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
807 #define CW_USEDEFAULT (1<<31)
808 #define ICS_TEXT_MENU_SIZE 90
809 #define DEBUG_FILE "xboard.debug"
810 #define SetCurrentDirectory chdir
811 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
815 // these two must some day move to frontend.h, when they are implemented
816 Boolean GameListIsUp();
818 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
821 // front-end part of option handling
823 // [HGM] This platform-dependent table provides the location for storing the color info
824 extern char *crWhite, * crBlack;
828 &appData.whitePieceColor,
829 &appData.blackPieceColor,
830 &appData.lightSquareColor,
831 &appData.darkSquareColor,
832 &appData.highlightSquareColor,
833 &appData.premoveHighlightColor,
834 &appData.lowTimeWarningColor,
845 // [HGM] font: keep a font for each square size, even non-stndard ones
848 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
849 char *fontTable[NUM_FONTS][MAX_SIZE];
852 ParseFont (char *name, int number)
853 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
855 if(sscanf(name, "size%d:", &size)) {
856 // [HGM] font: font is meant for specific boardSize (likely from settings file);
857 // defer processing it until we know if it matches our board size
858 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
859 fontTable[number][size] = strdup(strchr(name, ':')+1);
860 fontValid[number][size] = True;
865 case 0: // CLOCK_FONT
866 appData.clockFont = strdup(name);
868 case 1: // MESSAGE_FONT
869 appData.font = strdup(name);
871 case 2: // COORD_FONT
872 appData.coordFont = strdup(name);
877 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
882 { // only 2 fonts currently
883 appData.clockFont = CLOCK_FONT_NAME;
884 appData.coordFont = COORD_FONT_NAME;
885 appData.font = DEFAULT_FONT_NAME;
890 { // no-op, until we identify the code for this already in XBoard and move it here
894 ParseColor (int n, char *name)
895 { // in XBoard, just copy the color-name string
896 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
900 ParseTextAttribs (ColorClass cc, char *s)
902 (&appData.colorShout)[cc] = strdup(s);
906 ParseBoardSize (void *addr, char *name)
908 appData.boardSize = strdup(name);
913 { // In XBoard the sound-playing program takes care of obtaining the actual sound
917 SetCommPortDefaults ()
918 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
921 // [HGM] args: these three cases taken out to stay in front-end
923 SaveFontArg (FILE *f, ArgDescriptor *ad)
926 int i, n = (int)(intptr_t)ad->argLoc;
928 case 0: // CLOCK_FONT
929 name = appData.clockFont;
931 case 1: // MESSAGE_FONT
934 case 2: // COORD_FONT
935 name = appData.coordFont;
940 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
941 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
942 fontTable[n][squareSize] = strdup(name);
943 fontValid[n][squareSize] = True;
946 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
947 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
952 { // nothing to do, as the sounds are at all times represented by their text-string names already
956 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
957 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
958 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
962 SaveColor (FILE *f, ArgDescriptor *ad)
963 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
964 if(colorVariable[(int)(intptr_t)ad->argLoc])
965 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
969 SaveBoardSize (FILE *f, char *name, void *addr)
970 { // wrapper to shield back-end from BoardSize & sizeInfo
971 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
975 ParseCommPortSettings (char *s)
976 { // no such option in XBoard (yet)
979 extern Widget engineOutputShell;
983 GetActualPlacement (Widget wg, WindowPlacement *wp)
988 XWindowAttributes winAt;
995 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
996 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
997 wp->x = rx - winAt.x;
998 wp->y = ry - winAt.y;
999 wp->height = winAt.height;
1000 wp->width = winAt.width;
1001 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
1006 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1007 // In XBoard this will have to wait until awareness of window parameters is implemented
1008 GetActualPlacement(shellWidget, &wpMain);
1009 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1010 if(MoveHistoryIsUp()) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
1011 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1012 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1013 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
1014 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
1018 PrintCommPortSettings (FILE *f, char *name)
1019 { // This option does not exist in XBoard
1023 EnsureOnScreen (int *x, int *y, int minX, int minY)
1030 { // [HGM] args: allows testing if main window is realized from back-end
1031 return xBoardWindow != 0;
1035 PopUpStartupDialog ()
1036 { // start menu not implemented in XBoard
1040 ConvertToLine (int argc, char **argv)
1042 static char line[128*1024], buf[1024];
1046 for(i=1; i<argc; i++)
1048 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1049 && argv[i][0] != '{' )
1050 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1052 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1053 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1056 line[strlen(line)-1] = NULLCHAR;
1060 //--------------------------------------------------------------------------------------------
1063 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1065 #define BoardSize int
1067 InitDrawingSizes (BoardSize boardSize, int flags)
1068 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1069 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1071 XtGeometryResult gres;
1073 static Dimension oldWidth, oldHeight;
1074 static VariantClass oldVariant;
1075 static int oldDual = -1, oldMono = -1;
1077 if(!formWidget) return;
1079 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1080 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1081 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1083 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1085 * Enable shell resizing.
1087 shellArgs[0].value = (XtArgVal) &w;
1088 shellArgs[1].value = (XtArgVal) &h;
1089 XtGetValues(shellWidget, shellArgs, 2);
1091 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1092 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1093 XtSetValues(shellWidget, &shellArgs[2], 4);
1095 XtSetArg(args[0], XtNdefaultDistance, &sep);
1096 XtGetValues(formWidget, args, 1);
1098 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1100 hOffset = boardWidth + 10;
1101 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1102 secondSegments[i] = gridSegments[i];
1103 secondSegments[i].x1 += hOffset;
1104 secondSegments[i].x2 += hOffset;
1107 XtSetArg(args[0], XtNwidth, boardWidth);
1108 XtSetArg(args[1], XtNheight, boardHeight);
1109 XtSetValues(boardWidget, args, 2);
1111 timerWidth = (boardWidth - sep) / 2;
1112 XtSetArg(args[0], XtNwidth, timerWidth);
1113 XtSetValues(whiteTimerWidget, args, 1);
1114 XtSetValues(blackTimerWidget, args, 1);
1116 XawFormDoLayout(formWidget, False);
1118 if (appData.titleInWindow) {
1120 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1121 XtSetArg(args[i], XtNheight, &h); i++;
1122 XtGetValues(titleWidget, args, i);
1124 w = boardWidth - 2*bor;
1126 XtSetArg(args[0], XtNwidth, &w);
1127 XtGetValues(menuBarWidget, args, 1);
1128 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1131 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1132 if (gres != XtGeometryYes && appData.debugMode) {
1134 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1135 programName, gres, w, h, wr, hr);
1139 XawFormDoLayout(formWidget, True);
1142 * Inhibit shell resizing.
1144 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1145 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1146 shellArgs[4].value = shellArgs[2].value = w;
1147 shellArgs[5].value = shellArgs[3].value = h;
1148 XtSetValues(shellWidget, &shellArgs[0], 6);
1150 XSync(xDisplay, False);
1154 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1157 if(gameInfo.variant != oldVariant) { // and only if variant changed
1160 for(i=0; i<4; i++) {
1162 for(p=0; p<=(int)WhiteKing; p++)
1163 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1164 if(gameInfo.variant == VariantShogi) {
1165 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1166 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1167 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1168 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1169 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1172 if(gameInfo.variant == VariantGothic) {
1173 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1176 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1177 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1178 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1181 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1182 for(p=0; p<=(int)WhiteKing; p++)
1183 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1184 if(gameInfo.variant == VariantShogi) {
1185 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1186 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1187 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1188 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1189 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1192 if(gameInfo.variant == VariantGothic) {
1193 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1196 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1197 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1198 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1203 for(i=0; i<2; i++) {
1205 for(p=0; p<=(int)WhiteKing; p++)
1206 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1207 if(gameInfo.variant == VariantShogi) {
1208 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1209 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1210 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1211 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1212 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1215 if(gameInfo.variant == VariantGothic) {
1216 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1219 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1220 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1221 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1225 oldMono = -10; // kludge to force recreation of animation masks
1226 oldVariant = gameInfo.variant;
1229 if(appData.monoMode != oldMono)
1232 oldMono = appData.monoMode;
1237 MakeOneColor (char *name, Pixel *color)
1239 XrmValue vFrom, vTo;
1240 if (!appData.monoMode) {
1241 vFrom.addr = (caddr_t) name;
1242 vFrom.size = strlen(name);
1243 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1244 if (vTo.addr == NULL) {
1245 appData.monoMode = True;
1248 *color = *(Pixel *) vTo.addr;
1256 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1257 int forceMono = False;
1259 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1260 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1261 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1262 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1263 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1264 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1271 { // [HGM] taken out of main
1273 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1274 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1275 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1277 if (appData.bitmapDirectory[0] != NULLCHAR) {
1281 CreateXPMBoard(appData.liteBackTextureFile, 1);
1282 CreateXPMBoard(appData.darkBackTextureFile, 0);
1286 /* Create regular pieces */
1287 if (!useImages) CreatePieces();
1292 InitDrawingParams ()
1294 MakeColors(); CreateGCs(True);
1299 main (int argc, char **argv)
1301 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1302 XSetWindowAttributes window_attributes;
1304 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1305 XrmValue vFrom, vTo;
1306 XtGeometryResult gres;
1309 int forceMono = False;
1311 srandom(time(0)); // [HGM] book: make random truly random
1313 setbuf(stdout, NULL);
1314 setbuf(stderr, NULL);
1317 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1318 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1322 programName = strrchr(argv[0], '/');
1323 if (programName == NULL)
1324 programName = argv[0];
1329 XtSetLanguageProc(NULL, NULL, NULL);
1330 bindtextdomain(PACKAGE, LOCALEDIR);
1331 textdomain(PACKAGE);
1335 XtAppInitialize(&appContext, "XBoard", shellOptions,
1336 XtNumber(shellOptions),
1337 &argc, argv, xboardResources, NULL, 0);
1338 appData.boardSize = "";
1339 InitAppData(ConvertToLine(argc, argv));
1341 if (p == NULL) p = "/tmp";
1342 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1343 gameCopyFilename = (char*) malloc(i);
1344 gamePasteFilename = (char*) malloc(i);
1345 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1346 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1348 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1349 clientResources, XtNumber(clientResources),
1352 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1353 static char buf[MSG_SIZ];
1354 EscapeExpand(buf, appData.firstInitString);
1355 appData.firstInitString = strdup(buf);
1356 EscapeExpand(buf, appData.secondInitString);
1357 appData.secondInitString = strdup(buf);
1358 EscapeExpand(buf, appData.firstComputerString);
1359 appData.firstComputerString = strdup(buf);
1360 EscapeExpand(buf, appData.secondComputerString);
1361 appData.secondComputerString = strdup(buf);
1364 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1367 if (chdir(chessDir) != 0) {
1368 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1374 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1375 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1376 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1377 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1380 setbuf(debugFP, NULL);
1384 if (appData.debugMode) {
1385 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1389 /* [HGM,HR] make sure board size is acceptable */
1390 if(appData.NrFiles > BOARD_FILES ||
1391 appData.NrRanks > BOARD_RANKS )
1392 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1395 /* This feature does not work; animation needs a rewrite */
1396 appData.highlightDragging = FALSE;
1400 xDisplay = XtDisplay(shellWidget);
1401 xScreen = DefaultScreen(xDisplay);
1402 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1404 gameInfo.variant = StringToVariant(appData.variant);
1405 InitPosition(FALSE);
1408 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1410 if (isdigit(appData.boardSize[0])) {
1411 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1412 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1413 &fontPxlSize, &smallLayout, &tinyLayout);
1415 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1416 programName, appData.boardSize);
1420 /* Find some defaults; use the nearest known size */
1421 SizeDefaults *szd, *nearest;
1422 int distance = 99999;
1423 nearest = szd = sizeDefaults;
1424 while (szd->name != NULL) {
1425 if (abs(szd->squareSize - squareSize) < distance) {
1427 distance = abs(szd->squareSize - squareSize);
1428 if (distance == 0) break;
1432 if (i < 2) lineGap = nearest->lineGap;
1433 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1434 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1435 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1436 if (i < 6) smallLayout = nearest->smallLayout;
1437 if (i < 7) tinyLayout = nearest->tinyLayout;
1440 SizeDefaults *szd = sizeDefaults;
1441 if (*appData.boardSize == NULLCHAR) {
1442 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1443 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1446 if (szd->name == NULL) szd--;
1447 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1449 while (szd->name != NULL &&
1450 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1451 if (szd->name == NULL) {
1452 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1453 programName, appData.boardSize);
1457 squareSize = szd->squareSize;
1458 lineGap = szd->lineGap;
1459 clockFontPxlSize = szd->clockFontPxlSize;
1460 coordFontPxlSize = szd->coordFontPxlSize;
1461 fontPxlSize = szd->fontPxlSize;
1462 smallLayout = szd->smallLayout;
1463 tinyLayout = szd->tinyLayout;
1464 // [HGM] font: use defaults from settings file if available and not overruled
1466 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1467 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1468 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1469 appData.font = fontTable[MESSAGE_FONT][squareSize];
1470 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1471 appData.coordFont = fontTable[COORD_FONT][squareSize];
1473 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1474 if (strlen(appData.pixmapDirectory) > 0) {
1475 p = ExpandPathName(appData.pixmapDirectory);
1477 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1478 appData.pixmapDirectory);
1481 if (appData.debugMode) {
1482 fprintf(stderr, _("\
1483 XBoard square size (hint): %d\n\
1484 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1486 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1487 if (appData.debugMode) {
1488 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1491 defaultLineGap = lineGap;
1492 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1494 /* [HR] height treated separately (hacked) */
1495 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1496 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1497 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1498 XtSetArg(boardArgs[2], XtNheight, boardHeight);
1501 * Determine what fonts to use.
1504 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1505 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1506 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1507 fontSet = CreateFontSet(appData.font);
1508 clockFontSet = CreateFontSet(appData.clockFont);
1510 /* For the coordFont, use the 0th font of the fontset. */
1511 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1512 XFontStruct **font_struct_list;
1513 XFontSetExtents *fontSize;
1514 char **font_name_list;
1515 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1516 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1517 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1518 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1519 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1522 appData.font = FindFont(appData.font, fontPxlSize);
1523 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1524 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1525 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1526 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1527 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1528 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1530 countFontID = coordFontID; // [HGM] holdings
1531 countFontStruct = coordFontStruct;
1533 xdb = XtDatabase(xDisplay);
1535 XrmPutLineResource(&xdb, "*international: True");
1536 vTo.size = sizeof(XFontSet);
1537 vTo.addr = (XtPointer) &fontSet;
1538 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1540 XrmPutStringResource(&xdb, "*font", appData.font);
1544 * Detect if there are not enough colors available and adapt.
1546 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1547 appData.monoMode = True;
1550 forceMono = MakeColors();
1553 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1555 appData.monoMode = True;
1558 if (appData.lowTimeWarning && !appData.monoMode) {
1559 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1560 vFrom.size = strlen(appData.lowTimeWarningColor);
1561 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1562 if (vTo.addr == NULL)
1563 appData.monoMode = True;
1565 lowTimeWarningColor = *(Pixel *) vTo.addr;
1568 if (appData.monoMode && appData.debugMode) {
1569 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1570 (unsigned long) XWhitePixel(xDisplay, xScreen),
1571 (unsigned long) XBlackPixel(xDisplay, xScreen));
1574 ParseIcsTextColors();
1576 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1582 layoutName = "tinyLayout";
1583 } else if (smallLayout) {
1584 layoutName = "smallLayout";
1586 layoutName = "normalLayout";
1588 /* Outer layoutWidget is there only to provide a name for use in
1589 resources that depend on the layout style */
1591 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
1592 layoutArgs, XtNumber(layoutArgs));
1594 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
1595 formArgs, XtNumber(formArgs));
1596 XtSetArg(args[0], XtNdefaultDistance, &sep);
1597 XtGetValues(formWidget, args, 1);
1600 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
1601 XtSetArg(args[0], XtNtop, XtChainTop);
1602 XtSetArg(args[1], XtNbottom, XtChainTop);
1603 XtSetArg(args[2], XtNright, XtChainLeft);
1604 XtSetValues(menuBarWidget, args, 3);
1606 widgetList[j++] = whiteTimerWidget =
1607 XtCreateWidget("whiteTime", labelWidgetClass,
1608 formWidget, timerArgs, XtNumber(timerArgs));
1610 XtSetArg(args[0], XtNfontSet, clockFontSet);
1612 XtSetArg(args[0], XtNfont, clockFontStruct);
1614 XtSetArg(args[1], XtNtop, XtChainTop);
1615 XtSetArg(args[2], XtNbottom, XtChainTop);
1616 XtSetValues(whiteTimerWidget, args, 3);
1618 widgetList[j++] = blackTimerWidget =
1619 XtCreateWidget("blackTime", labelWidgetClass,
1620 formWidget, timerArgs, XtNumber(timerArgs));
1622 XtSetArg(args[0], XtNfontSet, clockFontSet);
1624 XtSetArg(args[0], XtNfont, clockFontStruct);
1626 XtSetArg(args[1], XtNtop, XtChainTop);
1627 XtSetArg(args[2], XtNbottom, XtChainTop);
1628 XtSetValues(blackTimerWidget, args, 3);
1630 if (appData.titleInWindow) {
1631 widgetList[j++] = titleWidget =
1632 XtCreateWidget("title", labelWidgetClass, formWidget,
1633 titleArgs, XtNumber(titleArgs));
1634 XtSetArg(args[0], XtNtop, XtChainTop);
1635 XtSetArg(args[1], XtNbottom, XtChainTop);
1636 XtSetValues(titleWidget, args, 2);
1639 if (appData.showButtonBar) {
1640 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
1641 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
1642 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
1643 XtSetArg(args[2], XtNtop, XtChainTop);
1644 XtSetArg(args[3], XtNbottom, XtChainTop);
1645 XtSetValues(buttonBarWidget, args, 4);
1648 widgetList[j++] = messageWidget =
1649 XtCreateWidget("message", labelWidgetClass, formWidget,
1650 messageArgs, XtNumber(messageArgs));
1651 XtSetArg(args[0], XtNtop, XtChainTop);
1652 XtSetArg(args[1], XtNbottom, XtChainTop);
1653 XtSetValues(messageWidget, args, 2);
1655 widgetList[j++] = boardWidget =
1656 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
1657 XtNumber(boardArgs));
1659 XtManageChildren(widgetList, j);
1661 timerWidth = (boardWidth - sep) / 2;
1662 XtSetArg(args[0], XtNwidth, timerWidth);
1663 XtSetValues(whiteTimerWidget, args, 1);
1664 XtSetValues(blackTimerWidget, args, 1);
1666 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1667 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1668 XtGetValues(whiteTimerWidget, args, 2);
1670 if (appData.showButtonBar) {
1671 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1672 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1673 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
1677 * formWidget uses these constraints but they are stored
1681 XtSetArg(args[i], XtNfromHoriz, 0); i++;
1682 XtSetValues(menuBarWidget, args, i);
1683 if (appData.titleInWindow) {
1686 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1687 XtSetValues(whiteTimerWidget, args, i);
1689 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1690 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1691 XtSetValues(blackTimerWidget, args, i);
1693 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1694 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
1695 XtSetValues(titleWidget, args, i);
1697 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1698 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1699 XtSetValues(messageWidget, args, i);
1700 if (appData.showButtonBar) {
1702 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1703 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1704 XtSetValues(buttonBarWidget, args, i);
1708 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1709 XtSetValues(whiteTimerWidget, args, i);
1711 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1712 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1713 XtSetValues(blackTimerWidget, args, i);
1715 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
1716 XtSetValues(titleWidget, args, i);
1718 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1719 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1720 XtSetValues(messageWidget, args, i);
1721 if (appData.showButtonBar) {
1723 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1724 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1725 XtSetValues(buttonBarWidget, args, i);
1730 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1731 XtSetValues(whiteTimerWidget, args, i);
1733 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1734 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1735 XtSetValues(blackTimerWidget, args, i);
1737 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1738 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1739 XtSetValues(messageWidget, args, i);
1740 if (appData.showButtonBar) {
1742 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1743 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1744 XtSetValues(buttonBarWidget, args, i);
1748 XtSetArg(args[0], XtNfromVert, messageWidget);
1749 XtSetArg(args[1], XtNtop, XtChainTop);
1750 XtSetArg(args[2], XtNbottom, XtChainBottom);
1751 XtSetArg(args[3], XtNleft, XtChainLeft);
1752 XtSetArg(args[4], XtNright, XtChainRight);
1753 XtSetValues(boardWidget, args, 5);
1755 XtRealizeWidget(shellWidget);
1758 XtSetArg(args[0], XtNx, wpMain.x);
1759 XtSetArg(args[1], XtNy, wpMain.y);
1760 XtSetValues(shellWidget, args, 2);
1764 * Correct the width of the message and title widgets.
1765 * It is not known why some systems need the extra fudge term.
1766 * The value "2" is probably larger than needed.
1768 XawFormDoLayout(formWidget, False);
1770 #define WIDTH_FUDGE 2
1772 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1773 XtSetArg(args[i], XtNheight, &h); i++;
1774 XtGetValues(messageWidget, args, i);
1775 if (appData.showButtonBar) {
1777 XtSetArg(args[i], XtNwidth, &w); i++;
1778 XtGetValues(buttonBarWidget, args, i);
1779 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1781 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
1784 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1785 if (gres != XtGeometryYes && appData.debugMode) {
1786 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1787 programName, gres, w, h, wr, hr);
1790 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
1791 /* The size used for the child widget in layout lags one resize behind
1792 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
1794 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1795 if (gres != XtGeometryYes && appData.debugMode) {
1796 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1797 programName, gres, w, h, wr, hr);
1800 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
1801 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
1802 XtSetArg(args[1], XtNright, XtChainRight);
1803 XtSetValues(messageWidget, args, 2);
1805 if (appData.titleInWindow) {
1807 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1808 XtSetArg(args[i], XtNheight, &h); i++;
1809 XtGetValues(titleWidget, args, i);
1811 w = boardWidth - 2*bor;
1813 XtSetArg(args[0], XtNwidth, &w);
1814 XtGetValues(menuBarWidget, args, 1);
1815 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1818 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1819 if (gres != XtGeometryYes && appData.debugMode) {
1821 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1822 programName, gres, w, h, wr, hr);
1825 XawFormDoLayout(formWidget, True);
1827 xBoardWindow = XtWindow(boardWidget);
1829 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1830 // not need to go into InitDrawingSizes().
1834 * Create X checkmark bitmap and initialize option menu checks.
1836 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1837 checkmark_bits, checkmark_width, checkmark_height);
1843 ReadBitmap(&wIconPixmap, "icon_white.bm",
1844 icon_white_bits, icon_white_width, icon_white_height);
1845 ReadBitmap(&bIconPixmap, "icon_black.bm",
1846 icon_black_bits, icon_black_width, icon_black_height);
1847 iconPixmap = wIconPixmap;
1849 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1850 XtSetValues(shellWidget, args, i);
1853 * Create a cursor for the board widget.
1855 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1856 XChangeWindowAttributes(xDisplay, xBoardWindow,
1857 CWCursor, &window_attributes);
1860 * Inhibit shell resizing.
1862 shellArgs[0].value = (XtArgVal) &w;
1863 shellArgs[1].value = (XtArgVal) &h;
1864 XtGetValues(shellWidget, shellArgs, 2);
1865 shellArgs[4].value = shellArgs[2].value = w;
1866 shellArgs[5].value = shellArgs[3].value = h;
1867 XtSetValues(shellWidget, &shellArgs[2], 4);
1868 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1869 marginH = h - boardHeight;
1871 CatchDeleteWindow(shellWidget, "QuitProc");
1879 if (appData.animate || appData.animateDragging)
1882 XtAugmentTranslations(formWidget,
1883 XtParseTranslationTable(globalTranslations));
1884 XtAugmentTranslations(boardWidget,
1885 XtParseTranslationTable(boardTranslations));
1886 XtAugmentTranslations(whiteTimerWidget,
1887 XtParseTranslationTable(whiteTranslations));
1888 XtAugmentTranslations(blackTimerWidget,
1889 XtParseTranslationTable(blackTranslations));
1891 /* Why is the following needed on some versions of X instead
1892 * of a translation? */
1893 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
1894 (XtEventHandler) EventProc, NULL);
1896 XtAddEventHandler(formWidget, KeyPressMask, False,
1897 (XtEventHandler) MoveTypeInProc, NULL);
1898 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1899 (XtEventHandler) EventProc, NULL);
1901 /* [AS] Restore layout */
1902 if( wpMoveHistory.visible ) {
1906 if( wpEvalGraph.visible )
1911 if( wpEngineOutput.visible ) {
1912 EngineOutputPopUp();
1917 if (errorExitStatus == -1) {
1918 if (appData.icsActive) {
1919 /* We now wait until we see "login:" from the ICS before
1920 sending the logon script (problems with timestamp otherwise) */
1921 /*ICSInitScript();*/
1922 if (appData.icsInputBox) ICSInputBoxPopUp();
1926 signal(SIGWINCH, TermSizeSigHandler);
1928 signal(SIGINT, IntSigHandler);
1929 signal(SIGTERM, IntSigHandler);
1930 if (*appData.cmailGameName != NULLCHAR) {
1931 signal(SIGUSR1, CmailSigHandler);
1935 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1937 // XtSetKeyboardFocus(shellWidget, formWidget);
1938 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1940 XtAppMainLoop(appContext);
1941 if (appData.debugMode) fclose(debugFP); // [DM] debug
1946 TermSizeSigHandler (int sig)
1952 IntSigHandler (int sig)
1958 CmailSigHandler (int sig)
1963 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1965 /* Activate call-back function CmailSigHandlerCallBack() */
1966 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1968 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1972 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1975 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1977 /**** end signal code ****/
1980 #define Abs(n) ((n)<0 ? -(n) : (n))
1984 InsertPxlSize (char *pattern, int targetPxlSize)
1986 char *base_fnt_lst, strInt[12], *p, *q;
1987 int alternatives, i, len, strIntLen;
1990 * Replace the "*" (if present) in the pixel-size slot of each
1991 * alternative with the targetPxlSize.
1995 while ((p = strchr(p, ',')) != NULL) {
1999 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
2000 strIntLen = strlen(strInt);
2001 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
2005 while (alternatives--) {
2006 char *comma = strchr(p, ',');
2007 for (i=0; i<14; i++) {
2008 char *hyphen = strchr(p, '-');
2010 if (comma && hyphen > comma) break;
2011 len = hyphen + 1 - p;
2012 if (i == 7 && *p == '*' && len == 2) {
2014 memcpy(q, strInt, strIntLen);
2024 len = comma + 1 - p;
2031 return base_fnt_lst;
2035 CreateFontSet (char *base_fnt_lst)
2038 char **missing_list;
2042 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
2043 &missing_list, &missing_count, &def_string);
2044 if (appData.debugMode) {
2046 XFontStruct **font_struct_list;
2047 char **font_name_list;
2048 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
2050 fprintf(debugFP, " got list %s, locale %s\n",
2051 XBaseFontNameListOfFontSet(fntSet),
2052 XLocaleOfFontSet(fntSet));
2053 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
2054 for (i = 0; i < count; i++) {
2055 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
2058 for (i = 0; i < missing_count; i++) {
2059 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
2062 if (fntSet == NULL) {
2063 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
2068 #else // not ENABLE_NLS
2070 * Find a font that matches "pattern" that is as close as
2071 * possible to the targetPxlSize. Prefer fonts that are k
2072 * pixels smaller to fonts that are k pixels larger. The
2073 * pattern must be in the X Consortium standard format,
2074 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2075 * The return value should be freed with XtFree when no
2079 FindFont (char *pattern, int targetPxlSize)
2081 char **fonts, *p, *best, *scalable, *scalableTail;
2082 int i, j, nfonts, minerr, err, pxlSize;
2084 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2086 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2087 programName, pattern);
2094 for (i=0; i<nfonts; i++) {
2097 if (*p != '-') continue;
2099 if (*p == NULLCHAR) break;
2100 if (*p++ == '-') j++;
2102 if (j < 7) continue;
2105 scalable = fonts[i];
2108 err = pxlSize - targetPxlSize;
2109 if (Abs(err) < Abs(minerr) ||
2110 (minerr > 0 && err < 0 && -err == minerr)) {
2116 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2117 /* If the error is too big and there is a scalable font,
2118 use the scalable font. */
2119 int headlen = scalableTail - scalable;
2120 p = (char *) XtMalloc(strlen(scalable) + 10);
2121 while (isdigit(*scalableTail)) scalableTail++;
2122 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2124 p = (char *) XtMalloc(strlen(best) + 2);
2125 safeStrCpy(p, best, strlen(best)+1 );
2127 if (appData.debugMode) {
2128 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2129 pattern, targetPxlSize, p);
2131 XFreeFontNames(fonts);
2138 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
2139 // must be called before all non-first callse to CreateGCs()
2140 XtReleaseGC(shellWidget, highlineGC);
2141 XtReleaseGC(shellWidget, lightSquareGC);
2142 XtReleaseGC(shellWidget, darkSquareGC);
2143 XtReleaseGC(shellWidget, lineGC);
2144 if (appData.monoMode) {
2145 if (DefaultDepth(xDisplay, xScreen) == 1) {
2146 XtReleaseGC(shellWidget, wbPieceGC);
2148 XtReleaseGC(shellWidget, bwPieceGC);
2151 XtReleaseGC(shellWidget, prelineGC);
2152 XtReleaseGC(shellWidget, wdPieceGC);
2153 XtReleaseGC(shellWidget, wlPieceGC);
2154 XtReleaseGC(shellWidget, bdPieceGC);
2155 XtReleaseGC(shellWidget, blPieceGC);
2160 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
2162 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2163 | GCBackground | GCFunction | GCPlaneMask;
2164 gc_values->foreground = foreground;
2165 gc_values->background = background;
2166 return XtGetGC(shellWidget, value_mask, gc_values);
2170 CreateGCs (int redo)
2172 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2173 | GCBackground | GCFunction | GCPlaneMask;
2174 XGCValues gc_values;
2176 Pixel white = XWhitePixel(xDisplay, xScreen);
2177 Pixel black = XBlackPixel(xDisplay, xScreen);
2179 gc_values.plane_mask = AllPlanes;
2180 gc_values.line_width = lineGap;
2181 gc_values.line_style = LineSolid;
2182 gc_values.function = GXcopy;
2185 DeleteGCs(); // called a second time; clean up old GCs first
2186 } else { // [HGM] grid and font GCs created on first call only
2187 coordGC = CreateOneGC(&gc_values, black, white);
2188 XSetFont(xDisplay, coordGC, coordFontID);
2190 // [HGM] make font for holdings counts (white on black)
2191 countGC = CreateOneGC(&gc_values, white, black);
2192 XSetFont(xDisplay, countGC, countFontID);
2194 lineGC = CreateOneGC(&gc_values, black, black);
2196 if (appData.monoMode) {
2198 highlineGC = CreateOneGC(&gc_values, white, white);
2199 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
2200 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
2202 if (DefaultDepth(xDisplay, xScreen) == 1) {
2203 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2204 gc_values.function = GXcopyInverted;
2205 copyInvertedGC = CreateOneGC(&gc_values, black, white);
2206 gc_values.function = GXcopy;
2207 if (XBlackPixel(xDisplay, xScreen) == 1) {
2208 bwPieceGC = darkSquareGC;
2209 wbPieceGC = copyInvertedGC;
2211 bwPieceGC = copyInvertedGC;
2212 wbPieceGC = lightSquareGC;
2217 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
2218 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
2219 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
2220 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
2221 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
2222 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
2223 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
2224 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
2229 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
2237 fp = fopen(filename, "rb");
2239 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
2246 for (y=0; y<h; ++y) {
2247 for (x=0; x<h; ++x) {
2252 XPutPixel(xim, x, y, blackPieceColor);
2254 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2257 XPutPixel(xim, x, y, darkSquareColor);
2259 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2262 XPutPixel(xim, x, y, whitePieceColor);
2264 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2267 XPutPixel(xim, x, y, lightSquareColor);
2269 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2277 /* create Pixmap of piece */
2278 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2280 XPutImage(xDisplay, *dest, lightSquareGC, xim,
2283 /* create Pixmap of clipmask
2284 Note: We assume the white/black pieces have the same
2285 outline, so we make only 6 masks. This is okay
2286 since the XPM clipmask routines do the same. */
2288 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2290 XPutImage(xDisplay, temp, lightSquareGC, xmask,
2293 /* now create the 1-bit version */
2294 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2297 values.foreground = 1;
2298 values.background = 0;
2300 /* Don't use XtGetGC, not read only */
2301 maskGC = XCreateGC(xDisplay, *mask,
2302 GCForeground | GCBackground, &values);
2303 XCopyPlane(xDisplay, temp, *mask, maskGC,
2304 0, 0, squareSize, squareSize, 0, 0, 1);
2305 XFreePixmap(xDisplay, temp);
2310 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
2318 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
2323 /* The XSynchronize calls were copied from CreatePieces.
2324 Not sure if needed, but can't hurt */
2325 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2328 /* temp needed by loadXIM() */
2329 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2330 0, 0, ss, ss, AllPlanes, XYPixmap);
2332 if (strlen(appData.pixmapDirectory) == 0) {
2336 if (appData.monoMode) {
2337 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
2341 fprintf(stderr, _("\nLoading XIMs...\n"));
2343 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2344 fprintf(stderr, "%d", piece+1);
2345 for (kind=0; kind<4; kind++) {
2346 fprintf(stderr, ".");
2347 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2348 ExpandPathName(appData.pixmapDirectory),
2349 piece <= (int) WhiteKing ? "" : "w",
2350 pieceBitmapNames[piece],
2352 ximPieceBitmap[kind][piece] =
2353 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2354 0, 0, ss, ss, AllPlanes, XYPixmap);
2355 if (appData.debugMode)
2356 fprintf(stderr, _("(File:%s:) "), buf);
2357 loadXIM(ximPieceBitmap[kind][piece],
2359 &(xpmPieceBitmap2[kind][piece]),
2360 &(ximMaskPm2[piece]));
2361 if(piece <= (int)WhiteKing)
2362 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2364 fprintf(stderr," ");
2366 /* Load light and dark squares */
2367 /* If the LSQ and DSQ pieces don't exist, we will
2368 draw them with solid squares. */
2369 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2370 if (access(buf, 0) != 0) {
2374 fprintf(stderr, _("light square "));
2376 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2377 0, 0, ss, ss, AllPlanes, XYPixmap);
2378 if (appData.debugMode)
2379 fprintf(stderr, _("(File:%s:) "), buf);
2381 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2382 fprintf(stderr, _("dark square "));
2383 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2384 ExpandPathName(appData.pixmapDirectory), ss);
2385 if (appData.debugMode)
2386 fprintf(stderr, _("(File:%s:) "), buf);
2388 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2389 0, 0, ss, ss, AllPlanes, XYPixmap);
2390 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2391 xpmJailSquare = xpmLightSquare;
2393 fprintf(stderr, _("Done.\n"));
2395 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2398 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2402 CreateXPMBoard (char *s, int kind)
2406 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2407 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2408 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2414 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2415 // thisroutine has to be called t free the old piece pixmaps
2417 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2418 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2420 XFreePixmap(xDisplay, xpmLightSquare);
2421 XFreePixmap(xDisplay, xpmDarkSquare);
2430 u_int ss = squareSize;
2432 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2433 XpmColorSymbol symbols[4];
2434 static int redo = False;
2436 if(redo) FreeXPMPieces(); else redo = 1;
2438 /* The XSynchronize calls were copied from CreatePieces.
2439 Not sure if needed, but can't hurt */
2440 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2442 /* Setup translations so piece colors match square colors */
2443 symbols[0].name = "light_piece";
2444 symbols[0].value = appData.whitePieceColor;
2445 symbols[1].name = "dark_piece";
2446 symbols[1].value = appData.blackPieceColor;
2447 symbols[2].name = "light_square";
2448 symbols[2].value = appData.lightSquareColor;
2449 symbols[3].name = "dark_square";
2450 symbols[3].value = appData.darkSquareColor;
2452 attr.valuemask = XpmColorSymbols;
2453 attr.colorsymbols = symbols;
2454 attr.numsymbols = 4;
2456 if (appData.monoMode) {
2457 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2461 if (strlen(appData.pixmapDirectory) == 0) {
2462 XpmPieces* pieces = builtInXpms;
2465 while (pieces->size != squareSize && pieces->size) pieces++;
2466 if (!pieces->size) {
2467 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2470 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2471 for (kind=0; kind<4; kind++) {
2473 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2474 pieces->xpm[piece][kind],
2475 &(xpmPieceBitmap2[kind][piece]),
2476 NULL, &attr)) != 0) {
2477 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2481 if(piece <= (int) WhiteKing)
2482 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2486 xpmJailSquare = xpmLightSquare;
2490 fprintf(stderr, _("\nLoading XPMs...\n"));
2493 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2494 fprintf(stderr, "%d ", piece+1);
2495 for (kind=0; kind<4; kind++) {
2496 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2497 ExpandPathName(appData.pixmapDirectory),
2498 piece > (int) WhiteKing ? "w" : "",
2499 pieceBitmapNames[piece],
2501 if (appData.debugMode) {
2502 fprintf(stderr, _("(File:%s:) "), buf);
2504 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2505 &(xpmPieceBitmap2[kind][piece]),
2506 NULL, &attr)) != 0) {
2507 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2508 // [HGM] missing: read of unorthodox piece failed; substitute King.
2509 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2510 ExpandPathName(appData.pixmapDirectory),
2512 if (appData.debugMode) {
2513 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2515 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2516 &(xpmPieceBitmap2[kind][piece]),
2520 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2525 if(piece <= (int) WhiteKing)
2526 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2529 /* Load light and dark squares */
2530 /* If the LSQ and DSQ pieces don't exist, we will
2531 draw them with solid squares. */
2532 fprintf(stderr, _("light square "));
2533 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2534 if (access(buf, 0) != 0) {
2538 if (appData.debugMode)
2539 fprintf(stderr, _("(File:%s:) "), buf);
2541 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2542 &xpmLightSquare, NULL, &attr)) != 0) {
2543 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2546 fprintf(stderr, _("dark square "));
2547 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2548 ExpandPathName(appData.pixmapDirectory), ss);
2549 if (appData.debugMode) {
2550 fprintf(stderr, _("(File:%s:) "), buf);
2552 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2553 &xpmDarkSquare, NULL, &attr)) != 0) {
2554 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2558 xpmJailSquare = xpmLightSquare;
2559 fprintf(stderr, _("Done.\n"));
2561 oldVariant = -1; // kludge to force re-makig of animation masks
2562 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2565 #endif /* HAVE_LIBXPM */
2568 /* No built-in bitmaps */
2573 u_int ss = squareSize;
2575 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2578 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2579 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2580 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2581 pieceBitmapNames[piece],
2582 ss, kind == SOLID ? 's' : 'o');
2583 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2584 if(piece <= (int)WhiteKing)
2585 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2589 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2593 /* With built-in bitmaps */
2597 BuiltInBits* bib = builtInBits;
2600 u_int ss = squareSize;
2602 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2605 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2607 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2608 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2609 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2610 pieceBitmapNames[piece],
2611 ss, kind == SOLID ? 's' : 'o');
2612 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2613 bib->bits[kind][piece], ss, ss);
2614 if(piece <= (int)WhiteKing)
2615 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2619 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2625 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2630 char msg[MSG_SIZ], fullname[MSG_SIZ];
2632 if (*appData.bitmapDirectory != NULLCHAR) {
2633 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2634 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2635 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2636 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2637 &w, &h, pm, &x_hot, &y_hot);
2638 fprintf(stderr, "load %s\n", name);
2639 if (errcode != BitmapSuccess) {
2641 case BitmapOpenFailed:
2642 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2644 case BitmapFileInvalid:
2645 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2647 case BitmapNoMemory:
2648 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2652 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2656 fprintf(stderr, _("%s: %s...using built-in\n"),
2658 } else if (w != wreq || h != hreq) {
2660 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2661 programName, fullname, w, h, wreq, hreq);
2667 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2677 if (lineGap == 0) return;
2679 /* [HR] Split this into 2 loops for non-square boards. */
2681 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2682 gridSegments[i].x1 = 0;
2683 gridSegments[i].x2 =
2684 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2685 gridSegments[i].y1 = gridSegments[i].y2
2686 = lineGap / 2 + (i * (squareSize + lineGap));
2689 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2690 gridSegments[j + i].y1 = 0;
2691 gridSegments[j + i].y2 =
2692 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2693 gridSegments[j + i].x1 = gridSegments[j + i].x2
2694 = lineGap / 2 + (j * (squareSize + lineGap));
2698 int nrOfMenuItems = 7;
2699 Widget menuWidget[150];
2700 MenuListItem menuItemList[150] = {
2701 { "LoadNextGameProc", LoadNextGameProc },
2702 { "LoadPrevGameProc", LoadPrevGameProc },
2703 { "ReloadGameProc", ReloadGameProc },
2704 { "ReloadPositionProc", ReloadPositionProc },
2705 #ifndef OPTIONSDIALOG
2706 { "AlwaysQueenProc", AlwaysQueenProc },
2707 { "AnimateDraggingProc", AnimateDraggingProc },
2708 { "AnimateMovingProc", AnimateMovingProc },
2709 { "AutoflagProc", AutoflagProc },
2710 { "AutoflipProc", AutoflipProc },
2711 { "BlindfoldProc", BlindfoldProc },
2712 { "FlashMovesProc", FlashMovesProc },
2714 { "HighlightDraggingProc", HighlightDraggingProc },
2716 { "HighlightLastMoveProc", HighlightLastMoveProc },
2717 // { "IcsAlarmProc", IcsAlarmProc },
2718 { "MoveSoundProc", MoveSoundProc },
2719 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
2720 { "PopupExitMessageProc", PopupExitMessageProc },
2721 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
2722 // { "PremoveProc", PremoveProc },
2723 { "ShowCoordsProc", ShowCoordsProc },
2724 { "ShowThinkingProc", ShowThinkingProc },
2725 { "HideThinkingProc", HideThinkingProc },
2726 { "TestLegalityProc", TestLegalityProc },
2728 { "AboutGameProc", AboutGameEvent },
2729 { "DebugProc", DebugProc },
2730 { "NothingProc", NothingProc },
2735 MarkMenuItem (char *menuRef, int state)
2737 int nr = MenuToNumber(menuRef);
2740 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2741 XtSetValues(menuWidget[nr], args, 1);
2746 EnableMenuItem (char *menuRef, int state)
2748 int nr = MenuToNumber(menuRef);
2749 if(nr >= 0) XtSetSensitive(menuWidget[nr], state);
2753 EnableButtonBar (int state)
2755 XtSetSensitive(buttonBarWidget, state);
2760 SetMenuEnables (Enables *enab)
2762 while (enab->name != NULL) {
2763 EnableMenuItem(enab->name, enab->value);
2769 Equal(char *p, char *s)
2770 { // compare strings skipping spaces in second
2772 if(*s == ' ') { s++; continue; }
2773 if(*s++ != *p++) return 0;
2779 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2780 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2782 if(*nprms == 0) return;
2783 for(i=0; menuItemList[i].name; i++) {
2784 if(Equal(prms[0], menuItemList[i].name)) {
2785 (menuItemList[i].proc) ();
2792 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
2794 MenuProc *proc = (MenuProc *) addr;
2800 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2802 RecentEngineEvent((int) (intptr_t) addr);
2805 // some stuff that must remain in front-end
2806 static Widget mainBar, currentMenu;
2807 static int wtot, nr = 0, widths[10];
2810 AppendMenuItem (char *text, char *name, MenuProc *action)
2817 XtSetArg(args[j], XtNleftMargin, 20); j++;
2818 XtSetArg(args[j], XtNrightMargin, 20); j++;
2820 if (strcmp(text, "----") == 0) {
2821 entry = XtCreateManagedWidget(text, smeLineObjectClass,
2822 currentMenu, args, j);
2824 XtSetArg(args[j], XtNlabel, XtNewString(_(text)));
2825 entry = XtCreateManagedWidget(name, smeBSBObjectClass,
2826 currentMenu, args, j+1);
2827 XtAddCallback(entry, XtNcallback,
2828 (XtCallbackProc) (strcmp(name, "recent") ? MenuBarSelect : MenuEngineSelect),
2830 menuWidget[nrOfMenuItems] = entry;
2835 CreateMenuButton (char *name, Menu *mb)
2836 { // create menu button on main bar, and shell for pull-down list
2842 XtSetArg(args[j], XtNmenuName, XtNewString(name)); j++;
2843 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
2844 XtSetArg(args[j], XtNborderWidth, 0); j++;
2845 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
2847 currentMenu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2850 XtSetArg(args[j], XtNwidth, &w); j++;
2851 XtGetValues(mb->subMenu, args, j);
2852 wtot += mb->textWidth = widths[nr++] = w;
2856 CreateMenuBar (Menu *mb, int boardWidth)
2860 char menuName[MSG_SIZ];
2864 // create bar itself
2866 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2867 XtSetArg(args[j], XtNvSpace, 0); j++;
2868 XtSetArg(args[j], XtNborderWidth, 0); j++;
2869 mainBar = XtCreateWidget("menuBar", boxWidgetClass,
2870 formWidget, args, j);
2872 CreateMainMenus(mb); // put menus in bar according to description in back-end
2874 // size buttons to make menu bar fit, clipping menu names where necessary
2875 while(wtot > boardWidth - 40) {
2877 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
2881 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
2883 XtSetArg(args[j], XtNwidth, widths[i]); j++;
2884 XtSetValues(ma[i].subMenu, args, j);
2891 CreateButtonBar (MenuItem *mi)
2894 Widget button, buttonBar;
2898 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2900 XtSetArg(args[j], XtNhSpace, 0); j++;
2902 XtSetArg(args[j], XtNborderWidth, 0); j++;
2903 XtSetArg(args[j], XtNvSpace, 0); j++;
2904 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
2905 formWidget, args, j);
2907 while (mi->string != NULL) {
2910 XtSetArg(args[j], XtNinternalWidth, 2); j++;
2911 XtSetArg(args[j], XtNborderWidth, 0); j++;
2913 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
2914 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
2915 buttonBar, args, j);
2916 XtAddCallback(button, XtNcallback,
2917 (XtCallbackProc) MenuBarSelect,
2918 (caddr_t) mi->proc);
2925 CreatePieceMenu (char *name, int color)
2930 ChessSquare selection;
2932 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2933 boardWidget, args, 0);
2935 for (i = 0; i < PIECE_MENU_SIZE; i++) {
2936 String item = pieceMenuStrings[color][i];
2938 if (strcmp(item, "----") == 0) {
2939 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2942 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2943 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2945 selection = pieceMenuTranslation[color][i];
2946 XtAddCallback(entry, XtNcallback,
2947 (XtCallbackProc) PieceMenuSelect,
2948 (caddr_t) selection);
2949 if (selection == WhitePawn || selection == BlackPawn) {
2950 XtSetArg(args[0], XtNpopupOnEntry, entry);
2951 XtSetValues(menu, args, 1);
2964 ChessSquare selection;
2966 whitePieceMenu = CreatePieceMenu("menuW", 0);
2967 blackPieceMenu = CreatePieceMenu("menuB", 1);
2969 if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
2970 XtRegisterGrabAction(PieceMenuPopup, True,
2971 (unsigned)(ButtonPressMask|ButtonReleaseMask),
2972 GrabModeAsync, GrabModeAsync);
2974 XtSetArg(args[0], XtNlabel, _("Drop"));
2975 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
2976 boardWidget, args, 1);
2977 for (i = 0; i < DROP_MENU_SIZE; i++) {
2978 String item = dropMenuStrings[i];
2980 if (strcmp(item, "----") == 0) {
2981 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2984 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2985 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2987 selection = dropMenuTranslation[i];
2988 XtAddCallback(entry, XtNcallback,
2989 (XtCallbackProc) DropMenuSelect,
2990 (caddr_t) selection);
3004 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3005 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3006 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3007 dmEnables[i].piece);
3008 XtSetSensitive(entry, p != NULL || !appData.testLegality
3009 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3010 && !appData.icsActive));
3012 while (p && *p++ == dmEnables[i].piece) count++;
3013 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3015 XtSetArg(args[j], XtNlabel, label); j++;
3016 XtSetValues(entry, args, j);
3021 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
3023 String whichMenu; int menuNr = -2;
3024 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3025 if (event->type == ButtonRelease)
3026 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3027 else if (event->type == ButtonPress)
3028 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3030 case 0: whichMenu = params[0]; break;
3031 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3033 case -1: if (errorUp) ErrorPopDown();
3036 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3040 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3042 if (pmFromX < 0 || pmFromY < 0) return;
3043 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3047 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3049 if (pmFromX < 0 || pmFromY < 0) return;
3050 DropMenuEvent(piece, pmFromX, pmFromY);
3054 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3056 shiftKey = prms[0][0] & 1;
3061 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3063 shiftKey = prms[0][0] & 1;
3069 do_flash_delay (unsigned long msec)
3075 DrawBorder (int x, int y, int type)
3079 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
3081 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3082 squareSize+lineGap, squareSize+lineGap);
3086 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
3088 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
3089 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
3091 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
3092 if(textureW[kind] < W*squareSize)
3093 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
3095 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
3096 if(textureH[kind] < H*squareSize)
3097 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
3099 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
3104 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
3105 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
3107 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
3108 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
3109 squareSize, squareSize, x*fac, y*fac);
3111 if (useImages && useImageSqs) {
3115 pm = xpmLightSquare;
3120 case 2: /* neutral */
3122 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
3125 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3126 squareSize, squareSize, x*fac, y*fac);
3136 case 2: /* neutral */
3141 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
3146 I split out the routines to draw a piece so that I could
3147 make a generic flash routine.
3150 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3152 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3153 switch (square_color) {
3155 case 2: /* neutral */
3157 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3158 ? *pieceToOutline(piece)
3159 : *pieceToSolid(piece),
3160 dest, bwPieceGC, 0, 0,
3161 squareSize, squareSize, x, y);
3164 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3165 ? *pieceToSolid(piece)
3166 : *pieceToOutline(piece),
3167 dest, wbPieceGC, 0, 0,
3168 squareSize, squareSize, x, y);
3174 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3176 switch (square_color) {
3178 case 2: /* neutral */
3180 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3181 ? *pieceToOutline(piece)
3182 : *pieceToSolid(piece),
3183 dest, bwPieceGC, 0, 0,
3184 squareSize, squareSize, x, y, 1);
3187 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3188 ? *pieceToSolid(piece)
3189 : *pieceToOutline(piece),
3190 dest, wbPieceGC, 0, 0,
3191 squareSize, squareSize, x, y, 1);
3197 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3199 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
3200 switch (square_color) {
3202 XCopyPlane(xDisplay, *pieceToSolid(piece),
3203 dest, (int) piece < (int) BlackPawn
3204 ? wlPieceGC : blPieceGC, 0, 0,
3205 squareSize, squareSize, x, y, 1);
3208 XCopyPlane(xDisplay, *pieceToSolid(piece),
3209 dest, (int) piece < (int) BlackPawn
3210 ? wdPieceGC : bdPieceGC, 0, 0,
3211 squareSize, squareSize, x, y, 1);
3213 case 2: /* neutral */
3215 break; // should never contain pieces
3220 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3222 int kind, p = piece;
3224 switch (square_color) {
3226 case 2: /* neutral */
3228 if ((int)piece < (int) BlackPawn) {
3236 if ((int)piece < (int) BlackPawn) {
3244 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
3245 if(useTexture & square_color+1) {
3246 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
3247 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
3248 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
3249 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
3250 XSetClipMask(xDisplay, wlPieceGC, None);
3251 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
3253 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3254 dest, wlPieceGC, 0, 0,
3255 squareSize, squareSize, x, y);
3258 typedef void (*DrawFunc)();
3263 if (appData.monoMode) {
3264 if (DefaultDepth(xDisplay, xScreen) == 1) {
3265 return monoDrawPiece_1bit;
3267 return monoDrawPiece;
3271 return colorDrawPieceImage;
3273 return colorDrawPiece;
3278 DrawDot (int marker, int x, int y, int r)
3280 if(appData.monoMode) {
3281 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
3282 x, y, r, r, 0, 64*360);
3283 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
3284 x, y, r, r, 0, 64*360);
3286 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
3287 x, y, r, r, 0, 64*360);
3291 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
3292 { // basic front-end board-draw function: takes care of everything that can be in square:
3293 // piece, background, coordinate/count, marker dot
3294 int direction, font_ascent, font_descent;
3295 XCharStruct overall;
3298 if (piece == EmptySquare) {
3299 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
3301 drawfunc = ChooseDrawFunc();
3302 drawfunc(piece, square_color, x, y, xBoardWindow);
3305 if(align) { // square carries inscription (coord or piece count)
3307 GC hGC = align < 3 ? coordGC : countGC;
3308 // first calculate where it goes
3309 XTextExtents(countFontStruct, string, 1, &direction,
3310 &font_ascent, &font_descent, &overall);
3312 xx += squareSize - overall.width - 2;
3313 yy += squareSize - font_descent - 1;
3314 } else if (align == 2) {
3315 xx += 2, yy += font_ascent + 1;
3316 } else if (align == 3) {
3317 xx += squareSize - overall.width - 2;
3318 yy += font_ascent + 1;
3319 } else if (align == 4) {
3320 xx += 2, yy += font_ascent + 1;
3323 if (appData.monoMode) {
3324 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3326 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3330 if(marker) { // print fat marker dot, if requested
3331 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
3336 FlashDelay (int flash_delay)
3338 XSync(xDisplay, False);
3339 if(flash_delay) do_flash_delay(flash_delay);
3343 Fraction (int x, int start, int stop)
3345 double f = ((double) x - start)/(stop - start);
3346 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
3350 static WindowPlacement wpNew;
3353 CoDrag (Widget sh, WindowPlacement *wp)
3356 int j=0, touch=0, fudge = 2;
3357 GetActualPlacement(sh, wp);
3358 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
3359 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
3360 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
3361 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
3362 if(!touch ) return; // only windows that touch co-move
3363 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
3364 int heightInc = wpNew.height - wpMain.height;
3365 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3366 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3367 wp->y += fracTop * heightInc;
3368 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
3369 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
3370 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
3371 int widthInc = wpNew.width - wpMain.width;
3372 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3373 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3374 wp->y += fracLeft * widthInc;
3375 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
3376 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
3378 wp->x += wpNew.x - wpMain.x;
3379 wp->y += wpNew.y - wpMain.y;
3380 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
3381 if(touch == 3) wp->y += wpNew.height - wpMain.height;
3382 XtSetArg(args[j], XtNx, wp->x); j++;
3383 XtSetArg(args[j], XtNy, wp->y); j++;
3384 XtSetValues(sh, args, j);
3387 static XtIntervalId delayedDragID = 0;
3392 GetActualPlacement(shellWidget, &wpNew);
3393 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
3394 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
3395 return; // false alarm
3396 if(EngineOutputIsUp()) CoDrag(engineOutputShell, &wpEngineOutput);
3397 if(MoveHistoryIsUp()) CoDrag(shells[HistoryDlg], &wpMoveHistory);
3398 if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
3399 if(GameListIsUp()) CoDrag(gameListShell, &wpGameList);
3401 DrawPosition(True, NULL);
3402 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
3409 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
3411 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
3414 /* Why is this needed on some versions of X? */
3416 EventProc (Widget widget, caddr_t unused, XEvent *event)
3418 if (!XtIsRealized(widget))
3420 switch (event->type) {
3421 case ConfigureNotify: // main window is being dragged: drag attached windows with it
3422 if(appData.useStickyWindows)
3423 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
3426 if (event->xexpose.count > 0) return; /* no clipping is done */
3427 DrawPosition(True, NULL);
3428 if(twoBoards) { // [HGM] dual: draw other board in other orientation
3429 flipView = !flipView; partnerUp = !partnerUp;
3430 DrawPosition(True, NULL);
3431 flipView = !flipView; partnerUp = !partnerUp;
3435 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
3442 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
3444 DrawSeekAxis (int x, int y, int xTo, int yTo)
3446 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
3450 DrawSeekBackground (int left, int top, int right, int bottom)
3452 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
3456 DrawSeekText (char *buf, int x, int y)
3458 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
3462 DrawSeekDot (int x, int y, int colorNr)
3464 int square = colorNr & 0x80;
3467 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
3469 XFillRectangle(xDisplay, xBoardWindow, color,
3470 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3472 XFillArc(xDisplay, xBoardWindow, color,
3473 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
3477 DrawGrid (int second)
3479 XDrawSegments(xDisplay, xBoardWindow, lineGC,
3480 second ? secondSegments : // [HGM] dual
3481 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
3486 * event handler for redrawing the board
3489 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3491 DrawPosition(True, NULL);
3496 * event handler for parsing user moves
3498 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
3499 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
3500 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
3501 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
3502 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
3503 // and at the end FinishMove() to perform the move after optional promotion popups.
3504 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
3506 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3508 if (w != boardWidget || errorExitStatus != -1) return;
3509 if(nprms) shiftKey = !strcmp(prms[0], "1");
3512 if (event->type == ButtonPress) {
3513 XtPopdown(promotionShell);
3514 XtDestroyWidget(promotionShell);
3515 promotionUp = False;
3523 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
3524 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
3525 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
3529 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
3531 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
3532 DragPieceMove(event->xmotion.x, event->xmotion.y);
3536 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3537 { // [HGM] pv: walk PV
3538 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3541 static int savedIndex; /* gross that this is global */
3544 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3547 XawTextPosition index, dummy;
3550 XawTextGetSelectionPos(w, &index, &dummy);
3551 XtSetArg(arg, XtNstring, &val);
3552 XtGetValues(w, &arg, 1);
3553 ReplaceComment(savedIndex, val);
3554 if(savedIndex != currentMove) ToNrEvent(savedIndex);
3555 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3559 EditCommentPopUp (int index, char *title, char *text)
3562 if (text == NULL) text = "";
3563 NewCommentPopup(title, text, index);
3567 CommentPopUp (char *title, char *text)
3569 savedIndex = currentMove; // [HGM] vari
3570 NewCommentPopup(title, text, currentMove);
3576 PopDown(CommentDlg);
3579 static char *openName;
3585 (void) (*fileProc)(openFP, 0, openName);
3589 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
3591 fileProc = proc; /* I can't see a way not */
3592 fileOpenMode = openMode; /* to use globals here */
3593 { // [HGM] use file-selector dialog stolen from Ghostview
3594 int index; // this is not supported yet
3595 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
3596 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
3597 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
3598 ScheduleDelayedEvent(&DelayedLoad, 50);
3606 Widget dialog, layout;
3608 Dimension bw_width, pw_width;
3610 char *PromoChars = "wglcqrbnkac+=\0";
3613 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3614 XtGetValues(boardWidget, args, j);
3617 XtSetArg(args[j], XtNresizable, True); j++;
3618 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
3620 XtCreatePopupShell("Promotion", transientShellWidgetClass,
3621 shellWidget, args, j);
3623 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
3624 layoutArgs, XtNumber(layoutArgs));
3627 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
3628 XtSetArg(args[j], XtNborderWidth, 0); j++;
3629 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
3632 if(gameInfo.variant != VariantShogi) {
3633 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
3634 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
3635 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
3636 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
3637 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
3639 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
3640 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
3641 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
3642 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
3644 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
3645 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
3646 gameInfo.variant == VariantGiveaway) {
3647 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
3649 if(gameInfo.variant == VariantCapablanca ||
3650 gameInfo.variant == VariantGothic ||
3651 gameInfo.variant == VariantCapaRandom) {
3652 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
3653 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars +&