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"
215 #define usleep(t) _sleep2(((t)+500)/1000)
219 # define _(s) gettext (s)
220 # define N_(s) gettext_noop (s)
226 int main P((int argc, char **argv));
227 FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
228 char *init_path, char *filter, char *mode, int (*show_entry)(), char **name_return));
229 RETSIGTYPE CmailSigHandler P((int sig));
230 RETSIGTYPE IntSigHandler P((int sig));
231 RETSIGTYPE TermSizeSigHandler P((int sig));
232 void CreateGCs P((int redo));
233 void CreateAnyPieces P((void));
234 void CreateXIMPieces P((void));
235 void CreateXPMPieces P((void));
236 void CreateXPMBoard P((char *s, int n));
237 void CreatePieces P((void));
238 void CreatePieceMenus P((void));
239 Widget CreateMenuBar P((Menu *mb, int boardWidth));
240 Widget CreateButtonBar P ((MenuItem *mi));
242 char *InsertPxlSize P((char *pattern, int targetPxlSize));
243 XFontSet CreateFontSet P((char *base_fnt_lst));
245 char *FindFont P((char *pattern, int targetPxlSize));
247 void PieceMenuPopup P((Widget w, XEvent *event,
248 String *params, Cardinal *num_params));
249 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
250 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
251 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
252 u_int wreq, u_int hreq));
253 void CreateGrid P((void));
254 int EventToSquare P((int x, int limit));
255 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
256 void DelayedDrag P((void));
257 void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
258 void HandleUserMove P((Widget w, XEvent *event,
259 String *prms, Cardinal *nprms));
260 void AnimateUserMove P((Widget w, XEvent * event,
261 String * params, Cardinal * nParams));
262 void HandlePV P((Widget w, XEvent * event,
263 String * params, Cardinal * nParams));
264 void SelectPV P((Widget w, XEvent * event,
265 String * params, Cardinal * nParams));
266 void StopPV P((Widget w, XEvent * event,
267 String * params, Cardinal * nParams));
268 void WhiteClock P((Widget w, XEvent *event,
269 String *prms, Cardinal *nprms));
270 void BlackClock P((Widget w, XEvent *event,
271 String *prms, Cardinal *nprms));
272 void DrawPositionProc P((Widget w, XEvent *event,
273 String *prms, Cardinal *nprms));
274 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
276 void CommentClick P((Widget w, XEvent * event,
277 String * params, Cardinal * nParams));
278 void CommentPopUp P((char *title, char *label));
279 void CommentPopDown P((void));
280 void ICSInputBoxPopUp P((void));
281 void ICSInputBoxPopDown P((void));
282 void FileNamePopUp P((char *label, char *def, char *filter,
283 FileProc proc, char *openMode));
284 void FileNamePopDown P((void));
285 void FileNameCallback P((Widget w, XtPointer client_data,
286 XtPointer call_data));
287 void FileNameAction P((Widget w, XEvent *event,
288 String *prms, Cardinal *nprms));
289 void AskQuestionReplyAction P((Widget w, XEvent *event,
290 String *prms, Cardinal *nprms));
291 void AskQuestionProc P((Widget w, XEvent *event,
292 String *prms, Cardinal *nprms));
293 void AskQuestionPopDown P((void));
294 void PromotionPopDown P((void));
295 void PromotionCallback P((Widget w, XtPointer client_data,
296 XtPointer call_data));
297 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
298 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
299 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
300 void TypeInProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
301 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
302 void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
303 void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
304 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
305 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
306 Boolean TempBackwardActive = False;
307 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
308 void DisplayMove P((int moveNumber));
309 void DisplayTitle P((char *title));
310 void ICSInitScript P((void));
311 void ErrorPopUp P((char *title, char *text, int modal));
312 void ErrorPopDown P((void));
313 static char *ExpandPathName P((char *path));
314 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
315 void GameListOptionsPopDown P(());
316 void GenericPopDown P(());
317 void update_ics_width P(());
318 int get_term_width P(());
319 int CopyMemoProc P(());
320 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
321 Boolean IsDrawArrowEnabled P(());
324 * XBoard depends on Xt R4 or higher
326 int xtVersion = XtSpecificationRelease;
331 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
332 jailSquareColor, highlightSquareColor, premoveHighlightColor;
333 Pixel lowTimeWarningColor;
334 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
335 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
336 wjPieceGC, bjPieceGC, prelineGC, countGC;
337 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
338 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
339 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
340 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
341 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
342 ICSInputShell, fileNameShell, askQuestionShell;
343 Widget historyShell, evalGraphShell, gameListShell;
344 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
345 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
346 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
348 XFontSet fontSet, clockFontSet;
351 XFontStruct *clockFontStruct;
353 Font coordFontID, countFontID;
354 XFontStruct *coordFontStruct, *countFontStruct;
355 XtAppContext appContext;
357 char *oldICSInteractionTitle;
361 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
363 Position commentX = -1, commentY = -1;
364 Dimension commentW, commentH;
365 typedef unsigned int BoardSize;
367 Boolean chessProgram;
369 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
370 int smallLayout = 0, tinyLayout = 0,
371 marginW, marginH, // [HGM] for run-time resizing
372 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
373 ICSInputBoxUp = False, askQuestionUp = False,
374 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
375 errorUp = False, errorExitStatus = -1, defaultLineGap;
376 Dimension textHeight;
377 Pixel timerForegroundPixel, timerBackgroundPixel;
378 Pixel buttonForegroundPixel, buttonBackgroundPixel;
379 char *chessDir, *programName, *programVersion;
380 Boolean alwaysOnTop = False;
381 char *icsTextMenuString;
383 char *firstChessProgramNames;
384 char *secondChessProgramNames;
386 WindowPlacement wpMain;
387 WindowPlacement wpConsole;
388 WindowPlacement wpComment;
389 WindowPlacement wpMoveHistory;
390 WindowPlacement wpEvalGraph;
391 WindowPlacement wpEngineOutput;
392 WindowPlacement wpGameList;
393 WindowPlacement wpTags;
395 extern Widget shells[];
396 extern Boolean shellUp[];
400 Pixmap pieceBitmap[2][(int)BlackPawn];
401 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
402 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
403 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
404 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
405 Pixmap xpmBoardBitmap[2];
406 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
407 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
408 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
409 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
410 XImage *ximLightSquare, *ximDarkSquare;
413 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
414 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
416 #define White(piece) ((int)(piece) < (int)BlackPawn)
418 /* Bitmaps for use as masks when drawing XPM pieces.
419 Need one for each black and white piece. */
420 static Pixmap xpmMask[BlackKing + 1];
422 /* This magic number is the number of intermediate frames used
423 in each half of the animation. For short moves it's reduced
424 by 1. The total number of frames will be factor * 2 + 1. */
427 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
429 #define PAUSE_BUTTON "P"
430 MenuItem buttonBar[] = {
431 {"<<", "<<", ToStartEvent},
432 {"<", "<", BackwardEvent},
433 {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseEvent},
434 {">", ">", ForwardEvent},
435 {">>", ">>", ToEndEvent},
439 #define PIECE_MENU_SIZE 18
440 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
441 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
442 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
443 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
444 N_("Empty square"), N_("Clear board") },
445 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
446 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
447 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
448 N_("Empty square"), N_("Clear board") }
450 /* must be in same order as pieceMenuStrings! */
451 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
452 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
453 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
454 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
455 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
456 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
457 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
458 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
459 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
462 #define DROP_MENU_SIZE 6
463 String dropMenuStrings[DROP_MENU_SIZE] = {
464 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
466 /* must be in same order as dropMenuStrings! */
467 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
468 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
469 WhiteRook, WhiteQueen
477 DropMenuEnables dmEnables[] = {
495 { XtNborderWidth, 0 },
496 { XtNdefaultDistance, 0 },
500 { XtNborderWidth, 0 },
501 { XtNresizable, (XtArgVal) True },
505 { XtNborderWidth, 0 },
511 { XtNjustify, (XtArgVal) XtJustifyRight },
512 { XtNlabel, (XtArgVal) "..." },
513 { XtNresizable, (XtArgVal) True },
514 { XtNresize, (XtArgVal) False }
517 Arg messageArgs[] = {
518 { XtNjustify, (XtArgVal) XtJustifyLeft },
519 { XtNlabel, (XtArgVal) "..." },
520 { XtNresizable, (XtArgVal) True },
521 { XtNresize, (XtArgVal) False }
525 { XtNborderWidth, 0 },
526 { XtNjustify, (XtArgVal) XtJustifyLeft }
529 XtResource clientResources[] = {
530 { "flashCount", "flashCount", XtRInt, sizeof(int),
531 XtOffset(AppDataPtr, flashCount), XtRImmediate,
532 (XtPointer) FLASH_COUNT },
535 XrmOptionDescRec shellOptions[] = {
536 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
537 { "-flash", "flashCount", XrmoptionNoArg, "3" },
538 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
541 XtActionsRec boardActions[] = {
542 { "DrawPosition", DrawPositionProc },
543 { "HandleUserMove", HandleUserMove },
544 { "AnimateUserMove", AnimateUserMove },
545 { "HandlePV", HandlePV },
546 { "SelectPV", SelectPV },
547 { "StopPV", StopPV },
548 { "FileNameAction", FileNameAction },
549 { "AskQuestionProc", AskQuestionProc },
550 { "AskQuestionReplyAction", AskQuestionReplyAction },
551 { "PieceMenuPopup", PieceMenuPopup },
552 { "WhiteClock", WhiteClock },
553 { "BlackClock", BlackClock },
554 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
555 { "QuitProc", QuitWrapper },
556 { "ManProc", ManInner },
557 { "TempBackwardProc", TempBackwardProc },
558 { "TempForwardProc", TempForwardProc },
559 { "CommentClick", (XtActionProc) CommentClick },
560 { "CommentPopDown", (XtActionProc) CommentPopDown },
561 { "TagsPopDown", (XtActionProc) TagsPopDown },
562 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
563 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
564 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
565 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
566 { "GameListPopDown", (XtActionProc) GameListPopDown },
567 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
568 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
569 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
570 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
571 { "GenericPopDown", (XtActionProc) GenericPopDown },
572 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
573 { "SelectMove", (XtActionProc) SelectMove },
574 { "LoadSelectedProc", LoadSelectedProc },
575 { "SetFilterProc", SetFilterProc },
576 { "TypeInProc", TypeInProc },
577 { "EnterKeyProc", EnterKeyProc },
578 { "UpKeyProc", UpKeyProc },
579 { "DownKeyProc", DownKeyProc },
582 char globalTranslations[] =
583 ":<Key>F9: MenuItem(ResignProc) \n \
584 :Ctrl<Key>n: MenuItem(NewGame) \n \
585 :Meta<Key>V: MenuItem(NewVariant) \n \
586 :Ctrl<Key>o: MenuItem(LoadGame) \n \
587 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
588 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
589 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
590 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
591 :Ctrl<Key>s: MenuItem(SaveGame) \n \
592 :Ctrl<Key>c: MenuItem(CopyGame) \n \
593 :Ctrl<Key>v: MenuItem(PasteGame) \n \
594 :Ctrl<Key>O: MenuItem(LoadPosition) \n \
595 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
596 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
597 :Ctrl<Key>S: MenuItem(SavePosition) \n \
598 :Ctrl<Key>C: MenuItem(CopyPosition) \n \
599 :Ctrl<Key>V: MenuItem(PastePosition) \n \
600 :Ctrl<Key>q: MenuItem(Exit) \n \
601 :Ctrl<Key>w: MenuItem(MachineWhite) \n \
602 :Ctrl<Key>b: MenuItem(MachineBlack) \n \
603 :Ctrl<Key>t: MenuItem(TwoMachines) \n \
604 :Ctrl<Key>a: MenuItem(AnalysisMode) \n \
605 :Ctrl<Key>g: MenuItem(AnalyzeFile) \n \
606 :Ctrl<Key>e: MenuItem(EditGame) \n \
607 :Ctrl<Key>E: MenuItem(EditPosition) \n \
608 :Meta<Key>O: MenuItem(ShowEngineOutput) \n \
609 :Meta<Key>E: MenuItem(ShowEvaluationGraph) \n \
610 :Meta<Key>G: MenuItem(ShowGameList) \n \
611 :Meta<Key>H: MenuItem(ShowMoveHistory) \n \
612 :<Key>Pause: MenuItem(Pause) \n \
613 :<Key>F3: MenuItem(Accept) \n \
614 :<Key>F4: MenuItem(Decline) \n \
615 :<Key>F12: MenuItem(Rematch) \n \
616 :<Key>F5: MenuItem(CallFlag) \n \
617 :<Key>F6: MenuItem(Draw) \n \
618 :<Key>F7: MenuItem(Adjourn) \n \
619 :<Key>F8: MenuItem(Abort) \n \
620 :<Key>F10: MenuItem(StopObserving) \n \
621 :<Key>F11: MenuItem(StopExamining) \n \
622 :Ctrl<Key>d: MenuItem(DebugProc) \n \
623 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
624 :Meta<Key>End: MenuItem(ToEnd) \n \
625 :Meta<Key>Right: MenuItem(Forward) \n \
626 :Meta<Key>Home: MenuItem(ToStart) \n \
627 :Meta<Key>Left: MenuItem(Backward) \n \
628 :<Key>Left: MenuItem(Backward) \n \
629 :<Key>Right: MenuItem(Forward) \n \
630 :<Key>Home: MenuItem(Revert) \n \
631 :<Key>End: MenuItem(TruncateGame) \n \
632 :Ctrl<Key>m: MenuItem(MoveNow) \n \
633 :Ctrl<Key>x: MenuItem(RetractMove) \n \
634 :Meta<Key>J: MenuItem(Adjudications) \n \
635 :Meta<Key>U: MenuItem(CommonEngine) \n \
636 :Meta<Key>T: MenuItem(TimeControl) \n \
637 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
638 #ifndef OPTIONSDIALOG
640 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
641 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
642 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
643 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
644 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
647 :<Key>F1: MenuItem(Manual) \n \
648 :<Key>F2: MenuItem(FlipView) \n \
649 :<KeyDown>Return: TempBackwardProc() \n \
650 :<KeyUp>Return: TempForwardProc() \n";
652 char boardTranslations[] =
653 "<Btn1Down>: HandleUserMove(0) \n \
654 Shift<Btn1Up>: HandleUserMove(1) \n \
655 <Btn1Up>: HandleUserMove(0) \n \
656 <Btn1Motion>: AnimateUserMove() \n \
657 <Btn3Motion>: HandlePV() \n \
658 <Btn2Motion>: HandlePV() \n \
659 <Btn3Up>: PieceMenuPopup(menuB) \n \
660 <Btn2Up>: PieceMenuPopup(menuB) \n \
661 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
662 PieceMenuPopup(menuB) \n \
663 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
664 PieceMenuPopup(menuW) \n \
665 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
666 PieceMenuPopup(menuW) \n \
667 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
668 PieceMenuPopup(menuB) \n";
670 char whiteTranslations[] =
671 "Shift<BtnDown>: WhiteClock(1)\n \
672 <BtnDown>: WhiteClock(0)\n";
673 char blackTranslations[] =
674 "Shift<BtnDown>: BlackClock(1)\n \
675 <BtnDown>: BlackClock(0)\n";
677 char ICSInputTranslations[] =
678 "<Key>Up: UpKeyProc() \n "
679 "<Key>Down: DownKeyProc() \n "
680 "<Key>Return: EnterKeyProc() \n";
682 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
683 // as the widget is destroyed before the up-click can call extend-end
684 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
686 String xboardResources[] = {
687 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
688 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
689 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
694 /* Max possible square size */
695 #define MAXSQSIZE 256
697 static int xpm_avail[MAXSQSIZE];
699 #ifdef HAVE_DIR_STRUCT
701 /* Extract piece size from filename */
703 xpm_getsize (char *name, int len, char *ext)
711 if ((p=strchr(name, '.')) == NULL ||
712 StrCaseCmp(p+1, ext) != 0)
718 while (*p && isdigit(*p))
725 /* Setup xpm_avail */
727 xpm_getavail (char *dirname, char *ext)
733 for (i=0; i<MAXSQSIZE; ++i)
736 if (appData.debugMode)
737 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
739 dir = opendir(dirname);
742 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
743 programName, dirname);
747 while ((ent=readdir(dir)) != NULL) {
748 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
749 if (i > 0 && i < MAXSQSIZE)
759 xpm_print_avail (FILE *fp, char *ext)
763 fprintf(fp, _("Available `%s' sizes:\n"), ext);
764 for (i=1; i<MAXSQSIZE; ++i) {
770 /* Return XPM piecesize closest to size */
772 xpm_closest_to (char *dirname, int size, char *ext)
775 int sm_diff = MAXSQSIZE;
779 xpm_getavail(dirname, ext);
781 if (appData.debugMode)
782 xpm_print_avail(stderr, ext);
784 for (i=1; i<MAXSQSIZE; ++i) {
787 diff = (diff<0) ? -diff : diff;
788 if (diff < sm_diff) {
796 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
802 #else /* !HAVE_DIR_STRUCT */
803 /* If we are on a system without a DIR struct, we can't
804 read the directory, so we can't collect a list of
805 filenames, etc., so we can't do any size-fitting. */
807 xpm_closest_to (char *dirname, int size, char *ext)
810 Warning: No DIR structure found on this system --\n\
811 Unable to autosize for XPM/XIM pieces.\n\
812 Please report this error to %s.\n\
813 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
816 #endif /* HAVE_DIR_STRUCT */
818 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
819 "magenta", "cyan", "white" };
823 TextColors textColors[(int)NColorClasses];
825 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
827 parse_color (char *str, int which)
829 char *p, buf[100], *d;
832 if (strlen(str) > 99) /* watch bounds on buf */
837 for (i=0; i<which; ++i) {
844 /* Could be looking at something like:
846 .. in which case we want to stop on a comma also */
847 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
851 return -1; /* Use default for empty field */
854 if (which == 2 || isdigit(*p))
857 while (*p && isalpha(*p))
862 for (i=0; i<8; ++i) {
863 if (!StrCaseCmp(buf, cnames[i]))
864 return which? (i+40) : (i+30);
866 if (!StrCaseCmp(buf, "default")) return -1;
868 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
873 parse_cpair (ColorClass cc, char *str)
875 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
876 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
881 /* bg and attr are optional */
882 textColors[(int)cc].bg = parse_color(str, 1);
883 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
884 textColors[(int)cc].attr = 0;
890 /* Arrange to catch delete-window events */
891 Atom wm_delete_window;
893 CatchDeleteWindow (Widget w, String procname)
896 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
897 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
898 XtAugmentTranslations(w, XtParseTranslationTable(buf));
905 XtSetArg(args[0], XtNiconic, False);
906 XtSetValues(shellWidget, args, 1);
908 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
911 //---------------------------------------------------------------------------------------------------------
912 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
915 #define CW_USEDEFAULT (1<<31)
916 #define ICS_TEXT_MENU_SIZE 90
917 #define DEBUG_FILE "xboard.debug"
918 #define SetCurrentDirectory chdir
919 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
923 // these two must some day move to frontend.h, when they are implemented
924 Boolean GameListIsUp();
926 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
929 // front-end part of option handling
931 // [HGM] This platform-dependent table provides the location for storing the color info
932 extern char *crWhite, * crBlack;
936 &appData.whitePieceColor,
937 &appData.blackPieceColor,
938 &appData.lightSquareColor,
939 &appData.darkSquareColor,
940 &appData.highlightSquareColor,
941 &appData.premoveHighlightColor,
942 &appData.lowTimeWarningColor,
953 // [HGM] font: keep a font for each square size, even non-stndard ones
956 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
957 char *fontTable[NUM_FONTS][MAX_SIZE];
960 ParseFont (char *name, int number)
961 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
963 if(sscanf(name, "size%d:", &size)) {
964 // [HGM] font: font is meant for specific boardSize (likely from settings file);
965 // defer processing it until we know if it matches our board size
966 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
967 fontTable[number][size] = strdup(strchr(name, ':')+1);
968 fontValid[number][size] = True;
973 case 0: // CLOCK_FONT
974 appData.clockFont = strdup(name);
976 case 1: // MESSAGE_FONT
977 appData.font = strdup(name);
979 case 2: // COORD_FONT
980 appData.coordFont = strdup(name);
985 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
990 { // only 2 fonts currently
991 appData.clockFont = CLOCK_FONT_NAME;
992 appData.coordFont = COORD_FONT_NAME;
993 appData.font = DEFAULT_FONT_NAME;
998 { // no-op, until we identify the code for this already in XBoard and move it here
1002 ParseColor (int n, char *name)
1003 { // in XBoard, just copy the color-name string
1004 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1008 ParseTextAttribs (ColorClass cc, char *s)
1010 (&appData.colorShout)[cc] = strdup(s);
1014 ParseBoardSize (void *addr, char *name)
1016 appData.boardSize = strdup(name);
1021 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1025 SetCommPortDefaults ()
1026 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1029 // [HGM] args: these three cases taken out to stay in front-end
1031 SaveFontArg (FILE *f, ArgDescriptor *ad)
1034 int i, n = (int)(intptr_t)ad->argLoc;
1036 case 0: // CLOCK_FONT
1037 name = appData.clockFont;
1039 case 1: // MESSAGE_FONT
1040 name = appData.font;
1042 case 2: // COORD_FONT
1043 name = appData.coordFont;
1048 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1049 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1050 fontTable[n][squareSize] = strdup(name);
1051 fontValid[n][squareSize] = True;
1054 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1055 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1060 { // nothing to do, as the sounds are at all times represented by their text-string names already
1064 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
1065 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1066 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1070 SaveColor (FILE *f, ArgDescriptor *ad)
1071 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1072 if(colorVariable[(int)(intptr_t)ad->argLoc])
1073 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1077 SaveBoardSize (FILE *f, char *name, void *addr)
1078 { // wrapper to shield back-end from BoardSize & sizeInfo
1079 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1083 ParseCommPortSettings (char *s)
1084 { // no such option in XBoard (yet)
1087 extern Widget engineOutputShell;
1091 GetActualPlacement (Widget wg, WindowPlacement *wp)
1096 XWindowAttributes winAt;
1103 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
1104 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
1105 wp->x = rx - winAt.x;
1106 wp->y = ry - winAt.y;
1107 wp->height = winAt.height;
1108 wp->width = winAt.width;
1109 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
1114 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1115 // In XBoard this will have to wait until awareness of window parameters is implemented
1116 GetActualPlacement(shellWidget, &wpMain);
1117 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1118 if(MoveHistoryIsUp()) GetActualPlacement(shells[7], &wpMoveHistory);
1119 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1120 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1121 if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1122 if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1126 PrintCommPortSettings (FILE *f, char *name)
1127 { // This option does not exist in XBoard
1131 MySearchPath (char *installDir, char *name, char *fullname)
1132 { // just append installDir and name. Perhaps ExpandPath should be used here?
1133 name = ExpandPathName(name);
1134 if(name && name[0] == '/')
1135 safeStrCpy(fullname, name, MSG_SIZ );
1137 sprintf(fullname, "%s%c%s", installDir, '/', name);
1143 MyGetFullPathName (char *name, char *fullname)
1144 { // should use ExpandPath?
1145 name = ExpandPathName(name);
1146 safeStrCpy(fullname, name, MSG_SIZ );
1151 EnsureOnScreen (int *x, int *y, int minX, int minY)
1158 { // [HGM] args: allows testing if main window is realized from back-end
1159 return xBoardWindow != 0;
1163 PopUpStartupDialog ()
1164 { // start menu not implemented in XBoard
1168 ConvertToLine (int argc, char **argv)
1170 static char line[128*1024], buf[1024];
1174 for(i=1; i<argc; i++)
1176 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1177 && argv[i][0] != '{' )
1178 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1180 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1181 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1184 line[strlen(line)-1] = NULLCHAR;
1188 //--------------------------------------------------------------------------------------------
1190 extern Boolean twoBoards, partnerUp;
1193 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1195 #define BoardSize int
1197 InitDrawingSizes (BoardSize boardSize, int flags)
1198 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1199 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1201 XtGeometryResult gres;
1203 static Dimension oldWidth, oldHeight;
1204 static VariantClass oldVariant;
1205 static int oldDual = -1, oldMono = -1;
1207 if(!formWidget) return;
1209 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1210 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1211 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1213 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1215 * Enable shell resizing.
1217 shellArgs[0].value = (XtArgVal) &w;
1218 shellArgs[1].value = (XtArgVal) &h;
1219 XtGetValues(shellWidget, shellArgs, 2);
1221 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1222 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1223 XtSetValues(shellWidget, &shellArgs[2], 4);
1225 XtSetArg(args[0], XtNdefaultDistance, &sep);
1226 XtGetValues(formWidget, args, 1);
1228 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1230 hOffset = boardWidth + 10;
1231 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1232 secondSegments[i] = gridSegments[i];
1233 secondSegments[i].x1 += hOffset;
1234 secondSegments[i].x2 += hOffset;
1237 XtSetArg(args[0], XtNwidth, boardWidth);
1238 XtSetArg(args[1], XtNheight, boardHeight);
1239 XtSetValues(boardWidget, args, 2);
1241 timerWidth = (boardWidth - sep) / 2;
1242 XtSetArg(args[0], XtNwidth, timerWidth);
1243 XtSetValues(whiteTimerWidget, args, 1);
1244 XtSetValues(blackTimerWidget, args, 1);
1246 XawFormDoLayout(formWidget, False);
1248 if (appData.titleInWindow) {
1250 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1251 XtSetArg(args[i], XtNheight, &h); i++;
1252 XtGetValues(titleWidget, args, i);
1254 w = boardWidth - 2*bor;
1256 XtSetArg(args[0], XtNwidth, &w);
1257 XtGetValues(menuBarWidget, args, 1);
1258 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1261 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1262 if (gres != XtGeometryYes && appData.debugMode) {
1264 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1265 programName, gres, w, h, wr, hr);
1269 XawFormDoLayout(formWidget, True);
1272 * Inhibit shell resizing.
1274 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1275 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1276 shellArgs[4].value = shellArgs[2].value = w;
1277 shellArgs[5].value = shellArgs[3].value = h;
1278 XtSetValues(shellWidget, &shellArgs[0], 6);
1280 XSync(xDisplay, False);
1284 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1287 if(gameInfo.variant != oldVariant) { // and only if variant changed
1290 for(i=0; i<4; i++) {
1292 for(p=0; p<=(int)WhiteKing; p++)
1293 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1294 if(gameInfo.variant == VariantShogi) {
1295 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1296 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1297 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1298 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1299 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1302 if(gameInfo.variant == VariantGothic) {
1303 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1306 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1307 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1308 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1311 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1312 for(p=0; p<=(int)WhiteKing; p++)
1313 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1314 if(gameInfo.variant == VariantShogi) {
1315 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1316 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1317 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1318 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1319 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1322 if(gameInfo.variant == VariantGothic) {
1323 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1326 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1327 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1328 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1333 for(i=0; i<2; i++) {
1335 for(p=0; p<=(int)WhiteKing; p++)
1336 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1337 if(gameInfo.variant == VariantShogi) {
1338 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1339 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1340 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1341 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1342 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1345 if(gameInfo.variant == VariantGothic) {
1346 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1349 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1350 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1351 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1355 oldMono = -10; // kludge to force recreation of animation masks
1356 oldVariant = gameInfo.variant;
1359 if(appData.monoMode != oldMono)
1362 oldMono = appData.monoMode;
1367 ParseIcsTextColors ()
1368 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1369 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1370 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1371 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1372 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1373 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1374 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1375 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1376 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1377 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1378 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1380 if (appData.colorize) {
1382 _("%s: can't parse color names; disabling colorization\n"),
1385 appData.colorize = FALSE;
1390 MakeOneColor (char *name, Pixel *color)
1392 XrmValue vFrom, vTo;
1393 if (!appData.monoMode) {
1394 vFrom.addr = (caddr_t) name;
1395 vFrom.size = strlen(name);
1396 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1397 if (vTo.addr == NULL) {
1398 appData.monoMode = True;
1401 *color = *(Pixel *) vTo.addr;
1409 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1410 int forceMono = False;
1412 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1413 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1414 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1415 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1416 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1417 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1424 { // [HGM] taken out of main
1426 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1427 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1428 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1430 if (appData.bitmapDirectory[0] != NULLCHAR) {
1434 CreateXPMBoard(appData.liteBackTextureFile, 1);
1435 CreateXPMBoard(appData.darkBackTextureFile, 0);
1439 /* Create regular pieces */
1440 if (!useImages) CreatePieces();
1445 main (int argc, char **argv)
1447 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1448 XSetWindowAttributes window_attributes;
1450 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1451 XrmValue vFrom, vTo;
1452 XtGeometryResult gres;
1455 int forceMono = False;
1457 srandom(time(0)); // [HGM] book: make random truly random
1459 setbuf(stdout, NULL);
1460 setbuf(stderr, NULL);
1463 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1464 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1468 programName = strrchr(argv[0], '/');
1469 if (programName == NULL)
1470 programName = argv[0];
1475 XtSetLanguageProc(NULL, NULL, NULL);
1476 bindtextdomain(PACKAGE, LOCALEDIR);
1477 textdomain(PACKAGE);
1481 XtAppInitialize(&appContext, "XBoard", shellOptions,
1482 XtNumber(shellOptions),
1483 &argc, argv, xboardResources, NULL, 0);
1484 appData.boardSize = "";
1485 InitAppData(ConvertToLine(argc, argv));
1487 if (p == NULL) p = "/tmp";
1488 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1489 gameCopyFilename = (char*) malloc(i);
1490 gamePasteFilename = (char*) malloc(i);
1491 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1492 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1494 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1495 clientResources, XtNumber(clientResources),
1498 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1499 static char buf[MSG_SIZ];
1500 EscapeExpand(buf, appData.firstInitString);
1501 appData.firstInitString = strdup(buf);
1502 EscapeExpand(buf, appData.secondInitString);
1503 appData.secondInitString = strdup(buf);
1504 EscapeExpand(buf, appData.firstComputerString);
1505 appData.firstComputerString = strdup(buf);
1506 EscapeExpand(buf, appData.secondComputerString);
1507 appData.secondComputerString = strdup(buf);
1510 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1513 if (chdir(chessDir) != 0) {
1514 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1520 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1521 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1522 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1523 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1526 setbuf(debugFP, NULL);
1530 if (appData.debugMode) {
1531 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1535 /* [HGM,HR] make sure board size is acceptable */
1536 if(appData.NrFiles > BOARD_FILES ||
1537 appData.NrRanks > BOARD_RANKS )
1538 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1541 /* This feature does not work; animation needs a rewrite */
1542 appData.highlightDragging = FALSE;
1546 xDisplay = XtDisplay(shellWidget);
1547 xScreen = DefaultScreen(xDisplay);
1548 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1550 gameInfo.variant = StringToVariant(appData.variant);
1551 InitPosition(FALSE);
1554 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1556 if (isdigit(appData.boardSize[0])) {
1557 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1558 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1559 &fontPxlSize, &smallLayout, &tinyLayout);
1561 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1562 programName, appData.boardSize);
1566 /* Find some defaults; use the nearest known size */
1567 SizeDefaults *szd, *nearest;
1568 int distance = 99999;
1569 nearest = szd = sizeDefaults;
1570 while (szd->name != NULL) {
1571 if (abs(szd->squareSize - squareSize) < distance) {
1573 distance = abs(szd->squareSize - squareSize);
1574 if (distance == 0) break;
1578 if (i < 2) lineGap = nearest->lineGap;
1579 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1580 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1581 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1582 if (i < 6) smallLayout = nearest->smallLayout;
1583 if (i < 7) tinyLayout = nearest->tinyLayout;
1586 SizeDefaults *szd = sizeDefaults;
1587 if (*appData.boardSize == NULLCHAR) {
1588 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1589 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1592 if (szd->name == NULL) szd--;
1593 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1595 while (szd->name != NULL &&
1596 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1597 if (szd->name == NULL) {
1598 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1599 programName, appData.boardSize);
1603 squareSize = szd->squareSize;
1604 lineGap = szd->lineGap;
1605 clockFontPxlSize = szd->clockFontPxlSize;
1606 coordFontPxlSize = szd->coordFontPxlSize;
1607 fontPxlSize = szd->fontPxlSize;
1608 smallLayout = szd->smallLayout;
1609 tinyLayout = szd->tinyLayout;
1610 // [HGM] font: use defaults from settings file if available and not overruled
1612 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1613 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1614 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1615 appData.font = fontTable[MESSAGE_FONT][squareSize];
1616 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1617 appData.coordFont = fontTable[COORD_FONT][squareSize];
1619 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1620 if (strlen(appData.pixmapDirectory) > 0) {
1621 p = ExpandPathName(appData.pixmapDirectory);
1623 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1624 appData.pixmapDirectory);
1627 if (appData.debugMode) {
1628 fprintf(stderr, _("\
1629 XBoard square size (hint): %d\n\
1630 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1632 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1633 if (appData.debugMode) {
1634 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1637 defaultLineGap = lineGap;
1638 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1640 /* [HR] height treated separately (hacked) */
1641 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1642 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1643 if (appData.showJail == 1) {
1644 /* Jail on top and bottom */
1645 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1646 XtSetArg(boardArgs[2], XtNheight,
1647 boardHeight + 2*(lineGap + squareSize));
1648 } else if (appData.showJail == 2) {
1650 XtSetArg(boardArgs[1], XtNwidth,
1651 boardWidth + 2*(lineGap + squareSize));
1652 XtSetArg(boardArgs[2], XtNheight, boardHeight);
1655 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1656 XtSetArg(boardArgs[2], XtNheight, boardHeight);
1660 * Determine what fonts to use.
1663 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1664 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1665 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1666 fontSet = CreateFontSet(appData.font);
1667 clockFontSet = CreateFontSet(appData.clockFont);
1669 /* For the coordFont, use the 0th font of the fontset. */
1670 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1671 XFontStruct **font_struct_list;
1672 XFontSetExtents *fontSize;
1673 char **font_name_list;
1674 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1675 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1676 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1677 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1678 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1681 appData.font = FindFont(appData.font, fontPxlSize);
1682 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1683 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1684 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1685 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1686 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1687 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1689 countFontID = coordFontID; // [HGM] holdings
1690 countFontStruct = coordFontStruct;
1692 xdb = XtDatabase(xDisplay);
1694 XrmPutLineResource(&xdb, "*international: True");
1695 vTo.size = sizeof(XFontSet);
1696 vTo.addr = (XtPointer) &fontSet;
1697 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1699 XrmPutStringResource(&xdb, "*font", appData.font);
1703 * Detect if there are not enough colors available and adapt.
1705 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1706 appData.monoMode = True;
1709 forceMono = MakeColors();
1712 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1714 appData.monoMode = True;
1717 if (appData.lowTimeWarning && !appData.monoMode) {
1718 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1719 vFrom.size = strlen(appData.lowTimeWarningColor);
1720 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1721 if (vTo.addr == NULL)
1722 appData.monoMode = True;
1724 lowTimeWarningColor = *(Pixel *) vTo.addr;
1727 if (appData.monoMode && appData.debugMode) {
1728 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1729 (unsigned long) XWhitePixel(xDisplay, xScreen),
1730 (unsigned long) XBlackPixel(xDisplay, xScreen));
1733 ParseIcsTextColors();
1734 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
1735 textColors[ColorNone].attr = 0;
1737 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1743 layoutName = "tinyLayout";
1744 } else if (smallLayout) {
1745 layoutName = "smallLayout";
1747 layoutName = "normalLayout";
1749 /* Outer layoutWidget is there only to provide a name for use in
1750 resources that depend on the layout style */
1752 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
1753 layoutArgs, XtNumber(layoutArgs));
1755 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
1756 formArgs, XtNumber(formArgs));
1757 XtSetArg(args[0], XtNdefaultDistance, &sep);
1758 XtGetValues(formWidget, args, 1);
1761 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
1762 XtSetArg(args[0], XtNtop, XtChainTop);
1763 XtSetArg(args[1], XtNbottom, XtChainTop);
1764 XtSetArg(args[2], XtNright, XtChainLeft);
1765 XtSetValues(menuBarWidget, args, 3);
1767 widgetList[j++] = whiteTimerWidget =
1768 XtCreateWidget("whiteTime", labelWidgetClass,
1769 formWidget, timerArgs, XtNumber(timerArgs));
1771 XtSetArg(args[0], XtNfontSet, clockFontSet);
1773 XtSetArg(args[0], XtNfont, clockFontStruct);
1775 XtSetArg(args[1], XtNtop, XtChainTop);
1776 XtSetArg(args[2], XtNbottom, XtChainTop);
1777 XtSetValues(whiteTimerWidget, args, 3);
1779 widgetList[j++] = blackTimerWidget =
1780 XtCreateWidget("blackTime", labelWidgetClass,
1781 formWidget, timerArgs, XtNumber(timerArgs));
1783 XtSetArg(args[0], XtNfontSet, clockFontSet);
1785 XtSetArg(args[0], XtNfont, clockFontStruct);
1787 XtSetArg(args[1], XtNtop, XtChainTop);
1788 XtSetArg(args[2], XtNbottom, XtChainTop);
1789 XtSetValues(blackTimerWidget, args, 3);
1791 if (appData.titleInWindow) {
1792 widgetList[j++] = titleWidget =
1793 XtCreateWidget("title", labelWidgetClass, formWidget,
1794 titleArgs, XtNumber(titleArgs));
1795 XtSetArg(args[0], XtNtop, XtChainTop);
1796 XtSetArg(args[1], XtNbottom, XtChainTop);
1797 XtSetValues(titleWidget, args, 2);
1800 if (appData.showButtonBar) {
1801 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
1802 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
1803 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
1804 XtSetArg(args[2], XtNtop, XtChainTop);
1805 XtSetArg(args[3], XtNbottom, XtChainTop);
1806 XtSetValues(buttonBarWidget, args, 4);
1809 widgetList[j++] = messageWidget =
1810 XtCreateWidget("message", labelWidgetClass, formWidget,
1811 messageArgs, XtNumber(messageArgs));
1812 XtSetArg(args[0], XtNtop, XtChainTop);
1813 XtSetArg(args[1], XtNbottom, XtChainTop);
1814 XtSetValues(messageWidget, args, 2);
1816 widgetList[j++] = boardWidget =
1817 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
1818 XtNumber(boardArgs));
1820 XtManageChildren(widgetList, j);
1822 timerWidth = (boardWidth - sep) / 2;
1823 XtSetArg(args[0], XtNwidth, timerWidth);
1824 XtSetValues(whiteTimerWidget, args, 1);
1825 XtSetValues(blackTimerWidget, args, 1);
1827 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1828 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1829 XtGetValues(whiteTimerWidget, args, 2);
1831 if (appData.showButtonBar) {
1832 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1833 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1834 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
1838 * formWidget uses these constraints but they are stored
1842 XtSetArg(args[i], XtNfromHoriz, 0); i++;
1843 XtSetValues(menuBarWidget, args, i);
1844 if (appData.titleInWindow) {
1847 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1848 XtSetValues(whiteTimerWidget, args, i);
1850 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1851 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1852 XtSetValues(blackTimerWidget, args, i);
1854 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1855 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
1856 XtSetValues(titleWidget, args, i);
1858 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1859 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1860 XtSetValues(messageWidget, args, i);
1861 if (appData.showButtonBar) {
1863 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1864 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1865 XtSetValues(buttonBarWidget, args, i);
1869 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1870 XtSetValues(whiteTimerWidget, args, i);
1872 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1873 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1874 XtSetValues(blackTimerWidget, args, i);
1876 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
1877 XtSetValues(titleWidget, args, i);
1879 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1880 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1881 XtSetValues(messageWidget, args, i);
1882 if (appData.showButtonBar) {
1884 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1885 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1886 XtSetValues(buttonBarWidget, args, i);
1891 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1892 XtSetValues(whiteTimerWidget, args, i);
1894 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1895 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1896 XtSetValues(blackTimerWidget, args, i);
1898 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1899 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1900 XtSetValues(messageWidget, args, i);
1901 if (appData.showButtonBar) {
1903 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1904 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1905 XtSetValues(buttonBarWidget, args, i);
1909 XtSetArg(args[0], XtNfromVert, messageWidget);
1910 XtSetArg(args[1], XtNtop, XtChainTop);
1911 XtSetArg(args[2], XtNbottom, XtChainBottom);
1912 XtSetArg(args[3], XtNleft, XtChainLeft);
1913 XtSetArg(args[4], XtNright, XtChainRight);
1914 XtSetValues(boardWidget, args, 5);
1916 XtRealizeWidget(shellWidget);
1919 XtSetArg(args[0], XtNx, wpMain.x);
1920 XtSetArg(args[1], XtNy, wpMain.y);
1921 XtSetValues(shellWidget, args, 2);
1925 * Correct the width of the message and title widgets.
1926 * It is not known why some systems need the extra fudge term.
1927 * The value "2" is probably larger than needed.
1929 XawFormDoLayout(formWidget, False);
1931 #define WIDTH_FUDGE 2
1933 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1934 XtSetArg(args[i], XtNheight, &h); i++;
1935 XtGetValues(messageWidget, args, i);
1936 if (appData.showButtonBar) {
1938 XtSetArg(args[i], XtNwidth, &w); i++;
1939 XtGetValues(buttonBarWidget, args, i);
1940 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1942 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
1945 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1946 if (gres != XtGeometryYes && appData.debugMode) {
1947 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1948 programName, gres, w, h, wr, hr);
1951 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
1952 /* The size used for the child widget in layout lags one resize behind
1953 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
1955 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1956 if (gres != XtGeometryYes && appData.debugMode) {
1957 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1958 programName, gres, w, h, wr, hr);
1961 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
1962 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
1963 XtSetArg(args[1], XtNright, XtChainRight);
1964 XtSetValues(messageWidget, args, 2);
1966 if (appData.titleInWindow) {
1968 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1969 XtSetArg(args[i], XtNheight, &h); i++;
1970 XtGetValues(titleWidget, args, i);
1972 w = boardWidth - 2*bor;
1974 XtSetArg(args[0], XtNwidth, &w);
1975 XtGetValues(menuBarWidget, args, 1);
1976 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1979 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1980 if (gres != XtGeometryYes && appData.debugMode) {
1982 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1983 programName, gres, w, h, wr, hr);
1986 XawFormDoLayout(formWidget, True);
1988 xBoardWindow = XtWindow(boardWidget);
1990 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1991 // not need to go into InitDrawingSizes().
1995 * Create X checkmark bitmap and initialize option menu checks.
1997 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1998 checkmark_bits, checkmark_width, checkmark_height);
2004 ReadBitmap(&wIconPixmap, "icon_white.bm",
2005 icon_white_bits, icon_white_width, icon_white_height);
2006 ReadBitmap(&bIconPixmap, "icon_black.bm",
2007 icon_black_bits, icon_black_width, icon_black_height);
2008 iconPixmap = wIconPixmap;
2010 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2011 XtSetValues(shellWidget, args, i);
2014 * Create a cursor for the board widget.
2016 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2017 XChangeWindowAttributes(xDisplay, xBoardWindow,
2018 CWCursor, &window_attributes);
2021 * Inhibit shell resizing.
2023 shellArgs[0].value = (XtArgVal) &w;
2024 shellArgs[1].value = (XtArgVal) &h;
2025 XtGetValues(shellWidget, shellArgs, 2);
2026 shellArgs[4].value = shellArgs[2].value = w;
2027 shellArgs[5].value = shellArgs[3].value = h;
2028 XtSetValues(shellWidget, &shellArgs[2], 4);
2029 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2030 marginH = h - boardHeight;
2032 CatchDeleteWindow(shellWidget, "QuitProc");
2040 if (appData.animate || appData.animateDragging)
2043 XtAugmentTranslations(formWidget,
2044 XtParseTranslationTable(globalTranslations));
2045 XtAugmentTranslations(boardWidget,
2046 XtParseTranslationTable(boardTranslations));
2047 XtAugmentTranslations(whiteTimerWidget,
2048 XtParseTranslationTable(whiteTranslations));
2049 XtAugmentTranslations(blackTimerWidget,
2050 XtParseTranslationTable(blackTranslations));
2052 /* Why is the following needed on some versions of X instead
2053 * of a translation? */
2054 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2055 (XtEventHandler) EventProc, NULL);
2057 XtAddEventHandler(formWidget, KeyPressMask, False,
2058 (XtEventHandler) MoveTypeInProc, NULL);
2059 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
2060 (XtEventHandler) EventProc, NULL);
2062 /* [AS] Restore layout */
2063 if( wpMoveHistory.visible ) {
2067 if( wpEvalGraph.visible )
2072 if( wpEngineOutput.visible ) {
2073 EngineOutputPopUp();
2078 if (errorExitStatus == -1) {
2079 if (appData.icsActive) {
2080 /* We now wait until we see "login:" from the ICS before
2081 sending the logon script (problems with timestamp otherwise) */
2082 /*ICSInitScript();*/
2083 if (appData.icsInputBox) ICSInputBoxPopUp();
2087 signal(SIGWINCH, TermSizeSigHandler);
2089 signal(SIGINT, IntSigHandler);
2090 signal(SIGTERM, IntSigHandler);
2091 if (*appData.cmailGameName != NULLCHAR) {
2092 signal(SIGUSR1, CmailSigHandler);
2096 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2098 // XtSetKeyboardFocus(shellWidget, formWidget);
2099 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2101 XtAppMainLoop(appContext);
2102 if (appData.debugMode) fclose(debugFP); // [DM] debug
2106 static Boolean noEcho;
2111 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2112 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2114 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2115 unlink(gameCopyFilename);
2116 unlink(gamePasteFilename);
2117 if(noEcho) EchoOn();
2121 TermSizeSigHandler (int sig)
2127 IntSigHandler (int sig)
2133 CmailSigHandler (int sig)
2138 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2140 /* Activate call-back function CmailSigHandlerCallBack() */
2141 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2143 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2147 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
2150 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2152 /**** end signal code ****/
2158 /* try to open the icsLogon script, either in the location given
2159 * or in the users HOME directory
2166 f = fopen(appData.icsLogon, "r");
2169 homedir = getenv("HOME");
2170 if (homedir != NULL)
2172 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2173 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2174 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2175 f = fopen(buf, "r");
2180 ProcessICSInitScript(f);
2182 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2195 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
2196 #define HISTORY_SIZE 64
2197 static char *history[HISTORY_SIZE];
2198 int histIn = 0, histP = 0;
2201 SaveInHistory (char *cmd)
2203 if (history[histIn] != NULL) {
2204 free(history[histIn]);
2205 history[histIn] = NULL;
2207 if (*cmd == NULLCHAR) return;
2208 history[histIn] = StrSave(cmd);
2209 histIn = (histIn + 1) % HISTORY_SIZE;
2210 if (history[histIn] != NULL) {
2211 free(history[histIn]);
2212 history[histIn] = NULL;
2218 PrevInHistory (char *cmd)
2221 if (histP == histIn) {
2222 if (history[histIn] != NULL) free(history[histIn]);
2223 history[histIn] = StrSave(cmd);
2225 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
2226 if (newhp == histIn || history[newhp] == NULL) return NULL;
2228 return history[histP];
2234 if (histP == histIn) return NULL;
2235 histP = (histP + 1) % HISTORY_SIZE;
2236 return history[histP];
2238 // end of borrowed code
2240 #define Abs(n) ((n)<0 ? -(n) : (n))
2244 InsertPxlSize (char *pattern, int targetPxlSize)
2246 char *base_fnt_lst, strInt[12], *p, *q;
2247 int alternatives, i, len, strIntLen;
2250 * Replace the "*" (if present) in the pixel-size slot of each
2251 * alternative with the targetPxlSize.
2255 while ((p = strchr(p, ',')) != NULL) {
2259 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
2260 strIntLen = strlen(strInt);
2261 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
2265 while (alternatives--) {
2266 char *comma = strchr(p, ',');
2267 for (i=0; i<14; i++) {
2268 char *hyphen = strchr(p, '-');
2270 if (comma && hyphen > comma) break;
2271 len = hyphen + 1 - p;
2272 if (i == 7 && *p == '*' && len == 2) {
2274 memcpy(q, strInt, strIntLen);
2284 len = comma + 1 - p;
2291 return base_fnt_lst;
2295 CreateFontSet (char *base_fnt_lst)
2298 char **missing_list;
2302 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
2303 &missing_list, &missing_count, &def_string);
2304 if (appData.debugMode) {
2306 XFontStruct **font_struct_list;
2307 char **font_name_list;
2308 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
2310 fprintf(debugFP, " got list %s, locale %s\n",
2311 XBaseFontNameListOfFontSet(fntSet),
2312 XLocaleOfFontSet(fntSet));
2313 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
2314 for (i = 0; i < count; i++) {
2315 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
2318 for (i = 0; i < missing_count; i++) {
2319 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
2322 if (fntSet == NULL) {
2323 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
2328 #else // not ENABLE_NLS
2330 * Find a font that matches "pattern" that is as close as
2331 * possible to the targetPxlSize. Prefer fonts that are k
2332 * pixels smaller to fonts that are k pixels larger. The
2333 * pattern must be in the X Consortium standard format,
2334 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2335 * The return value should be freed with XtFree when no
2339 FindFont (char *pattern, int targetPxlSize)
2341 char **fonts, *p, *best, *scalable, *scalableTail;
2342 int i, j, nfonts, minerr, err, pxlSize;
2344 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2346 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2347 programName, pattern);
2354 for (i=0; i<nfonts; i++) {
2357 if (*p != '-') continue;
2359 if (*p == NULLCHAR) break;
2360 if (*p++ == '-') j++;
2362 if (j < 7) continue;
2365 scalable = fonts[i];
2368 err = pxlSize - targetPxlSize;
2369 if (Abs(err) < Abs(minerr) ||
2370 (minerr > 0 && err < 0 && -err == minerr)) {
2376 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2377 /* If the error is too big and there is a scalable font,
2378 use the scalable font. */
2379 int headlen = scalableTail - scalable;
2380 p = (char *) XtMalloc(strlen(scalable) + 10);
2381 while (isdigit(*scalableTail)) scalableTail++;
2382 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2384 p = (char *) XtMalloc(strlen(best) + 2);
2385 safeStrCpy(p, best, strlen(best)+1 );
2387 if (appData.debugMode) {
2388 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2389 pattern, targetPxlSize, p);
2391 XFreeFontNames(fonts);
2398 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
2399 // must be called before all non-first callse to CreateGCs()
2400 XtReleaseGC(shellWidget, highlineGC);
2401 XtReleaseGC(shellWidget, lightSquareGC);
2402 XtReleaseGC(shellWidget, darkSquareGC);
2403 XtReleaseGC(shellWidget, lineGC);
2404 if (appData.monoMode) {
2405 if (DefaultDepth(xDisplay, xScreen) == 1) {
2406 XtReleaseGC(shellWidget, wbPieceGC);
2408 XtReleaseGC(shellWidget, bwPieceGC);
2411 XtReleaseGC(shellWidget, prelineGC);
2412 XtReleaseGC(shellWidget, jailSquareGC);
2413 XtReleaseGC(shellWidget, wdPieceGC);
2414 XtReleaseGC(shellWidget, wlPieceGC);
2415 XtReleaseGC(shellWidget, wjPieceGC);
2416 XtReleaseGC(shellWidget, bdPieceGC);
2417 XtReleaseGC(shellWidget, blPieceGC);
2418 XtReleaseGC(shellWidget, bjPieceGC);
2423 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
2425 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2426 | GCBackground | GCFunction | GCPlaneMask;
2427 gc_values->foreground = foreground;
2428 gc_values->background = background;
2429 return XtGetGC(shellWidget, value_mask, gc_values);
2433 CreateGCs (int redo)
2435 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2436 | GCBackground | GCFunction | GCPlaneMask;
2437 XGCValues gc_values;
2439 Pixel white = XWhitePixel(xDisplay, xScreen);
2440 Pixel black = XBlackPixel(xDisplay, xScreen);
2442 gc_values.plane_mask = AllPlanes;
2443 gc_values.line_width = lineGap;
2444 gc_values.line_style = LineSolid;
2445 gc_values.function = GXcopy;
2448 DeleteGCs(); // called a second time; clean up old GCs first
2449 } else { // [HGM] grid and font GCs created on first call only
2450 coordGC = CreateOneGC(&gc_values, black, white);
2451 XSetFont(xDisplay, coordGC, coordFontID);
2453 // [HGM] make font for holdings counts (white on black)
2454 countGC = CreateOneGC(&gc_values, white, black);
2455 XSetFont(xDisplay, countGC, countFontID);
2457 lineGC = CreateOneGC(&gc_values, black, black);
2459 if (appData.monoMode) {
2461 highlineGC = CreateOneGC(&gc_values, white, white);
2462 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
2463 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
2465 if (DefaultDepth(xDisplay, xScreen) == 1) {
2466 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2467 gc_values.function = GXcopyInverted;
2468 copyInvertedGC = CreateOneGC(&gc_values, black, white);
2469 gc_values.function = GXcopy;
2470 if (XBlackPixel(xDisplay, xScreen) == 1) {
2471 bwPieceGC = darkSquareGC;
2472 wbPieceGC = copyInvertedGC;
2474 bwPieceGC = copyInvertedGC;
2475 wbPieceGC = lightSquareGC;
2480 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
2481 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
2482 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
2483 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
2484 jailSquareGC = CreateOneGC(&gc_values, jailSquareColor, jailSquareColor);
2485 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
2486 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
2487 wjPieceGC = CreateOneGC(&gc_values, whitePieceColor, jailSquareColor);
2488 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
2489 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
2490 bjPieceGC = CreateOneGC(&gc_values, blackPieceColor, jailSquareColor);
2495 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
2503 fp = fopen(filename, "rb");
2505 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
2512 for (y=0; y<h; ++y) {
2513 for (x=0; x<h; ++x) {
2518 XPutPixel(xim, x, y, blackPieceColor);
2520 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2523 XPutPixel(xim, x, y, darkSquareColor);
2525 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2528 XPutPixel(xim, x, y, whitePieceColor);
2530 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2533 XPutPixel(xim, x, y, lightSquareColor);
2535 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2543 /* create Pixmap of piece */
2544 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2546 XPutImage(xDisplay, *dest, lightSquareGC, xim,
2549 /* create Pixmap of clipmask
2550 Note: We assume the white/black pieces have the same
2551 outline, so we make only 6 masks. This is okay
2552 since the XPM clipmask routines do the same. */
2554 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2556 XPutImage(xDisplay, temp, lightSquareGC, xmask,
2559 /* now create the 1-bit version */
2560 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2563 values.foreground = 1;
2564 values.background = 0;
2566 /* Don't use XtGetGC, not read only */
2567 maskGC = XCreateGC(xDisplay, *mask,
2568 GCForeground | GCBackground, &values);
2569 XCopyPlane(xDisplay, temp, *mask, maskGC,
2570 0, 0, squareSize, squareSize, 0, 0, 1);
2571 XFreePixmap(xDisplay, temp);
2576 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
2584 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
2589 /* The XSynchronize calls were copied from CreatePieces.
2590 Not sure if needed, but can't hurt */
2591 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2594 /* temp needed by loadXIM() */
2595 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2596 0, 0, ss, ss, AllPlanes, XYPixmap);
2598 if (strlen(appData.pixmapDirectory) == 0) {
2602 if (appData.monoMode) {
2603 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
2607 fprintf(stderr, _("\nLoading XIMs...\n"));
2609 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2610 fprintf(stderr, "%d", piece+1);
2611 for (kind=0; kind<4; kind++) {
2612 fprintf(stderr, ".");
2613 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2614 ExpandPathName(appData.pixmapDirectory),
2615 piece <= (int) WhiteKing ? "" : "w",
2616 pieceBitmapNames[piece],
2618 ximPieceBitmap[kind][piece] =
2619 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2620 0, 0, ss, ss, AllPlanes, XYPixmap);
2621 if (appData.debugMode)
2622 fprintf(stderr, _("(File:%s:) "), buf);
2623 loadXIM(ximPieceBitmap[kind][piece],
2625 &(xpmPieceBitmap2[kind][piece]),
2626 &(ximMaskPm2[piece]));
2627 if(piece <= (int)WhiteKing)
2628 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2630 fprintf(stderr," ");
2632 /* Load light and dark squares */
2633 /* If the LSQ and DSQ pieces don't exist, we will
2634 draw them with solid squares. */
2635 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2636 if (access(buf, 0) != 0) {
2640 fprintf(stderr, _("light square "));
2642 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2643 0, 0, ss, ss, AllPlanes, XYPixmap);
2644 if (appData.debugMode)
2645 fprintf(stderr, _("(File:%s:) "), buf);
2647 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2648 fprintf(stderr, _("dark square "));
2649 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2650 ExpandPathName(appData.pixmapDirectory), ss);
2651 if (appData.debugMode)
2652 fprintf(stderr, _("(File:%s:) "), buf);
2654 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2655 0, 0, ss, ss, AllPlanes, XYPixmap);
2656 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2657 xpmJailSquare = xpmLightSquare;
2659 fprintf(stderr, _("Done.\n"));
2661 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2664 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2668 CreateXPMBoard (char *s, int kind)
2672 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2673 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2674 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2680 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2681 // thisroutine has to be called t free the old piece pixmaps
2683 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2684 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2686 XFreePixmap(xDisplay, xpmLightSquare);
2687 XFreePixmap(xDisplay, xpmDarkSquare);
2696 u_int ss = squareSize;
2698 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2699 XpmColorSymbol symbols[4];
2700 static int redo = False;
2702 if(redo) FreeXPMPieces(); else redo = 1;
2704 /* The XSynchronize calls were copied from CreatePieces.
2705 Not sure if needed, but can't hurt */
2706 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2708 /* Setup translations so piece colors match square colors */
2709 symbols[0].name = "light_piece";
2710 symbols[0].value = appData.whitePieceColor;
2711 symbols[1].name = "dark_piece";
2712 symbols[1].value = appData.blackPieceColor;
2713 symbols[2].name = "light_square";
2714 symbols[2].value = appData.lightSquareColor;
2715 symbols[3].name = "dark_square";
2716 symbols[3].value = appData.darkSquareColor;
2718 attr.valuemask = XpmColorSymbols;
2719 attr.colorsymbols = symbols;
2720 attr.numsymbols = 4;
2722 if (appData.monoMode) {
2723 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2727 if (strlen(appData.pixmapDirectory) == 0) {
2728 XpmPieces* pieces = builtInXpms;
2731 while (pieces->size != squareSize && pieces->size) pieces++;
2732 if (!pieces->size) {
2733 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2736 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2737 for (kind=0; kind<4; kind++) {
2739 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2740 pieces->xpm[piece][kind],
2741 &(xpmPieceBitmap2[kind][piece]),
2742 NULL, &attr)) != 0) {
2743 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2747 if(piece <= (int) WhiteKing)
2748 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2752 xpmJailSquare = xpmLightSquare;
2756 fprintf(stderr, _("\nLoading XPMs...\n"));
2759 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2760 fprintf(stderr, "%d ", piece+1);
2761 for (kind=0; kind<4; kind++) {
2762 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2763 ExpandPathName(appData.pixmapDirectory),
2764 piece > (int) WhiteKing ? "w" : "",
2765 pieceBitmapNames[piece],
2767 if (appData.debugMode) {
2768 fprintf(stderr, _("(File:%s:) "), buf);
2770 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2771 &(xpmPieceBitmap2[kind][piece]),
2772 NULL, &attr)) != 0) {
2773 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2774 // [HGM] missing: read of unorthodox piece failed; substitute King.
2775 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2776 ExpandPathName(appData.pixmapDirectory),
2778 if (appData.debugMode) {
2779 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2781 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2782 &(xpmPieceBitmap2[kind][piece]),
2786 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2791 if(piece <= (int) WhiteKing)
2792 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2795 /* Load light and dark squares */
2796 /* If the LSQ and DSQ pieces don't exist, we will
2797 draw them with solid squares. */
2798 fprintf(stderr, _("light square "));
2799 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2800 if (access(buf, 0) != 0) {
2804 if (appData.debugMode)
2805 fprintf(stderr, _("(File:%s:) "), buf);
2807 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2808 &xpmLightSquare, NULL, &attr)) != 0) {
2809 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2812 fprintf(stderr, _("dark square "));
2813 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2814 ExpandPathName(appData.pixmapDirectory), ss);
2815 if (appData.debugMode) {
2816 fprintf(stderr, _("(File:%s:) "), buf);
2818 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2819 &xpmDarkSquare, NULL, &attr)) != 0) {
2820 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2824 xpmJailSquare = xpmLightSquare;
2825 fprintf(stderr, _("Done.\n"));
2827 oldVariant = -1; // kludge to force re-makig of animation masks
2828 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2831 #endif /* HAVE_LIBXPM */
2834 /* No built-in bitmaps */
2839 u_int ss = squareSize;
2841 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2844 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2845 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2846 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2847 pieceBitmapNames[piece],
2848 ss, kind == SOLID ? 's' : 'o');
2849 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2850 if(piece <= (int)WhiteKing)
2851 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2855 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2859 /* With built-in bitmaps */
2863 BuiltInBits* bib = builtInBits;
2866 u_int ss = squareSize;
2868 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2871 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2873 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2874 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2875 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2876 pieceBitmapNames[piece],
2877 ss, kind == SOLID ? 's' : 'o');
2878 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2879 bib->bits[kind][piece], ss, ss);
2880 if(piece <= (int)WhiteKing)
2881 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2885 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2891 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2896 char msg[MSG_SIZ], fullname[MSG_SIZ];
2898 if (*appData.bitmapDirectory != NULLCHAR) {
2899 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2900 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2901 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2902 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2903 &w, &h, pm, &x_hot, &y_hot);
2904 fprintf(stderr, "load %s\n", name);
2905 if (errcode != BitmapSuccess) {
2907 case BitmapOpenFailed:
2908 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2910 case BitmapFileInvalid:
2911 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2913 case BitmapNoMemory:
2914 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2918 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2922 fprintf(stderr, _("%s: %s...using built-in\n"),
2924 } else if (w != wreq || h != hreq) {
2926 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2927 programName, fullname, w, h, wreq, hreq);
2933 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2943 if (lineGap == 0) return;
2945 /* [HR] Split this into 2 loops for non-square boards. */
2947 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2948 gridSegments[i].x1 = 0;
2949 gridSegments[i].x2 =
2950 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2951 gridSegments[i].y1 = gridSegments[i].y2
2952 = lineGap / 2 + (i * (squareSize + lineGap));
2955 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2956 gridSegments[j + i].y1 = 0;
2957 gridSegments[j + i].y2 =
2958 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2959 gridSegments[j + i].x1 = gridSegments[j + i].x2
2960 = lineGap / 2 + (j * (squareSize + lineGap));
2964 int nrOfMenuItems = 7;
2965 Widget menuWidget[150];
2966 MenuListItem menuItemList[150] = {
2967 { "LoadNextGameProc", LoadNextGameProc },
2968 { "LoadPrevGameProc", LoadPrevGameProc },
2969 { "ReloadGameProc", ReloadGameProc },
2970 { "ReloadPositionProc", ReloadPositionProc },
2971 #ifndef OPTIONSDIALOG
2972 { "AlwaysQueenProc", AlwaysQueenProc },
2973 { "AnimateDraggingProc", AnimateDraggingProc },
2974 { "AnimateMovingProc", AnimateMovingProc },
2975 { "AutoflagProc", AutoflagProc },
2976 { "AutoflipProc", AutoflipProc },
2977 { "BlindfoldProc", BlindfoldProc },
2978 { "FlashMovesProc", FlashMovesProc },
2980 { "HighlightDraggingProc", HighlightDraggingProc },
2982 { "HighlightLastMoveProc", HighlightLastMoveProc },
2983 // { "IcsAlarmProc", IcsAlarmProc },
2984 { "MoveSoundProc", MoveSoundProc },
2985 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
2986 { "PopupExitMessageProc", PopupExitMessageProc },
2987 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
2988 // { "PremoveProc", PremoveProc },
2989 { "ShowCoordsProc", ShowCoordsProc },
2990 { "ShowThinkingProc", ShowThinkingProc },
2991 { "HideThinkingProc", HideThinkingProc },
2992 { "TestLegalityProc", TestLegalityProc },
2994 { "AboutGameProc", AboutGameEvent },
2995 { "DebugProc", DebugProc },
2996 { "NothingProc", NothingProc },
3001 MarkMenuItem (char *menuRef, int state)
3003 int nr = MenuToNumber(menuRef);
3006 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
3007 XtSetValues(menuWidget[nr], args, 1);
3012 EnableMenuItem (char *menuRef, int state)
3014 int nr = MenuToNumber(menuRef);
3015 if(nr >= 0) XtSetSensitive(menuWidget[nr], state);
3019 EnableButtonBar (int state)
3021 XtSetSensitive(buttonBarWidget, state);
3026 SetMenuEnables (Enables *enab)
3028 while (enab->name != NULL) {
3029 EnableMenuItem(enab->name, enab->value);
3035 Equal(char *p, char *s)
3036 { // compare strings skipping spaces in second
3038 if(*s == ' ') { s++; continue; }
3039 if(*s++ != *p++) return 0;
3045 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3046 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
3048 if(*nprms == 0) return;
3049 for(i=0; menuItemList[i].name; i++) {
3050 if(Equal(prms[0], menuItemList[i].name)) {
3051 (menuItemList[i].proc) ();
3058 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
3060 MenuProc *proc = (MenuProc *) addr;
3066 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
3068 RecentEngineEvent((int) (intptr_t) addr);
3071 // some stuff that must remain in front-end
3072 static Widget mainBar, currentMenu;
3073 static int wtot, nr = 0, widths[10];
3076 AppendMenuItem (char *text, char *name, MenuProc *action)
3083 XtSetArg(args[j], XtNleftMargin, 20); j++;
3084 XtSetArg(args[j], XtNrightMargin, 20); j++;
3086 if (strcmp(text, "----") == 0) {
3087 entry = XtCreateManagedWidget(text, smeLineObjectClass,
3088 currentMenu, args, j);
3090 XtSetArg(args[j], XtNlabel, XtNewString(_(text)));
3091 entry = XtCreateManagedWidget(name, smeBSBObjectClass,
3092 currentMenu, args, j+1);
3093 XtAddCallback(entry, XtNcallback,
3094 (XtCallbackProc) (strcmp(name, "recent") ? MenuBarSelect : MenuEngineSelect),
3096 menuWidget[nrOfMenuItems] = entry;
3101 CreateMenuButton (char *name, Menu *mb)
3102 { // create menu button on main bar, and shell for pull-down list
3108 XtSetArg(args[j], XtNmenuName, XtNewString(name)); j++;
3109 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3110 XtSetArg(args[j], XtNborderWidth, 0); j++;
3111 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3113 currentMenu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3116 XtSetArg(args[j], XtNwidth, &w); j++;
3117 XtGetValues(mb->subMenu, args, j);
3118 wtot += mb->textWidth = widths[nr++] = w;
3122 CreateMenuBar (Menu *mb, int boardWidth)
3126 char menuName[MSG_SIZ];
3130 // create bar itself
3132 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3133 XtSetArg(args[j], XtNvSpace, 0); j++;
3134 XtSetArg(args[j], XtNborderWidth, 0); j++;
3135 mainBar = XtCreateWidget("menuBar", boxWidgetClass,
3136 formWidget, args, j);
3138 CreateMainMenus(mb); // put menus in bar according to description in back-end
3140 // size buttons to make menu bar fit, clipping menu names where necessary
3141 while(wtot > boardWidth - 40) {
3143 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
3147 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
3149 XtSetArg(args[j], XtNwidth, widths[i]); j++;
3150 XtSetValues(ma[i].subMenu, args, j);
3157 CreateButtonBar (MenuItem *mi)
3160 Widget button, buttonBar;
3164 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3166 XtSetArg(args[j], XtNhSpace, 0); j++;
3168 XtSetArg(args[j], XtNborderWidth, 0); j++;
3169 XtSetArg(args[j], XtNvSpace, 0); j++;
3170 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3171 formWidget, args, j);
3173 while (mi->string != NULL) {
3176 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3177 XtSetArg(args[j], XtNborderWidth, 0); j++;
3179 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3180 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3181 buttonBar, args, j);
3182 XtAddCallback(button, XtNcallback,
3183 (XtCallbackProc) MenuBarSelect,
3184 (caddr_t) mi->proc);
3191 CreatePieceMenu (char *name, int color)
3196 ChessSquare selection;
3198 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3199 boardWidget, args, 0);
3201 for (i = 0; i < PIECE_MENU_SIZE; i++) {
3202 String item = pieceMenuStrings[color][i];
3204 if (strcmp(item, "----") == 0) {
3205 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3208 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3209 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3211 selection = pieceMenuTranslation[color][i];
3212 XtAddCallback(entry, XtNcallback,
3213 (XtCallbackProc) PieceMenuSelect,
3214 (caddr_t) selection);
3215 if (selection == WhitePawn || selection == BlackPawn) {
3216 XtSetArg(args[0], XtNpopupOnEntry, entry);
3217 XtSetValues(menu, args, 1);
3230 ChessSquare selection;
3232 whitePieceMenu = CreatePieceMenu("menuW", 0);
3233 blackPieceMenu = CreatePieceMenu("menuB", 1);
3235 if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
3236 XtRegisterGrabAction(PieceMenuPopup, True,
3237 (unsigned)(ButtonPressMask|ButtonReleaseMask),
3238 GrabModeAsync, GrabModeAsync);
3240 XtSetArg(args[0], XtNlabel, _("Drop"));
3241 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3242 boardWidget, args, 1);
3243 for (i = 0; i < DROP_MENU_SIZE; i++) {
3244 String item = dropMenuStrings[i];
3246 if (strcmp(item, "----") == 0) {
3247 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3250 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3251 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3253 selection = dropMenuTranslation[i];
3254 XtAddCallback(entry, XtNcallback,
3255 (XtCallbackProc) DropMenuSelect,
3256 (caddr_t) selection);
3270 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3271 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3272 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3273 dmEnables[i].piece);
3274 XtSetSensitive(entry, p != NULL || !appData.testLegality
3275 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3276 && !appData.icsActive));
3278 while (p && *p++ == dmEnables[i].piece) count++;
3279 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3281 XtSetArg(args[j], XtNlabel, label); j++;
3282 XtSetValues(entry, args, j);
3287 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
3289 String whichMenu; int menuNr = -2;
3290 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3291 if (event->type == ButtonRelease)
3292 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3293 else if (event->type == ButtonPress)
3294 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3296 case 0: whichMenu = params[0]; break;
3297 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3299 case -1: if (errorUp) ErrorPopDown();
3302 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3306 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3308 if (pmFromX < 0 || pmFromY < 0) return;
3309 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3313 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3315 if (pmFromX < 0 || pmFromY < 0) return;
3316 DropMenuEvent(piece, pmFromX, pmFromY);
3320 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3322 shiftKey = prms[0][0] & 1;
3327 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3329 shiftKey = prms[0][0] & 1;
3335 do_flash_delay (unsigned long msec)
3341 DrawBorder (int x, int y, int type)
3345 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
3347 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3348 squareSize+lineGap, squareSize+lineGap);
3352 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
3354 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
3355 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
3357 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
3358 if(textureW[kind] < W*squareSize)
3359 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
3361 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
3362 if(textureH[kind] < H*squareSize)
3363 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
3365 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
3370 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
3371 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
3373 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
3374 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
3375 squareSize, squareSize, x*fac, y*fac);
3377 if (useImages && useImageSqs) {
3381 pm = xpmLightSquare;
3386 case 2: /* neutral */
3391 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3392 squareSize, squareSize, x*fac, y*fac);
3402 case 2: /* neutral */
3407 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
3412 I split out the routines to draw a piece so that I could
3413 make a generic flash routine.
3416 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3418 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3419 switch (square_color) {
3421 case 2: /* neutral */
3423 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3424 ? *pieceToOutline(piece)
3425 : *pieceToSolid(piece),
3426 dest, bwPieceGC, 0, 0,
3427 squareSize, squareSize, x, y);
3430 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3431 ? *pieceToSolid(piece)
3432 : *pieceToOutline(piece),
3433 dest, wbPieceGC, 0, 0,
3434 squareSize, squareSize, x, y);
3440 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3442 switch (square_color) {
3444 case 2: /* neutral */
3446 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3447 ? *pieceToOutline(piece)
3448 : *pieceToSolid(piece),
3449 dest, bwPieceGC, 0, 0,
3450 squareSize, squareSize, x, y, 1);
3453 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3454 ? *pieceToSolid(piece)
3455 : *pieceToOutline(piece),
3456 dest, wbPieceGC, 0, 0,
3457 squareSize, squareSize, x, y, 1);
3463 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3465 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
3466 switch (square_color) {
3468 XCopyPlane(xDisplay, *pieceToSolid(piece),
3469 dest, (int) piece < (int) BlackPawn
3470 ? wlPieceGC : blPieceGC, 0, 0,
3471 squareSize, squareSize, x, y, 1);
3474 XCopyPlane(xDisplay, *pieceToSolid(piece),
3475 dest, (int) piece < (int) BlackPawn
3476 ? wdPieceGC : bdPieceGC, 0, 0,
3477 squareSize, squareSize, x, y, 1);
3479 case 2: /* neutral */
3481 XCopyPlane(xDisplay, *pieceToSolid(piece),
3482 dest, (int) piece < (int) BlackPawn
3483 ? wjPieceGC : bjPieceGC, 0, 0,
3484 squareSize, squareSize, x, y, 1);
3490 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3492 int kind, p = piece;
3494 switch (square_color) {
3496 case 2: /* neutral */
3498 if ((int)piece < (int) BlackPawn) {
3506 if ((int)piece < (int) BlackPawn) {
3514 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
3515 if(useTexture & square_color+1) {
3516 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
3517 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
3518 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
3519 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
3520 XSetClipMask(xDisplay, wlPieceGC, None);
3521 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
3523 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3524 dest, wlPieceGC, 0, 0,
3525 squareSize, squareSize, x, y);
3528 typedef void (*DrawFunc)();
3533 if (appData.monoMode) {
3534 if (DefaultDepth(xDisplay, xScreen) == 1) {
3535 return monoDrawPiece_1bit;
3537 return monoDrawPiece;
3541 return colorDrawPieceImage;
3543 return colorDrawPiece;
3548 DrawDot (int marker, int x, int y, int r)
3550 if(appData.monoMode) {
3551 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
3552 x, y, r, r, 0, 64*360);
3553 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
3554 x, y, r, r, 0, 64*360);
3556 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
3557 x, y, r, r, 0, 64*360);
3561 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
3562 { // basic front-end board-draw function: takes care of everything that can be in square:
3563 // piece, background, coordinate/count, marker dot
3564 int direction, font_ascent, font_descent;
3565 XCharStruct overall;
3568 if (piece == EmptySquare) {
3569 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
3571 drawfunc = ChooseDrawFunc();
3572 drawfunc(piece, square_color, x, y, xBoardWindow);
3575 if(align) { // square carries inscription (coord or piece count)
3577 GC hGC = align < 3 ? coordGC : countGC;
3578 // first calculate where it goes
3579 XTextExtents(countFontStruct, string, 1, &direction,
3580 &font_ascent, &font_descent, &overall);
3582 xx += squareSize - overall.width - 2;
3583 yy += squareSize - font_descent - 1;
3584 } else if (align == 2) {
3585 xx += 2, yy += font_ascent + 1;
3586 } else if (align == 3) {
3587 xx += squareSize - overall.width - 2;
3588 yy += font_ascent + 1;
3589 } else if (align == 4) {
3590 xx += 2, yy += font_ascent + 1;
3593 if (appData.monoMode) {
3594 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3596 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3600 if(marker) { // print fat marker dot, if requested
3601 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
3606 FlashDelay (int flash_delay)
3608 XSync(xDisplay, False);
3609 if(flash_delay) do_flash_delay(flash_delay);
3613 Fraction (int x, int start, int stop)
3615 double f = ((double) x - start)/(stop - start);
3616 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
3620 static WindowPlacement wpNew;
3623 CoDrag (Widget sh, WindowPlacement *wp)
3626 int j=0, touch=0, fudge = 2;
3627 GetActualPlacement(sh, wp);
3628 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
3629 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
3630 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
3631 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
3632 if(!touch ) return; // only windows that touch co-move
3633 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
3634 int heightInc = wpNew.height - wpMain.height;
3635 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3636 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3637 wp->y += fracTop * heightInc;
3638 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
3639 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
3640 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
3641 int widthInc = wpNew.width - wpMain.width;
3642 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3643 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3644 wp->y += fracLeft * widthInc;
3645 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
3646 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
3648 wp->x += wpNew.x - wpMain.x;
3649 wp->y += wpNew.y - wpMain.y;
3650 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
3651 if(touch == 3) wp->y += wpNew.height - wpMain.height;
3652 XtSetArg(args[j], XtNx, wp->x); j++;
3653 XtSetArg(args[j], XtNy, wp->y); j++;
3654 XtSetValues(sh, args, j);
3657 static XtIntervalId delayedDragID = 0;
3662 GetActualPlacement(shellWidget, &wpNew);
3663 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
3664 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
3665 return; // false alarm
3666 if(EngineOutputIsUp()) CoDrag(engineOutputShell, &wpEngineOutput);
3667 if(MoveHistoryIsUp()) CoDrag(shells[7], &wpMoveHistory);
3668 if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
3669 if(GameListIsUp()) CoDrag(gameListShell, &wpGameList);
3671 DrawPosition(True, NULL);
3672 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
3679 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
3681 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
3684 /* Why is this needed on some versions of X? */
3686 EventProc (Widget widget, caddr_t unused, XEvent *event)
3688 if (!XtIsRealized(widget))
3690 switch (event->type) {
3691 case ConfigureNotify: // main window is being dragged: drag attached windows with it
3692 if(appData.useStickyWindows)
3693 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
3696 if (event->xexpose.count > 0) return; /* no clipping is done */
3697 DrawPosition(True, NULL);
3698 if(twoBoards) { // [HGM] dual: draw other board in other orientation
3699 flipView = !flipView; partnerUp = !partnerUp;
3700 DrawPosition(True, NULL);
3701 flipView = !flipView; partnerUp = !partnerUp;
3705 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
3712 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
3714 DrawSeekAxis (int x, int y, int xTo, int yTo)
3716 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
3720 DrawSeekBackground (int left, int top, int right, int bottom)
3722 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
3726 DrawSeekText (char *buf, int x, int y)
3728 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
3732 DrawSeekDot (int x, int y, int colorNr)
3734 int square = colorNr & 0x80;
3737 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
3739 XFillRectangle(xDisplay, xBoardWindow, color,
3740 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3742 XFillArc(xDisplay, xBoardWindow, color,
3743 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
3747 DrawGrid (int second)
3749 XDrawSegments(xDisplay, xBoardWindow, lineGC,
3750 second ? secondSegments : // [HGM] dual
3751 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
3756 * event handler for redrawing the board
3759 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3761 DrawPosition(True, NULL);
3766 * event handler for parsing user moves
3768 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
3769 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
3770 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
3771 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
3772 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
3773 // and at the end FinishMove() to perform the move after optional promotion popups.
3774 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
3776 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3778 if (w != boardWidget || errorExitStatus != -1) return;
3779 if(nprms) shiftKey = !strcmp(prms[0], "1");
3782 if (event->type == ButtonPress) {
3783 XtPopdown(promotionShell);
3784 XtDestroyWidget(promotionShell);
3785 promotionUp = False;
3793 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
3794 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
3795 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
3799 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
3801 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
3802 DragPieceMove(event->xmotion.x, event->xmotion.y);
3806 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3807 { // [HGM] pv: walk PV
3808 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3811 static int savedIndex; /* gross that this is global */
3814 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3817 XawTextPosition index, dummy;
3820 XawTextGetSelectionPos(w, &index, &dummy);
3821 XtSetArg(arg, XtNstring, &val);
3822 XtGetValues(w, &arg, 1);
3823 ReplaceComment(savedIndex, val);
3824 if(savedIndex != currentMove) ToNrEvent(savedIndex);
3825 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3829 EditCommentPopUp (int index, char *title, char *text)
3832 if (text == NULL) text = "";
3833 NewCommentPopup(title, text, index);
3842 extern Option boxOptions[];
3852 edit = boxOptions[0].handle;
3854 XtSetArg(args[j], XtNstring, &val); j++;
3855 XtGetValues(edit, args, j);
3857 SendMultiLineToICS(val);
3858 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
3859 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
3863 ICSInputBoxPopDown ()
3869 CommentPopUp (char *title, char *text)
3871 savedIndex = currentMove; // [HGM] vari
3872 NewCommentPopup(title, text, currentMove);
3881 static char *openName;
3887 (void) (*fileProc)(openFP, 0, openName);
3891 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
3893 fileProc = proc; /* I can't see a way not */
3894 fileOpenMode = openMode; /* to use globals here */
3895 { // [HGM] use file-selector dialog stolen from Ghostview
3896 int index; // this is not supported yet
3897 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
3898 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
3899 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
3900 ScheduleDelayedEvent(&DelayedLoad, 50);
3907 if (!filenameUp) return;
3908 XtPopdown(fileNameShell);
3909 XtDestroyWidget(fileNameShell);
3915 FileNameCallback (Widget w, XtPointer client_data, XtPointer call_data)
3920 XtSetArg(args[0], XtNlabel, &name);
3921 XtGetValues(w, args, 1);
3923 if (strcmp(name, _("cancel")) == 0) {
3928 FileNameAction(w, NULL, NULL, NULL);
3932 FileNameAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3940 name = XawDialogGetValueString(w = XtParent(w));
3942 if ((name != NULL) && (*name != NULLCHAR)) {
3943 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
3944 XtPopdown(w = XtParent(XtParent(w)));
3948 p = strrchr(buf, ' ');
3955 fullname = ExpandPathName(buf);
3957 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
3960 f = fopen(fullname, fileOpenMode);
3962 DisplayError(_("Failed to open file"), errno);
3964 (void) (*fileProc)(f, index, buf);
3971 XtPopdown(w = XtParent(XtParent(w)));
3981 Widget dialog, layout;
3983 Dimension bw_width, pw_width;
3985 char *PromoChars = "wglcqrbnkac+=\0";
3988 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3989 XtGetValues(boardWidget, args, j);
3992 XtSetArg(args[j], XtNresizable, True); j++;
3993 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
3995 XtCreatePopupShell("Promotion", transientShellWidgetClass,
3996 shellWidget, args, j);
3998 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
3999 layoutArgs, XtNumber(layoutArgs));
4002 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
4003 XtSetArg(args[j], XtNborderWidth, 0); j++;
4004 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
4007 if(gameInfo.variant != VariantShogi) {
4008 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
4009 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
4010 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
4011 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
4012 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
4014 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
4015 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
4016 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
4017 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
4019 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
4020 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
4021 gameInfo.variant == VariantGiveaway) {
4022 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
4024 if(gameInfo.variant == VariantCapablanca ||
4025 gameInfo.variant == VariantGothic ||
4026 gameInfo.variant == VariantCapaRandom) {
4027 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
4028 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
4030 } else // [HGM] shogi
4032 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
4033 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
4035 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
4037 XtRealizeWidget(promotionShell);
4038 CatchDeleteWindow(promotionShell, "PromotionPopDown");
4041 XtSetArg(args[j], XtNwidth, &pw_width); j++;
4042 XtGetValues(promotionShell, args, j);
4044 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
4045 lineGap + squareSize/3 +
4046 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
4047 0 : 6*(squareSize + lineGap)), &x, &y);
4050 XtSetArg(args[j], XtNx, x); j++;
4051 XtSetArg(args[j], XtNy, y); j++;
4052 XtSetValues(promotionShell, args, j);
4054 XtPopup(promotionShell, XtGrabNone);
4062 if (!promotionUp) return;
4063 XtPopdown(promotionShell);
4064 XtDestroyWidget(promotionShell);
4065 promotionUp = False;
4069 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
4071 int promoChar = * (const char *) client_data;
4075 if (fromX == -1) return;
4082 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
4084 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
4085 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
4091 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
4093 dialogError = errorUp = False;
4094 XtPopdown(w = XtParent(XtParent(XtParent(w))));
4096 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
4103 if (!errorUp) return;
4104 dialogError = errorUp = False;
4105 XtPopdown(errorShell);
4106 XtDestroyWidget(errorShell);
4107 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
4111 ErrorPopUp (char *title, char *label, int modal)
4114 Widget dialog, layout;
4118 Dimension bw_width, pw_width;
4119 Dimension pw_height;
4123 XtSetArg(args[i], XtNresizable, True); i++;
4124 XtSetArg(args[i], XtNtitle, title); i++;
4126 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
4127 shellUp[0] ? (dialogError = modal = TRUE, shells[0]) : shellWidget, args, i);
4129 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
4130 layoutArgs, XtNumber(layoutArgs));
4133 XtSetArg(args[i], XtNlabel, label); i++;
4134 XtSetArg(args[i], XtNborderWidth, 0); i++;
4135 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
4138 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
4140 XtRealizeWidget(errorShell);
4141 CatchDeleteWindow(errorShell, "ErrorPopDown");
4144 XtSetArg(args[i], XtNwidth, &bw_width); i++;
4145 XtGetValues(boardWidget, args, i);
4147 XtSetArg(args[i], XtNwidth, &pw_width); i++;
4148 XtSetArg(args[i], XtNheight, &pw_height); i++;
4149 XtGetValues(errorShell, args, i);
4152 /* This code seems to tickle an X bug if it is executed too soon
4153 after xboard starts up. The coordinates get transformed as if
4154 the main window was positioned at (0, 0).
4156 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
4157 0 - pw_height + squareSize / 3, &x, &y);
4159 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
4160 RootWindowOfScreen(XtScreen(boardWidget)),
4161 (bw_width - pw_width) / 2,
4162 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
4166 if (y < 0) y = 0; /*avoid positioning top offscreen*/
4169 XtSetArg(args[i], XtNx, x); i++;
4170 XtSetArg(args[i], XtNy, y); i++;
4171 XtSetValues(errorShell, args, i);
4174 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
4177 /* Disable all user input other than deleting the window */
4178 static int frozen = 0;
4184 /* Grab by a widget that doesn't accept input */
4185 XtAddGrab(messageWidget, TRUE, FALSE);
4189 /* Undo a FreezeUI */
4193 if (!frozen) return;
4194 XtRemoveGrab(messageWidget);
4202 static int oldPausing = FALSE;
4203 static GameMode oldmode = (GameMode) -1;
4206 if (!boardWidget || !XtIsRealized(boardWidget)) return;
4208 if (pausing != oldPausing) {
4209 oldPausing = pausing;
4210 MarkMenuItem("Pause", pausing);
4212 if (appData.showButtonBar) {
4213 /* Always toggle, don't set. Previous code messes up when
4214 invoked while the button is pressed, as releasing it
4215 toggles the state again. */
4218 XtSetArg(args[0], XtNbackground, &oldbg);
4219 XtSetArg(args[1], XtNforeground, &oldfg);
4220 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
4222 XtSetArg(args[0], XtNbackground, oldfg);
4223 XtSetArg(args[1], XtNforeground, oldbg);
4225 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
4229 wname = ModeToWidgetName(oldmode);
4230 if (wname != NULL) {
4231 MarkMenuItem(wname, False);
4233 wname = ModeToWidgetName(gameMode);
4234 if (wname != NULL) {
4235 MarkMenuItem(wname, True);
4238 MarkMenuItem("Machine Match", matchMode && matchGame < appData.matchGames);
4240 /* Maybe all the enables should be handled here, not just this one */
4241 EnableMenuItem("Training", gameMode == Training || gameMode == PlayFromGameFile);
4246 * Button/menu procedures
4249 LoadGamePopUp (FILE *f, int gameNumber, char *title)
4251 cmailMsgLoaded = FALSE;
4252 if (gameNumber == 0) {
4253 int error = GameListBuild(f);
4255 DisplayError(_("Cannot build game list"), error);
4256 } else if (!ListEmpty(&gameList) &&
4257 ((ListGame *) gameList.tailPred)->number > 1) {
4258 GameListPopUp(f, title);
4264 return LoadGame(f, gameNumber, title, FALSE);
4267 /* this variable is shared between CopyPositionProc and SendPositionSelection */
4268 char *selected_fen_position=NULL;
4271 SendPositionSelection (Widget w, Atom *selection, Atom *target,
4272 Atom *type_return, XtPointer *value_return,
4273 unsigned long *length_return, int *format_return)
4275 char *selection_tmp;
4277 if (!selected_fen_position) return False; /* should never happen */
4278 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4279 /* note: since no XtSelectionDoneProc was registered, Xt will
4280 * automatically call XtFree on the value returned. So have to
4281 * make a copy of it allocated with XtMalloc */
4282 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
4283 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
4285 *value_return=selection_tmp;
4286 *length_return=strlen(selection_tmp);
4287 *type_return=*target;
4288 *format_return = 8; /* bits per byte */
4290 } else if (*target == XA_TARGETS(xDisplay)) {
4291 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4292 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4293 targets_tmp[1] = XA_STRING;
4294 *value_return = targets_tmp;
4295 *type_return = XA_ATOM;
4298 // This code leads to a read of value_return out of bounds on 64-bit systems.
4299 // Other code which I have seen always sets *format_return to 32 independent of
4300 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
4301 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
4302 *format_return = 8 * sizeof(Atom);
4303 if (*format_return > 32) {
4304 *length_return *= *format_return / 32;
4305 *format_return = 32;
4308 *format_return = 32;
4316 /* note: when called from menu all parameters are NULL, so no clue what the
4317 * Widget which was clicked on was, or what the click event was
4323 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4324 * have a notion of a position that is selected but not copied.
4325 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4327 if(gameMode == EditPosition) EditPositionDone(TRUE);
4328 if (selected_fen_position) free(selected_fen_position);
4329 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
4330 if (!selected_fen_position) return;
4331 XtOwnSelection(menuBarWidget, XA_PRIMARY,
4333 SendPositionSelection,
4334 NULL/* lose_ownership_proc */ ,
4335 NULL/* transfer_done_proc */);
4336 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4338 SendPositionSelection,
4339 NULL/* lose_ownership_proc */ ,
4340 NULL/* transfer_done_proc */);
4343 /* function called when the data to Paste is ready */
4345 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
4346 Atom *type, XtPointer value, unsigned long *len, int *format)
4349 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
4350 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
4351 EditPositionPasteFEN(fenstr);
4355 /* called when Paste Position button is pressed,
4356 * all parameters will be NULL */
4358 PastePositionProc ()
4360 XtGetSelectionValue(menuBarWidget,
4361 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4362 /* (XtSelectionCallbackProc) */ PastePositionCB,
4363 NULL, /* client_data passed to PastePositionCB */
4365 /* better to use the time field from the event that triggered the
4366 * call to this function, but that isn't trivial to get
4374 SendGameSelection (Widget w, Atom *selection, Atom *target,
4375 Atom *type_return, XtPointer *value_return,
4376 unsigned long *length_return, int *format_return)
4378 char *selection_tmp;
4380 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4381 FILE* f = fopen(gameCopyFilename, "r");
4384 if (f == NULL) return False;
4388 selection_tmp = XtMalloc(len + 1);
4389 count = fread(selection_tmp, 1, len, f);
4392 XtFree(selection_tmp);
4395 selection_tmp[len] = NULLCHAR;
4396 *value_return = selection_tmp;
4397 *length_return = len;
4398 *type_return = *target;
4399 *format_return = 8; /* bits per byte */
4401 } else if (*target == XA_TARGETS(xDisplay)) {
4402 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4403 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4404 targets_tmp[1] = XA_STRING;
4405 *value_return = targets_tmp;
4406 *type_return = XA_ATOM;
4409 // This code leads to a read of value_return out of bounds on 64-bit systems.
4410 // Other code which I have seen always sets *format_return to 32 independent of
4411 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
4412 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
4413 *format_return = 8 * sizeof(Atom);
4414 if (*format_return > 32) {
4415 *length_return *= *format_return / 32;
4416 *format_return = 32;
4419 *format_return = 32;
4431 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4432 * have a notion of a game that is selected but not copied.
4433 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4435 XtOwnSelection(menuBarWidget, XA_PRIMARY,
4438 NULL/* lose_ownership_proc */ ,
4439 NULL/* transfer_done_proc */);
4440 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4443 NULL/* lose_ownership_proc */ ,
4444 NULL/* transfer_done_proc */);
4447 /* note: when called from menu all parameters are NULL, so no clue what the
4448 * Widget which was clicked on was, or what the click event was
4450 /* function called when the data to Paste is ready */
4452 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
4453 Atom *type, XtPointer value, unsigned long *len, int *format)
4456 if (value == NULL || *len == 0) {
4457 return; /* nothing had been selected to copy */
4459 f = fopen(gamePasteFilename, "w");
4461 DisplayError(_("Can't open temp file"), errno);
4464 fwrite(value, 1, *len, f);
4467 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
4470 /* called when Paste Game button is pressed,
4471 * all parameters will be NULL */
4475 XtGetSelectionValue(menuBarWidget,
4476 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4477 /* (XtSelectionCallbackProc) */ PasteGameCB,
4478 NULL, /* client_data passed to PasteGameCB */
4480 /* better to use the time field from the event that triggered the
4481 * call to this function, but that isn't trivial to get
4490 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4496 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4497 { // [HGM] input: let up-arrow recall previous line from history
4504 if (!shellUp[4]) return;
4505 edit = boxOptions[0].handle;
4507 XtSetArg(args[j], XtNstring, &val); j++;
4508 XtGetValues(edit, args, j);
4509 val = PrevInHistory(val);
4510 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4511 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4513 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
4514 XawTextReplace(edit, 0, 0, &t);
4515 XawTextSetInsertionPoint(edit, 9999);
4520 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4521 { // [HGM] input: let down-arrow recall next line from history
4526 if (!shellUp[4]) return;
4527 edit = boxOptions[0].handle;
4528 val = NextInHistory();
4529 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4530 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4532 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
4533 XawTextReplace(edit, 0, 0, &t);
4534 XawTextSetInsertionPoint(edit, 9999);
4539 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4541 if (shellUp[4] == True)
4546 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4548 if (!TempBackwardActive) {
4549 TempBackwardActive = True;
4555 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4557 /* Check to see if triggered by a key release event for a repeating key.
4558 * If so the next queued event will be a key press of the same key at the same time */
4559 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
4561 XPeekEvent(xDisplay, &next);
4562 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
4563 next.xkey.keycode == event->xkey.keycode)
4567 TempBackwardActive = False;
4571 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4572 { // called as key binding
4575 if (nprms && *nprms > 0)
4579 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
4584 DisplayMessage (char *message, char *extMessage)
4586 /* display a message in the message widget */
4595 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
4600 message = extMessage;
4604 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
4606 /* need to test if messageWidget already exists, since this function
4607 can also be called during the startup, if for example a Xresource
4608 is not set up correctly */
4611 XtSetArg(arg, XtNlabel, message);
4612 XtSetValues(messageWidget, &arg, 1);
4619 DisplayTitle (char *text)
4623 char title[MSG_SIZ];
4626 if (text == NULL) text = "";
4628 if (appData.titleInWindow) {
4630 XtSetArg(args[i], XtNlabel, text); i++;
4631 XtSetValues(titleWidget, args, i);
4634 if (*text != NULLCHAR) {
4635 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
4636 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
4637 } else if (appData.icsActive) {
4638 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
4639 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
4640 } else if (appData.cmailGameName[0] != NULLCHAR) {
4641 snprintf(icon, sizeof(icon), "%s", "CMail");
4642 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
4644 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
4645 } else if (gameInfo.variant == VariantGothic) {
4646 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
4647 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
4650 } else if (gameInfo.variant == VariantFalcon) {
4651 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
4652 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
4654 } else if (appData.noChessProgram) {
4655 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
4656 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
4658 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
4659 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
4662 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
4663 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
4664 XtSetValues(shellWidget, args, i);
4665 XSync(xDisplay, False);
4670 DisplayError (String message, int error)
4675 if (appData.debugMode || appData.matchMode) {
4676 fprintf(stderr, "%s: %s\n", programName, message);
4679 if (appData.debugMode || appData.matchMode) {
4680 fprintf(stderr, "%s: %s: %s\n",
4681 programName, message, strerror(error));
4683 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
4686 ErrorPopUp(_("Error"), message, FALSE);
4691 DisplayMoveError (String message)
4695 DrawPosition(FALSE, NULL);
4696 if (appData.debugMode || appData.matchMode) {
4697 fprintf(stderr, "%s: %s\n", programName, message);
4699 if (appData.popupMoveErrors) {
4700 ErrorPopUp(_("Error"), message, FALSE);
4702 DisplayMessage(message, "");
4708 DisplayFatalError (String message, int error, int status)
4712 errorExitStatus = status;
4714 fprintf(stderr, "%s: %s\n", programName, message);
4716 fprintf(stderr, "%s: %s: %s\n",
4717 programName, message, strerror(error));
4718 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
4721 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
4722 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
4729 DisplayInformation (String message)
4732 ErrorPopUp(_("Information"), message, TRUE);
4736 DisplayNote (String message)
4739 ErrorPopUp(_("Note"), message, FALSE);
4743 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
4749 DisplayIcsInteractionTitle (String message)
4751 if (oldICSInteractionTitle == NULL) {
4752 /* Magic to find the old window title, adapted from vim */
4753 char *wina = getenv("WINDOWID");
4755 Window win = (Window) atoi(wina);
4756 Window root, parent, *children;
4757 unsigned int nchildren;
4758 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4760 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4761 if (!XQueryTree(xDisplay, win, &root, &parent,
4762 &children, &nchildren)) break;
4763 if (children) XFree((void *)children);
4764 if (parent == root || parent == 0) break;
4767 XSetErrorHandler(oldHandler);
4769 if (oldICSInteractionTitle == NULL) {
4770 oldICSInteractionTitle = "xterm";
4773 printf("\033]0;%s\007", message);
4777 char pendingReplyPrefix[MSG_SIZ];
4778 ProcRef pendingReplyPR;
4781 AskQuestionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4784 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
4788 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
4792 AskQuestionPopDown ()
4794 if (!askQuestionUp) return;
4795 XtPopdown(askQuestionShell);
4796 XtDestroyWidget(askQuestionShell);
4797 askQuestionUp = False;
4801 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4807 reply = XawDialogGetValueString(w = XtParent(w));
4808 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
4809 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
4810 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
4811 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
4812 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
4813 AskQuestionPopDown();
4815 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
4819 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
4824 XtSetArg(args[0], XtNlabel, &name);
4825 XtGetValues(w, args, 1);
4827 if (strcmp(name, _("cancel")) == 0) {
4828 AskQuestionPopDown();
4830 AskQuestionReplyAction(w, NULL, NULL, NULL);
4835 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
4838 Widget popup, layout, dialog, edit;
4844 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
4845 pendingReplyPR = pr;
4848 XtSetArg(args[i], XtNresizable, True); i++;
4849 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
4850 askQuestionShell = popup =
4851 XtCreatePopupShell(title, transientShellWidgetClass,
4852 shellWidget, args, i);
4855 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
4856 layoutArgs, XtNumber(layoutArgs));
4859 XtSetArg(args[i], XtNlabel, question); i++;
4860 XtSetArg(args[i], XtNvalue, ""); i++;
4861 XtSetArg(args[i], XtNborderWidth, 0); i++;
4862 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
4865 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
4866 (XtPointer) dialog);
4867 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
4868 (XtPointer) dialog);
4870 XtRealizeWidget(popup);
4871 CatchDeleteWindow(popup, "AskQuestionPopDown");
4873 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
4874 &x, &y, &win_x, &win_y, &mask);
4876 XtSetArg(args[0], XtNx, x - 10);
4877 XtSetArg(args[1], XtNy, y - 30);
4878 XtSetValues(popup, args, 2);
4880 XtPopup(popup, XtGrabExclusive);
4881 askQuestionUp = True;
4883 edit = XtNameToWidget(dialog, "*value");
4884 XtSetKeyboardFocus(popup, edit);
4889 PlaySound (char *name)
4891 if (*name == NULLCHAR) {
4893 } else if (strcmp(name, "$") == 0) {
4894 putc(BELLCHAR, stderr);
4897 char *prefix = "", *sep = "";
4898 if(appData.soundProgram[0] == NULLCHAR) return;
4899 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
4900 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
4908 PlaySound(appData.soundMove);
4914 PlaySound(appData.soundIcsWin);
4920 PlaySound(appData.soundIcsLoss);
4926 PlaySound(appData.soundIcsDraw);
4930 PlayIcsUnfinishedSound ()
4932 PlaySound(appData.soundIcsUnfinished);
4938 PlaySound(appData.soundIcsAlarm);
4944 PlaySound(appData.soundTell);
4950 system("stty echo");
4957 system("stty -echo");
4962 RunCommand (char *buf)
4968 Colorize (ColorClass cc, int continuation)
4971 int count, outCount, error;
4973 if (textColors[(int)cc].bg > 0) {
4974 if (textColors[(int)cc].fg > 0) {
4975 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
4976 textColors[(int)cc].fg, textColors[(int)cc].bg);
4978 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
4979 textColors[(int)cc].bg);
4982 if (textColors[(int)cc].fg > 0) {
4983 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
4984 textColors[(int)cc].fg);
4986 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
4989 count = strlen(buf);
4990 outCount = OutputToProcess(NoProc, buf, count, &error);
4991 if (outCount < count) {
4992 DisplayFatalError(_("Error writing to display"), error, 1);
4995 if (continuation) return;
4998 PlaySound(appData.soundShout);
5001 PlaySound(appData.soundSShout);
5004 PlaySound(appData.soundChannel1);
5007 PlaySound(appData.soundChannel);
5010 PlaySound(appData.soundKibitz);
5013 PlaySound(appData.soundTell);
5015 case ColorChallenge:
5016 PlaySound(appData.soundChallenge);
5019 PlaySound(appData.soundRequest);
5022 PlaySound(appData.soundSeek);
5034 return getpwuid(getuid())->pw_name;
5038 ExpandPathName (char *path)
5040 static char static_buf[4*MSG_SIZ];
5041 char *d, *s, buf[4*MSG_SIZ];
5047 while (*s && isspace(*s))
5056 if (*(s+1) == '/') {
5057 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
5061 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
5062 { char *p; if(p = strchr(buf, '/')) *p = 0; }
5063 pwd = getpwnam(buf);
5066 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
5070 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
5071 strcat(d, strchr(s+1, '/'));
5075 safeStrCpy(d, s, 4*MSG_SIZ );
5083 static char host_name[MSG_SIZ];
5085 #if HAVE_GETHOSTNAME
5086 gethostname(host_name, MSG_SIZ);
5088 #else /* not HAVE_GETHOSTNAME */
5089 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
5090 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
5092 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
5094 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
5095 #endif /* not HAVE_GETHOSTNAME */
5098 XtIntervalId delayedEventTimerXID = 0;
5099 DelayedEventCallback delayedEventCallback = 0;
5104 delayedEventTimerXID = 0;
5105 delayedEventCallback();
5109 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
5111 if(delayedEventTimerXID && delayedEventCallback == cb)
5112 // [HGM] alive: replace, rather than add or flush identical event
5113 XtRemoveTimeOut(delayedEventTimerXID);
5114 delayedEventCallback = cb;
5115 delayedEventTimerXID =
5116 XtAppAddTimeOut(appContext, millisec,
5117 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
5120 DelayedEventCallback
5123 if (delayedEventTimerXID) {
5124 return delayedEventCallback;
5131 CancelDelayedEvent ()
5133 if (delayedEventTimerXID) {
5134 XtRemoveTimeOut(delayedEventTimerXID);
5135 delayedEventTimerXID = 0;
5139 XtIntervalId loadGameTimerXID = 0;
5142 LoadGameTimerRunning ()
5144 return loadGameTimerXID != 0;
5148 StopLoadGameTimer ()
5150 if (loadGameTimerXID != 0) {
5151 XtRemoveTimeOut(loadGameTimerXID);
5152 loadGameTimerXID = 0;
5160 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
5162 loadGameTimerXID = 0;
5167 StartLoadGameTimer (long millisec)
5170 XtAppAddTimeOut(appContext, millisec,
5171 (XtTimerCallbackProc) LoadGameTimerCallback,
5175 XtIntervalId analysisClockXID = 0;
5178 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
5180 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
5181 || appData.icsEngineAnalyze) { // [DM]
5182 AnalysisPeriodicEvent(0);
5183 StartAnalysisClock();
5188 StartAnalysisClock ()
5191 XtAppAddTimeOut(appContext, 2000,
5192 (XtTimerCallbackProc) AnalysisClockCallback,
5196 XtIntervalId clockTimerXID = 0;
5199 ClockTimerRunning ()
5201 return clockTimerXID != 0;
5207 if (clockTimerXID != 0) {
5208 XtRemoveTimeOut(clockTimerXID);
5217 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
5224 StartClockTimer (long millisec)
5227 XtAppAddTimeOut(appContext, millisec,
5228 (XtTimerCallbackProc) ClockTimerCallback,
5233 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
5238 /* check for low time warning */
5239 Pixel foregroundOrWarningColor = timerForegroundPixel;
5242 appData.lowTimeWarning &&
5243 (timer / 1000) < appData.icsAlarmTime)
5244 foregroundOrWarningColor = lowTimeWarningColor;
5246 if (appData.clockMode) {
5247 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
5248 XtSetArg(args[0], XtNlabel, buf);
5250 snprintf(buf, MSG_SIZ, "%s ", color);
5251 XtSetArg(args[0], XtNlabel, buf);
5256 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
5257 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
5259 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
5260 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
5263 XtSetValues(w, args, 3);
5267 DisplayWhiteClock (long timeRemaining, int highlight)
5271 if(appData.noGUI) return;
5272 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
5273 if (highlight && iconPixmap == bIconPixmap) {
5274 iconPixmap = wIconPixmap;
5275 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
5276 XtSetValues(shellWidget, args, 1);
5281 DisplayBlackClock (long timeRemaining, int highlight)
5285 if(appData.noGUI) return;
5286 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
5287 if (highlight && iconPixmap == wIconPixmap) {
5288 iconPixmap = bIconPixmap;
5289 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
5290 XtSetValues(shellWidget, args, 1);
5309 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
5313 int to_prog[2], from_prog[2];
5317 if (appData.debugMode) {
5318 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
5321 /* We do NOT feed the cmdLine to the shell; we just
5322 parse it into blank-separated arguments in the
5323 most simple-minded way possible.
5326 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
5329 while(*p == ' ') p++;
5331 if(*p == '"' || *p == '\'')
5332 p = strchr(++argv[i-1], *p);
5333 else p = strchr(p, ' ');
5334 if (p == NULL) break;
5339 SetUpChildIO(to_prog, from_prog);
5341 if ((pid = fork()) == 0) {
5343 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
5344 close(to_prog[1]); // first close the unused pipe ends
5345 close(from_prog[0]);
5346 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
5347 dup2(from_prog[1], 1);
5348 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
5349 close(from_prog[1]); // and closing again loses one of the pipes!
5350 if(fileno(stderr) >= 2) // better safe than sorry...
5351 dup2(1, fileno(stderr)); /* force stderr to the pipe */
5353 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
5358 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
5360 execvp(argv[0], argv);
5362 /* If we get here, exec failed */
5367 /* Parent process */
5369 close(from_prog[1]);
5371 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5374 cp->fdFrom = from_prog[0];
5375 cp->fdTo = to_prog[1];
5380 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
5382 AlarmCallBack (int n)
5388 DestroyChildProcess (ProcRef pr, int signalType)
5390 ChildProc *cp = (ChildProc *) pr;
5392 if (cp->kind != CPReal) return;
5394 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
5395 signal(SIGALRM, AlarmCallBack);
5397 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
5398 kill(cp->pid, SIGKILL); // kill it forcefully
5399 wait((int *) 0); // and wait again
5403 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
5405 /* Process is exiting either because of the kill or because of
5406 a quit command sent by the backend; either way, wait for it to die.
5415 InterruptChildProcess (ProcRef pr)
5417 ChildProc *cp = (ChildProc *) pr;
5419 if (cp->kind != CPReal) return;
5420 (void) kill(cp->pid, SIGINT); /* stop it thinking */
5424 OpenTelnet (char *host, char *port, ProcRef *pr)
5426 char cmdLine[MSG_SIZ];
5428 if (port[0] == NULLCHAR) {
5429 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
5431 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
5433 return StartChildProcess(cmdLine, "", pr);
5437 OpenTCP (char *host, char *port, ProcRef *pr)
5440 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
5441 #else /* !OMIT_SOCKETS */
5442 struct addrinfo hints;
5443 struct addrinfo *ais, *ai;
5448 memset(&hints, 0, sizeof(hints));
5449 hints.ai_family = AF_UNSPEC;
5450 hints.ai_socktype = SOCK_STREAM;
5452 error = getaddrinfo(host, port, &hints, &ais);
5454 /* a getaddrinfo error is not an errno, so can't return it */
5455 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
5456 host, port, gai_strerror(error));
5460 for (ai = ais; ai != NULL; ai = ai->ai_next) {
5461 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
5465 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
5478 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5484 #endif /* !OMIT_SOCKETS */
5490 OpenCommPort (char *name, ProcRef *pr)
5495 fd = open(name, 2, 0);
5496 if (fd < 0) return errno;
5498 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5509 OpenLoopback (ProcRef *pr)
5514 SetUpChildIO(to, from);
5516 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5519 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
5527 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
5529 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
5533 #define INPUT_SOURCE_BUF_SIZE 8192
5542 char buf[INPUT_SOURCE_BUF_SIZE];
5547 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
5549 InputSource *is = (InputSource *) closure;
5554 if (is->lineByLine) {
5555 count = read(is->fd, is->unused,
5556 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
5558 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
5561 is->unused += count;
5563 while (p < is->unused) {
5564 q = memchr(p, '\n', is->unused - p);
5565 if (q == NULL) break;
5567 (is->func)(is, is->closure, p, q - p, 0);
5571 while (p < is->unused) {
5576 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
5581 (is->func)(is, is->closure, is->buf, count, error);
5586 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
5589 ChildProc *cp = (ChildProc *) pr;
5591 is = (InputSource *) calloc(1, sizeof(InputSource));
5592 is->lineByLine = lineByLine;
5596 is->fd = fileno(stdin);
5598 is->kind = cp->kind;
5599 is->fd = cp->fdFrom;
5602 is->unused = is->buf;
5605 is->xid = XtAppAddInput(appContext, is->fd,
5606 (XtPointer) (XtInputReadMask),
5607 (XtInputCallbackProc) DoInputCallback,
5609 is->closure = closure;
5610 return (InputSourceRef) is;
5614 RemoveInputSource (InputSourceRef isr)
5616 InputSource *is = (InputSource *) isr;
5618 if (is->xid == 0) return;
5619 XtRemoveInput(is->xid);
5624 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
5626 static int line = 0;
5627 ChildProc *cp = (ChildProc *) pr;
5632 if (appData.noJoin || !appData.useInternalWrap)
5633 outCount = fwrite(message, 1, count, stdout);
5636 int width = get_term_width();
5637 int len = wrap(NULL, message, count, width, &line);
5638 char *msg = malloc(len);
5642 outCount = fwrite(message, 1, count, stdout);
5645 dbgchk = wrap(msg, message, count, width, &line);
5646 if (dbgchk != len && appData.debugMode)
5647 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
5648 outCount = fwrite(msg, 1, dbgchk, stdout);
5654 outCount = write(cp->fdTo, message, count);
5664 /* Output message to process, with "ms" milliseconds of delay
5665 between each character. This is needed when sending the logon
5666 script to ICC, which for some reason doesn't like the
5667 instantaneous send. */
5669 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
5671 ChildProc *cp = (ChildProc *) pr;
5676 r = write(cp->fdTo, message++, 1);
5689 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
5691 /* Masks for XPM pieces. Black and white pieces can have
5692 different shapes, but in the interest of retaining my
5693 sanity pieces must have the same outline on both light
5694 and dark squares, and all pieces must use the same
5695 background square colors/images. */
5697 static int xpmDone = 0;
5698 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
5699 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
5702 CreateAnimMasks (int pieceDepth)
5708 unsigned long plane;
5711 /* Need a bitmap just to get a GC with right depth */
5712 buf = XCreatePixmap(xDisplay, xBoardWindow,
5714 values.foreground = 1;
5715 values.background = 0;
5716 /* Don't use XtGetGC, not read only */
5717 maskGC = XCreateGC(xDisplay, buf,
5718 GCForeground | GCBackground, &values);
5719 XFreePixmap(xDisplay, buf);
5721 buf = XCreatePixmap(xDisplay, xBoardWindow,
5722 squareSize, squareSize, pieceDepth);
5723 values.foreground = XBlackPixel(xDisplay, xScreen);
5724 values.background = XWhitePixel(xDisplay, xScreen);
5725 bufGC = XCreateGC(xDisplay, buf,
5726 GCForeground | GCBackground, &values);
5728 for (piece = WhitePawn; piece <= BlackKing; piece++) {
5729 /* Begin with empty mask */
5730 if(!xpmDone) // [HGM] pieces: keep using existing
5731 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
5732 squareSize, squareSize, 1);
5733 XSetFunction(xDisplay, maskGC, GXclear);
5734 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
5735 0, 0, squareSize, squareSize);
5737 /* Take a copy of the piece */
5742 XSetFunction(xDisplay, bufGC, GXcopy);
5743 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
5745 0, 0, squareSize, squareSize, 0, 0);
5747 /* XOR the background (light) over the piece */
5748 XSetFunction(xDisplay, bufGC, GXxor);
5750 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
5751 0, 0, squareSize, squareSize, 0, 0);
5753 XSetForeground(xDisplay, bufGC, lightSquareColor);
5754 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
5757 /* We now have an inverted piece image with the background
5758 erased. Construct mask by just selecting all the non-zero
5759 pixels - no need to reconstruct the original image. */
5760 XSetFunction(xDisplay, maskGC, GXor);
5762 /* Might be quicker to download an XImage and create bitmap
5763 data from it rather than this N copies per piece, but it
5764 only takes a fraction of a second and there is a much
5765 longer delay for loading the pieces. */
5766 for (n = 0; n < pieceDepth; n ++) {
5767 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
5768 0, 0, squareSize, squareSize,
5774 XFreePixmap(xDisplay, buf);
5775 XFreeGC(xDisplay, bufGC);
5776 XFreeGC(xDisplay, maskGC);
5780 InitAnimState (AnimNr anr, XWindowAttributes *info)
5785 /* Each buffer is square size, same depth as window */
5786 animBufs[anr+4] = xBoardWindow;
5787 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
5788 squareSize, squareSize, info->depth);
5789 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
5790 squareSize, squareSize, info->depth);
5792 /* Create a plain GC for blitting */
5793 mask = GCForeground | GCBackground | GCFunction |
5794 GCPlaneMask | GCGraphicsExposures;
5795 values.foreground = XBlackPixel(xDisplay, xScreen);
5796 values.background = XWhitePixel(xDisplay, xScreen);
5797 values.function = GXcopy;
5798 values.plane_mask = AllPlanes;
5799 values.graphics_exposures = False;
5800 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
5802 /* Piece will be copied from an existing context at
5803 the start of each new animation/drag. */
5804 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
5806 /* Outline will be a read-only copy of an existing */
5807 animGCs[anr+4] = None;
5813 XWindowAttributes info;
5815 if (xpmDone && gameInfo.variant == oldVariant) return;
5816 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
5817 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
5819 InitAnimState(Game, &info);
5820 InitAnimState(Player, &info);
5822 /* For XPM pieces, we need bitmaps to use as masks. */
5824 CreateAnimMasks(info.depth), xpmDone = 1;
5829 static Boolean frameWaiting;
5832 FrameAlarm (int sig)
5834 frameWaiting = False;
5835 /* In case System-V style signals. Needed?? */
5836 signal(SIGALRM, FrameAlarm);
5840 FrameDelay (int time)
5842 struct itimerval delay;
5844 XSync(xDisplay, False);
5847 frameWaiting = True;
5848 signal(SIGALRM, FrameAlarm);
5849 delay.it_interval.tv_sec =
5850 delay.it_value.tv_sec = time / 1000;
5851 delay.it_interval.tv_usec =
5852 delay.it_value.tv_usec = (time % 1000) * 1000;
5853 setitimer(ITIMER_REAL, &delay, NULL);
5854 while (frameWaiting) pause();
5855 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
5856 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
5857 setitimer(ITIMER_REAL, &delay, NULL);
5864 FrameDelay (int time)
5866 XSync(xDisplay, False);
5868 usleep(time * 1000);
5874 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
5878 /* Bitmap for piece being moved. */
5879 if (appData.monoMode) {
5880 *mask = *pieceToSolid(piece);
5881 } else if (useImages) {
5883 *mask = xpmMask[piece];
5885 *mask = ximMaskPm[piece];
5888 *mask = *pieceToSolid(piece);
5891 /* GC for piece being moved. Square color doesn't matter, but
5892 since it gets modified we make a copy of the original. */
5894 if (appData.monoMode)
5899 if (appData.monoMode)
5904 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
5906 /* Outline only used in mono mode and is not modified */
5908 *outline = bwPieceGC;
5910 *outline = wbPieceGC;
5914 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
5919 /* Draw solid rectangle which will be clipped to shape of piece */
5920 XFillRectangle(xDisplay, dest, clip,
5921 0, 0, squareSize, squareSize);
5922 if (appData.monoMode)
5923 /* Also draw outline in contrasting color for black
5924 on black / white on white cases */
5925 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
5926 0, 0, squareSize, squareSize, 0, 0, 1);
5928 /* Copy the piece */
5933 if(appData.upsideDown && flipView) kind ^= 2;
5934 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
5936 0, 0, squareSize, squareSize,
5942 InsertPiece (AnimNr anr, ChessSquare piece)
5944 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
5948 DrawBlank (AnimNr anr, int x, int y, int startColor)
5950 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
5953 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
5954 int srcX, int srcY, int width, int height, int destX, int destY)
5956 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
5957 srcX, srcY, width, height, destX, destY);
5961 SetDragPiece (AnimNr anr, ChessSquare piece)
5964 /* The piece will be drawn using its own bitmap as a matte */
5965 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
5966 XSetClipMask(xDisplay, animGCs[anr+2], mask);
5969 #include <sys/ioctl.h>
5973 int fd, default_width;
5976 default_width = 79; // this is FICS default anyway...
5978 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
5980 if (!ioctl(fd, TIOCGSIZE, &win))
5981 default_width = win.ts_cols;
5982 #elif defined(TIOCGWINSZ)
5984 if (!ioctl(fd, TIOCGWINSZ, &win))
5985 default_width = win.ws_col;
5987 return default_width;
5993 static int old_width = 0;
5994 int new_width = get_term_width();
5996 if (old_width != new_width)
5997 ics_printf("set width %d\n", new_width);
5998 old_width = new_width;
6002 NotifyFrontendLogin ()
6007 /* [AS] Arrow highlighting support */
6010 DrawPolygon (Pnt arrow[], int nr)
6014 for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
6015 XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
6016 if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
6020 UpdateLogos (int displ)
6022 return; // no logos in XBoard yet