2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
66 # if HAVE_SYS_SOCKET_H
67 # include <sys/socket.h>
68 # include <netinet/in.h>
70 # else /* not HAVE_SYS_SOCKET_H */
71 # if HAVE_LAN_SOCKET_H
72 # include <lan/socket.h>
74 # include <lan/netdb.h>
75 # else /* not HAVE_LAN_SOCKET_H */
76 # define OMIT_SOCKETS 1
77 # endif /* not HAVE_LAN_SOCKET_H */
78 # endif /* not HAVE_SYS_SOCKET_H */
79 #endif /* !OMIT_SOCKETS */
84 #else /* not STDC_HEADERS */
85 extern char *getenv();
88 # else /* not HAVE_STRING_H */
90 # endif /* not HAVE_STRING_H */
91 #endif /* not STDC_HEADERS */
94 # include <sys/fcntl.h>
95 #else /* not HAVE_SYS_FCNTL_H */
98 # endif /* HAVE_FCNTL_H */
99 #endif /* not HAVE_SYS_FCNTL_H */
101 #if HAVE_SYS_SYSTEMINFO_H
102 # include <sys/systeminfo.h>
103 #endif /* HAVE_SYS_SYSTEMINFO_H */
105 #if TIME_WITH_SYS_TIME
106 # include <sys/time.h>
110 # include <sys/time.h>
121 # include <sys/wait.h>
126 # define NAMLEN(dirent) strlen((dirent)->d_name)
127 # define HAVE_DIR_STRUCT
129 # define dirent direct
130 # define NAMLEN(dirent) (dirent)->d_namlen
132 # include <sys/ndir.h>
133 # define HAVE_DIR_STRUCT
136 # include <sys/dir.h>
137 # define HAVE_DIR_STRUCT
141 # define HAVE_DIR_STRUCT
149 #include <X11/Intrinsic.h>
150 #include <X11/StringDefs.h>
151 #include <X11/Shell.h>
152 #include <X11/cursorfont.h>
153 #include <X11/Xatom.h>
154 #include <X11/Xmu/Atoms.h>
156 #include <X11/Xaw3d/Dialog.h>
157 #include <X11/Xaw3d/Form.h>
158 #include <X11/Xaw3d/List.h>
159 #include <X11/Xaw3d/Label.h>
160 #include <X11/Xaw3d/SimpleMenu.h>
161 #include <X11/Xaw3d/SmeBSB.h>
162 #include <X11/Xaw3d/SmeLine.h>
163 #include <X11/Xaw3d/Box.h>
164 #include <X11/Xaw3d/MenuButton.h>
165 #include <X11/Xaw3d/Text.h>
166 #include <X11/Xaw3d/AsciiText.h>
168 #include <X11/Xaw/Dialog.h>
169 #include <X11/Xaw/Form.h>
170 #include <X11/Xaw/List.h>
171 #include <X11/Xaw/Label.h>
172 #include <X11/Xaw/SimpleMenu.h>
173 #include <X11/Xaw/SmeBSB.h>
174 #include <X11/Xaw/SmeLine.h>
175 #include <X11/Xaw/Box.h>
176 #include <X11/Xaw/MenuButton.h>
177 #include <X11/Xaw/Text.h>
178 #include <X11/Xaw/AsciiText.h>
181 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
186 #include "pixmaps/pixmaps.h"
187 #define IMAGE_EXT "xpm"
189 #define IMAGE_EXT "xim"
190 #include "bitmaps/bitmaps.h"
193 #include "bitmaps/icon_white.bm"
194 #include "bitmaps/icon_black.bm"
195 #include "bitmaps/checkmark.bm"
197 #include "frontend.h"
199 #include "backendz.h"
203 #include "xgamelist.h"
204 #include "xhistory.h"
205 #include "xedittags.h"
216 #define usleep(t) _sleep2(((t)+500)/1000)
220 # define _(s) gettext (s)
221 # define N_(s) gettext_noop (s)
227 int main P((int argc, char **argv));
228 FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
229 char *init_path, char *filter, char *mode, int (*show_entry)(), char **name_return));
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 int EventToSquare P((int x, int limit));
256 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
257 void DelayedDrag P((void));
258 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
259 void HandleUserMove P((Widget w, XEvent *event,
260 String *prms, Cardinal *nprms));
261 void AnimateUserMove P((Widget w, XEvent * event,
262 String * params, Cardinal * nParams));
263 void HandlePV P((Widget w, XEvent * event,
264 String * params, Cardinal * nParams));
265 void SelectPV P((Widget w, XEvent * event,
266 String * params, Cardinal * nParams));
267 void StopPV P((Widget w, XEvent * event,
268 String * params, Cardinal * nParams));
269 void WhiteClock P((Widget w, XEvent *event,
270 String *prms, Cardinal *nprms));
271 void BlackClock P((Widget w, XEvent *event,
272 String *prms, Cardinal *nprms));
273 void DrawPositionProc P((Widget w, XEvent *event,
274 String *prms, Cardinal *nprms));
275 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
277 void CommentClick P((Widget w, XEvent * event,
278 String * params, Cardinal * nParams));
279 void CommentPopUp P((char *title, char *label));
280 void CommentPopDown P((void));
281 void ICSInputBoxPopUp P((void));
282 void ICSInputBoxPopDown P((void));
283 void FileNamePopUp P((char *label, char *def, char *filter,
284 FileProc proc, char *openMode));
285 void FileNamePopDown P((void));
286 void FileNameCallback P((Widget w, XtPointer client_data,
287 XtPointer call_data));
288 void FileNameAction P((Widget w, XEvent *event,
289 String *prms, Cardinal *nprms));
290 void AskQuestionReplyAction P((Widget w, XEvent *event,
291 String *prms, Cardinal *nprms));
292 void AskQuestionProc P((Widget w, XEvent *event,
293 String *prms, Cardinal *nprms));
294 void AskQuestionPopDown P((void));
295 void PromotionPopDown P((void));
296 void PromotionCallback P((Widget w, XtPointer client_data,
297 XtPointer call_data));
298 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
299 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
300 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
301 void TypeInProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
302 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
303 void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
304 void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
305 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
306 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
307 Boolean TempBackwardActive = False;
308 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
309 void DisplayMove P((int moveNumber));
310 void DisplayTitle P((char *title));
311 void ICSInitScript P((void));
312 void ErrorPopUp P((char *title, char *text, int modal));
313 void ErrorPopDown P((void));
314 static char *ExpandPathName P((char *path));
315 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
316 void GameListOptionsPopDown P(());
317 void GenericPopDown P(());
318 void update_ics_width P(());
319 int get_term_width P(());
320 int CopyMemoProc P(());
321 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
322 Boolean IsDrawArrowEnabled P(());
325 * XBoard depends on Xt R4 or higher
327 int xtVersion = XtSpecificationRelease;
332 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
333 jailSquareColor, highlightSquareColor, premoveHighlightColor;
334 Pixel lowTimeWarningColor;
335 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
336 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
337 wjPieceGC, bjPieceGC, prelineGC, countGC;
338 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
339 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
340 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
341 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
342 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
343 ICSInputShell, fileNameShell, askQuestionShell;
344 Widget historyShell, evalGraphShell, gameListShell;
345 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
346 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
347 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
349 XFontSet fontSet, clockFontSet;
352 XFontStruct *clockFontStruct;
354 Font coordFontID, countFontID;
355 XFontStruct *coordFontStruct, *countFontStruct;
356 XtAppContext appContext;
358 char *oldICSInteractionTitle;
362 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
364 Position commentX = -1, commentY = -1;
365 Dimension commentW, commentH;
366 typedef unsigned int BoardSize;
368 Boolean chessProgram;
370 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
371 int smallLayout = 0, tinyLayout = 0,
372 marginW, marginH, // [HGM] for run-time resizing
373 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
374 ICSInputBoxUp = False, askQuestionUp = False,
375 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
376 errorUp = False, errorExitStatus = -1, defaultLineGap;
377 Dimension textHeight;
378 Pixel timerForegroundPixel, timerBackgroundPixel;
379 Pixel buttonForegroundPixel, buttonBackgroundPixel;
380 char *chessDir, *programName, *programVersion;
381 Boolean alwaysOnTop = False;
382 char *icsTextMenuString;
384 char *firstChessProgramNames;
385 char *secondChessProgramNames;
387 WindowPlacement wpMain;
388 WindowPlacement wpConsole;
389 WindowPlacement wpComment;
390 WindowPlacement wpMoveHistory;
391 WindowPlacement wpEvalGraph;
392 WindowPlacement wpEngineOutput;
393 WindowPlacement wpGameList;
394 WindowPlacement wpTags;
399 Pixmap pieceBitmap[2][(int)BlackPawn];
400 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
401 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
402 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
403 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
404 Pixmap xpmBoardBitmap[2];
405 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
406 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
407 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
408 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
409 XImage *ximLightSquare, *ximDarkSquare;
412 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
413 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
415 #define White(piece) ((int)(piece) < (int)BlackPawn)
417 /* Bitmaps for use as masks when drawing XPM pieces.
418 Need one for each black and white piece. */
419 static Pixmap xpmMask[BlackKing + 1];
421 /* This magic number is the number of intermediate frames used
422 in each half of the animation. For short moves it's reduced
423 by 1. The total number of frames will be factor * 2 + 1. */
426 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
428 #define PAUSE_BUTTON "P"
429 MenuItem buttonBar[] = {
430 {"<<", "<<", ToStartEvent},
431 {"<", "<", BackwardEvent},
432 {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseEvent},
433 {">", ">", ForwardEvent},
434 {">>", ">>", ToEndEvent},
438 #define PIECE_MENU_SIZE 18
439 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
440 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
441 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
442 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
443 N_("Empty square"), N_("Clear board") },
444 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
445 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
446 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
447 N_("Empty square"), N_("Clear board") }
449 /* must be in same order as pieceMenuStrings! */
450 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
451 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
452 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
453 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
454 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
455 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
456 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
457 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
458 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
461 #define DROP_MENU_SIZE 6
462 String dropMenuStrings[DROP_MENU_SIZE] = {
463 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
465 /* must be in same order as dropMenuStrings! */
466 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
467 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
468 WhiteRook, WhiteQueen
476 DropMenuEnables dmEnables[] = {
494 { XtNborderWidth, 0 },
495 { XtNdefaultDistance, 0 },
499 { XtNborderWidth, 0 },
500 { XtNresizable, (XtArgVal) True },
504 { XtNborderWidth, 0 },
510 { XtNjustify, (XtArgVal) XtJustifyRight },
511 { XtNlabel, (XtArgVal) "..." },
512 { XtNresizable, (XtArgVal) True },
513 { XtNresize, (XtArgVal) False }
516 Arg messageArgs[] = {
517 { XtNjustify, (XtArgVal) XtJustifyLeft },
518 { XtNlabel, (XtArgVal) "..." },
519 { XtNresizable, (XtArgVal) True },
520 { XtNresize, (XtArgVal) False }
524 { XtNborderWidth, 0 },
525 { XtNjustify, (XtArgVal) XtJustifyLeft }
528 XtResource clientResources[] = {
529 { "flashCount", "flashCount", XtRInt, sizeof(int),
530 XtOffset(AppDataPtr, flashCount), XtRImmediate,
531 (XtPointer) FLASH_COUNT },
534 XrmOptionDescRec shellOptions[] = {
535 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
536 { "-flash", "flashCount", XrmoptionNoArg, "3" },
537 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
540 XtActionsRec boardActions[] = {
541 { "DrawPosition", DrawPositionProc },
542 { "HandleUserMove", HandleUserMove },
543 { "AnimateUserMove", AnimateUserMove },
544 { "HandlePV", HandlePV },
545 { "SelectPV", SelectPV },
546 { "StopPV", StopPV },
547 { "FileNameAction", FileNameAction },
548 { "AskQuestionProc", AskQuestionProc },
549 { "AskQuestionReplyAction", AskQuestionReplyAction },
550 { "PieceMenuPopup", PieceMenuPopup },
551 { "WhiteClock", WhiteClock },
552 { "BlackClock", BlackClock },
553 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
554 { "QuitProc", QuitWrapper },
555 { "ManProc", ManInner },
556 { "TempBackwardProc", TempBackwardProc },
557 { "TempForwardProc", TempForwardProc },
558 { "CommentClick", (XtActionProc) CommentClick },
559 { "CommentPopDown", (XtActionProc) CommentPopDown },
560 { "TagsPopDown", (XtActionProc) TagsPopDown },
561 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
562 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
563 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
564 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
565 { "GameListPopDown", (XtActionProc) GameListPopDown },
566 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
567 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
568 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
569 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
570 { "GenericPopDown", (XtActionProc) GenericPopDown },
571 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
572 { "SelectMove", (XtActionProc) SelectMove },
573 { "LoadSelectedProc", LoadSelectedProc },
574 { "SetFilterProc", SetFilterProc },
575 { "TypeInProc", TypeInProc },
576 { "EnterKeyProc", EnterKeyProc },
577 { "UpKeyProc", UpKeyProc },
578 { "DownKeyProc", DownKeyProc },
581 char globalTranslations[] =
582 ":<Key>F9: MenuItem(ResignProc) \n \
583 :Ctrl<Key>n: MenuItem(NewGame) \n \
584 :Meta<Key>V: MenuItem(NewVariant) \n \
585 :Ctrl<Key>o: MenuItem(LoadGame) \n \
586 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
587 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
588 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
589 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
590 :Ctrl<Key>s: MenuItem(SaveGame) \n \
591 :Ctrl<Key>c: MenuItem(CopyGame) \n \
592 :Ctrl<Key>v: MenuItem(PasteGame) \n \
593 :Ctrl<Key>O: MenuItem(LoadPosition) \n \
594 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
595 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
596 :Ctrl<Key>S: MenuItem(SavePosition) \n \
597 :Ctrl<Key>C: MenuItem(CopyPosition) \n \
598 :Ctrl<Key>V: MenuItem(PastePosition) \n \
599 :Ctrl<Key>q: MenuItem(Exit) \n \
600 :Ctrl<Key>w: MenuItem(MachineWhite) \n \
601 :Ctrl<Key>b: MenuItem(MachineBlack) \n \
602 :Ctrl<Key>t: MenuItem(TwoMachines) \n \
603 :Ctrl<Key>a: MenuItem(AnalysisMode) \n \
604 :Ctrl<Key>g: MenuItem(AnalyzeFile) \n \
605 :Ctrl<Key>e: MenuItem(EditGame) \n \
606 :Ctrl<Key>E: MenuItem(EditPosition) \n \
607 :Meta<Key>O: MenuItem(ShowEngineOutput) \n \
608 :Meta<Key>E: MenuItem(ShowEvaluationGraph) \n \
609 :Meta<Key>G: MenuItem(ShowGameList) \n \
610 :Meta<Key>H: MenuItem(ShowMoveHistory) \n \
611 :<Key>Pause: MenuItem(Pause) \n \
612 :<Key>F3: MenuItem(Accept) \n \
613 :<Key>F4: MenuItem(Decline) \n \
614 :<Key>F12: MenuItem(Rematch) \n \
615 :<Key>F5: MenuItem(CallFlag) \n \
616 :<Key>F6: MenuItem(Draw) \n \
617 :<Key>F7: MenuItem(Adjourn) \n \
618 :<Key>F8: MenuItem(Abort) \n \
619 :<Key>F10: MenuItem(StopObserving) \n \
620 :<Key>F11: MenuItem(StopExamining) \n \
621 :Ctrl<Key>d: MenuItem(DebugProc) \n \
622 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
623 :Meta<Key>End: MenuItem(ToEnd) \n \
624 :Meta<Key>Right: MenuItem(Forward) \n \
625 :Meta<Key>Home: MenuItem(ToStart) \n \
626 :Meta<Key>Left: MenuItem(Backward) \n \
627 :<Key>Left: MenuItem(Backward) \n \
628 :<Key>Right: MenuItem(Forward) \n \
629 :<Key>Home: MenuItem(Revert) \n \
630 :<Key>End: MenuItem(TruncateGame) \n \
631 :Ctrl<Key>m: MenuItem(MoveNow) \n \
632 :Ctrl<Key>x: MenuItem(RetractMove) \n \
633 :Meta<Key>J: MenuItem(Adjudications) \n \
634 :Meta<Key>U: MenuItem(CommonEngine) \n \
635 :Meta<Key>T: MenuItem(TimeControl) \n \
636 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
637 #ifndef OPTIONSDIALOG
639 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
640 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
641 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
642 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
643 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
646 :<Key>F1: MenuItem(Manual) \n \
647 :<Key>F2: MenuItem(FlipView) \n \
648 :<KeyDown>Return: TempBackwardProc() \n \
649 :<KeyUp>Return: TempForwardProc() \n";
651 char boardTranslations[] =
652 "<Btn1Down>: HandleUserMove(0) \n \
653 Shift<Btn1Up>: HandleUserMove(1) \n \
654 <Btn1Up>: HandleUserMove(0) \n \
655 <Btn1Motion>: AnimateUserMove() \n \
656 <Btn3Motion>: HandlePV() \n \
657 <Btn2Motion>: HandlePV() \n \
658 <Btn3Up>: PieceMenuPopup(menuB) \n \
659 <Btn2Up>: PieceMenuPopup(menuB) \n \
660 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
661 PieceMenuPopup(menuB) \n \
662 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
663 PieceMenuPopup(menuW) \n \
664 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
665 PieceMenuPopup(menuW) \n \
666 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
667 PieceMenuPopup(menuB) \n";
669 char whiteTranslations[] =
670 "Shift<BtnDown>: WhiteClock(1)\n \
671 <BtnDown>: WhiteClock(0)\n";
672 char blackTranslations[] =
673 "Shift<BtnDown>: BlackClock(1)\n \
674 <BtnDown>: BlackClock(0)\n";
676 char ICSInputTranslations[] =
677 "<Key>Up: UpKeyProc() \n "
678 "<Key>Down: DownKeyProc() \n "
679 "<Key>Return: EnterKeyProc() \n";
681 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
682 // as the widget is destroyed before the up-click can call extend-end
683 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
685 String xboardResources[] = {
686 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
687 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
688 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
693 /* Max possible square size */
694 #define MAXSQSIZE 256
696 static int xpm_avail[MAXSQSIZE];
698 #ifdef HAVE_DIR_STRUCT
700 /* Extract piece size from filename */
702 xpm_getsize (char *name, int len, char *ext)
710 if ((p=strchr(name, '.')) == NULL ||
711 StrCaseCmp(p+1, ext) != 0)
717 while (*p && isdigit(*p))
724 /* Setup xpm_avail */
726 xpm_getavail (char *dirname, char *ext)
732 for (i=0; i<MAXSQSIZE; ++i)
735 if (appData.debugMode)
736 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
738 dir = opendir(dirname);
741 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
742 programName, dirname);
746 while ((ent=readdir(dir)) != NULL) {
747 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
748 if (i > 0 && i < MAXSQSIZE)
758 xpm_print_avail (FILE *fp, char *ext)
762 fprintf(fp, _("Available `%s' sizes:\n"), ext);
763 for (i=1; i<MAXSQSIZE; ++i) {
769 /* Return XPM piecesize closest to size */
771 xpm_closest_to (char *dirname, int size, char *ext)
774 int sm_diff = MAXSQSIZE;
778 xpm_getavail(dirname, ext);
780 if (appData.debugMode)
781 xpm_print_avail(stderr, ext);
783 for (i=1; i<MAXSQSIZE; ++i) {
786 diff = (diff<0) ? -diff : diff;
787 if (diff < sm_diff) {
795 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
801 #else /* !HAVE_DIR_STRUCT */
802 /* If we are on a system without a DIR struct, we can't
803 read the directory, so we can't collect a list of
804 filenames, etc., so we can't do any size-fitting. */
806 xpm_closest_to (char *dirname, int size, char *ext)
809 Warning: No DIR structure found on this system --\n\
810 Unable to autosize for XPM/XIM pieces.\n\
811 Please report this error to %s.\n\
812 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
815 #endif /* HAVE_DIR_STRUCT */
817 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
818 "magenta", "cyan", "white" };
822 TextColors textColors[(int)NColorClasses];
824 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
826 parse_color (char *str, int which)
828 char *p, buf[100], *d;
831 if (strlen(str) > 99) /* watch bounds on buf */
836 for (i=0; i<which; ++i) {
843 /* Could be looking at something like:
845 .. in which case we want to stop on a comma also */
846 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
850 return -1; /* Use default for empty field */
853 if (which == 2 || isdigit(*p))
856 while (*p && isalpha(*p))
861 for (i=0; i<8; ++i) {
862 if (!StrCaseCmp(buf, cnames[i]))
863 return which? (i+40) : (i+30);
865 if (!StrCaseCmp(buf, "default")) return -1;
867 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
872 parse_cpair (ColorClass cc, char *str)
874 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
875 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
880 /* bg and attr are optional */
881 textColors[(int)cc].bg = parse_color(str, 1);
882 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
883 textColors[(int)cc].attr = 0;
889 /* Arrange to catch delete-window events */
890 Atom wm_delete_window;
892 CatchDeleteWindow (Widget w, String procname)
895 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
896 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
897 XtAugmentTranslations(w, XtParseTranslationTable(buf));
904 XtSetArg(args[0], XtNiconic, False);
905 XtSetValues(shellWidget, args, 1);
907 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
910 //---------------------------------------------------------------------------------------------------------
911 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
914 #define CW_USEDEFAULT (1<<31)
915 #define ICS_TEXT_MENU_SIZE 90
916 #define DEBUG_FILE "xboard.debug"
917 #define SetCurrentDirectory chdir
918 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
922 // these two must some day move to frontend.h, when they are implemented
923 Boolean GameListIsUp();
925 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
928 // front-end part of option handling
930 // [HGM] This platform-dependent table provides the location for storing the color info
931 extern char *crWhite, * crBlack;
935 &appData.whitePieceColor,
936 &appData.blackPieceColor,
937 &appData.lightSquareColor,
938 &appData.darkSquareColor,
939 &appData.highlightSquareColor,
940 &appData.premoveHighlightColor,
941 &appData.lowTimeWarningColor,
952 // [HGM] font: keep a font for each square size, even non-stndard ones
955 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
956 char *fontTable[NUM_FONTS][MAX_SIZE];
959 ParseFont (char *name, int number)
960 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
962 if(sscanf(name, "size%d:", &size)) {
963 // [HGM] font: font is meant for specific boardSize (likely from settings file);
964 // defer processing it until we know if it matches our board size
965 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
966 fontTable[number][size] = strdup(strchr(name, ':')+1);
967 fontValid[number][size] = True;
972 case 0: // CLOCK_FONT
973 appData.clockFont = strdup(name);
975 case 1: // MESSAGE_FONT
976 appData.font = strdup(name);
978 case 2: // COORD_FONT
979 appData.coordFont = strdup(name);
984 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
989 { // only 2 fonts currently
990 appData.clockFont = CLOCK_FONT_NAME;
991 appData.coordFont = COORD_FONT_NAME;
992 appData.font = DEFAULT_FONT_NAME;
997 { // no-op, until we identify the code for this already in XBoard and move it here
1001 ParseColor (int n, char *name)
1002 { // in XBoard, just copy the color-name string
1003 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1007 ParseTextAttribs (ColorClass cc, char *s)
1009 (&appData.colorShout)[cc] = strdup(s);
1013 ParseBoardSize (void *addr, char *name)
1015 appData.boardSize = strdup(name);
1020 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1024 SetCommPortDefaults ()
1025 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1028 // [HGM] args: these three cases taken out to stay in front-end
1030 SaveFontArg (FILE *f, ArgDescriptor *ad)
1033 int i, n = (int)(intptr_t)ad->argLoc;
1035 case 0: // CLOCK_FONT
1036 name = appData.clockFont;
1038 case 1: // MESSAGE_FONT
1039 name = appData.font;
1041 case 2: // COORD_FONT
1042 name = appData.coordFont;
1047 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1048 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1049 fontTable[n][squareSize] = strdup(name);
1050 fontValid[n][squareSize] = True;
1053 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1054 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1059 { // nothing to do, as the sounds are at all times represented by their text-string names already
1063 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
1064 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1065 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1069 SaveColor (FILE *f, ArgDescriptor *ad)
1070 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1071 if(colorVariable[(int)(intptr_t)ad->argLoc])
1072 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1076 SaveBoardSize (FILE *f, char *name, void *addr)
1077 { // wrapper to shield back-end from BoardSize & sizeInfo
1078 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1082 ParseCommPortSettings (char *s)
1083 { // no such option in XBoard (yet)
1086 extern Widget engineOutputShell;
1090 GetActualPlacement (Widget wg, WindowPlacement *wp)
1095 XWindowAttributes winAt;
1102 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
1103 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
1104 wp->x = rx - winAt.x;
1105 wp->y = ry - winAt.y;
1106 wp->height = winAt.height;
1107 wp->width = winAt.width;
1108 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
1113 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1114 // In XBoard this will have to wait until awareness of window parameters is implemented
1115 GetActualPlacement(shellWidget, &wpMain);
1116 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1117 if(MoveHistoryIsUp()) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
1118 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1119 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1120 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
1121 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
1125 PrintCommPortSettings (FILE *f, char *name)
1126 { // This option does not exist in XBoard
1130 MySearchPath (char *installDir, char *name, char *fullname)
1131 { // just append installDir and name. Perhaps ExpandPath should be used here?
1132 name = ExpandPathName(name);
1133 if(name && name[0] == '/')
1134 safeStrCpy(fullname, name, MSG_SIZ );
1136 sprintf(fullname, "%s%c%s", installDir, '/', name);
1142 MyGetFullPathName (char *name, char *fullname)
1143 { // should use ExpandPath?
1144 name = ExpandPathName(name);
1145 safeStrCpy(fullname, name, MSG_SIZ );
1150 EnsureOnScreen (int *x, int *y, int minX, int minY)
1157 { // [HGM] args: allows testing if main window is realized from back-end
1158 return xBoardWindow != 0;
1162 PopUpStartupDialog ()
1163 { // start menu not implemented in XBoard
1167 ConvertToLine (int argc, char **argv)
1169 static char line[128*1024], buf[1024];
1173 for(i=1; i<argc; i++)
1175 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1176 && argv[i][0] != '{' )
1177 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1179 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1180 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1183 line[strlen(line)-1] = NULLCHAR;
1187 //--------------------------------------------------------------------------------------------
1190 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1192 #define BoardSize int
1194 InitDrawingSizes (BoardSize boardSize, int flags)
1195 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1196 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1198 XtGeometryResult gres;
1200 static Dimension oldWidth, oldHeight;
1201 static VariantClass oldVariant;
1202 static int oldDual = -1, oldMono = -1;
1204 if(!formWidget) return;
1206 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1207 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1208 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1210 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1212 * Enable shell resizing.
1214 shellArgs[0].value = (XtArgVal) &w;
1215 shellArgs[1].value = (XtArgVal) &h;
1216 XtGetValues(shellWidget, shellArgs, 2);
1218 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1219 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1220 XtSetValues(shellWidget, &shellArgs[2], 4);
1222 XtSetArg(args[0], XtNdefaultDistance, &sep);
1223 XtGetValues(formWidget, args, 1);
1225 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1227 hOffset = boardWidth + 10;
1228 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1229 secondSegments[i] = gridSegments[i];
1230 secondSegments[i].x1 += hOffset;
1231 secondSegments[i].x2 += hOffset;
1234 XtSetArg(args[0], XtNwidth, boardWidth);
1235 XtSetArg(args[1], XtNheight, boardHeight);
1236 XtSetValues(boardWidget, args, 2);
1238 timerWidth = (boardWidth - sep) / 2;
1239 XtSetArg(args[0], XtNwidth, timerWidth);
1240 XtSetValues(whiteTimerWidget, args, 1);
1241 XtSetValues(blackTimerWidget, args, 1);
1243 XawFormDoLayout(formWidget, False);
1245 if (appData.titleInWindow) {
1247 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1248 XtSetArg(args[i], XtNheight, &h); i++;
1249 XtGetValues(titleWidget, args, i);
1251 w = boardWidth - 2*bor;
1253 XtSetArg(args[0], XtNwidth, &w);
1254 XtGetValues(menuBarWidget, args, 1);
1255 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1258 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1259 if (gres != XtGeometryYes && appData.debugMode) {
1261 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1262 programName, gres, w, h, wr, hr);
1266 XawFormDoLayout(formWidget, True);
1269 * Inhibit shell resizing.
1271 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1272 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1273 shellArgs[4].value = shellArgs[2].value = w;
1274 shellArgs[5].value = shellArgs[3].value = h;
1275 XtSetValues(shellWidget, &shellArgs[0], 6);
1277 XSync(xDisplay, False);
1281 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1284 if(gameInfo.variant != oldVariant) { // and only if variant changed
1287 for(i=0; i<4; i++) {
1289 for(p=0; p<=(int)WhiteKing; p++)
1290 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1291 if(gameInfo.variant == VariantShogi) {
1292 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1293 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1294 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1295 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1296 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1299 if(gameInfo.variant == VariantGothic) {
1300 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1303 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1304 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1305 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1308 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1309 for(p=0; p<=(int)WhiteKing; p++)
1310 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1311 if(gameInfo.variant == VariantShogi) {
1312 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1313 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1314 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1315 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1316 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1319 if(gameInfo.variant == VariantGothic) {
1320 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1323 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1324 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1325 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1330 for(i=0; i<2; i++) {
1332 for(p=0; p<=(int)WhiteKing; p++)
1333 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1334 if(gameInfo.variant == VariantShogi) {
1335 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1336 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1337 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1338 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1339 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1342 if(gameInfo.variant == VariantGothic) {
1343 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1346 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1347 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1348 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1352 oldMono = -10; // kludge to force recreation of animation masks
1353 oldVariant = gameInfo.variant;
1356 if(appData.monoMode != oldMono)
1359 oldMono = appData.monoMode;
1364 ParseIcsTextColors ()
1365 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1366 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1367 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1368 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1369 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1370 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1371 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1372 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1373 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1374 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1375 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1377 if (appData.colorize) {
1379 _("%s: can't parse color names; disabling colorization\n"),
1382 appData.colorize = FALSE;
1387 MakeOneColor (char *name, Pixel *color)
1389 XrmValue vFrom, vTo;
1390 if (!appData.monoMode) {
1391 vFrom.addr = (caddr_t) name;
1392 vFrom.size = strlen(name);
1393 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1394 if (vTo.addr == NULL) {
1395 appData.monoMode = True;
1398 *color = *(Pixel *) vTo.addr;
1406 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1407 int forceMono = False;
1409 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1410 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1411 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1412 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1413 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1414 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1421 { // [HGM] taken out of main
1423 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1424 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1425 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1427 if (appData.bitmapDirectory[0] != NULLCHAR) {
1431 CreateXPMBoard(appData.liteBackTextureFile, 1);
1432 CreateXPMBoard(appData.darkBackTextureFile, 0);
1436 /* Create regular pieces */
1437 if (!useImages) CreatePieces();
1442 InitDrawingParams ()
1444 MakeColors(); CreateGCs(True);
1449 main (int argc, char **argv)
1451 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1452 XSetWindowAttributes window_attributes;
1454 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1455 XrmValue vFrom, vTo;
1456 XtGeometryResult gres;
1459 int forceMono = False;
1461 srandom(time(0)); // [HGM] book: make random truly random
1463 setbuf(stdout, NULL);
1464 setbuf(stderr, NULL);
1467 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1468 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1472 programName = strrchr(argv[0], '/');
1473 if (programName == NULL)
1474 programName = argv[0];
1479 XtSetLanguageProc(NULL, NULL, NULL);
1480 bindtextdomain(PACKAGE, LOCALEDIR);
1481 textdomain(PACKAGE);
1485 XtAppInitialize(&appContext, "XBoard", shellOptions,
1486 XtNumber(shellOptions),
1487 &argc, argv, xboardResources, NULL, 0);
1488 appData.boardSize = "";
1489 InitAppData(ConvertToLine(argc, argv));
1491 if (p == NULL) p = "/tmp";
1492 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1493 gameCopyFilename = (char*) malloc(i);
1494 gamePasteFilename = (char*) malloc(i);
1495 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1496 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1498 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1499 clientResources, XtNumber(clientResources),
1502 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1503 static char buf[MSG_SIZ];
1504 EscapeExpand(buf, appData.firstInitString);
1505 appData.firstInitString = strdup(buf);
1506 EscapeExpand(buf, appData.secondInitString);
1507 appData.secondInitString = strdup(buf);
1508 EscapeExpand(buf, appData.firstComputerString);
1509 appData.firstComputerString = strdup(buf);
1510 EscapeExpand(buf, appData.secondComputerString);
1511 appData.secondComputerString = strdup(buf);
1514 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1517 if (chdir(chessDir) != 0) {
1518 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1524 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1525 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1526 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1527 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1530 setbuf(debugFP, NULL);
1534 if (appData.debugMode) {
1535 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1539 /* [HGM,HR] make sure board size is acceptable */
1540 if(appData.NrFiles > BOARD_FILES ||
1541 appData.NrRanks > BOARD_RANKS )
1542 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1545 /* This feature does not work; animation needs a rewrite */
1546 appData.highlightDragging = FALSE;
1550 xDisplay = XtDisplay(shellWidget);
1551 xScreen = DefaultScreen(xDisplay);
1552 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1554 gameInfo.variant = StringToVariant(appData.variant);
1555 InitPosition(FALSE);
1558 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1560 if (isdigit(appData.boardSize[0])) {
1561 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1562 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1563 &fontPxlSize, &smallLayout, &tinyLayout);
1565 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1566 programName, appData.boardSize);
1570 /* Find some defaults; use the nearest known size */
1571 SizeDefaults *szd, *nearest;
1572 int distance = 99999;
1573 nearest = szd = sizeDefaults;
1574 while (szd->name != NULL) {
1575 if (abs(szd->squareSize - squareSize) < distance) {
1577 distance = abs(szd->squareSize - squareSize);
1578 if (distance == 0) break;
1582 if (i < 2) lineGap = nearest->lineGap;
1583 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1584 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1585 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1586 if (i < 6) smallLayout = nearest->smallLayout;
1587 if (i < 7) tinyLayout = nearest->tinyLayout;
1590 SizeDefaults *szd = sizeDefaults;
1591 if (*appData.boardSize == NULLCHAR) {
1592 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1593 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1596 if (szd->name == NULL) szd--;
1597 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1599 while (szd->name != NULL &&
1600 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1601 if (szd->name == NULL) {
1602 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1603 programName, appData.boardSize);
1607 squareSize = szd->squareSize;
1608 lineGap = szd->lineGap;
1609 clockFontPxlSize = szd->clockFontPxlSize;
1610 coordFontPxlSize = szd->coordFontPxlSize;
1611 fontPxlSize = szd->fontPxlSize;
1612 smallLayout = szd->smallLayout;
1613 tinyLayout = szd->tinyLayout;
1614 // [HGM] font: use defaults from settings file if available and not overruled
1616 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1617 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1618 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1619 appData.font = fontTable[MESSAGE_FONT][squareSize];
1620 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1621 appData.coordFont = fontTable[COORD_FONT][squareSize];
1623 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1624 if (strlen(appData.pixmapDirectory) > 0) {
1625 p = ExpandPathName(appData.pixmapDirectory);
1627 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1628 appData.pixmapDirectory);
1631 if (appData.debugMode) {
1632 fprintf(stderr, _("\
1633 XBoard square size (hint): %d\n\
1634 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1636 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1637 if (appData.debugMode) {
1638 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1641 defaultLineGap = lineGap;
1642 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1644 /* [HR] height treated separately (hacked) */
1645 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1646 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1647 if (appData.showJail == 1) {
1648 /* Jail on top and bottom */
1649 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1650 XtSetArg(boardArgs[2], XtNheight,
1651 boardHeight + 2*(lineGap + squareSize));
1652 } else if (appData.showJail == 2) {
1654 XtSetArg(boardArgs[1], XtNwidth,
1655 boardWidth + 2*(lineGap + squareSize));
1656 XtSetArg(boardArgs[2], XtNheight, boardHeight);
1659 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1660 XtSetArg(boardArgs[2], XtNheight, boardHeight);
1664 * Determine what fonts to use.
1667 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1668 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1669 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1670 fontSet = CreateFontSet(appData.font);
1671 clockFontSet = CreateFontSet(appData.clockFont);
1673 /* For the coordFont, use the 0th font of the fontset. */
1674 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1675 XFontStruct **font_struct_list;
1676 XFontSetExtents *fontSize;
1677 char **font_name_list;
1678 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1679 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1680 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1681 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1682 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1685 appData.font = FindFont(appData.font, fontPxlSize);
1686 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1687 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1688 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1689 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1690 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1691 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1693 countFontID = coordFontID; // [HGM] holdings
1694 countFontStruct = coordFontStruct;
1696 xdb = XtDatabase(xDisplay);
1698 XrmPutLineResource(&xdb, "*international: True");
1699 vTo.size = sizeof(XFontSet);
1700 vTo.addr = (XtPointer) &fontSet;
1701 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1703 XrmPutStringResource(&xdb, "*font", appData.font);
1707 * Detect if there are not enough colors available and adapt.
1709 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1710 appData.monoMode = True;
1713 forceMono = MakeColors();
1716 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1718 appData.monoMode = True;
1721 if (appData.lowTimeWarning && !appData.monoMode) {
1722 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1723 vFrom.size = strlen(appData.lowTimeWarningColor);
1724 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1725 if (vTo.addr == NULL)
1726 appData.monoMode = True;
1728 lowTimeWarningColor = *(Pixel *) vTo.addr;
1731 if (appData.monoMode && appData.debugMode) {
1732 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1733 (unsigned long) XWhitePixel(xDisplay, xScreen),
1734 (unsigned long) XBlackPixel(xDisplay, xScreen));
1737 ParseIcsTextColors();
1738 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
1739 textColors[ColorNone].attr = 0;
1741 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1747 layoutName = "tinyLayout";
1748 } else if (smallLayout) {
1749 layoutName = "smallLayout";
1751 layoutName = "normalLayout";
1753 /* Outer layoutWidget is there only to provide a name for use in
1754 resources that depend on the layout style */
1756 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
1757 layoutArgs, XtNumber(layoutArgs));
1759 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
1760 formArgs, XtNumber(formArgs));
1761 XtSetArg(args[0], XtNdefaultDistance, &sep);
1762 XtGetValues(formWidget, args, 1);
1765 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
1766 XtSetArg(args[0], XtNtop, XtChainTop);
1767 XtSetArg(args[1], XtNbottom, XtChainTop);
1768 XtSetArg(args[2], XtNright, XtChainLeft);
1769 XtSetValues(menuBarWidget, args, 3);
1771 widgetList[j++] = whiteTimerWidget =
1772 XtCreateWidget("whiteTime", labelWidgetClass,
1773 formWidget, timerArgs, XtNumber(timerArgs));
1775 XtSetArg(args[0], XtNfontSet, clockFontSet);
1777 XtSetArg(args[0], XtNfont, clockFontStruct);
1779 XtSetArg(args[1], XtNtop, XtChainTop);
1780 XtSetArg(args[2], XtNbottom, XtChainTop);
1781 XtSetValues(whiteTimerWidget, args, 3);
1783 widgetList[j++] = blackTimerWidget =
1784 XtCreateWidget("blackTime", labelWidgetClass,
1785 formWidget, timerArgs, XtNumber(timerArgs));
1787 XtSetArg(args[0], XtNfontSet, clockFontSet);
1789 XtSetArg(args[0], XtNfont, clockFontStruct);
1791 XtSetArg(args[1], XtNtop, XtChainTop);
1792 XtSetArg(args[2], XtNbottom, XtChainTop);
1793 XtSetValues(blackTimerWidget, args, 3);
1795 if (appData.titleInWindow) {
1796 widgetList[j++] = titleWidget =
1797 XtCreateWidget("title", labelWidgetClass, formWidget,
1798 titleArgs, XtNumber(titleArgs));
1799 XtSetArg(args[0], XtNtop, XtChainTop);
1800 XtSetArg(args[1], XtNbottom, XtChainTop);
1801 XtSetValues(titleWidget, args, 2);
1804 if (appData.showButtonBar) {
1805 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
1806 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
1807 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
1808 XtSetArg(args[2], XtNtop, XtChainTop);
1809 XtSetArg(args[3], XtNbottom, XtChainTop);
1810 XtSetValues(buttonBarWidget, args, 4);
1813 widgetList[j++] = messageWidget =
1814 XtCreateWidget("message", labelWidgetClass, formWidget,
1815 messageArgs, XtNumber(messageArgs));
1816 XtSetArg(args[0], XtNtop, XtChainTop);
1817 XtSetArg(args[1], XtNbottom, XtChainTop);
1818 XtSetValues(messageWidget, args, 2);
1820 widgetList[j++] = boardWidget =
1821 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
1822 XtNumber(boardArgs));
1824 XtManageChildren(widgetList, j);
1826 timerWidth = (boardWidth - sep) / 2;
1827 XtSetArg(args[0], XtNwidth, timerWidth);
1828 XtSetValues(whiteTimerWidget, args, 1);
1829 XtSetValues(blackTimerWidget, args, 1);
1831 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1832 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1833 XtGetValues(whiteTimerWidget, args, 2);
1835 if (appData.showButtonBar) {
1836 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1837 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1838 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
1842 * formWidget uses these constraints but they are stored
1846 XtSetArg(args[i], XtNfromHoriz, 0); i++;
1847 XtSetValues(menuBarWidget, args, i);
1848 if (appData.titleInWindow) {
1851 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1852 XtSetValues(whiteTimerWidget, args, i);
1854 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1855 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1856 XtSetValues(blackTimerWidget, args, i);
1858 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1859 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
1860 XtSetValues(titleWidget, args, i);
1862 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1863 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1864 XtSetValues(messageWidget, args, i);
1865 if (appData.showButtonBar) {
1867 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1868 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1869 XtSetValues(buttonBarWidget, args, i);
1873 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1874 XtSetValues(whiteTimerWidget, args, i);
1876 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1877 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1878 XtSetValues(blackTimerWidget, args, i);
1880 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
1881 XtSetValues(titleWidget, args, i);
1883 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1884 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1885 XtSetValues(messageWidget, args, i);
1886 if (appData.showButtonBar) {
1888 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1889 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1890 XtSetValues(buttonBarWidget, args, i);
1895 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1896 XtSetValues(whiteTimerWidget, args, i);
1898 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1899 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1900 XtSetValues(blackTimerWidget, args, i);
1902 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1903 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1904 XtSetValues(messageWidget, args, i);
1905 if (appData.showButtonBar) {
1907 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1908 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1909 XtSetValues(buttonBarWidget, args, i);
1913 XtSetArg(args[0], XtNfromVert, messageWidget);
1914 XtSetArg(args[1], XtNtop, XtChainTop);
1915 XtSetArg(args[2], XtNbottom, XtChainBottom);
1916 XtSetArg(args[3], XtNleft, XtChainLeft);
1917 XtSetArg(args[4], XtNright, XtChainRight);
1918 XtSetValues(boardWidget, args, 5);
1920 XtRealizeWidget(shellWidget);
1923 XtSetArg(args[0], XtNx, wpMain.x);
1924 XtSetArg(args[1], XtNy, wpMain.y);
1925 XtSetValues(shellWidget, args, 2);
1929 * Correct the width of the message and title widgets.
1930 * It is not known why some systems need the extra fudge term.
1931 * The value "2" is probably larger than needed.
1933 XawFormDoLayout(formWidget, False);
1935 #define WIDTH_FUDGE 2
1937 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1938 XtSetArg(args[i], XtNheight, &h); i++;
1939 XtGetValues(messageWidget, args, i);
1940 if (appData.showButtonBar) {
1942 XtSetArg(args[i], XtNwidth, &w); i++;
1943 XtGetValues(buttonBarWidget, args, i);
1944 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1946 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
1949 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1950 if (gres != XtGeometryYes && appData.debugMode) {
1951 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1952 programName, gres, w, h, wr, hr);
1955 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
1956 /* The size used for the child widget in layout lags one resize behind
1957 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
1959 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1960 if (gres != XtGeometryYes && appData.debugMode) {
1961 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1962 programName, gres, w, h, wr, hr);
1965 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
1966 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
1967 XtSetArg(args[1], XtNright, XtChainRight);
1968 XtSetValues(messageWidget, args, 2);
1970 if (appData.titleInWindow) {
1972 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1973 XtSetArg(args[i], XtNheight, &h); i++;
1974 XtGetValues(titleWidget, args, i);
1976 w = boardWidth - 2*bor;
1978 XtSetArg(args[0], XtNwidth, &w);
1979 XtGetValues(menuBarWidget, args, 1);
1980 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1983 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1984 if (gres != XtGeometryYes && appData.debugMode) {
1986 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1987 programName, gres, w, h, wr, hr);
1990 XawFormDoLayout(formWidget, True);
1992 xBoardWindow = XtWindow(boardWidget);
1994 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1995 // not need to go into InitDrawingSizes().
1999 * Create X checkmark bitmap and initialize option menu checks.
2001 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2002 checkmark_bits, checkmark_width, checkmark_height);
2008 ReadBitmap(&wIconPixmap, "icon_white.bm",
2009 icon_white_bits, icon_white_width, icon_white_height);
2010 ReadBitmap(&bIconPixmap, "icon_black.bm",
2011 icon_black_bits, icon_black_width, icon_black_height);
2012 iconPixmap = wIconPixmap;
2014 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2015 XtSetValues(shellWidget, args, i);
2018 * Create a cursor for the board widget.
2020 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2021 XChangeWindowAttributes(xDisplay, xBoardWindow,
2022 CWCursor, &window_attributes);
2025 * Inhibit shell resizing.
2027 shellArgs[0].value = (XtArgVal) &w;
2028 shellArgs[1].value = (XtArgVal) &h;
2029 XtGetValues(shellWidget, shellArgs, 2);
2030 shellArgs[4].value = shellArgs[2].value = w;
2031 shellArgs[5].value = shellArgs[3].value = h;
2032 XtSetValues(shellWidget, &shellArgs[2], 4);
2033 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2034 marginH = h - boardHeight;
2036 CatchDeleteWindow(shellWidget, "QuitProc");
2044 if (appData.animate || appData.animateDragging)
2047 XtAugmentTranslations(formWidget,
2048 XtParseTranslationTable(globalTranslations));
2049 XtAugmentTranslations(boardWidget,
2050 XtParseTranslationTable(boardTranslations));
2051 XtAugmentTranslations(whiteTimerWidget,
2052 XtParseTranslationTable(whiteTranslations));
2053 XtAugmentTranslations(blackTimerWidget,
2054 XtParseTranslationTable(blackTranslations));
2056 /* Why is the following needed on some versions of X instead
2057 * of a translation? */
2058 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2059 (XtEventHandler) EventProc, NULL);
2061 XtAddEventHandler(formWidget, KeyPressMask, False,
2062 (XtEventHandler) MoveTypeInProc, NULL);
2063 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
2064 (XtEventHandler) EventProc, NULL);
2066 /* [AS] Restore layout */
2067 if( wpMoveHistory.visible ) {
2071 if( wpEvalGraph.visible )
2076 if( wpEngineOutput.visible ) {
2077 EngineOutputPopUp();
2082 if (errorExitStatus == -1) {
2083 if (appData.icsActive) {
2084 /* We now wait until we see "login:" from the ICS before
2085 sending the logon script (problems with timestamp otherwise) */
2086 /*ICSInitScript();*/
2087 if (appData.icsInputBox) ICSInputBoxPopUp();
2091 signal(SIGWINCH, TermSizeSigHandler);
2093 signal(SIGINT, IntSigHandler);
2094 signal(SIGTERM, IntSigHandler);
2095 if (*appData.cmailGameName != NULLCHAR) {
2096 signal(SIGUSR1, CmailSigHandler);
2100 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2102 // XtSetKeyboardFocus(shellWidget, formWidget);
2103 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2105 XtAppMainLoop(appContext);
2106 if (appData.debugMode) fclose(debugFP); // [DM] debug
2110 static Boolean noEcho;
2115 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2116 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2118 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2119 unlink(gameCopyFilename);
2120 unlink(gamePasteFilename);
2121 if(noEcho) EchoOn();
2125 TermSizeSigHandler (int sig)
2131 IntSigHandler (int sig)
2137 CmailSigHandler (int sig)
2142 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2144 /* Activate call-back function CmailSigHandlerCallBack() */
2145 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2147 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2151 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
2154 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2156 /**** end signal code ****/
2162 /* try to open the icsLogon script, either in the location given
2163 * or in the users HOME directory
2170 f = fopen(appData.icsLogon, "r");
2173 homedir = getenv("HOME");
2174 if (homedir != NULL)
2176 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2177 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2178 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2179 f = fopen(buf, "r");
2184 ProcessICSInitScript(f);
2186 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2199 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
2200 #define HISTORY_SIZE 64
2201 static char *history[HISTORY_SIZE];
2202 int histIn = 0, histP = 0;
2205 SaveInHistory (char *cmd)
2207 if (history[histIn] != NULL) {
2208 free(history[histIn]);
2209 history[histIn] = NULL;
2211 if (*cmd == NULLCHAR) return;
2212 history[histIn] = StrSave(cmd);
2213 histIn = (histIn + 1) % HISTORY_SIZE;
2214 if (history[histIn] != NULL) {
2215 free(history[histIn]);
2216 history[histIn] = NULL;
2222 PrevInHistory (char *cmd)
2225 if (histP == histIn) {
2226 if (history[histIn] != NULL) free(history[histIn]);
2227 history[histIn] = StrSave(cmd);
2229 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
2230 if (newhp == histIn || history[newhp] == NULL) return NULL;
2232 return history[histP];
2238 if (histP == histIn) return NULL;
2239 histP = (histP + 1) % HISTORY_SIZE;
2240 return history[histP];
2242 // end of borrowed code
2244 #define Abs(n) ((n)<0 ? -(n) : (n))
2248 InsertPxlSize (char *pattern, int targetPxlSize)
2250 char *base_fnt_lst, strInt[12], *p, *q;
2251 int alternatives, i, len, strIntLen;
2254 * Replace the "*" (if present) in the pixel-size slot of each
2255 * alternative with the targetPxlSize.
2259 while ((p = strchr(p, ',')) != NULL) {
2263 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
2264 strIntLen = strlen(strInt);
2265 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
2269 while (alternatives--) {
2270 char *comma = strchr(p, ',');
2271 for (i=0; i<14; i++) {
2272 char *hyphen = strchr(p, '-');
2274 if (comma && hyphen > comma) break;
2275 len = hyphen + 1 - p;
2276 if (i == 7 && *p == '*' && len == 2) {
2278 memcpy(q, strInt, strIntLen);
2288 len = comma + 1 - p;
2295 return base_fnt_lst;
2299 CreateFontSet (char *base_fnt_lst)
2302 char **missing_list;
2306 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
2307 &missing_list, &missing_count, &def_string);
2308 if (appData.debugMode) {
2310 XFontStruct **font_struct_list;
2311 char **font_name_list;
2312 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
2314 fprintf(debugFP, " got list %s, locale %s\n",
2315 XBaseFontNameListOfFontSet(fntSet),
2316 XLocaleOfFontSet(fntSet));
2317 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
2318 for (i = 0; i < count; i++) {
2319 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
2322 for (i = 0; i < missing_count; i++) {
2323 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
2326 if (fntSet == NULL) {
2327 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
2332 #else // not ENABLE_NLS
2334 * Find a font that matches "pattern" that is as close as
2335 * possible to the targetPxlSize. Prefer fonts that are k
2336 * pixels smaller to fonts that are k pixels larger. The
2337 * pattern must be in the X Consortium standard format,
2338 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2339 * The return value should be freed with XtFree when no
2343 FindFont (char *pattern, int targetPxlSize)
2345 char **fonts, *p, *best, *scalable, *scalableTail;
2346 int i, j, nfonts, minerr, err, pxlSize;
2348 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2350 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2351 programName, pattern);
2358 for (i=0; i<nfonts; i++) {
2361 if (*p != '-') continue;
2363 if (*p == NULLCHAR) break;
2364 if (*p++ == '-') j++;
2366 if (j < 7) continue;
2369 scalable = fonts[i];
2372 err = pxlSize - targetPxlSize;
2373 if (Abs(err) < Abs(minerr) ||
2374 (minerr > 0 && err < 0 && -err == minerr)) {
2380 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2381 /* If the error is too big and there is a scalable font,
2382 use the scalable font. */
2383 int headlen = scalableTail - scalable;
2384 p = (char *) XtMalloc(strlen(scalable) + 10);
2385 while (isdigit(*scalableTail)) scalableTail++;
2386 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2388 p = (char *) XtMalloc(strlen(best) + 2);
2389 safeStrCpy(p, best, strlen(best)+1 );
2391 if (appData.debugMode) {
2392 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2393 pattern, targetPxlSize, p);
2395 XFreeFontNames(fonts);
2402 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
2403 // must be called before all non-first callse to CreateGCs()
2404 XtReleaseGC(shellWidget, highlineGC);
2405 XtReleaseGC(shellWidget, lightSquareGC);
2406 XtReleaseGC(shellWidget, darkSquareGC);
2407 XtReleaseGC(shellWidget, lineGC);
2408 if (appData.monoMode) {
2409 if (DefaultDepth(xDisplay, xScreen) == 1) {
2410 XtReleaseGC(shellWidget, wbPieceGC);
2412 XtReleaseGC(shellWidget, bwPieceGC);
2415 XtReleaseGC(shellWidget, prelineGC);
2416 XtReleaseGC(shellWidget, jailSquareGC);
2417 XtReleaseGC(shellWidget, wdPieceGC);
2418 XtReleaseGC(shellWidget, wlPieceGC);
2419 XtReleaseGC(shellWidget, wjPieceGC);
2420 XtReleaseGC(shellWidget, bdPieceGC);
2421 XtReleaseGC(shellWidget, blPieceGC);
2422 XtReleaseGC(shellWidget, bjPieceGC);
2427 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
2429 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2430 | GCBackground | GCFunction | GCPlaneMask;
2431 gc_values->foreground = foreground;
2432 gc_values->background = background;
2433 return XtGetGC(shellWidget, value_mask, gc_values);
2437 CreateGCs (int redo)
2439 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2440 | GCBackground | GCFunction | GCPlaneMask;
2441 XGCValues gc_values;
2443 Pixel white = XWhitePixel(xDisplay, xScreen);
2444 Pixel black = XBlackPixel(xDisplay, xScreen);
2446 gc_values.plane_mask = AllPlanes;
2447 gc_values.line_width = lineGap;
2448 gc_values.line_style = LineSolid;
2449 gc_values.function = GXcopy;
2452 DeleteGCs(); // called a second time; clean up old GCs first
2453 } else { // [HGM] grid and font GCs created on first call only
2454 coordGC = CreateOneGC(&gc_values, black, white);
2455 XSetFont(xDisplay, coordGC, coordFontID);
2457 // [HGM] make font for holdings counts (white on black)
2458 countGC = CreateOneGC(&gc_values, white, black);
2459 XSetFont(xDisplay, countGC, countFontID);
2461 lineGC = CreateOneGC(&gc_values, black, black);
2463 if (appData.monoMode) {
2465 highlineGC = CreateOneGC(&gc_values, white, white);
2466 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
2467 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
2469 if (DefaultDepth(xDisplay, xScreen) == 1) {
2470 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2471 gc_values.function = GXcopyInverted;
2472 copyInvertedGC = CreateOneGC(&gc_values, black, white);
2473 gc_values.function = GXcopy;
2474 if (XBlackPixel(xDisplay, xScreen) == 1) {
2475 bwPieceGC = darkSquareGC;
2476 wbPieceGC = copyInvertedGC;
2478 bwPieceGC = copyInvertedGC;
2479 wbPieceGC = lightSquareGC;
2484 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
2485 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
2486 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
2487 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
2488 jailSquareGC = CreateOneGC(&gc_values, jailSquareColor, jailSquareColor);
2489 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
2490 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
2491 wjPieceGC = CreateOneGC(&gc_values, whitePieceColor, jailSquareColor);
2492 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
2493 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
2494 bjPieceGC = CreateOneGC(&gc_values, blackPieceColor, jailSquareColor);
2499 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
2507 fp = fopen(filename, "rb");
2509 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
2516 for (y=0; y<h; ++y) {
2517 for (x=0; x<h; ++x) {
2522 XPutPixel(xim, x, y, blackPieceColor);
2524 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2527 XPutPixel(xim, x, y, darkSquareColor);
2529 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2532 XPutPixel(xim, x, y, whitePieceColor);
2534 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2537 XPutPixel(xim, x, y, lightSquareColor);
2539 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2547 /* create Pixmap of piece */
2548 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2550 XPutImage(xDisplay, *dest, lightSquareGC, xim,
2553 /* create Pixmap of clipmask
2554 Note: We assume the white/black pieces have the same
2555 outline, so we make only 6 masks. This is okay
2556 since the XPM clipmask routines do the same. */
2558 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2560 XPutImage(xDisplay, temp, lightSquareGC, xmask,
2563 /* now create the 1-bit version */
2564 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2567 values.foreground = 1;
2568 values.background = 0;
2570 /* Don't use XtGetGC, not read only */
2571 maskGC = XCreateGC(xDisplay, *mask,
2572 GCForeground | GCBackground, &values);
2573 XCopyPlane(xDisplay, temp, *mask, maskGC,
2574 0, 0, squareSize, squareSize, 0, 0, 1);
2575 XFreePixmap(xDisplay, temp);
2580 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
2588 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
2593 /* The XSynchronize calls were copied from CreatePieces.
2594 Not sure if needed, but can't hurt */
2595 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2598 /* temp needed by loadXIM() */
2599 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2600 0, 0, ss, ss, AllPlanes, XYPixmap);
2602 if (strlen(appData.pixmapDirectory) == 0) {
2606 if (appData.monoMode) {
2607 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
2611 fprintf(stderr, _("\nLoading XIMs...\n"));
2613 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2614 fprintf(stderr, "%d", piece+1);
2615 for (kind=0; kind<4; kind++) {
2616 fprintf(stderr, ".");
2617 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2618 ExpandPathName(appData.pixmapDirectory),
2619 piece <= (int) WhiteKing ? "" : "w",
2620 pieceBitmapNames[piece],
2622 ximPieceBitmap[kind][piece] =
2623 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2624 0, 0, ss, ss, AllPlanes, XYPixmap);
2625 if (appData.debugMode)
2626 fprintf(stderr, _("(File:%s:) "), buf);
2627 loadXIM(ximPieceBitmap[kind][piece],
2629 &(xpmPieceBitmap2[kind][piece]),
2630 &(ximMaskPm2[piece]));
2631 if(piece <= (int)WhiteKing)
2632 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2634 fprintf(stderr," ");
2636 /* Load light and dark squares */
2637 /* If the LSQ and DSQ pieces don't exist, we will
2638 draw them with solid squares. */
2639 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2640 if (access(buf, 0) != 0) {
2644 fprintf(stderr, _("light square "));
2646 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2647 0, 0, ss, ss, AllPlanes, XYPixmap);
2648 if (appData.debugMode)
2649 fprintf(stderr, _("(File:%s:) "), buf);
2651 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2652 fprintf(stderr, _("dark square "));
2653 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2654 ExpandPathName(appData.pixmapDirectory), ss);
2655 if (appData.debugMode)
2656 fprintf(stderr, _("(File:%s:) "), buf);
2658 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2659 0, 0, ss, ss, AllPlanes, XYPixmap);
2660 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2661 xpmJailSquare = xpmLightSquare;
2663 fprintf(stderr, _("Done.\n"));
2665 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2668 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2672 CreateXPMBoard (char *s, int kind)
2676 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2677 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2678 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2684 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2685 // thisroutine has to be called t free the old piece pixmaps
2687 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2688 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2690 XFreePixmap(xDisplay, xpmLightSquare);
2691 XFreePixmap(xDisplay, xpmDarkSquare);
2700 u_int ss = squareSize;
2702 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2703 XpmColorSymbol symbols[4];
2704 static int redo = False;
2706 if(redo) FreeXPMPieces(); else redo = 1;
2708 /* The XSynchronize calls were copied from CreatePieces.
2709 Not sure if needed, but can't hurt */
2710 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2712 /* Setup translations so piece colors match square colors */
2713 symbols[0].name = "light_piece";
2714 symbols[0].value = appData.whitePieceColor;
2715 symbols[1].name = "dark_piece";
2716 symbols[1].value = appData.blackPieceColor;
2717 symbols[2].name = "light_square";
2718 symbols[2].value = appData.lightSquareColor;
2719 symbols[3].name = "dark_square";
2720 symbols[3].value = appData.darkSquareColor;
2722 attr.valuemask = XpmColorSymbols;
2723 attr.colorsymbols = symbols;
2724 attr.numsymbols = 4;
2726 if (appData.monoMode) {
2727 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2731 if (strlen(appData.pixmapDirectory) == 0) {
2732 XpmPieces* pieces = builtInXpms;
2735 while (pieces->size != squareSize && pieces->size) pieces++;
2736 if (!pieces->size) {
2737 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2740 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2741 for (kind=0; kind<4; kind++) {
2743 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2744 pieces->xpm[piece][kind],
2745 &(xpmPieceBitmap2[kind][piece]),
2746 NULL, &attr)) != 0) {
2747 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2751 if(piece <= (int) WhiteKing)
2752 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2756 xpmJailSquare = xpmLightSquare;
2760 fprintf(stderr, _("\nLoading XPMs...\n"));
2763 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2764 fprintf(stderr, "%d ", piece+1);
2765 for (kind=0; kind<4; kind++) {
2766 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2767 ExpandPathName(appData.pixmapDirectory),
2768 piece > (int) WhiteKing ? "w" : "",
2769 pieceBitmapNames[piece],
2771 if (appData.debugMode) {
2772 fprintf(stderr, _("(File:%s:) "), buf);
2774 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2775 &(xpmPieceBitmap2[kind][piece]),
2776 NULL, &attr)) != 0) {
2777 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2778 // [HGM] missing: read of unorthodox piece failed; substitute King.
2779 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2780 ExpandPathName(appData.pixmapDirectory),
2782 if (appData.debugMode) {
2783 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2785 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2786 &(xpmPieceBitmap2[kind][piece]),
2790 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2795 if(piece <= (int) WhiteKing)
2796 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2799 /* Load light and dark squares */
2800 /* If the LSQ and DSQ pieces don't exist, we will
2801 draw them with solid squares. */
2802 fprintf(stderr, _("light square "));
2803 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2804 if (access(buf, 0) != 0) {
2808 if (appData.debugMode)
2809 fprintf(stderr, _("(File:%s:) "), buf);
2811 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2812 &xpmLightSquare, NULL, &attr)) != 0) {
2813 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2816 fprintf(stderr, _("dark square "));
2817 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2818 ExpandPathName(appData.pixmapDirectory), ss);
2819 if (appData.debugMode) {
2820 fprintf(stderr, _("(File:%s:) "), buf);
2822 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2823 &xpmDarkSquare, NULL, &attr)) != 0) {
2824 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2828 xpmJailSquare = xpmLightSquare;
2829 fprintf(stderr, _("Done.\n"));
2831 oldVariant = -1; // kludge to force re-makig of animation masks
2832 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2835 #endif /* HAVE_LIBXPM */
2838 /* No built-in bitmaps */
2843 u_int ss = squareSize;
2845 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2848 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2849 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2850 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2851 pieceBitmapNames[piece],
2852 ss, kind == SOLID ? 's' : 'o');
2853 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2854 if(piece <= (int)WhiteKing)
2855 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2859 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2863 /* With built-in bitmaps */
2867 BuiltInBits* bib = builtInBits;
2870 u_int ss = squareSize;
2872 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2875 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2877 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2878 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2879 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2880 pieceBitmapNames[piece],
2881 ss, kind == SOLID ? 's' : 'o');
2882 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2883 bib->bits[kind][piece], ss, ss);
2884 if(piece <= (int)WhiteKing)
2885 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2889 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2895 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2900 char msg[MSG_SIZ], fullname[MSG_SIZ];
2902 if (*appData.bitmapDirectory != NULLCHAR) {
2903 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2904 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2905 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2906 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2907 &w, &h, pm, &x_hot, &y_hot);
2908 fprintf(stderr, "load %s\n", name);
2909 if (errcode != BitmapSuccess) {
2911 case BitmapOpenFailed:
2912 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2914 case BitmapFileInvalid:
2915 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2917 case BitmapNoMemory:
2918 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2922 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2926 fprintf(stderr, _("%s: %s...using built-in\n"),
2928 } else if (w != wreq || h != hreq) {
2930 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2931 programName, fullname, w, h, wreq, hreq);
2937 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2947 if (lineGap == 0) return;
2949 /* [HR] Split this into 2 loops for non-square boards. */
2951 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2952 gridSegments[i].x1 = 0;
2953 gridSegments[i].x2 =
2954 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2955 gridSegments[i].y1 = gridSegments[i].y2
2956 = lineGap / 2 + (i * (squareSize + lineGap));
2959 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2960 gridSegments[j + i].y1 = 0;
2961 gridSegments[j + i].y2 =
2962 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2963 gridSegments[j + i].x1 = gridSegments[j + i].x2
2964 = lineGap / 2 + (j * (squareSize + lineGap));
2968 int nrOfMenuItems = 7;
2969 Widget menuWidget[150];
2970 MenuListItem menuItemList[150] = {
2971 { "LoadNextGameProc", LoadNextGameProc },
2972 { "LoadPrevGameProc", LoadPrevGameProc },
2973 { "ReloadGameProc", ReloadGameProc },
2974 { "ReloadPositionProc", ReloadPositionProc },
2975 #ifndef OPTIONSDIALOG
2976 { "AlwaysQueenProc", AlwaysQueenProc },
2977 { "AnimateDraggingProc", AnimateDraggingProc },
2978 { "AnimateMovingProc", AnimateMovingProc },
2979 { "AutoflagProc", AutoflagProc },
2980 { "AutoflipProc", AutoflipProc },
2981 { "BlindfoldProc", BlindfoldProc },
2982 { "FlashMovesProc", FlashMovesProc },
2984 { "HighlightDraggingProc", HighlightDraggingProc },
2986 { "HighlightLastMoveProc", HighlightLastMoveProc },
2987 // { "IcsAlarmProc", IcsAlarmProc },
2988 { "MoveSoundProc", MoveSoundProc },
2989 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
2990 { "PopupExitMessageProc", PopupExitMessageProc },
2991 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
2992 // { "PremoveProc", PremoveProc },
2993 { "ShowCoordsProc", ShowCoordsProc },
2994 { "ShowThinkingProc", ShowThinkingProc },
2995 { "HideThinkingProc", HideThinkingProc },
2996 { "TestLegalityProc", TestLegalityProc },
2998 { "AboutGameProc", AboutGameEvent },
2999 { "DebugProc", DebugProc },
3000 { "NothingProc", NothingProc },
3005 MarkMenuItem (char *menuRef, int state)
3007 int nr = MenuToNumber(menuRef);
3010 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
3011 XtSetValues(menuWidget[nr], args, 1);
3016 EnableMenuItem (char *menuRef, int state)
3018 int nr = MenuToNumber(menuRef);
3019 if(nr >= 0) XtSetSensitive(menuWidget[nr], state);
3023 EnableButtonBar (int state)
3025 XtSetSensitive(buttonBarWidget, state);
3030 SetMenuEnables (Enables *enab)
3032 while (enab->name != NULL) {
3033 EnableMenuItem(enab->name, enab->value);
3039 Equal(char *p, char *s)
3040 { // compare strings skipping spaces in second
3042 if(*s == ' ') { s++; continue; }
3043 if(*s++ != *p++) return 0;
3049 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3050 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
3052 if(*nprms == 0) return;
3053 for(i=0; menuItemList[i].name; i++) {
3054 if(Equal(prms[0], menuItemList[i].name)) {
3055 (menuItemList[i].proc) ();
3062 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
3064 MenuProc *proc = (MenuProc *) addr;
3070 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
3072 RecentEngineEvent((int) (intptr_t) addr);
3075 // some stuff that must remain in front-end
3076 static Widget mainBar, currentMenu;
3077 static int wtot, nr = 0, widths[10];
3080 AppendMenuItem (char *text, char *name, MenuProc *action)
3087 XtSetArg(args[j], XtNleftMargin, 20); j++;
3088 XtSetArg(args[j], XtNrightMargin, 20); j++;
3090 if (strcmp(text, "----") == 0) {
3091 entry = XtCreateManagedWidget(text, smeLineObjectClass,
3092 currentMenu, args, j);
3094 XtSetArg(args[j], XtNlabel, XtNewString(_(text)));
3095 entry = XtCreateManagedWidget(name, smeBSBObjectClass,
3096 currentMenu, args, j+1);
3097 XtAddCallback(entry, XtNcallback,
3098 (XtCallbackProc) (strcmp(name, "recent") ? MenuBarSelect : MenuEngineSelect),
3100 menuWidget[nrOfMenuItems] = entry;
3105 CreateMenuButton (char *name, Menu *mb)
3106 { // create menu button on main bar, and shell for pull-down list
3112 XtSetArg(args[j], XtNmenuName, XtNewString(name)); j++;
3113 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3114 XtSetArg(args[j], XtNborderWidth, 0); j++;
3115 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3117 currentMenu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3120 XtSetArg(args[j], XtNwidth, &w); j++;
3121 XtGetValues(mb->subMenu, args, j);
3122 wtot += mb->textWidth = widths[nr++] = w;
3126 CreateMenuBar (Menu *mb, int boardWidth)
3130 char menuName[MSG_SIZ];
3134 // create bar itself
3136 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3137 XtSetArg(args[j], XtNvSpace, 0); j++;
3138 XtSetArg(args[j], XtNborderWidth, 0); j++;
3139 mainBar = XtCreateWidget("menuBar", boxWidgetClass,
3140 formWidget, args, j);
3142 CreateMainMenus(mb); // put menus in bar according to description in back-end
3144 // size buttons to make menu bar fit, clipping menu names where necessary
3145 while(wtot > boardWidth - 40) {
3147 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
3151 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
3153 XtSetArg(args[j], XtNwidth, widths[i]); j++;
3154 XtSetValues(ma[i].subMenu, args, j);
3161 CreateButtonBar (MenuItem *mi)
3164 Widget button, buttonBar;
3168 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3170 XtSetArg(args[j], XtNhSpace, 0); j++;
3172 XtSetArg(args[j], XtNborderWidth, 0); j++;
3173 XtSetArg(args[j], XtNvSpace, 0); j++;
3174 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3175 formWidget, args, j);
3177 while (mi->string != NULL) {
3180 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3181 XtSetArg(args[j], XtNborderWidth, 0); j++;
3183 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3184 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3185 buttonBar, args, j);
3186 XtAddCallback(button, XtNcallback,
3187 (XtCallbackProc) MenuBarSelect,
3188 (caddr_t) mi->proc);
3195 CreatePieceMenu (char *name, int color)
3200 ChessSquare selection;
3202 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3203 boardWidget, args, 0);
3205 for (i = 0; i < PIECE_MENU_SIZE; i++) {
3206 String item = pieceMenuStrings[color][i];
3208 if (strcmp(item, "----") == 0) {
3209 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3212 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3213 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3215 selection = pieceMenuTranslation[color][i];
3216 XtAddCallback(entry, XtNcallback,
3217 (XtCallbackProc) PieceMenuSelect,
3218 (caddr_t) selection);
3219 if (selection == WhitePawn || selection == BlackPawn) {
3220 XtSetArg(args[0], XtNpopupOnEntry, entry);
3221 XtSetValues(menu, args, 1);
3234 ChessSquare selection;
3236 whitePieceMenu = CreatePieceMenu("menuW", 0);
3237 blackPieceMenu = CreatePieceMenu("menuB", 1);
3239 if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
3240 XtRegisterGrabAction(PieceMenuPopup, True,
3241 (unsigned)(ButtonPressMask|ButtonReleaseMask),
3242 GrabModeAsync, GrabModeAsync);
3244 XtSetArg(args[0], XtNlabel, _("Drop"));
3245 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3246 boardWidget, args, 1);
3247 for (i = 0; i < DROP_MENU_SIZE; i++) {
3248 String item = dropMenuStrings[i];
3250 if (strcmp(item, "----") == 0) {
3251 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3254 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3255 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3257 selection = dropMenuTranslation[i];
3258 XtAddCallback(entry, XtNcallback,
3259 (XtCallbackProc) DropMenuSelect,
3260 (caddr_t) selection);
3274 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3275 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3276 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3277 dmEnables[i].piece);
3278 XtSetSensitive(entry, p != NULL || !appData.testLegality
3279 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3280 && !appData.icsActive));
3282 while (p && *p++ == dmEnables[i].piece) count++;
3283 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3285 XtSetArg(args[j], XtNlabel, label); j++;
3286 XtSetValues(entry, args, j);
3291 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
3293 String whichMenu; int menuNr = -2;
3294 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3295 if (event->type == ButtonRelease)
3296 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3297 else if (event->type == ButtonPress)
3298 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3300 case 0: whichMenu = params[0]; break;
3301 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3303 case -1: if (errorUp) ErrorPopDown();
3306 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3310 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3312 if (pmFromX < 0 || pmFromY < 0) return;
3313 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3317 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3319 if (pmFromX < 0 || pmFromY < 0) return;
3320 DropMenuEvent(piece, pmFromX, pmFromY);
3324 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3326 shiftKey = prms[0][0] & 1;
3331 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3333 shiftKey = prms[0][0] & 1;
3339 do_flash_delay (unsigned long msec)
3345 DrawBorder (int x, int y, int type)
3349 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
3351 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3352 squareSize+lineGap, squareSize+lineGap);
3356 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
3358 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
3359 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
3361 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
3362 if(textureW[kind] < W*squareSize)
3363 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
3365 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
3366 if(textureH[kind] < H*squareSize)
3367 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
3369 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
3374 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
3375 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
3377 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
3378 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
3379 squareSize, squareSize, x*fac, y*fac);
3381 if (useImages && useImageSqs) {
3385 pm = xpmLightSquare;
3390 case 2: /* neutral */
3395 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3396 squareSize, squareSize, x*fac, y*fac);
3406 case 2: /* neutral */
3411 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
3416 I split out the routines to draw a piece so that I could
3417 make a generic flash routine.
3420 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3422 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3423 switch (square_color) {
3425 case 2: /* neutral */
3427 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3428 ? *pieceToOutline(piece)
3429 : *pieceToSolid(piece),
3430 dest, bwPieceGC, 0, 0,
3431 squareSize, squareSize, x, y);
3434 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3435 ? *pieceToSolid(piece)
3436 : *pieceToOutline(piece),
3437 dest, wbPieceGC, 0, 0,
3438 squareSize, squareSize, x, y);
3444 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3446 switch (square_color) {
3448 case 2: /* neutral */
3450 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3451 ? *pieceToOutline(piece)
3452 : *pieceToSolid(piece),
3453 dest, bwPieceGC, 0, 0,
3454 squareSize, squareSize, x, y, 1);
3457 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3458 ? *pieceToSolid(piece)
3459 : *pieceToOutline(piece),
3460 dest, wbPieceGC, 0, 0,
3461 squareSize, squareSize, x, y, 1);
3467 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3469 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
3470 switch (square_color) {
3472 XCopyPlane(xDisplay, *pieceToSolid(piece),
3473 dest, (int) piece < (int) BlackPawn
3474 ? wlPieceGC : blPieceGC, 0, 0,
3475 squareSize, squareSize, x, y, 1);
3478 XCopyPlane(xDisplay, *pieceToSolid(piece),
3479 dest, (int) piece < (int) BlackPawn
3480 ? wdPieceGC : bdPieceGC, 0, 0,
3481 squareSize, squareSize, x, y, 1);
3483 case 2: /* neutral */
3485 XCopyPlane(xDisplay, *pieceToSolid(piece),
3486 dest, (int) piece < (int) BlackPawn
3487 ? wjPieceGC : bjPieceGC, 0, 0,
3488 squareSize, squareSize, x, y, 1);
3494 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3496 int kind, p = piece;
3498 switch (square_color) {
3500 case 2: /* neutral */
3502 if ((int)piece < (int) BlackPawn) {
3510 if ((int)piece < (int) BlackPawn) {
3518 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
3519 if(useTexture & square_color+1) {
3520 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
3521 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
3522 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
3523 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
3524 XSetClipMask(xDisplay, wlPieceGC, None);
3525 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
3527 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3528 dest, wlPieceGC, 0, 0,
3529 squareSize, squareSize, x, y);
3532 typedef void (*DrawFunc)();
3537 if (appData.monoMode) {
3538 if (DefaultDepth(xDisplay, xScreen) == 1) {
3539 return monoDrawPiece_1bit;
3541 return monoDrawPiece;
3545 return colorDrawPieceImage;
3547 return colorDrawPiece;
3552 DrawDot (int marker, int x, int y, int r)
3554 if(appData.monoMode) {
3555 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
3556 x, y, r, r, 0, 64*360);
3557 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
3558 x, y, r, r, 0, 64*360);
3560 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
3561 x, y, r, r, 0, 64*360);
3565 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
3566 { // basic front-end board-draw function: takes care of everything that can be in square:
3567 // piece, background, coordinate/count, marker dot
3568 int direction, font_ascent, font_descent;
3569 XCharStruct overall;
3572 if (piece == EmptySquare) {
3573 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
3575 drawfunc = ChooseDrawFunc();
3576 drawfunc(piece, square_color, x, y, xBoardWindow);
3579 if(align) { // square carries inscription (coord or piece count)
3581 GC hGC = align < 3 ? coordGC : countGC;
3582 // first calculate where it goes
3583 XTextExtents(countFontStruct, string, 1, &direction,
3584 &font_ascent, &font_descent, &overall);
3586 xx += squareSize - overall.width - 2;
3587 yy += squareSize - font_descent - 1;
3588 } else if (align == 2) {
3589 xx += 2, yy += font_ascent + 1;
3590 } else if (align == 3) {
3591 xx += squareSize - overall.width - 2;
3592 yy += font_ascent + 1;
3593 } else if (align == 4) {
3594 xx += 2, yy += font_ascent + 1;
3597 if (appData.monoMode) {
3598 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3600 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3604 if(marker) { // print fat marker dot, if requested
3605 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
3610 FlashDelay (int flash_delay)
3612 XSync(xDisplay, False);
3613 if(flash_delay) do_flash_delay(flash_delay);
3617 Fraction (int x, int start, int stop)
3619 double f = ((double) x - start)/(stop - start);
3620 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
3624 static WindowPlacement wpNew;
3627 CoDrag (Widget sh, WindowPlacement *wp)
3630 int j=0, touch=0, fudge = 2;
3631 GetActualPlacement(sh, wp);
3632 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
3633 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
3634 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
3635 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
3636 if(!touch ) return; // only windows that touch co-move
3637 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
3638 int heightInc = wpNew.height - wpMain.height;
3639 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3640 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3641 wp->y += fracTop * heightInc;
3642 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
3643 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
3644 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
3645 int widthInc = wpNew.width - wpMain.width;
3646 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3647 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3648 wp->y += fracLeft * widthInc;
3649 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
3650 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
3652 wp->x += wpNew.x - wpMain.x;
3653 wp->y += wpNew.y - wpMain.y;
3654 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
3655 if(touch == 3) wp->y += wpNew.height - wpMain.height;
3656 XtSetArg(args[j], XtNx, wp->x); j++;
3657 XtSetArg(args[j], XtNy, wp->y); j++;
3658 XtSetValues(sh, args, j);
3661 static XtIntervalId delayedDragID = 0;
3666 GetActualPlacement(shellWidget, &wpNew);
3667 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
3668 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
3669 return; // false alarm
3670 if(EngineOutputIsUp()) CoDrag(engineOutputShell, &wpEngineOutput);
3671 if(MoveHistoryIsUp()) CoDrag(shells[HistoryDlg], &wpMoveHistory);
3672 if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
3673 if(GameListIsUp()) CoDrag(gameListShell, &wpGameList);
3675 DrawPosition(True, NULL);
3676 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
3683 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
3685 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
3688 /* Why is this needed on some versions of X? */
3690 EventProc (Widget widget, caddr_t unused, XEvent *event)
3692 if (!XtIsRealized(widget))
3694 switch (event->type) {
3695 case ConfigureNotify: // main window is being dragged: drag attached windows with it
3696 if(appData.useStickyWindows)
3697 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
3700 if (event->xexpose.count > 0) return; /* no clipping is done */
3701 DrawPosition(True, NULL);
3702 if(twoBoards) { // [HGM] dual: draw other board in other orientation
3703 flipView = !flipView; partnerUp = !partnerUp;
3704 DrawPosition(True, NULL);
3705 flipView = !flipView; partnerUp = !partnerUp;
3709 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
3716 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
3718 DrawSeekAxis (int x, int y, int xTo, int yTo)
3720 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
3724 DrawSeekBackground (int left, int top, int right, int bottom)
3726 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
3730 DrawSeekText (char *buf, int x, int y)
3732 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
3736 DrawSeekDot (int x, int y, int colorNr)
3738 int square = colorNr & 0x80;
3741 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
3743 XFillRectangle(xDisplay, xBoardWindow, color,
3744 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3746 XFillArc(xDisplay, xBoardWindow, color,
3747 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
3751 DrawGrid (int second)
3753 XDrawSegments(xDisplay, xBoardWindow, lineGC,
3754 second ? secondSegments : // [HGM] dual
3755 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
3760 * event handler for redrawing the board
3763 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3765 DrawPosition(True, NULL);
3770 * event handler for parsing user moves
3772 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
3773 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
3774 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
3775 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
3776 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
3777 // and at the end FinishMove() to perform the move after optional promotion popups.
3778 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
3780 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3782 if (w != boardWidget || errorExitStatus != -1) return;
3783 if(nprms) shiftKey = !strcmp(prms[0], "1");
3786 if (event->type == ButtonPress) {
3787 XtPopdown(promotionShell);
3788 XtDestroyWidget(promotionShell);
3789 promotionUp = False;
3797 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
3798 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
3799 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
3803 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
3805 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
3806 DragPieceMove(event->xmotion.x, event->xmotion.y);
3810 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3811 { // [HGM] pv: walk PV
3812 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3815 static int savedIndex; /* gross that this is global */
3818 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3821 XawTextPosition index, dummy;
3824 XawTextGetSelectionPos(w, &index, &dummy);
3825 XtSetArg(arg, XtNstring, &val);
3826 XtGetValues(w, &arg, 1);
3827 ReplaceComment(savedIndex, val);
3828 if(savedIndex != currentMove) ToNrEvent(savedIndex);
3829 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3833 EditCommentPopUp (int index, char *title, char *text)
3836 if (text == NULL) text = "";
3837 NewCommentPopup(title, text, index);
3846 extern Option boxOptions[];
3856 edit = boxOptions[0].handle;
3858 XtSetArg(args[j], XtNstring, &val); j++;
3859 XtGetValues(edit, args, j);
3861 SendMultiLineToICS(val);
3862 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
3863 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
3867 ICSInputBoxPopDown ()
3869 PopDown(InputBoxDlg);
3873 CommentPopUp (char *title, char *text)
3875 savedIndex = currentMove; // [HGM] vari
3876 NewCommentPopup(title, text, currentMove);
3882 PopDown(CommentDlg);
3885 static char *openName;
3891 (void) (*fileProc)(openFP, 0, openName);
3895 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
3897 fileProc = proc; /* I can't see a way not */
3898 fileOpenMode = openMode; /* to use globals here */
3899 { // [HGM] use file-selector dialog stolen from Ghostview
3900 int index; // this is not supported yet
3901 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
3902 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
3903 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
3904 ScheduleDelayedEvent(&DelayedLoad, 50);
3911 if (!filenameUp) return;
3912 XtPopdown(fileNameShell);
3913 XtDestroyWidget(fileNameShell);
3919 FileNameCallback (Widget w, XtPointer client_data, XtPointer call_data)
3924 XtSetArg(args[0], XtNlabel, &name);
3925 XtGetValues(w, args, 1);
3927 if (strcmp(name, _("cancel")) == 0) {
3932 FileNameAction(w, NULL, NULL, NULL);
3936 FileNameAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3944 name = XawDialogGetValueString(w = XtParent(w));
3946 if ((name != NULL) && (*name != NULLCHAR)) {
3947 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
3948 XtPopdown(w = XtParent(XtParent(w)));
3952 p = strrchr(buf, ' ');
3959 fullname = ExpandPathName(buf);
3961 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
3964 f = fopen(fullname, fileOpenMode);
3966 DisplayError(_("Failed to open file"), errno);
3968 (void) (*fileProc)(f, index, buf);
3975 XtPopdown(w = XtParent(XtParent(w)));
3985 Widget dialog, layout;
3987 Dimension bw_width, pw_width;
3989 char *PromoChars = "wglcqrbnkac+=\0";
3992 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3993 XtGetValues(boardWidget, args, j);
3996 XtSetArg(args[j], XtNresizable, True); j++;
3997 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
3999 XtCreatePopupShell("Promotion", transientShellWidgetClass,
4000 shellWidget, args, j);
4002 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
4003 layoutArgs, XtNumber(layoutArgs));
4006 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
4007 XtSetArg(args[j], XtNborderWidth, 0); j++;
4008 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
4011 if(gameInfo.variant != VariantShogi) {
4012 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
4013 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
4014 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
4015 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
4016 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
4018 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
4019 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
4020 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
4021 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
4023 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
4024 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
4025 gameInfo.variant == VariantGiveaway) {
4026 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
4028 if(gameInfo.variant == VariantCapablanca ||
4029 gameInfo.variant == VariantGothic ||
4030 gameInfo.variant == VariantCapaRandom) {
4031 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
4032 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
4034 } else // [HGM] shogi
4036 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
4037 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
4039 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
4041 XtRealizeWidget(promotionShell);
4042 CatchDeleteWindow(promotionShell, "PromotionPopDown");
4045 XtSetArg(args[j], XtNwidth, &pw_width); j++;
4046 XtGetValues(promotionShell, args, j);
4048 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
4049 lineGap + squareSize/3 +
4050 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
4051 0 : 6*(squareSize + lineGap)), &x, &y);
4054 XtSetArg(args[j], XtNx, x); j++;
4055 XtSetArg(args[j], XtNy, y); j++;
4056 XtSetValues(promotionShell, args, j);
4058 XtPopup(promotionShell, XtGrabNone);
4066 if (!promotionUp) return;
4067 XtPopdown(promotionShell);
4068 XtDestroyWidget(promotionShell);
4069 promotionUp = False;
4073 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
4075 int promoChar = * (const char *) client_data;
4079 if (fromX == -1) return;
4086 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
4088 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
4089 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
4095 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
4097 dialogError = errorUp = False;
4098 XtPopdown(w = XtParent(XtParent(XtParent(w))));
4100 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
4107 if (!errorUp) return;
4108 dialogError = errorUp = False;
4109 XtPopdown(errorShell);
4110 XtDestroyWidget(errorShell);
4111 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
4115 ErrorPopUp (char *title, char *label, int modal)
4118 Widget dialog, layout;
4122 Dimension bw_width, pw_width;
4123 Dimension pw_height;
4127 XtSetArg(args[i], XtNresizable, True); i++;
4128 XtSetArg(args[i], XtNtitle, title); i++;
4130 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
4131 shellUp[TransientDlg] ? (dialogError = modal = TRUE, shells[TransientDlg]) : shellWidget, args, i);
4133 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
4134 layoutArgs, XtNumber(layoutArgs));
4137 XtSetArg(args[i], XtNlabel, label); i++;
4138 XtSetArg(args[i], XtNborderWidth, 0); i++;
4139 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
4142 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
4144 XtRealizeWidget(errorShell);
4145 CatchDeleteWindow(errorShell, "ErrorPopDown");
4148 XtSetArg(args[i], XtNwidth, &bw_width); i++;
4149 XtGetValues(boardWidget, args, i);
4151 XtSetArg(args[i], XtNwidth, &pw_width); i++;
4152 XtSetArg(args[i], XtNheight, &pw_height); i++;
4153 XtGetValues(errorShell, args, i);
4156 /* This code seems to tickle an X bug if it is executed too soon
4157 after xboard starts up. The coordinates get transformed as if
4158 the main window was positioned at (0, 0).
4160 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
4161 0 - pw_height + squareSize / 3, &x, &y);
4163 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
4164 RootWindowOfScreen(XtScreen(boardWidget)),
4165 (bw_width - pw_width) / 2,
4166 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
4170 if (y < 0) y = 0; /*avoid positioning top offscreen*/
4173 XtSetArg(args[i], XtNx, x); i++;
4174 XtSetArg(args[i], XtNy, y); i++;
4175 XtSetValues(errorShell, args, i);
4178 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
4181 /* Disable all user input other than deleting the window */
4182 static int frozen = 0;
4188 /* Grab by a widget that doesn't accept input */
4189 XtAddGrab(messageWidget, TRUE, FALSE);
4193 /* Undo a FreezeUI */
4197 if (!frozen) return;
4198 XtRemoveGrab(messageWidget);
4206 static int oldPausing = FALSE;
4207 static GameMode oldmode = (GameMode) -1;
4210 if (!boardWidget || !XtIsRealized(boardWidget)) return;
4212 if (pausing != oldPausing) {
4213 oldPausing = pausing;
4214 MarkMenuItem("Pause", pausing);
4216 if (appData.showButtonBar) {
4217 /* Always toggle, don't set. Previous code messes up when
4218 invoked while the button is pressed, as releasing it
4219 toggles the state again. */
4222 XtSetArg(args[0], XtNbackground, &oldbg);
4223 XtSetArg(args[1], XtNforeground, &oldfg);
4224 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
4226 XtSetArg(args[0], XtNbackground, oldfg);
4227 XtSetArg(args[1], XtNforeground, oldbg);
4229 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
4233 wname = ModeToWidgetName(oldmode);
4234 if (wname != NULL) {
4235 MarkMenuItem(wname, False);
4237 wname = ModeToWidgetName(gameMode);
4238 if (wname != NULL) {
4239 MarkMenuItem(wname, True);
4242 MarkMenuItem("Machine Match", matchMode && matchGame < appData.matchGames);
4244 /* Maybe all the enables should be handled here, not just this one */
4245 EnableMenuItem("Training", gameMode == Training || gameMode == PlayFromGameFile);
4250 * Button/menu procedures
4253 LoadGamePopUp (FILE *f, int gameNumber, char *title)
4255 cmailMsgLoaded = FALSE;
4256 if (gameNumber == 0) {
4257 int error = GameListBuild(f);
4259 DisplayError(_("Cannot build game list"), error);
4260 } else if (!ListEmpty(&gameList) &&
4261 ((ListGame *) gameList.tailPred)->number > 1) {
4262 GameListPopUp(f, title);
4268 return LoadGame(f, gameNumber, title, FALSE);
4271 /* this variable is shared between CopyPositionProc and SendPositionSelection */
4272 char *selected_fen_position=NULL;
4275 SendPositionSelection (Widget w, Atom *selection, Atom *target,
4276 Atom *type_return, XtPointer *value_return,
4277 unsigned long *length_return, int *format_return)
4279 char *selection_tmp;
4281 if (!selected_fen_position) return False; /* should never happen */
4282 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4283 /* note: since no XtSelectionDoneProc was registered, Xt will
4284 * automatically call XtFree on the value returned. So have to
4285 * make a copy of it allocated with XtMalloc */
4286 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
4287 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
4289 *value_return=selection_tmp;
4290 *length_return=strlen(selection_tmp);
4291 *type_return=*target;
4292 *format_return = 8; /* bits per byte */
4294 } else if (*target == XA_TARGETS(xDisplay)) {
4295 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4296 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4297 targets_tmp[1] = XA_STRING;
4298 *value_return = targets_tmp;
4299 *type_return = XA_ATOM;
4302 // This code leads to a read of value_return out of bounds on 64-bit systems.
4303 // Other code which I have seen always sets *format_return to 32 independent of
4304 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
4305 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
4306 *format_return = 8 * sizeof(Atom);
4307 if (*format_return > 32) {
4308 *length_return *= *format_return / 32;
4309 *format_return = 32;
4312 *format_return = 32;
4320 /* note: when called from menu all parameters are NULL, so no clue what the
4321 * Widget which was clicked on was, or what the click event was
4327 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4328 * have a notion of a position that is selected but not copied.
4329 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4331 if(gameMode == EditPosition) EditPositionDone(TRUE);
4332 if (selected_fen_position) free(selected_fen_position);
4333 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
4334 if (!selected_fen_position) return;
4335 XtOwnSelection(menuBarWidget, XA_PRIMARY,
4337 SendPositionSelection,
4338 NULL/* lose_ownership_proc */ ,
4339 NULL/* transfer_done_proc */);
4340 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4342 SendPositionSelection,
4343 NULL/* lose_ownership_proc */ ,
4344 NULL/* transfer_done_proc */);
4347 /* function called when the data to Paste is ready */
4349 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
4350 Atom *type, XtPointer value, unsigned long *len, int *format)
4353 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
4354 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
4355 EditPositionPasteFEN(fenstr);
4359 /* called when Paste Position button is pressed,
4360 * all parameters will be NULL */
4362 PastePositionProc ()
4364 XtGetSelectionValue(menuBarWidget,
4365 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4366 /* (XtSelectionCallbackProc) */ PastePositionCB,
4367 NULL, /* client_data passed to PastePositionCB */
4369 /* better to use the time field from the event that triggered the
4370 * call to this function, but that isn't trivial to get
4378 SendGameSelection (Widget w, Atom *selection, Atom *target,
4379 Atom *type_return, XtPointer *value_return,
4380 unsigned long *length_return, int *format_return)
4382 char *selection_tmp;
4384 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4385 FILE* f = fopen(gameCopyFilename, "r");
4388 if (f == NULL) return False;
4392 selection_tmp = XtMalloc(len + 1);
4393 count = fread(selection_tmp, 1, len, f);
4396 XtFree(selection_tmp);
4399 selection_tmp[len] = NULLCHAR;
4400 *value_return = selection_tmp;
4401 *length_return = len;
4402 *type_return = *target;
4403 *format_return = 8; /* bits per byte */
4405 } else if (*target == XA_TARGETS(xDisplay)) {
4406 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4407 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4408 targets_tmp[1] = XA_STRING;
4409 *value_return = targets_tmp;
4410 *type_return = XA_ATOM;
4413 // This code leads to a read of value_return out of bounds on 64-bit systems.
4414 // Other code which I have seen always sets *format_return to 32 independent of
4415 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
4416 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
4417 *format_return = 8 * sizeof(Atom);
4418 if (*format_return > 32) {
4419 *length_return *= *format_return / 32;
4420 *format_return = 32;
4423 *format_return = 32;
4435 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4436 * have a notion of a game that is selected but not copied.
4437 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4439 XtOwnSelection(menuBarWidget, XA_PRIMARY,
4442 NULL/* lose_ownership_proc */ ,
4443 NULL/* transfer_done_proc */);
4444 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4447 NULL/* lose_ownership_proc */ ,
4448 NULL/* transfer_done_proc */);
4451 /* note: when called from menu all parameters are NULL, so no clue what the
4452 * Widget which was clicked on was, or what the click event was
4454 /* function called when the data to Paste is ready */
4456 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
4457 Atom *type, XtPointer value, unsigned long *len, int *format)
4460 if (value == NULL || *len == 0) {
4461 return; /* nothing had been selected to copy */
4463 f = fopen(gamePasteFilename, "w");
4465 DisplayError(_("Can't open temp file"), errno);
4468 fwrite(value, 1, *len, f);
4471 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
4474 /* called when Paste Game button is pressed,
4475 * all parameters will be NULL */
4479 XtGetSelectionValue(menuBarWidget,
4480 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4481 /* (XtSelectionCallbackProc) */ PasteGameCB,
4482 NULL, /* client_data passed to PasteGameCB */
4484 /* better to use the time field from the event that triggered the
4485 * call to this function, but that isn't trivial to get
4494 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4500 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
4502 char buf[10], keys[32];
4504 KeyCode metaL, metaR; //, ctrlL, ctrlR;
4505 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
4506 XQueryKeymap(xDisplay,keys);
4507 metaL = XKeysymToKeycode(xDisplay, XK_Meta_L);
4508 metaR = XKeysymToKeycode(xDisplay, XK_Meta_R);
4509 // ctrlL = XKeysymToKeycode(xDisplay, XK_Control_L);
4510 // ctrlR = XKeysymToKeycode(xDisplay, XK_Control_R);
4511 if ( n == 1 && *buf >= 32 // printable
4512 && !(keys[metaL>>3]&1<<(metaL&7)) && !(keys[metaR>>3]&1<<(metaR&7)) // no alt key pressed
4513 // && !(keys[ctrlL>>3]&1<<(ctrlL&7)) && !(keys[ctrlR>>3]&1<<(ctrlR&7)) // no ctrl key pressed
4514 ) BoxAutoPopUp (buf);
4518 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4519 { // [HGM] input: let up-arrow recall previous line from history
4526 if (!shellUp[InputBoxDlg]) return;
4527 edit = boxOptions[0].handle;
4529 XtSetArg(args[j], XtNstring, &val); j++;
4530 XtGetValues(edit, args, j);
4531 val = PrevInHistory(val);
4532 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4533 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4535 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
4536 XawTextReplace(edit, 0, 0, &t);
4537 XawTextSetInsertionPoint(edit, 9999);
4542 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4543 { // [HGM] input: let down-arrow recall next line from history
4548 if (!shellUp[InputBoxDlg]) return;
4549 edit = boxOptions[0].handle;
4550 val = NextInHistory();
4551 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4552 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4554 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
4555 XawTextReplace(edit, 0, 0, &t);
4556 XawTextSetInsertionPoint(edit, 9999);
4561 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4563 if (shellUp[InputBoxDlg] == True)
4568 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4570 if (!TempBackwardActive) {
4571 TempBackwardActive = True;
4577 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4579 /* Check to see if triggered by a key release event for a repeating key.
4580 * If so the next queued event will be a key press of the same key at the same time */
4581 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
4583 XPeekEvent(xDisplay, &next);
4584 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
4585 next.xkey.keycode == event->xkey.keycode)
4589 TempBackwardActive = False;
4593 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4594 { // called as key binding
4597 if (nprms && *nprms > 0)
4601 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
4606 DisplayMessage (char *message, char *extMessage)
4608 /* display a message in the message widget */
4617 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
4622 message = extMessage;
4626 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
4628 /* need to test if messageWidget already exists, since this function
4629 can also be called during the startup, if for example a Xresource
4630 is not set up correctly */
4633 XtSetArg(arg, XtNlabel, message);
4634 XtSetValues(messageWidget, &arg, 1);
4641 DisplayTitle (char *text)
4645 char title[MSG_SIZ];
4648 if (text == NULL) text = "";
4650 if (appData.titleInWindow) {
4652 XtSetArg(args[i], XtNlabel, text); i++;
4653 XtSetValues(titleWidget, args, i);
4656 if (*text != NULLCHAR) {
4657 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
4658 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
4659 } else if (appData.icsActive) {
4660 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
4661 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
4662 } else if (appData.cmailGameName[0] != NULLCHAR) {
4663 snprintf(icon, sizeof(icon), "%s", "CMail");
4664 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
4666 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
4667 } else if (gameInfo.variant == VariantGothic) {
4668 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
4669 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
4672 } else if (gameInfo.variant == VariantFalcon) {
4673 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
4674 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
4676 } else if (appData.noChessProgram) {
4677 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
4678 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
4680 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
4681 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
4684 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
4685 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
4686 XtSetValues(shellWidget, args, i);
4687 XSync(xDisplay, False);
4692 DisplayError (String message, int error)
4697 if (appData.debugMode || appData.matchMode) {
4698 fprintf(stderr, "%s: %s\n", programName, message);
4701 if (appData.debugMode || appData.matchMode) {
4702 fprintf(stderr, "%s: %s: %s\n",
4703 programName, message, strerror(error));
4705 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
4708 ErrorPopUp(_("Error"), message, FALSE);
4713 DisplayMoveError (String message)
4717 DrawPosition(FALSE, NULL);
4718 if (appData.debugMode || appData.matchMode) {
4719 fprintf(stderr, "%s: %s\n", programName, message);
4721 if (appData.popupMoveErrors) {
4722 ErrorPopUp(_("Error"), message, FALSE);
4724 DisplayMessage(message, "");
4730 DisplayFatalError (String message, int error, int status)
4734 errorExitStatus = status;
4736 fprintf(stderr, "%s: %s\n", programName, message);
4738 fprintf(stderr, "%s: %s: %s\n",
4739 programName, message, strerror(error));
4740 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
4743 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
4744 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
4751 DisplayInformation (String message)
4754 ErrorPopUp(_("Information"), message, TRUE);
4758 DisplayNote (String message)
4761 ErrorPopUp(_("Note"), message, FALSE);
4765 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
4771 DisplayIcsInteractionTitle (String message)
4773 if (oldICSInteractionTitle == NULL) {
4774 /* Magic to find the old window title, adapted from vim */
4775 char *wina = getenv("WINDOWID");
4777 Window win = (Window) atoi(wina);
4778 Window root, parent, *children;
4779 unsigned int nchildren;
4780 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4782 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4783 if (!XQueryTree(xDisplay, win, &root, &parent,
4784 &children, &nchildren)) break;
4785 if (children) XFree((void *)children);
4786 if (parent == root || parent == 0) break;
4789 XSetErrorHandler(oldHandler);
4791 if (oldICSInteractionTitle == NULL) {
4792 oldICSInteractionTitle = "xterm";
4795 printf("\033]0;%s\007", message);
4799 char pendingReplyPrefix[MSG_SIZ];
4800 ProcRef pendingReplyPR;
4803 AskQuestionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4806 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
4810 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
4814 AskQuestionPopDown ()
4816 if (!askQuestionUp) return;
4817 XtPopdown(askQuestionShell);
4818 XtDestroyWidget(askQuestionShell);
4819 askQuestionUp = False;
4823 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4829 reply = XawDialogGetValueString(w = XtParent(w));
4830 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
4831 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
4832 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
4833 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
4834 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
4835 AskQuestionPopDown();
4837 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
4841 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
4846 XtSetArg(args[0], XtNlabel, &name);
4847 XtGetValues(w, args, 1);
4849 if (strcmp(name, _("cancel")) == 0) {
4850 AskQuestionPopDown();
4852 AskQuestionReplyAction(w, NULL, NULL, NULL);
4857 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
4860 Widget popup, layout, dialog, edit;
4866 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
4867 pendingReplyPR = pr;
4870 XtSetArg(args[i], XtNresizable, True); i++;
4871 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
4872 askQuestionShell = popup =
4873 XtCreatePopupShell(title, transientShellWidgetClass,
4874 shellWidget, args, i);
4877 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
4878 layoutArgs, XtNumber(layoutArgs));
4881 XtSetArg(args[i], XtNlabel, question); i++;
4882 XtSetArg(args[i], XtNvalue, ""); i++;
4883 XtSetArg(args[i], XtNborderWidth, 0); i++;
4884 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
4887 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
4888 (XtPointer) dialog);
4889 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
4890 (XtPointer) dialog);
4892 XtRealizeWidget(popup);
4893 CatchDeleteWindow(popup, "AskQuestionPopDown");
4895 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
4896 &x, &y, &win_x, &win_y, &mask);
4898 XtSetArg(args[0], XtNx, x - 10);
4899 XtSetArg(args[1], XtNy, y - 30);
4900 XtSetValues(popup, args, 2);
4902 XtPopup(popup, XtGrabExclusive);
4903 askQuestionUp = True;
4905 edit = XtNameToWidget(dialog, "*value");
4906 XtSetKeyboardFocus(popup, edit);
4911 PlaySound (char *name)
4913 if (*name == NULLCHAR) {
4915 } else if (strcmp(name, "$") == 0) {
4916 putc(BELLCHAR, stderr);
4919 char *prefix = "", *sep = "";
4920 if(appData.soundProgram[0] == NULLCHAR) return;
4921 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
4922 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
4930 PlaySound(appData.soundMove);
4936 PlaySound(appData.soundIcsWin);
4942 PlaySound(appData.soundIcsLoss);
4948 PlaySound(appData.soundIcsDraw);
4952 PlayIcsUnfinishedSound ()
4954 PlaySound(appData.soundIcsUnfinished);
4960 PlaySound(appData.soundIcsAlarm);
4966 PlaySound(appData.soundTell);
4972 system("stty echo");
4979 system("stty -echo");
4984 RunCommand (char *buf)
4990 Colorize (ColorClass cc, int continuation)
4993 int count, outCount, error;
4995 if (textColors[(int)cc].bg > 0) {
4996 if (textColors[(int)cc].fg > 0) {
4997 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
4998 textColors[(int)cc].fg, textColors[(int)cc].bg);
5000 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
5001 textColors[(int)cc].bg);
5004 if (textColors[(int)cc].fg > 0) {
5005 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
5006 textColors[(int)cc].fg);
5008 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
5011 count = strlen(buf);
5012 outCount = OutputToProcess(NoProc, buf, count, &error);
5013 if (outCount < count) {
5014 DisplayFatalError(_("Error writing to display"), error, 1);
5017 if (continuation) return;
5020 PlaySound(appData.soundShout);
5023 PlaySound(appData.soundSShout);
5026 PlaySound(appData.soundChannel1);
5029 PlaySound(appData.soundChannel);
5032 PlaySound(appData.soundKibitz);
5035 PlaySound(appData.soundTell);
5037 case ColorChallenge:
5038 PlaySound(appData.soundChallenge);
5041 PlaySound(appData.soundRequest);
5044 PlaySound(appData.soundSeek);
5056 return getpwuid(getuid())->pw_name;
5060 ExpandPathName (char *path)
5062 static char static_buf[4*MSG_SIZ];
5063 char *d, *s, buf[4*MSG_SIZ];
5069 while (*s && isspace(*s))
5078 if (*(s+1) == '/') {
5079 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
5083 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
5084 { char *p; if(p = strchr(buf, '/')) *p = 0; }
5085 pwd = getpwnam(buf);
5088 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
5092 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
5093 strcat(d, strchr(s+1, '/'));
5097 safeStrCpy(d, s, 4*MSG_SIZ );
5105 static char host_name[MSG_SIZ];
5107 #if HAVE_GETHOSTNAME
5108 gethostname(host_name, MSG_SIZ);
5110 #else /* not HAVE_GETHOSTNAME */
5111 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
5112 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
5114 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
5116 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
5117 #endif /* not HAVE_GETHOSTNAME */
5120 XtIntervalId delayedEventTimerXID = 0;
5121 DelayedEventCallback delayedEventCallback = 0;
5126 delayedEventTimerXID = 0;
5127 delayedEventCallback();
5131 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
5133 if(delayedEventTimerXID && delayedEventCallback == cb)
5134 // [HGM] alive: replace, rather than add or flush identical event
5135 XtRemoveTimeOut(delayedEventTimerXID);
5136 delayedEventCallback = cb;
5137 delayedEventTimerXID =
5138 XtAppAddTimeOut(appContext, millisec,
5139 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
5142 DelayedEventCallback
5145 if (delayedEventTimerXID) {
5146 return delayedEventCallback;
5153 CancelDelayedEvent ()
5155 if (delayedEventTimerXID) {
5156 XtRemoveTimeOut(delayedEventTimerXID);
5157 delayedEventTimerXID = 0;
5161 XtIntervalId loadGameTimerXID = 0;
5164 LoadGameTimerRunning ()
5166 return loadGameTimerXID != 0;
5170 StopLoadGameTimer ()
5172 if (loadGameTimerXID != 0) {
5173 XtRemoveTimeOut(loadGameTimerXID);
5174 loadGameTimerXID = 0;
5182 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
5184 loadGameTimerXID = 0;
5189 StartLoadGameTimer (long millisec)
5192 XtAppAddTimeOut(appContext, millisec,
5193 (XtTimerCallbackProc) LoadGameTimerCallback,
5197 XtIntervalId analysisClockXID = 0;
5200 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
5202 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
5203 || appData.icsEngineAnalyze) { // [DM]
5204 AnalysisPeriodicEvent(0);
5205 StartAnalysisClock();
5210 StartAnalysisClock ()
5213 XtAppAddTimeOut(appContext, 2000,
5214 (XtTimerCallbackProc) AnalysisClockCallback,
5218 XtIntervalId clockTimerXID = 0;
5221 ClockTimerRunning ()
5223 return clockTimerXID != 0;
5229 if (clockTimerXID != 0) {
5230 XtRemoveTimeOut(clockTimerXID);
5239 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
5246 StartClockTimer (long millisec)
5249 XtAppAddTimeOut(appContext, millisec,
5250 (XtTimerCallbackProc) ClockTimerCallback,
5255 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
5260 /* check for low time warning */
5261 Pixel foregroundOrWarningColor = timerForegroundPixel;
5264 appData.lowTimeWarning &&
5265 (timer / 1000) < appData.icsAlarmTime)
5266 foregroundOrWarningColor = lowTimeWarningColor;
5268 if (appData.clockMode) {
5269 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
5270 XtSetArg(args[0], XtNlabel, buf);
5272 snprintf(buf, MSG_SIZ, "%s ", color);
5273 XtSetArg(args[0], XtNlabel, buf);
5278 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
5279 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
5281 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
5282 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
5285 XtSetValues(w, args, 3);
5289 DisplayWhiteClock (long timeRemaining, int highlight)
5293 if(appData.noGUI) return;
5294 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
5295 if (highlight && iconPixmap == bIconPixmap) {
5296 iconPixmap = wIconPixmap;
5297 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
5298 XtSetValues(shellWidget, args, 1);
5303 DisplayBlackClock (long timeRemaining, int highlight)
5307 if(appData.noGUI) return;
5308 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
5309 if (highlight && iconPixmap == wIconPixmap) {
5310 iconPixmap = bIconPixmap;
5311 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
5312 XtSetValues(shellWidget, args, 1);
5331 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
5335 int to_prog[2], from_prog[2];
5339 if (appData.debugMode) {
5340 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
5343 /* We do NOT feed the cmdLine to the shell; we just
5344 parse it into blank-separated arguments in the
5345 most simple-minded way possible.
5348 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
5351 while(*p == ' ') p++;
5353 if(*p == '"' || *p == '\'')
5354 p = strchr(++argv[i-1], *p);
5355 else p = strchr(p, ' ');
5356 if (p == NULL) break;
5361 SetUpChildIO(to_prog, from_prog);
5363 if ((pid = fork()) == 0) {
5365 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
5366 close(to_prog[1]); // first close the unused pipe ends
5367 close(from_prog[0]);
5368 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
5369 dup2(from_prog[1], 1);
5370 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
5371 close(from_prog[1]); // and closing again loses one of the pipes!
5372 if(fileno(stderr) >= 2) // better safe than sorry...
5373 dup2(1, fileno(stderr)); /* force stderr to the pipe */
5375 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
5380 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
5382 execvp(argv[0], argv);
5384 /* If we get here, exec failed */
5389 /* Parent process */
5391 close(from_prog[1]);
5393 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5396 cp->fdFrom = from_prog[0];
5397 cp->fdTo = to_prog[1];
5402 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
5404 AlarmCallBack (int n)
5410 DestroyChildProcess (ProcRef pr, int signalType)
5412 ChildProc *cp = (ChildProc *) pr;
5414 if (cp->kind != CPReal) return;
5416 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
5417 signal(SIGALRM, AlarmCallBack);
5419 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
5420 kill(cp->pid, SIGKILL); // kill it forcefully
5421 wait((int *) 0); // and wait again
5425 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
5427 /* Process is exiting either because of the kill or because of
5428 a quit command sent by the backend; either way, wait for it to die.
5437 InterruptChildProcess (ProcRef pr)
5439 ChildProc *cp = (ChildProc *) pr;
5441 if (cp->kind != CPReal) return;
5442 (void) kill(cp->pid, SIGINT); /* stop it thinking */
5446 OpenTelnet (char *host, char *port, ProcRef *pr)
5448 char cmdLine[MSG_SIZ];
5450 if (port[0] == NULLCHAR) {
5451 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
5453 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
5455 return StartChildProcess(cmdLine, "", pr);
5459 OpenTCP (char *host, char *port, ProcRef *pr)
5462 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
5463 #else /* !OMIT_SOCKETS */
5464 struct addrinfo hints;
5465 struct addrinfo *ais, *ai;
5470 memset(&hints, 0, sizeof(hints));
5471 hints.ai_family = AF_UNSPEC;
5472 hints.ai_socktype = SOCK_STREAM;
5474 error = getaddrinfo(host, port, &hints, &ais);
5476 /* a getaddrinfo error is not an errno, so can't return it */
5477 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
5478 host, port, gai_strerror(error));
5482 for (ai = ais; ai != NULL; ai = ai->ai_next) {
5483 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
5487 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
5500 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5506 #endif /* !OMIT_SOCKETS */
5512 OpenCommPort (char *name, ProcRef *pr)
5517 fd = open(name, 2, 0);
5518 if (fd < 0) return errno;
5520 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5531 OpenLoopback (ProcRef *pr)
5536 SetUpChildIO(to, from);
5538 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5541 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
5549 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
5551 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
5555 #define INPUT_SOURCE_BUF_SIZE 8192
5564 char buf[INPUT_SOURCE_BUF_SIZE];
5569 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
5571 InputSource *is = (InputSource *) closure;
5576 if (is->lineByLine) {
5577 count = read(is->fd, is->unused,
5578 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
5580 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
5583 is->unused += count;
5585 while (p < is->unused) {
5586 q = memchr(p, '\n', is->unused - p);
5587 if (q == NULL) break;
5589 (is->func)(is, is->closure, p, q - p, 0);
5593 while (p < is->unused) {
5598 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
5603 (is->func)(is, is->closure, is->buf, count, error);
5608 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
5611 ChildProc *cp = (ChildProc *) pr;
5613 is = (InputSource *) calloc(1, sizeof(InputSource));
5614 is->lineByLine = lineByLine;
5618 is->fd = fileno(stdin);
5620 is->kind = cp->kind;
5621 is->fd = cp->fdFrom;
5624 is->unused = is->buf;
5627 is->xid = XtAppAddInput(appContext, is->fd,
5628 (XtPointer) (XtInputReadMask),
5629 (XtInputCallbackProc) DoInputCallback,
5631 is->closure = closure;
5632 return (InputSourceRef) is;
5636 RemoveInputSource (InputSourceRef isr)
5638 InputSource *is = (InputSource *) isr;
5640 if (is->xid == 0) return;
5641 XtRemoveInput(is->xid);
5646 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
5648 static int line = 0;
5649 ChildProc *cp = (ChildProc *) pr;
5654 if (appData.noJoin || !appData.useInternalWrap)
5655 outCount = fwrite(message, 1, count, stdout);
5658 int width = get_term_width();
5659 int len = wrap(NULL, message, count, width, &line);
5660 char *msg = malloc(len);
5664 outCount = fwrite(message, 1, count, stdout);
5667 dbgchk = wrap(msg, message, count, width, &line);
5668 if (dbgchk != len && appData.debugMode)
5669 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
5670 outCount = fwrite(msg, 1, dbgchk, stdout);
5676 outCount = write(cp->fdTo, message, count);
5686 /* Output message to process, with "ms" milliseconds of delay
5687 between each character. This is needed when sending the logon
5688 script to ICC, which for some reason doesn't like the
5689 instantaneous send. */
5691 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
5693 ChildProc *cp = (ChildProc *) pr;
5698 r = write(cp->fdTo, message++, 1);
5711 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
5713 /* Masks for XPM pieces. Black and white pieces can have
5714 different shapes, but in the interest of retaining my
5715 sanity pieces must have the same outline on both light
5716 and dark squares, and all pieces must use the same
5717 background square colors/images. */
5719 static int xpmDone = 0;
5720 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
5721 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
5724 CreateAnimMasks (int pieceDepth)
5730 unsigned long plane;
5733 /* Need a bitmap just to get a GC with right depth */
5734 buf = XCreatePixmap(xDisplay, xBoardWindow,
5736 values.foreground = 1;
5737 values.background = 0;
5738 /* Don't use XtGetGC, not read only */
5739 maskGC = XCreateGC(xDisplay, buf,
5740 GCForeground | GCBackground, &values);
5741 XFreePixmap(xDisplay, buf);
5743 buf = XCreatePixmap(xDisplay, xBoardWindow,
5744 squareSize, squareSize, pieceDepth);
5745 values.foreground = XBlackPixel(xDisplay, xScreen);
5746 values.background = XWhitePixel(xDisplay, xScreen);
5747 bufGC = XCreateGC(xDisplay, buf,
5748 GCForeground | GCBackground, &values);
5750 for (piece = WhitePawn; piece <= BlackKing; piece++) {
5751 /* Begin with empty mask */
5752 if(!xpmDone) // [HGM] pieces: keep using existing
5753 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
5754 squareSize, squareSize, 1);
5755 XSetFunction(xDisplay, maskGC, GXclear);
5756 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
5757 0, 0, squareSize, squareSize);
5759 /* Take a copy of the piece */
5764 XSetFunction(xDisplay, bufGC, GXcopy);
5765 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
5767 0, 0, squareSize, squareSize, 0, 0);
5769 /* XOR the background (light) over the piece */
5770 XSetFunction(xDisplay, bufGC, GXxor);
5772 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
5773 0, 0, squareSize, squareSize, 0, 0);
5775 XSetForeground(xDisplay, bufGC, lightSquareColor);
5776 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
5779 /* We now have an inverted piece image with the background
5780 erased. Construct mask by just selecting all the non-zero
5781 pixels - no need to reconstruct the original image. */
5782 XSetFunction(xDisplay, maskGC, GXor);
5784 /* Might be quicker to download an XImage and create bitmap
5785 data from it rather than this N copies per piece, but it
5786 only takes a fraction of a second and there is a much
5787 longer delay for loading the pieces. */
5788 for (n = 0; n < pieceDepth; n ++) {
5789 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
5790 0, 0, squareSize, squareSize,
5796 XFreePixmap(xDisplay, buf);
5797 XFreeGC(xDisplay, bufGC);
5798 XFreeGC(xDisplay, maskGC);
5802 InitAnimState (AnimNr anr, XWindowAttributes *info)
5807 /* Each buffer is square size, same depth as window */
5808 animBufs[anr+4] = xBoardWindow;
5809 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
5810 squareSize, squareSize, info->depth);
5811 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
5812 squareSize, squareSize, info->depth);
5814 /* Create a plain GC for blitting */
5815 mask = GCForeground | GCBackground | GCFunction |
5816 GCPlaneMask | GCGraphicsExposures;
5817 values.foreground = XBlackPixel(xDisplay, xScreen);
5818 values.background = XWhitePixel(xDisplay, xScreen);
5819 values.function = GXcopy;
5820 values.plane_mask = AllPlanes;
5821 values.graphics_exposures = False;
5822 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
5824 /* Piece will be copied from an existing context at
5825 the start of each new animation/drag. */
5826 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
5828 /* Outline will be a read-only copy of an existing */
5829 animGCs[anr+4] = None;
5835 XWindowAttributes info;
5837 if (xpmDone && gameInfo.variant == oldVariant) return;
5838 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
5839 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
5841 InitAnimState(Game, &info);
5842 InitAnimState(Player, &info);
5844 /* For XPM pieces, we need bitmaps to use as masks. */
5846 CreateAnimMasks(info.depth), xpmDone = 1;
5851 static Boolean frameWaiting;
5854 FrameAlarm (int sig)
5856 frameWaiting = False;
5857 /* In case System-V style signals. Needed?? */
5858 signal(SIGALRM, FrameAlarm);
5862 FrameDelay (int time)
5864 struct itimerval delay;
5866 XSync(xDisplay, False);
5869 frameWaiting = True;
5870 signal(SIGALRM, FrameAlarm);
5871 delay.it_interval.tv_sec =
5872 delay.it_value.tv_sec = time / 1000;
5873 delay.it_interval.tv_usec =
5874 delay.it_value.tv_usec = (time % 1000) * 1000;
5875 setitimer(ITIMER_REAL, &delay, NULL);
5876 while (frameWaiting) pause();
5877 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
5878 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
5879 setitimer(ITIMER_REAL, &delay, NULL);
5886 FrameDelay (int time)
5888 XSync(xDisplay, False);
5890 usleep(time * 1000);
5896 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
5900 /* Bitmap for piece being moved. */
5901 if (appData.monoMode) {
5902 *mask = *pieceToSolid(piece);
5903 } else if (useImages) {
5905 *mask = xpmMask[piece];
5907 *mask = ximMaskPm[piece];
5910 *mask = *pieceToSolid(piece);
5913 /* GC for piece being moved. Square color doesn't matter, but
5914 since it gets modified we make a copy of the original. */
5916 if (appData.monoMode)
5921 if (appData.monoMode)
5926 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
5928 /* Outline only used in mono mode and is not modified */
5930 *outline = bwPieceGC;
5932 *outline = wbPieceGC;
5936 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
5941 /* Draw solid rectangle which will be clipped to shape of piece */
5942 XFillRectangle(xDisplay, dest, clip,
5943 0, 0, squareSize, squareSize);
5944 if (appData.monoMode)
5945 /* Also draw outline in contrasting color for black
5946 on black / white on white cases */
5947 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
5948 0, 0, squareSize, squareSize, 0, 0, 1);
5950 /* Copy the piece */
5955 if(appData.upsideDown && flipView) kind ^= 2;
5956 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
5958 0, 0, squareSize, squareSize,
5964 InsertPiece (AnimNr anr, ChessSquare piece)
5966 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
5970 DrawBlank (AnimNr anr, int x, int y, int startColor)
5972 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
5975 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
5976 int srcX, int srcY, int width, int height, int destX, int destY)
5978 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
5979 srcX, srcY, width, height, destX, destY);
5983 SetDragPiece (AnimNr anr, ChessSquare piece)
5986 /* The piece will be drawn using its own bitmap as a matte */
5987 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
5988 XSetClipMask(xDisplay, animGCs[anr+2], mask);
5991 #include <sys/ioctl.h>
5995 int fd, default_width;
5998 default_width = 79; // this is FICS default anyway...
6000 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
6002 if (!ioctl(fd, TIOCGSIZE, &win))
6003 default_width = win.ts_cols;
6004 #elif defined(TIOCGWINSZ)
6006 if (!ioctl(fd, TIOCGWINSZ, &win))
6007 default_width = win.ws_col;
6009 return default_width;
6015 static int old_width = 0;
6016 int new_width = get_term_width();
6018 if (old_width != new_width)
6019 ics_printf("set width %d\n", new_width);
6020 old_width = new_width;
6024 NotifyFrontendLogin ()
6029 /* [AS] Arrow highlighting support */
6032 DrawPolygon (Pnt arrow[], int nr)
6036 for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
6037 XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
6038 if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
6042 UpdateLogos (int displ)
6044 return; // no logos in XBoard yet