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"
209 #include "engineoutput.h"
218 #define usleep(t) _sleep2(((t)+500)/1000)
222 # define _(s) gettext (s)
223 # define N_(s) gettext_noop (s)
229 int main P((int argc, char **argv));
230 RETSIGTYPE CmailSigHandler P((int sig));
231 RETSIGTYPE IntSigHandler P((int sig));
232 RETSIGTYPE TermSizeSigHandler P((int sig));
233 static void CreateGCs P((int redo));
234 static void CreateAnyPieces P((void));
235 void CreateXIMPieces P((void));
236 void CreateXPMPieces P((void));
237 void CreateXPMBoard P((char *s, int n));
238 void CreatePieces P((void));
239 void CreatePieceMenus P((void));
240 Widget CreateMenuBar P((Menu *mb, int boardWidth));
241 Widget CreateButtonBar P ((MenuItem *mi));
243 char *InsertPxlSize P((char *pattern, int targetPxlSize));
244 XFontSet CreateFontSet P((char *base_fnt_lst));
246 char *FindFont P((char *pattern, int targetPxlSize));
248 void PieceMenuPopup P((Widget w, XEvent *event,
249 String *params, Cardinal *num_params));
250 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
251 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
252 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
253 u_int wreq, u_int hreq));
254 void CreateGrid P((void));
255 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
256 void DelayedDrag P((void));
257 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
258 void HandleUserMove P((Widget w, XEvent *event,
259 String *prms, Cardinal *nprms));
260 void AnimateUserMove P((Widget w, XEvent * event,
261 String * params, Cardinal * nParams));
262 void HandlePV P((Widget w, XEvent * event,
263 String * params, Cardinal * nParams));
264 void WhiteClock P((Widget w, XEvent *event,
265 String *prms, Cardinal *nprms));
266 void BlackClock P((Widget w, XEvent *event,
267 String *prms, Cardinal *nprms));
268 void DrawPositionProc P((Widget w, XEvent *event,
269 String *prms, Cardinal *nprms));
270 void CommentClick P((Widget w, XEvent * event,
271 String * params, Cardinal * nParams));
272 void ICSInputBoxPopUp P((void));
273 void FileNamePopUp P((char *label, char *def, char *filter,
274 FileProc proc, char *openMode));
275 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
276 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
277 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
278 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
279 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
280 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
281 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
282 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
283 Boolean TempBackwardActive = False;
284 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
285 void DisplayMove P((int moveNumber));
286 void ICSInitScript P((void));
287 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
288 void update_ics_width P(());
289 int get_term_width P(());
290 int CopyMemoProc P(());
293 * XBoard depends on Xt R4 or higher
295 int xtVersion = XtSpecificationRelease;
300 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
301 highlightSquareColor, premoveHighlightColor, dialogColor, buttonColor;
302 Pixel lowTimeWarningColor;
303 GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
304 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
306 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
307 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
308 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
309 commentShell, whitePieceMenu, blackPieceMenu, dropMenu,
310 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
311 ICSInputShell, fileNameShell;
312 Widget historyShell, evalGraphShell;
313 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
314 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
316 XFontSet fontSet, clockFontSet;
319 XFontStruct *clockFontStruct;
321 Font coordFontID, countFontID;
322 XFontStruct *coordFontStruct, *countFontStruct;
323 XtAppContext appContext;
328 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
330 Position commentX = -1, commentY = -1;
331 Dimension commentW, commentH;
332 typedef unsigned int BoardSize;
334 Boolean chessProgram;
336 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
337 int smallLayout = 0, tinyLayout = 0,
338 marginW, marginH, // [HGM] for run-time resizing
339 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
340 ICSInputBoxUp = False,
341 filenameUp = False, pmFromX = -1, pmFromY = -1,
342 errorExitStatus = -1, defaultLineGap;
343 Dimension textHeight;
344 Pixel timerForegroundPixel, timerBackgroundPixel;
345 Pixel buttonForegroundPixel, buttonBackgroundPixel;
346 char *chessDir, *programName, *programVersion;
347 Boolean alwaysOnTop = False;
348 char *icsTextMenuString;
350 char *firstChessProgramNames;
351 char *secondChessProgramNames;
353 WindowPlacement wpMain;
354 WindowPlacement wpConsole;
355 WindowPlacement wpComment;
356 WindowPlacement wpMoveHistory;
357 WindowPlacement wpEvalGraph;
358 WindowPlacement wpEngineOutput;
359 WindowPlacement wpGameList;
360 WindowPlacement wpTags;
365 Pixmap pieceBitmap[2][(int)BlackPawn];
366 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
367 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
368 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
369 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
370 Pixmap xpmBoardBitmap[2];
371 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
372 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
373 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
374 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
375 XImage *ximLightSquare, *ximDarkSquare;
378 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
379 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
381 #define White(piece) ((int)(piece) < (int)BlackPawn)
383 /* Bitmaps for use as masks when drawing XPM pieces.
384 Need one for each black and white piece. */
385 static Pixmap xpmMask[BlackKing + 1];
387 /* This magic number is the number of intermediate frames used
388 in each half of the animation. For short moves it's reduced
389 by 1. The total number of frames will be factor * 2 + 1. */
392 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
394 #define PAUSE_BUTTON "P"
395 MenuItem buttonBar[] = {
396 {"<<", "<<", ToStartEvent},
397 {"<", "<", BackwardEvent},
398 {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseEvent},
399 {">", ">", ForwardEvent},
400 {">>", ">>", ToEndEvent},
404 #define PIECE_MENU_SIZE 18
405 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
406 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
407 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
408 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
409 N_("Empty square"), N_("Clear board") },
410 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
411 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
412 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
413 N_("Empty square"), N_("Clear board") }
415 /* must be in same order as pieceMenuStrings! */
416 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
417 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
418 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
419 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
420 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
421 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
422 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
423 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
424 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
427 #define DROP_MENU_SIZE 6
428 String dropMenuStrings[DROP_MENU_SIZE] = {
429 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
431 /* must be in same order as dropMenuStrings! */
432 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
433 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
434 WhiteRook, WhiteQueen
442 DropMenuEnables dmEnables[] = {
460 { XtNborderWidth, 0 },
461 { XtNdefaultDistance, 0 },
465 { XtNborderWidth, 0 },
466 { XtNresizable, (XtArgVal) True },
470 { XtNborderWidth, 0 },
476 { XtNjustify, (XtArgVal) XtJustifyRight },
477 { XtNlabel, (XtArgVal) "..." },
478 { XtNresizable, (XtArgVal) True },
479 { XtNresize, (XtArgVal) False }
482 Arg messageArgs[] = {
483 { XtNjustify, (XtArgVal) XtJustifyLeft },
484 { XtNlabel, (XtArgVal) "..." },
485 { XtNresizable, (XtArgVal) True },
486 { XtNresize, (XtArgVal) False }
490 { XtNborderWidth, 0 },
491 { XtNjustify, (XtArgVal) XtJustifyLeft }
494 XtResource clientResources[] = {
495 { "flashCount", "flashCount", XtRInt, sizeof(int),
496 XtOffset(AppDataPtr, flashCount), XtRImmediate,
497 (XtPointer) FLASH_COUNT },
500 XrmOptionDescRec shellOptions[] = {
501 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
502 { "-flash", "flashCount", XrmoptionNoArg, "3" },
503 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
506 XtActionsRec boardActions[] = {
507 { "DrawPosition", DrawPositionProc },
508 { "HandleUserMove", HandleUserMove },
509 { "AnimateUserMove", AnimateUserMove },
510 { "HandlePV", HandlePV },
511 { "SelectPV", SelectPV },
512 { "StopPV", StopPV },
513 { "PieceMenuPopup", PieceMenuPopup },
514 { "WhiteClock", WhiteClock },
515 { "BlackClock", BlackClock },
516 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
517 { "QuitProc", QuitWrapper },
518 { "ManProc", ManInner },
519 { "TempBackwardProc", TempBackwardProc },
520 { "TempForwardProc", TempForwardProc },
521 { "CommentClick", (XtActionProc) CommentClick },
522 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
523 { "GenericPopDown", (XtActionProc) GenericPopDown },
524 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
525 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
526 { "SelectMove", (XtActionProc) SelectMove },
527 { "LoadSelectedProc", LoadSelectedProc },
528 { "SetFilterProc", SetFilterProc },
529 { "TypeInProc", TypeInProc },
530 { "EnterKeyProc", EnterKeyProc },
531 { "UpKeyProc", UpKeyProc },
532 { "DownKeyProc", DownKeyProc },
533 { "WheelProc", WheelProc },
534 { "TabProc", TabProc },
537 char globalTranslations[] =
538 ":<Key>F9: MenuItem(ResignProc) \n \
539 :Ctrl<Key>n: MenuItem(NewGame) \n \
540 :Meta<Key>V: MenuItem(NewVariant) \n \
541 :Ctrl<Key>o: MenuItem(LoadGame) \n \
542 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
543 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
544 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
545 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
546 :Ctrl<Key>s: MenuItem(SaveGame) \n \
547 :Ctrl<Key>c: MenuItem(CopyGame) \n \
548 :Ctrl<Key>v: MenuItem(PasteGame) \n \
549 :Ctrl<Key>O: MenuItem(LoadPosition) \n \
550 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
551 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
552 :Ctrl<Key>S: MenuItem(SavePosition) \n \
553 :Ctrl<Key>C: MenuItem(CopyPosition) \n \
554 :Ctrl<Key>V: MenuItem(PastePosition) \n \
555 :Ctrl<Key>q: MenuItem(Exit) \n \
556 :Ctrl<Key>w: MenuItem(MachineWhite) \n \
557 :Ctrl<Key>b: MenuItem(MachineBlack) \n \
558 :Ctrl<Key>t: MenuItem(TwoMachines) \n \
559 :Ctrl<Key>a: MenuItem(AnalysisMode) \n \
560 :Ctrl<Key>g: MenuItem(AnalyzeFile) \n \
561 :Ctrl<Key>e: MenuItem(EditGame) \n \
562 :Ctrl<Key>E: MenuItem(EditPosition) \n \
563 :Meta<Key>O: MenuItem(ShowEngineOutput) \n \
564 :Meta<Key>E: MenuItem(ShowEvaluationGraph) \n \
565 :Meta<Key>G: MenuItem(ShowGameList) \n \
566 :Meta<Key>H: MenuItem(ShowMoveHistory) \n \
567 :<Key>Pause: MenuItem(Pause) \n \
568 :<Key>F3: MenuItem(Accept) \n \
569 :<Key>F4: MenuItem(Decline) \n \
570 :<Key>F12: MenuItem(Rematch) \n \
571 :<Key>F5: MenuItem(CallFlag) \n \
572 :<Key>F6: MenuItem(Draw) \n \
573 :<Key>F7: MenuItem(Adjourn) \n \
574 :<Key>F8: MenuItem(Abort) \n \
575 :<Key>F10: MenuItem(StopObserving) \n \
576 :<Key>F11: MenuItem(StopExamining) \n \
577 :Ctrl<Key>d: MenuItem(DebugProc) \n \
578 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
579 :Meta<Key>End: MenuItem(ToEnd) \n \
580 :Meta<Key>Right: MenuItem(Forward) \n \
581 :Meta<Key>Home: MenuItem(ToStart) \n \
582 :Meta<Key>Left: MenuItem(Backward) \n \
583 :<Key>Left: MenuItem(Backward) \n \
584 :<Key>Right: MenuItem(Forward) \n \
585 :<Key>Home: MenuItem(Revert) \n \
586 :<Key>End: MenuItem(TruncateGame) \n \
587 :Ctrl<Key>m: MenuItem(MoveNow) \n \
588 :Ctrl<Key>x: MenuItem(RetractMove) \n \
589 :Meta<Key>J: MenuItem(Adjudications) \n \
590 :Meta<Key>U: MenuItem(CommonEngine) \n \
591 :Meta<Key>T: MenuItem(TimeControl) \n \
592 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
593 #ifndef OPTIONSDIALOG
595 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
596 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
597 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
598 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
599 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
602 :<Key>F1: MenuItem(Manual) \n \
603 :<Key>F2: MenuItem(FlipView) \n \
604 :<KeyDown>Return: TempBackwardProc() \n \
605 :<KeyUp>Return: TempForwardProc() \n";
607 char boardTranslations[] =
608 "<Btn1Down>: HandleUserMove(0) \n \
609 Shift<Btn1Up>: HandleUserMove(1) \n \
610 <Btn1Up>: HandleUserMove(0) \n \
611 <Btn1Motion>: AnimateUserMove() \n \
612 <Btn3Motion>: HandlePV() \n \
613 <Btn2Motion>: HandlePV() \n \
614 <Btn3Up>: PieceMenuPopup(menuB) \n \
615 <Btn2Up>: PieceMenuPopup(menuB) \n \
616 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
617 PieceMenuPopup(menuB) \n \
618 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
619 PieceMenuPopup(menuW) \n \
620 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
621 PieceMenuPopup(menuW) \n \
622 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
623 PieceMenuPopup(menuB) \n";
625 char whiteTranslations[] =
626 "Shift<BtnDown>: WhiteClock(1)\n \
627 <BtnDown>: WhiteClock(0)\n";
628 char blackTranslations[] =
629 "Shift<BtnDown>: BlackClock(1)\n \
630 <BtnDown>: BlackClock(0)\n";
632 char ICSInputTranslations[] =
633 "<Key>Up: UpKeyProc() \n "
634 "<Key>Down: DownKeyProc() \n "
635 "<Key>Return: EnterKeyProc() \n";
637 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
638 // as the widget is destroyed before the up-click can call extend-end
639 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
641 String xboardResources[] = {
642 "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
647 /* Max possible square size */
648 #define MAXSQSIZE 256
650 static int xpm_avail[MAXSQSIZE];
652 #ifdef HAVE_DIR_STRUCT
654 /* Extract piece size from filename */
656 xpm_getsize (char *name, int len, char *ext)
664 if ((p=strchr(name, '.')) == NULL ||
665 StrCaseCmp(p+1, ext) != 0)
671 while (*p && isdigit(*p))
678 /* Setup xpm_avail */
680 xpm_getavail (char *dirname, char *ext)
686 for (i=0; i<MAXSQSIZE; ++i)
689 if (appData.debugMode)
690 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
692 dir = opendir(dirname);
695 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
696 programName, dirname);
700 while ((ent=readdir(dir)) != NULL) {
701 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
702 if (i > 0 && i < MAXSQSIZE)
712 xpm_print_avail (FILE *fp, char *ext)
716 fprintf(fp, _("Available `%s' sizes:\n"), ext);
717 for (i=1; i<MAXSQSIZE; ++i) {
723 /* Return XPM piecesize closest to size */
725 xpm_closest_to (char *dirname, int size, char *ext)
728 int sm_diff = MAXSQSIZE;
732 xpm_getavail(dirname, ext);
734 if (appData.debugMode)
735 xpm_print_avail(stderr, ext);
737 for (i=1; i<MAXSQSIZE; ++i) {
740 diff = (diff<0) ? -diff : diff;
741 if (diff < sm_diff) {
749 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
755 #else /* !HAVE_DIR_STRUCT */
756 /* If we are on a system without a DIR struct, we can't
757 read the directory, so we can't collect a list of
758 filenames, etc., so we can't do any size-fitting. */
760 xpm_closest_to (char *dirname, int size, char *ext)
763 Warning: No DIR structure found on this system --\n\
764 Unable to autosize for XPM/XIM pieces.\n\
765 Please report this error to %s.\n\
766 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
769 #endif /* HAVE_DIR_STRUCT */
772 /* Arrange to catch delete-window events */
773 Atom wm_delete_window;
775 CatchDeleteWindow (Widget w, String procname)
778 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
779 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
780 XtAugmentTranslations(w, XtParseTranslationTable(buf));
787 XtSetArg(args[0], XtNiconic, False);
788 XtSetValues(shellWidget, args, 1);
790 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
793 //---------------------------------------------------------------------------------------------------------
794 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
797 #define CW_USEDEFAULT (1<<31)
798 #define ICS_TEXT_MENU_SIZE 90
799 #define DEBUG_FILE "xboard.debug"
800 #define SetCurrentDirectory chdir
801 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
805 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
808 // front-end part of option handling
810 // [HGM] This platform-dependent table provides the location for storing the color info
811 extern char *crWhite, * crBlack;
815 &appData.whitePieceColor,
816 &appData.blackPieceColor,
817 &appData.lightSquareColor,
818 &appData.darkSquareColor,
819 &appData.highlightSquareColor,
820 &appData.premoveHighlightColor,
821 &appData.lowTimeWarningColor,
832 // [HGM] font: keep a font for each square size, even non-stndard ones
835 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
836 char *fontTable[NUM_FONTS][MAX_SIZE];
839 ParseFont (char *name, int number)
840 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
842 if(sscanf(name, "size%d:", &size)) {
843 // [HGM] font: font is meant for specific boardSize (likely from settings file);
844 // defer processing it until we know if it matches our board size
845 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
846 fontTable[number][size] = strdup(strchr(name, ':')+1);
847 fontValid[number][size] = True;
852 case 0: // CLOCK_FONT
853 appData.clockFont = strdup(name);
855 case 1: // MESSAGE_FONT
856 appData.font = strdup(name);
858 case 2: // COORD_FONT
859 appData.coordFont = strdup(name);
864 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
869 { // only 2 fonts currently
870 appData.clockFont = CLOCK_FONT_NAME;
871 appData.coordFont = COORD_FONT_NAME;
872 appData.font = DEFAULT_FONT_NAME;
877 { // no-op, until we identify the code for this already in XBoard and move it here
881 ParseColor (int n, char *name)
882 { // in XBoard, just copy the color-name string
883 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
887 ParseTextAttribs (ColorClass cc, char *s)
889 (&appData.colorShout)[cc] = strdup(s);
893 ParseBoardSize (void *addr, char *name)
895 appData.boardSize = strdup(name);
900 { // In XBoard the sound-playing program takes care of obtaining the actual sound
904 SetCommPortDefaults ()
905 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
908 // [HGM] args: these three cases taken out to stay in front-end
910 SaveFontArg (FILE *f, ArgDescriptor *ad)
913 int i, n = (int)(intptr_t)ad->argLoc;
915 case 0: // CLOCK_FONT
916 name = appData.clockFont;
918 case 1: // MESSAGE_FONT
921 case 2: // COORD_FONT
922 name = appData.coordFont;
927 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
928 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
929 fontTable[n][squareSize] = strdup(name);
930 fontValid[n][squareSize] = True;
933 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
934 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
939 { // nothing to do, as the sounds are at all times represented by their text-string names already
943 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
944 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
945 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
949 SaveColor (FILE *f, ArgDescriptor *ad)
950 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
951 if(colorVariable[(int)(intptr_t)ad->argLoc])
952 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
956 SaveBoardSize (FILE *f, char *name, void *addr)
957 { // wrapper to shield back-end from BoardSize & sizeInfo
958 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
962 ParseCommPortSettings (char *s)
963 { // no such option in XBoard (yet)
969 GetActualPlacement (Widget wg, WindowPlacement *wp)
974 XWindowAttributes winAt;
981 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
982 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
983 wp->x = rx - winAt.x;
984 wp->y = ry - winAt.y;
985 wp->height = winAt.height;
986 wp->width = winAt.width;
987 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
992 { // wrapper to shield use of window handles from back-end (make addressible by number?)
993 // In XBoard this will have to wait until awareness of window parameters is implemented
994 GetActualPlacement(shellWidget, &wpMain);
995 if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
996 if(MoveHistoryIsUp()) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
997 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
998 if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
999 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
1000 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
1004 PrintCommPortSettings (FILE *f, char *name)
1005 { // This option does not exist in XBoard
1009 EnsureOnScreen (int *x, int *y, int minX, int minY)
1016 { // [HGM] args: allows testing if main window is realized from back-end
1017 return xBoardWindow != 0;
1021 PopUpStartupDialog ()
1022 { // start menu not implemented in XBoard
1026 ConvertToLine (int argc, char **argv)
1028 static char line[128*1024], buf[1024];
1032 for(i=1; i<argc; i++)
1034 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1035 && argv[i][0] != '{' )
1036 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1038 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1039 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1042 line[strlen(line)-1] = NULLCHAR;
1046 //--------------------------------------------------------------------------------------------
1049 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1051 #define BoardSize int
1053 InitDrawingSizes (BoardSize boardSize, int flags)
1054 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1055 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1057 XtGeometryResult gres;
1059 static Dimension oldWidth, oldHeight;
1060 static VariantClass oldVariant;
1061 static int oldDual = -1, oldMono = -1;
1063 if(!formWidget) return;
1065 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1066 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1067 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1069 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1071 * Enable shell resizing.
1073 shellArgs[0].value = (XtArgVal) &w;
1074 shellArgs[1].value = (XtArgVal) &h;
1075 XtGetValues(shellWidget, shellArgs, 2);
1077 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1078 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1079 XtSetValues(shellWidget, &shellArgs[2], 4);
1081 XtSetArg(args[0], XtNdefaultDistance, &sep);
1082 XtGetValues(formWidget, args, 1);
1084 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1086 hOffset = boardWidth + 10;
1087 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1088 secondSegments[i] = gridSegments[i];
1089 secondSegments[i].x1 += hOffset;
1090 secondSegments[i].x2 += hOffset;
1093 XtSetArg(args[0], XtNwidth, boardWidth);
1094 XtSetArg(args[1], XtNheight, boardHeight);
1095 XtSetValues(boardWidget, args, 2);
1097 timerWidth = (boardWidth - sep) / 2;
1098 XtSetArg(args[0], XtNwidth, timerWidth);
1099 XtSetValues(whiteTimerWidget, args, 1);
1100 XtSetValues(blackTimerWidget, args, 1);
1102 XawFormDoLayout(formWidget, False);
1104 if (appData.titleInWindow) {
1106 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1107 XtSetArg(args[i], XtNheight, &h); i++;
1108 XtGetValues(titleWidget, args, i);
1110 w = boardWidth - 2*bor;
1112 XtSetArg(args[0], XtNwidth, &w);
1113 XtGetValues(menuBarWidget, args, 1);
1114 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1117 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1118 if (gres != XtGeometryYes && appData.debugMode) {
1120 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1121 programName, gres, w, h, wr, hr);
1125 XawFormDoLayout(formWidget, True);
1128 * Inhibit shell resizing.
1130 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1131 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1132 shellArgs[4].value = shellArgs[2].value = w;
1133 shellArgs[5].value = shellArgs[3].value = h;
1134 XtSetValues(shellWidget, &shellArgs[0], 6);
1136 XSync(xDisplay, False);
1140 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1143 if(gameInfo.variant != oldVariant) { // and only if variant changed
1146 for(i=0; i<4; i++) {
1148 for(p=0; p<=(int)WhiteKing; p++)
1149 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1150 if(gameInfo.variant == VariantShogi) {
1151 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1152 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1153 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1154 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1155 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1158 if(gameInfo.variant == VariantGothic) {
1159 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1162 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1163 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1164 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1167 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1168 for(p=0; p<=(int)WhiteKing; p++)
1169 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1170 if(gameInfo.variant == VariantShogi) {
1171 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1172 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1173 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1174 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1175 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1178 if(gameInfo.variant == VariantGothic) {
1179 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1182 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1183 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1184 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1189 for(i=0; i<2; i++) {
1191 for(p=0; p<=(int)WhiteKing; p++)
1192 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1193 if(gameInfo.variant == VariantShogi) {
1194 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1195 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1196 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1197 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1198 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1201 if(gameInfo.variant == VariantGothic) {
1202 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1205 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1206 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1207 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1211 oldMono = -10; // kludge to force recreation of animation masks
1212 oldVariant = gameInfo.variant;
1215 if(appData.monoMode != oldMono)
1218 oldMono = appData.monoMode;
1223 MakeOneColor (char *name, Pixel *color)
1225 XrmValue vFrom, vTo;
1226 if (!appData.monoMode) {
1227 vFrom.addr = (caddr_t) name;
1228 vFrom.size = strlen(name);
1229 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1230 if (vTo.addr == NULL) {
1231 appData.monoMode = True;
1234 *color = *(Pixel *) vTo.addr;
1242 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1243 int forceMono = False;
1245 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1246 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1247 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1248 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1249 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1250 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1251 if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
1252 if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
1259 { // [HGM] taken out of main
1261 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1262 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1263 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1265 if (appData.bitmapDirectory[0] != NULLCHAR) {
1269 CreateXPMBoard(appData.liteBackTextureFile, 1);
1270 CreateXPMBoard(appData.darkBackTextureFile, 0);
1274 /* Create regular pieces */
1275 if (!useImages) CreatePieces();
1280 InitDrawingParams ()
1282 MakeColors(); CreateGCs(True);
1287 main (int argc, char **argv)
1289 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1290 XSetWindowAttributes window_attributes;
1292 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1293 XrmValue vFrom, vTo;
1294 XtGeometryResult gres;
1297 int forceMono = False;
1299 srandom(time(0)); // [HGM] book: make random truly random
1301 setbuf(stdout, NULL);
1302 setbuf(stderr, NULL);
1305 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1306 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1310 programName = strrchr(argv[0], '/');
1311 if (programName == NULL)
1312 programName = argv[0];
1317 XtSetLanguageProc(NULL, NULL, NULL);
1318 bindtextdomain(PACKAGE, LOCALEDIR);
1319 textdomain(PACKAGE);
1323 XtAppInitialize(&appContext, "XBoard", shellOptions,
1324 XtNumber(shellOptions),
1325 &argc, argv, xboardResources, NULL, 0);
1326 appData.boardSize = "";
1327 InitAppData(ConvertToLine(argc, argv));
1329 if (p == NULL) p = "/tmp";
1330 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1331 gameCopyFilename = (char*) malloc(i);
1332 gamePasteFilename = (char*) malloc(i);
1333 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1334 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1336 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1337 clientResources, XtNumber(clientResources),
1340 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1341 static char buf[MSG_SIZ];
1342 EscapeExpand(buf, appData.firstInitString);
1343 appData.firstInitString = strdup(buf);
1344 EscapeExpand(buf, appData.secondInitString);
1345 appData.secondInitString = strdup(buf);
1346 EscapeExpand(buf, appData.firstComputerString);
1347 appData.firstComputerString = strdup(buf);
1348 EscapeExpand(buf, appData.secondComputerString);
1349 appData.secondComputerString = strdup(buf);
1352 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1355 if (chdir(chessDir) != 0) {
1356 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1362 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1363 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1364 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1365 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1368 setbuf(debugFP, NULL);
1372 if (appData.debugMode) {
1373 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1377 /* [HGM,HR] make sure board size is acceptable */
1378 if(appData.NrFiles > BOARD_FILES ||
1379 appData.NrRanks > BOARD_RANKS )
1380 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1383 /* This feature does not work; animation needs a rewrite */
1384 appData.highlightDragging = FALSE;
1388 xDisplay = XtDisplay(shellWidget);
1389 xScreen = DefaultScreen(xDisplay);
1390 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1392 gameInfo.variant = StringToVariant(appData.variant);
1393 InitPosition(FALSE);
1396 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1398 if (isdigit(appData.boardSize[0])) {
1399 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1400 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1401 &fontPxlSize, &smallLayout, &tinyLayout);
1403 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1404 programName, appData.boardSize);
1408 /* Find some defaults; use the nearest known size */
1409 SizeDefaults *szd, *nearest;
1410 int distance = 99999;
1411 nearest = szd = sizeDefaults;
1412 while (szd->name != NULL) {
1413 if (abs(szd->squareSize - squareSize) < distance) {
1415 distance = abs(szd->squareSize - squareSize);
1416 if (distance == 0) break;
1420 if (i < 2) lineGap = nearest->lineGap;
1421 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1422 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1423 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1424 if (i < 6) smallLayout = nearest->smallLayout;
1425 if (i < 7) tinyLayout = nearest->tinyLayout;
1428 SizeDefaults *szd = sizeDefaults;
1429 if (*appData.boardSize == NULLCHAR) {
1430 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1431 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1434 if (szd->name == NULL) szd--;
1435 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1437 while (szd->name != NULL &&
1438 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1439 if (szd->name == NULL) {
1440 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1441 programName, appData.boardSize);
1445 squareSize = szd->squareSize;
1446 lineGap = szd->lineGap;
1447 clockFontPxlSize = szd->clockFontPxlSize;
1448 coordFontPxlSize = szd->coordFontPxlSize;
1449 fontPxlSize = szd->fontPxlSize;
1450 smallLayout = szd->smallLayout;
1451 tinyLayout = szd->tinyLayout;
1452 // [HGM] font: use defaults from settings file if available and not overruled
1454 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1455 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1456 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1457 appData.font = fontTable[MESSAGE_FONT][squareSize];
1458 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1459 appData.coordFont = fontTable[COORD_FONT][squareSize];
1461 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1462 if (strlen(appData.pixmapDirectory) > 0) {
1463 p = ExpandPathName(appData.pixmapDirectory);
1465 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1466 appData.pixmapDirectory);
1469 if (appData.debugMode) {
1470 fprintf(stderr, _("\
1471 XBoard square size (hint): %d\n\
1472 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1474 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1475 if (appData.debugMode) {
1476 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1479 defaultLineGap = lineGap;
1480 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1482 /* [HR] height treated separately (hacked) */
1483 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1484 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1485 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1486 XtSetArg(boardArgs[2], XtNheight, boardHeight);
1489 * Determine what fonts to use.
1492 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1493 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1494 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1495 fontSet = CreateFontSet(appData.font);
1496 clockFontSet = CreateFontSet(appData.clockFont);
1498 /* For the coordFont, use the 0th font of the fontset. */
1499 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1500 XFontStruct **font_struct_list;
1501 XFontSetExtents *fontSize;
1502 char **font_name_list;
1503 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1504 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1505 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1506 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1507 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1510 appData.font = FindFont(appData.font, fontPxlSize);
1511 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1512 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1513 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1514 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1515 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1516 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1518 countFontID = coordFontID; // [HGM] holdings
1519 countFontStruct = coordFontStruct;
1521 xdb = XtDatabase(xDisplay);
1523 XrmPutLineResource(&xdb, "*international: True");
1524 vTo.size = sizeof(XFontSet);
1525 vTo.addr = (XtPointer) &fontSet;
1526 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1528 XrmPutStringResource(&xdb, "*font", appData.font);
1532 * Detect if there are not enough colors available and adapt.
1534 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1535 appData.monoMode = True;
1538 forceMono = MakeColors();
1541 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1543 appData.monoMode = True;
1546 if (appData.lowTimeWarning && !appData.monoMode) {
1547 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1548 vFrom.size = strlen(appData.lowTimeWarningColor);
1549 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1550 if (vTo.addr == NULL)
1551 appData.monoMode = True;
1553 lowTimeWarningColor = *(Pixel *) vTo.addr;
1556 if (appData.monoMode && appData.debugMode) {
1557 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1558 (unsigned long) XWhitePixel(xDisplay, xScreen),
1559 (unsigned long) XBlackPixel(xDisplay, xScreen));
1562 ParseIcsTextColors();
1564 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1570 layoutName = "tinyLayout";
1571 } else if (smallLayout) {
1572 layoutName = "smallLayout";
1574 layoutName = "normalLayout";
1576 /* Outer layoutWidget is there only to provide a name for use in
1577 resources that depend on the layout style */
1579 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
1580 layoutArgs, XtNumber(layoutArgs));
1582 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
1583 formArgs, XtNumber(formArgs));
1584 XtSetArg(args[0], XtNdefaultDistance, &sep);
1585 XtGetValues(formWidget, args, 1);
1588 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
1589 XtSetArg(args[0], XtNtop, XtChainTop);
1590 XtSetArg(args[1], XtNbottom, XtChainTop);
1591 XtSetArg(args[2], XtNright, XtChainLeft);
1592 XtSetValues(menuBarWidget, args, 3);
1594 widgetList[j++] = whiteTimerWidget =
1595 XtCreateWidget("whiteTime", labelWidgetClass,
1596 formWidget, timerArgs, XtNumber(timerArgs));
1598 XtSetArg(args[0], XtNfontSet, clockFontSet);
1600 XtSetArg(args[0], XtNfont, clockFontStruct);
1602 XtSetArg(args[1], XtNtop, XtChainTop);
1603 XtSetArg(args[2], XtNbottom, XtChainTop);
1604 XtSetValues(whiteTimerWidget, args, 3);
1606 widgetList[j++] = blackTimerWidget =
1607 XtCreateWidget("blackTime", 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(blackTimerWidget, args, 3);
1618 if (appData.titleInWindow) {
1619 widgetList[j++] = titleWidget =
1620 XtCreateWidget("title", labelWidgetClass, formWidget,
1621 titleArgs, XtNumber(titleArgs));
1622 XtSetArg(args[0], XtNtop, XtChainTop);
1623 XtSetArg(args[1], XtNbottom, XtChainTop);
1624 XtSetValues(titleWidget, args, 2);
1627 if (appData.showButtonBar) {
1628 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
1629 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
1630 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
1631 XtSetArg(args[2], XtNtop, XtChainTop);
1632 XtSetArg(args[3], XtNbottom, XtChainTop);
1633 XtSetValues(buttonBarWidget, args, 4);
1636 widgetList[j++] = messageWidget =
1637 XtCreateWidget("message", labelWidgetClass, formWidget,
1638 messageArgs, XtNumber(messageArgs));
1639 XtSetArg(args[0], XtNtop, XtChainTop);
1640 XtSetArg(args[1], XtNbottom, XtChainTop);
1641 XtSetValues(messageWidget, args, 2);
1643 widgetList[j++] = boardWidget =
1644 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
1645 XtNumber(boardArgs));
1647 XtManageChildren(widgetList, j);
1649 timerWidth = (boardWidth - sep) / 2;
1650 XtSetArg(args[0], XtNwidth, timerWidth);
1651 XtSetValues(whiteTimerWidget, args, 1);
1652 XtSetValues(blackTimerWidget, args, 1);
1654 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1655 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1656 XtGetValues(whiteTimerWidget, args, 2);
1658 if (appData.showButtonBar) {
1659 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1660 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1661 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
1665 * formWidget uses these constraints but they are stored
1669 XtSetArg(args[i], XtNfromHoriz, 0); i++;
1670 XtSetValues(menuBarWidget, args, i);
1671 if (appData.titleInWindow) {
1674 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1675 XtSetValues(whiteTimerWidget, args, i);
1677 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1678 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1679 XtSetValues(blackTimerWidget, args, i);
1681 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1682 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
1683 XtSetValues(titleWidget, args, i);
1685 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1686 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1687 XtSetValues(messageWidget, args, i);
1688 if (appData.showButtonBar) {
1690 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1691 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1692 XtSetValues(buttonBarWidget, args, i);
1696 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1697 XtSetValues(whiteTimerWidget, args, i);
1699 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1700 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1701 XtSetValues(blackTimerWidget, args, i);
1703 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
1704 XtSetValues(titleWidget, args, i);
1706 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1707 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1708 XtSetValues(messageWidget, args, i);
1709 if (appData.showButtonBar) {
1711 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1712 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1713 XtSetValues(buttonBarWidget, args, i);
1718 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1719 XtSetValues(whiteTimerWidget, args, i);
1721 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1722 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1723 XtSetValues(blackTimerWidget, args, i);
1725 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1726 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1727 XtSetValues(messageWidget, args, i);
1728 if (appData.showButtonBar) {
1730 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1731 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1732 XtSetValues(buttonBarWidget, args, i);
1736 XtSetArg(args[0], XtNfromVert, messageWidget);
1737 XtSetArg(args[1], XtNtop, XtChainTop);
1738 XtSetArg(args[2], XtNbottom, XtChainBottom);
1739 XtSetArg(args[3], XtNleft, XtChainLeft);
1740 XtSetArg(args[4], XtNright, XtChainRight);
1741 XtSetValues(boardWidget, args, 5);
1743 XtRealizeWidget(shellWidget);
1746 XtSetArg(args[0], XtNx, wpMain.x);
1747 XtSetArg(args[1], XtNy, wpMain.y);
1748 XtSetValues(shellWidget, args, 2);
1752 * Correct the width of the message and title widgets.
1753 * It is not known why some systems need the extra fudge term.
1754 * The value "2" is probably larger than needed.
1756 XawFormDoLayout(formWidget, False);
1758 #define WIDTH_FUDGE 2
1760 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1761 XtSetArg(args[i], XtNheight, &h); i++;
1762 XtGetValues(messageWidget, args, i);
1763 if (appData.showButtonBar) {
1765 XtSetArg(args[i], XtNwidth, &w); i++;
1766 XtGetValues(buttonBarWidget, args, i);
1767 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1769 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
1772 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1773 if (gres != XtGeometryYes && appData.debugMode) {
1774 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1775 programName, gres, w, h, wr, hr);
1778 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
1779 /* The size used for the child widget in layout lags one resize behind
1780 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
1782 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1783 if (gres != XtGeometryYes && appData.debugMode) {
1784 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1785 programName, gres, w, h, wr, hr);
1788 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
1789 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
1790 XtSetArg(args[1], XtNright, XtChainRight);
1791 XtSetValues(messageWidget, args, 2);
1793 if (appData.titleInWindow) {
1795 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1796 XtSetArg(args[i], XtNheight, &h); i++;
1797 XtGetValues(titleWidget, args, i);
1799 w = boardWidth - 2*bor;
1801 XtSetArg(args[0], XtNwidth, &w);
1802 XtGetValues(menuBarWidget, args, 1);
1803 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1806 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1807 if (gres != XtGeometryYes && appData.debugMode) {
1809 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1810 programName, gres, w, h, wr, hr);
1813 XawFormDoLayout(formWidget, True);
1815 xBoardWindow = XtWindow(boardWidget);
1817 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1818 // not need to go into InitDrawingSizes().
1822 * Create X checkmark bitmap and initialize option menu checks.
1824 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1825 checkmark_bits, checkmark_width, checkmark_height);
1831 ReadBitmap(&wIconPixmap, "icon_white.bm",
1832 icon_white_bits, icon_white_width, icon_white_height);
1833 ReadBitmap(&bIconPixmap, "icon_black.bm",
1834 icon_black_bits, icon_black_width, icon_black_height);
1835 iconPixmap = wIconPixmap;
1837 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
1838 XtSetValues(shellWidget, args, i);
1841 * Create a cursor for the board widget.
1843 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1844 XChangeWindowAttributes(xDisplay, xBoardWindow,
1845 CWCursor, &window_attributes);
1848 * Inhibit shell resizing.
1850 shellArgs[0].value = (XtArgVal) &w;
1851 shellArgs[1].value = (XtArgVal) &h;
1852 XtGetValues(shellWidget, shellArgs, 2);
1853 shellArgs[4].value = shellArgs[2].value = w;
1854 shellArgs[5].value = shellArgs[3].value = h;
1855 XtSetValues(shellWidget, &shellArgs[2], 4);
1856 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1857 marginH = h - boardHeight;
1859 CatchDeleteWindow(shellWidget, "QuitProc");
1867 if (appData.animate || appData.animateDragging)
1870 XtAugmentTranslations(formWidget,
1871 XtParseTranslationTable(globalTranslations));
1872 XtAugmentTranslations(boardWidget,
1873 XtParseTranslationTable(boardTranslations));
1874 XtAugmentTranslations(whiteTimerWidget,
1875 XtParseTranslationTable(whiteTranslations));
1876 XtAugmentTranslations(blackTimerWidget,
1877 XtParseTranslationTable(blackTranslations));
1879 /* Why is the following needed on some versions of X instead
1880 * of a translation? */
1881 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
1882 (XtEventHandler) EventProc, NULL);
1884 XtAddEventHandler(formWidget, KeyPressMask, False,
1885 (XtEventHandler) MoveTypeInProc, NULL);
1886 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1887 (XtEventHandler) EventProc, NULL);
1889 /* [AS] Restore layout */
1890 if( wpMoveHistory.visible ) {
1894 if( wpEvalGraph.visible )
1899 if( wpEngineOutput.visible ) {
1900 EngineOutputPopUp();
1905 if (errorExitStatus == -1) {
1906 if (appData.icsActive) {
1907 /* We now wait until we see "login:" from the ICS before
1908 sending the logon script (problems with timestamp otherwise) */
1909 /*ICSInitScript();*/
1910 if (appData.icsInputBox) ICSInputBoxPopUp();
1914 signal(SIGWINCH, TermSizeSigHandler);
1916 signal(SIGINT, IntSigHandler);
1917 signal(SIGTERM, IntSigHandler);
1918 if (*appData.cmailGameName != NULLCHAR) {
1919 signal(SIGUSR1, CmailSigHandler);
1923 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1925 // XtSetKeyboardFocus(shellWidget, formWidget);
1926 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1928 XtAppMainLoop(appContext);
1929 if (appData.debugMode) fclose(debugFP); // [DM] debug
1934 TermSizeSigHandler (int sig)
1940 IntSigHandler (int sig)
1946 CmailSigHandler (int sig)
1951 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1953 /* Activate call-back function CmailSigHandlerCallBack() */
1954 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1956 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1960 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1963 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1965 /**** end signal code ****/
1968 #define Abs(n) ((n)<0 ? -(n) : (n))
1972 InsertPxlSize (char *pattern, int targetPxlSize)
1974 char *base_fnt_lst, strInt[12], *p, *q;
1975 int alternatives, i, len, strIntLen;
1978 * Replace the "*" (if present) in the pixel-size slot of each
1979 * alternative with the targetPxlSize.
1983 while ((p = strchr(p, ',')) != NULL) {
1987 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1988 strIntLen = strlen(strInt);
1989 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1993 while (alternatives--) {
1994 char *comma = strchr(p, ',');
1995 for (i=0; i<14; i++) {
1996 char *hyphen = strchr(p, '-');
1998 if (comma && hyphen > comma) break;
1999 len = hyphen + 1 - p;
2000 if (i == 7 && *p == '*' && len == 2) {
2002 memcpy(q, strInt, strIntLen);
2012 len = comma + 1 - p;
2019 return base_fnt_lst;
2023 CreateFontSet (char *base_fnt_lst)
2026 char **missing_list;
2030 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
2031 &missing_list, &missing_count, &def_string);
2032 if (appData.debugMode) {
2034 XFontStruct **font_struct_list;
2035 char **font_name_list;
2036 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
2038 fprintf(debugFP, " got list %s, locale %s\n",
2039 XBaseFontNameListOfFontSet(fntSet),
2040 XLocaleOfFontSet(fntSet));
2041 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
2042 for (i = 0; i < count; i++) {
2043 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
2046 for (i = 0; i < missing_count; i++) {
2047 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
2050 if (fntSet == NULL) {
2051 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
2056 #else // not ENABLE_NLS
2058 * Find a font that matches "pattern" that is as close as
2059 * possible to the targetPxlSize. Prefer fonts that are k
2060 * pixels smaller to fonts that are k pixels larger. The
2061 * pattern must be in the X Consortium standard format,
2062 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2063 * The return value should be freed with XtFree when no
2067 FindFont (char *pattern, int targetPxlSize)
2069 char **fonts, *p, *best, *scalable, *scalableTail;
2070 int i, j, nfonts, minerr, err, pxlSize;
2072 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2074 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2075 programName, pattern);
2082 for (i=0; i<nfonts; i++) {
2085 if (*p != '-') continue;
2087 if (*p == NULLCHAR) break;
2088 if (*p++ == '-') j++;
2090 if (j < 7) continue;
2093 scalable = fonts[i];
2096 err = pxlSize - targetPxlSize;
2097 if (Abs(err) < Abs(minerr) ||
2098 (minerr > 0 && err < 0 && -err == minerr)) {
2104 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2105 /* If the error is too big and there is a scalable font,
2106 use the scalable font. */
2107 int headlen = scalableTail - scalable;
2108 p = (char *) XtMalloc(strlen(scalable) + 10);
2109 while (isdigit(*scalableTail)) scalableTail++;
2110 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2112 p = (char *) XtMalloc(strlen(best) + 2);
2113 safeStrCpy(p, best, strlen(best)+1 );
2115 if (appData.debugMode) {
2116 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2117 pattern, targetPxlSize, p);
2119 XFreeFontNames(fonts);
2126 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
2127 // must be called before all non-first callse to CreateGCs()
2128 XtReleaseGC(shellWidget, highlineGC);
2129 XtReleaseGC(shellWidget, lightSquareGC);
2130 XtReleaseGC(shellWidget, darkSquareGC);
2131 XtReleaseGC(shellWidget, lineGC);
2132 if (appData.monoMode) {
2133 if (DefaultDepth(xDisplay, xScreen) == 1) {
2134 XtReleaseGC(shellWidget, wbPieceGC);
2136 XtReleaseGC(shellWidget, bwPieceGC);
2139 XtReleaseGC(shellWidget, prelineGC);
2140 XtReleaseGC(shellWidget, wdPieceGC);
2141 XtReleaseGC(shellWidget, wlPieceGC);
2142 XtReleaseGC(shellWidget, bdPieceGC);
2143 XtReleaseGC(shellWidget, blPieceGC);
2148 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
2150 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2151 | GCBackground | GCFunction | GCPlaneMask;
2152 gc_values->foreground = foreground;
2153 gc_values->background = background;
2154 return XtGetGC(shellWidget, value_mask, gc_values);
2158 CreateGCs (int redo)
2160 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2161 | GCBackground | GCFunction | GCPlaneMask;
2162 XGCValues gc_values;
2164 Pixel white = XWhitePixel(xDisplay, xScreen);
2165 Pixel black = XBlackPixel(xDisplay, xScreen);
2167 gc_values.plane_mask = AllPlanes;
2168 gc_values.line_width = lineGap;
2169 gc_values.line_style = LineSolid;
2170 gc_values.function = GXcopy;
2173 DeleteGCs(); // called a second time; clean up old GCs first
2174 } else { // [HGM] grid and font GCs created on first call only
2175 coordGC = CreateOneGC(&gc_values, black, white);
2176 XSetFont(xDisplay, coordGC, coordFontID);
2178 // [HGM] make font for holdings counts (white on black)
2179 countGC = CreateOneGC(&gc_values, white, black);
2180 XSetFont(xDisplay, countGC, countFontID);
2182 lineGC = CreateOneGC(&gc_values, black, black);
2184 if (appData.monoMode) {
2186 highlineGC = CreateOneGC(&gc_values, white, white);
2187 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
2188 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
2190 if (DefaultDepth(xDisplay, xScreen) == 1) {
2191 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2192 gc_values.function = GXcopyInverted;
2193 copyInvertedGC = CreateOneGC(&gc_values, black, white);
2194 gc_values.function = GXcopy;
2195 if (XBlackPixel(xDisplay, xScreen) == 1) {
2196 bwPieceGC = darkSquareGC;
2197 wbPieceGC = copyInvertedGC;
2199 bwPieceGC = copyInvertedGC;
2200 wbPieceGC = lightSquareGC;
2205 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
2206 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
2207 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
2208 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
2209 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
2210 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
2211 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
2212 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
2217 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
2225 fp = fopen(filename, "rb");
2227 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
2234 for (y=0; y<h; ++y) {
2235 for (x=0; x<h; ++x) {
2240 XPutPixel(xim, x, y, blackPieceColor);
2242 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2245 XPutPixel(xim, x, y, darkSquareColor);
2247 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2250 XPutPixel(xim, x, y, whitePieceColor);
2252 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2255 XPutPixel(xim, x, y, lightSquareColor);
2257 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2265 /* create Pixmap of piece */
2266 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2268 XPutImage(xDisplay, *dest, lightSquareGC, xim,
2271 /* create Pixmap of clipmask
2272 Note: We assume the white/black pieces have the same
2273 outline, so we make only 6 masks. This is okay
2274 since the XPM clipmask routines do the same. */
2276 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2278 XPutImage(xDisplay, temp, lightSquareGC, xmask,
2281 /* now create the 1-bit version */
2282 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2285 values.foreground = 1;
2286 values.background = 0;
2288 /* Don't use XtGetGC, not read only */
2289 maskGC = XCreateGC(xDisplay, *mask,
2290 GCForeground | GCBackground, &values);
2291 XCopyPlane(xDisplay, temp, *mask, maskGC,
2292 0, 0, squareSize, squareSize, 0, 0, 1);
2293 XFreePixmap(xDisplay, temp);
2298 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
2306 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
2311 /* The XSynchronize calls were copied from CreatePieces.
2312 Not sure if needed, but can't hurt */
2313 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2316 /* temp needed by loadXIM() */
2317 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2318 0, 0, ss, ss, AllPlanes, XYPixmap);
2320 if (strlen(appData.pixmapDirectory) == 0) {
2324 if (appData.monoMode) {
2325 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
2329 fprintf(stderr, _("\nLoading XIMs...\n"));
2331 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2332 fprintf(stderr, "%d", piece+1);
2333 for (kind=0; kind<4; kind++) {
2334 fprintf(stderr, ".");
2335 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2336 ExpandPathName(appData.pixmapDirectory),
2337 piece <= (int) WhiteKing ? "" : "w",
2338 pieceBitmapNames[piece],
2340 ximPieceBitmap[kind][piece] =
2341 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2342 0, 0, ss, ss, AllPlanes, XYPixmap);
2343 if (appData.debugMode)
2344 fprintf(stderr, _("(File:%s:) "), buf);
2345 loadXIM(ximPieceBitmap[kind][piece],
2347 &(xpmPieceBitmap2[kind][piece]),
2348 &(ximMaskPm2[piece]));
2349 if(piece <= (int)WhiteKing)
2350 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2352 fprintf(stderr," ");
2354 /* Load light and dark squares */
2355 /* If the LSQ and DSQ pieces don't exist, we will
2356 draw them with solid squares. */
2357 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2358 if (access(buf, 0) != 0) {
2362 fprintf(stderr, _("light square "));
2364 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2365 0, 0, ss, ss, AllPlanes, XYPixmap);
2366 if (appData.debugMode)
2367 fprintf(stderr, _("(File:%s:) "), buf);
2369 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2370 fprintf(stderr, _("dark square "));
2371 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2372 ExpandPathName(appData.pixmapDirectory), ss);
2373 if (appData.debugMode)
2374 fprintf(stderr, _("(File:%s:) "), buf);
2376 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2377 0, 0, ss, ss, AllPlanes, XYPixmap);
2378 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2379 xpmJailSquare = xpmLightSquare;
2381 fprintf(stderr, _("Done.\n"));
2383 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2386 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2390 CreateXPMBoard (char *s, int kind)
2394 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2395 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2396 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2402 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2403 // thisroutine has to be called t free the old piece pixmaps
2405 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2406 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2408 XFreePixmap(xDisplay, xpmLightSquare);
2409 XFreePixmap(xDisplay, xpmDarkSquare);
2418 u_int ss = squareSize;
2420 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2421 XpmColorSymbol symbols[4];
2422 static int redo = False;
2424 if(redo) FreeXPMPieces(); else redo = 1;
2426 /* The XSynchronize calls were copied from CreatePieces.
2427 Not sure if needed, but can't hurt */
2428 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2430 /* Setup translations so piece colors match square colors */
2431 symbols[0].name = "light_piece";
2432 symbols[0].value = appData.whitePieceColor;
2433 symbols[1].name = "dark_piece";
2434 symbols[1].value = appData.blackPieceColor;
2435 symbols[2].name = "light_square";
2436 symbols[2].value = appData.lightSquareColor;
2437 symbols[3].name = "dark_square";
2438 symbols[3].value = appData.darkSquareColor;
2440 attr.valuemask = XpmColorSymbols;
2441 attr.colorsymbols = symbols;
2442 attr.numsymbols = 4;
2444 if (appData.monoMode) {
2445 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2449 if (strlen(appData.pixmapDirectory) == 0) {
2450 XpmPieces* pieces = builtInXpms;
2453 while (pieces->size != squareSize && pieces->size) pieces++;
2454 if (!pieces->size) {
2455 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2458 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2459 for (kind=0; kind<4; kind++) {
2461 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2462 pieces->xpm[piece][kind],
2463 &(xpmPieceBitmap2[kind][piece]),
2464 NULL, &attr)) != 0) {
2465 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2469 if(piece <= (int) WhiteKing)
2470 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2474 xpmJailSquare = xpmLightSquare;
2478 fprintf(stderr, _("\nLoading XPMs...\n"));
2481 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2482 fprintf(stderr, "%d ", piece+1);
2483 for (kind=0; kind<4; kind++) {
2484 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2485 ExpandPathName(appData.pixmapDirectory),
2486 piece > (int) WhiteKing ? "w" : "",
2487 pieceBitmapNames[piece],
2489 if (appData.debugMode) {
2490 fprintf(stderr, _("(File:%s:) "), buf);
2492 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2493 &(xpmPieceBitmap2[kind][piece]),
2494 NULL, &attr)) != 0) {
2495 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2496 // [HGM] missing: read of unorthodox piece failed; substitute King.
2497 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2498 ExpandPathName(appData.pixmapDirectory),
2500 if (appData.debugMode) {
2501 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2503 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2504 &(xpmPieceBitmap2[kind][piece]),
2508 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2513 if(piece <= (int) WhiteKing)
2514 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2517 /* Load light and dark squares */
2518 /* If the LSQ and DSQ pieces don't exist, we will
2519 draw them with solid squares. */
2520 fprintf(stderr, _("light square "));
2521 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2522 if (access(buf, 0) != 0) {
2526 if (appData.debugMode)
2527 fprintf(stderr, _("(File:%s:) "), buf);
2529 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2530 &xpmLightSquare, NULL, &attr)) != 0) {
2531 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2534 fprintf(stderr, _("dark square "));
2535 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2536 ExpandPathName(appData.pixmapDirectory), ss);
2537 if (appData.debugMode) {
2538 fprintf(stderr, _("(File:%s:) "), buf);
2540 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2541 &xpmDarkSquare, NULL, &attr)) != 0) {
2542 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2546 xpmJailSquare = xpmLightSquare;
2547 fprintf(stderr, _("Done.\n"));
2549 oldVariant = -1; // kludge to force re-makig of animation masks
2550 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2553 #endif /* HAVE_LIBXPM */
2556 /* No built-in bitmaps */
2561 u_int ss = squareSize;
2563 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2566 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2567 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2568 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2569 pieceBitmapNames[piece],
2570 ss, kind == SOLID ? 's' : 'o');
2571 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2572 if(piece <= (int)WhiteKing)
2573 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2577 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2581 /* With built-in bitmaps */
2585 BuiltInBits* bib = builtInBits;
2588 u_int ss = squareSize;
2590 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2593 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2595 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2596 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2597 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2598 pieceBitmapNames[piece],
2599 ss, kind == SOLID ? 's' : 'o');
2600 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2601 bib->bits[kind][piece], ss, ss);
2602 if(piece <= (int)WhiteKing)
2603 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2607 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2613 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2618 char msg[MSG_SIZ], fullname[MSG_SIZ];
2620 if (*appData.bitmapDirectory != NULLCHAR) {
2621 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2622 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2623 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2624 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2625 &w, &h, pm, &x_hot, &y_hot);
2626 fprintf(stderr, "load %s\n", name);
2627 if (errcode != BitmapSuccess) {
2629 case BitmapOpenFailed:
2630 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2632 case BitmapFileInvalid:
2633 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2635 case BitmapNoMemory:
2636 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2640 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2644 fprintf(stderr, _("%s: %s...using built-in\n"),
2646 } else if (w != wreq || h != hreq) {
2648 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2649 programName, fullname, w, h, wreq, hreq);
2655 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2665 if (lineGap == 0) return;
2667 /* [HR] Split this into 2 loops for non-square boards. */
2669 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2670 gridSegments[i].x1 = 0;
2671 gridSegments[i].x2 =
2672 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2673 gridSegments[i].y1 = gridSegments[i].y2
2674 = lineGap / 2 + (i * (squareSize + lineGap));
2677 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2678 gridSegments[j + i].y1 = 0;
2679 gridSegments[j + i].y2 =
2680 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2681 gridSegments[j + i].x1 = gridSegments[j + i].x2
2682 = lineGap / 2 + (j * (squareSize + lineGap));
2686 int nrOfMenuItems = 7;
2687 Widget menuWidget[150];
2688 MenuListItem menuItemList[150] = {
2689 { "LoadNextGameProc", LoadNextGameProc },
2690 { "LoadPrevGameProc", LoadPrevGameProc },
2691 { "ReloadGameProc", ReloadGameProc },
2692 { "ReloadPositionProc", ReloadPositionProc },
2693 #ifndef OPTIONSDIALOG
2694 { "AlwaysQueenProc", AlwaysQueenProc },
2695 { "AnimateDraggingProc", AnimateDraggingProc },
2696 { "AnimateMovingProc", AnimateMovingProc },
2697 { "AutoflagProc", AutoflagProc },
2698 { "AutoflipProc", AutoflipProc },
2699 { "BlindfoldProc", BlindfoldProc },
2700 { "FlashMovesProc", FlashMovesProc },
2702 { "HighlightDraggingProc", HighlightDraggingProc },
2704 { "HighlightLastMoveProc", HighlightLastMoveProc },
2705 // { "IcsAlarmProc", IcsAlarmProc },
2706 { "MoveSoundProc", MoveSoundProc },
2707 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
2708 { "PopupExitMessageProc", PopupExitMessageProc },
2709 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
2710 // { "PremoveProc", PremoveProc },
2711 { "ShowCoordsProc", ShowCoordsProc },
2712 { "ShowThinkingProc", ShowThinkingProc },
2713 { "HideThinkingProc", HideThinkingProc },
2714 { "TestLegalityProc", TestLegalityProc },
2716 { "AboutGameProc", AboutGameEvent },
2717 { "DebugProc", DebugProc },
2718 { "NothingProc", NothingProc },
2723 MarkMenuItem (char *menuRef, int state)
2725 int nr = MenuToNumber(menuRef);
2728 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2729 XtSetValues(menuWidget[nr], args, 1);
2734 EnableMenuItem (char *menuRef, int state)
2736 int nr = MenuToNumber(menuRef);
2737 if(nr >= 0) XtSetSensitive(menuWidget[nr], state);
2741 EnableButtonBar (int state)
2743 XtSetSensitive(buttonBarWidget, state);
2748 SetMenuEnables (Enables *enab)
2750 while (enab->name != NULL) {
2751 EnableMenuItem(enab->name, enab->value);
2757 Equal(char *p, char *s)
2758 { // compare strings skipping spaces in second
2760 if(*s == ' ') { s++; continue; }
2761 if(*s++ != *p++) return 0;
2767 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2768 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2770 if(*nprms == 0) return;
2771 for(i=0; menuItemList[i].name; i++) {
2772 if(Equal(prms[0], menuItemList[i].name)) {
2773 (menuItemList[i].proc) ();
2780 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
2782 MenuProc *proc = (MenuProc *) addr;
2788 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2790 RecentEngineEvent((int) (intptr_t) addr);
2793 // some stuff that must remain in front-end
2794 static Widget mainBar, currentMenu;
2795 static int wtot, nr = 0, widths[10];
2798 AppendMenuItem (char *text, char *name, MenuProc *action)
2805 XtSetArg(args[j], XtNleftMargin, 20); j++;
2806 XtSetArg(args[j], XtNrightMargin, 20); j++;
2808 if (strcmp(text, "----") == 0) {
2809 entry = XtCreateManagedWidget(text, smeLineObjectClass,
2810 currentMenu, args, j);
2812 XtSetArg(args[j], XtNlabel, XtNewString(_(text)));
2813 entry = XtCreateManagedWidget(name, smeBSBObjectClass,
2814 currentMenu, args, j+1);
2815 XtAddCallback(entry, XtNcallback,
2816 (XtCallbackProc) (strcmp(name, "recent") ? MenuBarSelect : MenuEngineSelect),
2818 menuWidget[nrOfMenuItems] = entry;
2823 CreateMenuButton (char *name, Menu *mb)
2824 { // create menu button on main bar, and shell for pull-down list
2830 XtSetArg(args[j], XtNmenuName, XtNewString(name)); j++;
2831 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
2832 XtSetArg(args[j], XtNborderWidth, 0); j++;
2833 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
2835 currentMenu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2838 XtSetArg(args[j], XtNwidth, &w); j++;
2839 XtGetValues(mb->subMenu, args, j);
2840 wtot += mb->textWidth = widths[nr++] = w;
2844 CreateMenuBar (Menu *mb, int boardWidth)
2848 char menuName[MSG_SIZ];
2852 // create bar itself
2854 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2855 XtSetArg(args[j], XtNvSpace, 0); j++;
2856 XtSetArg(args[j], XtNborderWidth, 0); j++;
2857 mainBar = XtCreateWidget("menuBar", boxWidgetClass,
2858 formWidget, args, j);
2860 CreateMainMenus(mb); // put menus in bar according to description in back-end
2862 // size buttons to make menu bar fit, clipping menu names where necessary
2863 while(wtot > boardWidth - 40) {
2865 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
2869 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
2871 XtSetArg(args[j], XtNwidth, widths[i]); j++;
2872 XtSetValues(ma[i].subMenu, args, j);
2879 CreateButtonBar (MenuItem *mi)
2882 Widget button, buttonBar;
2886 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2888 XtSetArg(args[j], XtNhSpace, 0); j++;
2890 XtSetArg(args[j], XtNborderWidth, 0); j++;
2891 XtSetArg(args[j], XtNvSpace, 0); j++;
2892 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
2893 formWidget, args, j);
2895 while (mi->string != NULL) {
2898 XtSetArg(args[j], XtNinternalWidth, 2); j++;
2899 XtSetArg(args[j], XtNborderWidth, 0); j++;
2901 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
2902 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
2903 buttonBar, args, j);
2904 XtAddCallback(button, XtNcallback,
2905 (XtCallbackProc) MenuBarSelect,
2906 (caddr_t) mi->proc);
2913 CreatePieceMenu (char *name, int color)
2918 ChessSquare selection;
2920 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2921 boardWidget, args, 0);
2923 for (i = 0; i < PIECE_MENU_SIZE; i++) {
2924 String item = pieceMenuStrings[color][i];
2926 if (strcmp(item, "----") == 0) {
2927 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2930 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2931 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2933 selection = pieceMenuTranslation[color][i];
2934 XtAddCallback(entry, XtNcallback,
2935 (XtCallbackProc) PieceMenuSelect,
2936 (caddr_t) selection);
2937 if (selection == WhitePawn || selection == BlackPawn) {
2938 XtSetArg(args[0], XtNpopupOnEntry, entry);
2939 XtSetValues(menu, args, 1);
2952 ChessSquare selection;
2954 whitePieceMenu = CreatePieceMenu("menuW", 0);
2955 blackPieceMenu = CreatePieceMenu("menuB", 1);
2957 if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
2958 XtRegisterGrabAction(PieceMenuPopup, True,
2959 (unsigned)(ButtonPressMask|ButtonReleaseMask),
2960 GrabModeAsync, GrabModeAsync);
2962 XtSetArg(args[0], XtNlabel, _("Drop"));
2963 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
2964 boardWidget, args, 1);
2965 for (i = 0; i < DROP_MENU_SIZE; i++) {
2966 String item = dropMenuStrings[i];
2968 if (strcmp(item, "----") == 0) {
2969 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2972 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2973 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2975 selection = dropMenuTranslation[i];
2976 XtAddCallback(entry, XtNcallback,
2977 (XtCallbackProc) DropMenuSelect,
2978 (caddr_t) selection);
2992 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2993 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2994 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2995 dmEnables[i].piece);
2996 XtSetSensitive(entry, p != NULL || !appData.testLegality
2997 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2998 && !appData.icsActive));
3000 while (p && *p++ == dmEnables[i].piece) count++;
3001 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3003 XtSetArg(args[j], XtNlabel, label); j++;
3004 XtSetValues(entry, args, j);
3009 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
3011 String whichMenu; int menuNr = -2;
3012 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3013 if (event->type == ButtonRelease)
3014 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3015 else if (event->type == ButtonPress)
3016 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3018 case 0: whichMenu = params[0]; break;
3019 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3021 case -1: ErrorPopDown();
3024 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3028 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3030 if (pmFromX < 0 || pmFromY < 0) return;
3031 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3035 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3037 if (pmFromX < 0 || pmFromY < 0) return;
3038 DropMenuEvent(piece, pmFromX, pmFromY);
3042 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3044 shiftKey = prms[0][0] & 1;
3049 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3051 shiftKey = prms[0][0] & 1;
3057 do_flash_delay (unsigned long msec)
3063 DrawBorder (int x, int y, int type)
3067 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
3069 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3070 squareSize+lineGap, squareSize+lineGap);
3074 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
3076 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
3077 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
3079 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
3080 if(textureW[kind] < W*squareSize)
3081 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
3083 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
3084 if(textureH[kind] < H*squareSize)
3085 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
3087 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
3092 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
3093 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
3095 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
3096 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
3097 squareSize, squareSize, x*fac, y*fac);
3099 if (useImages && useImageSqs) {
3103 pm = xpmLightSquare;
3108 case 2: /* neutral */
3110 pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
3113 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3114 squareSize, squareSize, x*fac, y*fac);
3124 case 2: /* neutral */
3129 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
3134 I split out the routines to draw a piece so that I could
3135 make a generic flash routine.
3138 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3140 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3141 switch (square_color) {
3143 case 2: /* neutral */
3145 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3146 ? *pieceToOutline(piece)
3147 : *pieceToSolid(piece),
3148 dest, bwPieceGC, 0, 0,
3149 squareSize, squareSize, x, y);
3152 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3153 ? *pieceToSolid(piece)
3154 : *pieceToOutline(piece),
3155 dest, wbPieceGC, 0, 0,
3156 squareSize, squareSize, x, y);
3162 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3164 switch (square_color) {
3166 case 2: /* neutral */
3168 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3169 ? *pieceToOutline(piece)
3170 : *pieceToSolid(piece),
3171 dest, bwPieceGC, 0, 0,
3172 squareSize, squareSize, x, y, 1);
3175 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3176 ? *pieceToSolid(piece)
3177 : *pieceToOutline(piece),
3178 dest, wbPieceGC, 0, 0,
3179 squareSize, squareSize, x, y, 1);
3185 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3187 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
3188 switch (square_color) {
3190 XCopyPlane(xDisplay, *pieceToSolid(piece),
3191 dest, (int) piece < (int) BlackPawn
3192 ? wlPieceGC : blPieceGC, 0, 0,
3193 squareSize, squareSize, x, y, 1);
3196 XCopyPlane(xDisplay, *pieceToSolid(piece),
3197 dest, (int) piece < (int) BlackPawn
3198 ? wdPieceGC : bdPieceGC, 0, 0,
3199 squareSize, squareSize, x, y, 1);
3201 case 2: /* neutral */
3203 break; // should never contain pieces
3208 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3210 int kind, p = piece;
3212 switch (square_color) {
3214 case 2: /* neutral */
3216 if ((int)piece < (int) BlackPawn) {
3224 if ((int)piece < (int) BlackPawn) {
3232 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
3233 if(useTexture & square_color+1) {
3234 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
3235 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
3236 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
3237 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
3238 XSetClipMask(xDisplay, wlPieceGC, None);
3239 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
3241 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3242 dest, wlPieceGC, 0, 0,
3243 squareSize, squareSize, x, y);
3246 typedef void (*DrawFunc)();
3251 if (appData.monoMode) {
3252 if (DefaultDepth(xDisplay, xScreen) == 1) {
3253 return monoDrawPiece_1bit;
3255 return monoDrawPiece;
3259 return colorDrawPieceImage;
3261 return colorDrawPiece;
3266 DrawDot (int marker, int x, int y, int r)
3268 if(appData.monoMode) {
3269 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
3270 x, y, r, r, 0, 64*360);
3271 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
3272 x, y, r, r, 0, 64*360);
3274 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
3275 x, y, r, r, 0, 64*360);
3279 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
3280 { // basic front-end board-draw function: takes care of everything that can be in square:
3281 // piece, background, coordinate/count, marker dot
3282 int direction, font_ascent, font_descent;
3283 XCharStruct overall;
3286 if (piece == EmptySquare) {
3287 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
3289 drawfunc = ChooseDrawFunc();
3290 drawfunc(piece, square_color, x, y, xBoardWindow);
3293 if(align) { // square carries inscription (coord or piece count)
3295 GC hGC = align < 3 ? coordGC : countGC;
3296 // first calculate where it goes
3297 XTextExtents(countFontStruct, string, 1, &direction,
3298 &font_ascent, &font_descent, &overall);
3300 xx += squareSize - overall.width - 2;
3301 yy += squareSize - font_descent - 1;
3302 } else if (align == 2) {
3303 xx += 2, yy += font_ascent + 1;
3304 } else if (align == 3) {
3305 xx += squareSize - overall.width - 2;
3306 yy += font_ascent + 1;
3307 } else if (align == 4) {
3308 xx += 2, yy += font_ascent + 1;
3311 if (appData.monoMode) {
3312 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3314 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3318 if(marker) { // print fat marker dot, if requested
3319 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
3324 FlashDelay (int flash_delay)
3326 XSync(xDisplay, False);
3327 if(flash_delay) do_flash_delay(flash_delay);
3331 Fraction (int x, int start, int stop)
3333 double f = ((double) x - start)/(stop - start);
3334 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
3338 static WindowPlacement wpNew;
3341 CoDrag (Widget sh, WindowPlacement *wp)
3344 int j=0, touch=0, fudge = 2;
3345 GetActualPlacement(sh, wp);
3346 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
3347 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
3348 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
3349 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
3350 if(!touch ) return; // only windows that touch co-move
3351 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
3352 int heightInc = wpNew.height - wpMain.height;
3353 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3354 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3355 wp->y += fracTop * heightInc;
3356 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
3357 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
3358 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
3359 int widthInc = wpNew.width - wpMain.width;
3360 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3361 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3362 wp->y += fracLeft * widthInc;
3363 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
3364 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
3366 wp->x += wpNew.x - wpMain.x;
3367 wp->y += wpNew.y - wpMain.y;
3368 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
3369 if(touch == 3) wp->y += wpNew.height - wpMain.height;
3370 XtSetArg(args[j], XtNx, wp->x); j++;
3371 XtSetArg(args[j], XtNy, wp->y); j++;
3372 XtSetValues(sh, args, j);
3375 static XtIntervalId delayedDragID = 0;
3380 GetActualPlacement(shellWidget, &wpNew);
3381 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
3382 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
3383 return; // false alarm
3384 if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
3385 if(MoveHistoryIsUp()) CoDrag(shells[HistoryDlg], &wpMoveHistory);
3386 if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
3387 if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
3389 DrawPosition(True, NULL);
3390 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
3397 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
3399 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
3402 /* Why is this needed on some versions of X? */
3404 EventProc (Widget widget, caddr_t unused, XEvent *event)
3406 if (!XtIsRealized(widget))
3408 switch (event->type) {
3409 case ConfigureNotify: // main window is being dragged: drag attached windows with it
3410 if(appData.useStickyWindows)
3411 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
3414 if (event->xexpose.count > 0) return; /* no clipping is done */
3415 DrawPosition(True, NULL);
3416 if(twoBoards) { // [HGM] dual: draw other board in other orientation
3417 flipView = !flipView; partnerUp = !partnerUp;
3418 DrawPosition(True, NULL);
3419 flipView = !flipView; partnerUp = !partnerUp;
3423 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
3430 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
3432 DrawSeekAxis (int x, int y, int xTo, int yTo)
3434 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
3438 DrawSeekBackground (int left, int top, int right, int bottom)
3440 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
3444 DrawSeekText (char *buf, int x, int y)
3446 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
3450 DrawSeekDot (int x, int y, int colorNr)
3452 int square = colorNr & 0x80;
3455 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
3457 XFillRectangle(xDisplay, xBoardWindow, color,
3458 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3460 XFillArc(xDisplay, xBoardWindow, color,
3461 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
3465 DrawGrid (int second)
3467 XDrawSegments(xDisplay, xBoardWindow, lineGC,
3468 second ? secondSegments : // [HGM] dual
3469 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
3474 * event handler for redrawing the board
3477 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3479 DrawPosition(True, NULL);
3484 * event handler for parsing user moves
3486 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
3487 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
3488 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
3489 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
3490 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
3491 // and at the end FinishMove() to perform the move after optional promotion popups.
3492 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
3494 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3496 if (w != boardWidget || errorExitStatus != -1) return;
3497 if(nprms) shiftKey = !strcmp(prms[0], "1");
3499 if (shellUp[PromoDlg]) { // [HGM] is this still needed?
3500 if (event->type == ButtonPress) {
3509 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
3510 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
3511 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
3515 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
3517 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
3518 DragPieceMove(event->xmotion.x, event->xmotion.y);
3522 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3523 { // [HGM] pv: walk PV
3524 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3527 static int savedIndex; /* gross that this is global */
3530 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3533 XawTextPosition index, dummy;
3536 XawTextGetSelectionPos(w, &index, &dummy);
3537 XtSetArg(arg, XtNstring, &val);
3538 XtGetValues(w, &arg, 1);
3539 ReplaceComment(savedIndex, val);
3540 if(savedIndex != currentMove) ToNrEvent(savedIndex);
3541 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3545 EditCommentPopUp (int index, char *title, char *text)
3548 if (text == NULL) text = "";
3549 NewCommentPopup(title, text, index);
3553 CommentPopUp (char *title, char *text)
3555 savedIndex = currentMove; // [HGM] vari
3556 NewCommentPopup(title, text, currentMove);
3562 PopDown(CommentDlg);
3565 static char *openName;
3571 (void) (*fileProc)(openFP, 0, openName);
3575 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
3577 fileProc = proc; /* I can't see a way not */
3578 fileOpenMode = openMode; /* to use globals here */
3579 { // [HGM] use file-selector dialog stolen from Ghostview
3580 int index; // this is not supported yet
3581 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
3582 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
3583 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
3584 ScheduleDelayedEvent(&DelayedLoad, 50);
3589 /* Disable all user input other than deleting the window */
3590 static int frozen = 0;
3596 /* Grab by a widget that doesn't accept input */
3597 XtAddGrab(messageWidget, TRUE, FALSE);
3601 /* Undo a FreezeUI */
3605 if (!frozen) return;
3606 XtRemoveGrab(messageWidget);
3614 static int oldPausing = FALSE;
3615 static GameMode oldmode = (GameMode) -1;
3618 if (!boardWidget || !XtIsRealized(boardWidget)) return;
3620 if (pausing != oldPausing) {
3621 oldPausing = pausing;
3622 MarkMenuItem("Pause", pausing);
3624 if (appData.showButtonBar) {
3625 /* Always toggle, don't set. Previous code messes up when
3626 invoked while the button is pressed, as releasing it
3627 toggles the state again. */
3630 XtSetArg(args[0], XtNbackground, &oldbg);
3631 XtSetArg(args[1], XtNforeground, &oldfg);
3632 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
3634 XtSetArg(args[0], XtNbackground, oldfg);
3635 XtSetArg(args[1], XtNforeground, oldbg);
3637 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
3641 wname = ModeToWidgetName(oldmode);
3642 if (wname != NULL) {
3643 MarkMenuItem(wname, False);
3645 wname = ModeToWidgetName(gameMode);
3646 if (wname != NULL) {
3647 MarkMenuItem(wname, True);
3650 MarkMenuItem("Machine Match", matchMode && matchGame < appData.matchGames);
3652 /* Maybe all the enables should be handled here, not just this one */
3653 EnableMenuItem("Training", gameMode == Training || gameMode == PlayFromGameFile);
3658 * Button/menu procedures
3661 LoadGamePopUp (FILE *f, int gameNumber, char *title)
3663 cmailMsgLoaded = FALSE;
3664 if (gameNumber == 0) {
3665 int error = GameListBuild(f);