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 //--------------------------------------------------------------------------------------------
1191 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1193 #define BoardSize int
1195 InitDrawingSizes (BoardSize boardSize, int flags)
1196 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1197 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1199 XtGeometryResult gres;
1201 static Dimension oldWidth, oldHeight;
1202 static VariantClass oldVariant;
1203 static int oldDual = -1, oldMono = -1;
1205 if(!formWidget) return;
1207 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1208 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1209 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1211 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1213 * Enable shell resizing.
1215 shellArgs[0].value = (XtArgVal) &w;
1216 shellArgs[1].value = (XtArgVal) &h;
1217 XtGetValues(shellWidget, shellArgs, 2);
1219 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1220 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1221 XtSetValues(shellWidget, &shellArgs[2], 4);
1223 XtSetArg(args[0], XtNdefaultDistance, &sep);
1224 XtGetValues(formWidget, args, 1);
1226 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1228 hOffset = boardWidth + 10;
1229 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1230 secondSegments[i] = gridSegments[i];
1231 secondSegments[i].x1 += hOffset;
1232 secondSegments[i].x2 += hOffset;
1235 XtSetArg(args[0], XtNwidth, boardWidth);
1236 XtSetArg(args[1], XtNheight, boardHeight);
1237 XtSetValues(boardWidget, args, 2);
1239 timerWidth = (boardWidth - sep) / 2;
1240 XtSetArg(args[0], XtNwidth, timerWidth);
1241 XtSetValues(whiteTimerWidget, args, 1);
1242 XtSetValues(blackTimerWidget, args, 1);
1244 XawFormDoLayout(formWidget, False);
1246 if (appData.titleInWindow) {
1248 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1249 XtSetArg(args[i], XtNheight, &h); i++;
1250 XtGetValues(titleWidget, args, i);
1252 w = boardWidth - 2*bor;
1254 XtSetArg(args[0], XtNwidth, &w);
1255 XtGetValues(menuBarWidget, args, 1);
1256 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1259 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1260 if (gres != XtGeometryYes && appData.debugMode) {
1262 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1263 programName, gres, w, h, wr, hr);
1267 XawFormDoLayout(formWidget, True);
1270 * Inhibit shell resizing.
1272 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1273 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1274 shellArgs[4].value = shellArgs[2].value = w;
1275 shellArgs[5].value = shellArgs[3].value = h;
1276 XtSetValues(shellWidget, &shellArgs[0], 6);
1278 XSync(xDisplay, False);
1282 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1285 if(gameInfo.variant != oldVariant) { // and only if variant changed
1288 for(i=0; i<4; i++) {
1290 for(p=0; p<=(int)WhiteKing; p++)
1291 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1292 if(gameInfo.variant == VariantShogi) {
1293 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1294 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1295 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1296 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1297 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1300 if(gameInfo.variant == VariantGothic) {
1301 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1304 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1305 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1306 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1309 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1310 for(p=0; p<=(int)WhiteKing; p++)
1311 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1312 if(gameInfo.variant == VariantShogi) {
1313 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1314 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1315 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1316 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1317 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1320 if(gameInfo.variant == VariantGothic) {
1321 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1324 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1325 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1326 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1331 for(i=0; i<2; i++) {
1333 for(p=0; p<=(int)WhiteKing; p++)
1334 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1335 if(gameInfo.variant == VariantShogi) {
1336 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1337 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1338 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1339 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1340 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1343 if(gameInfo.variant == VariantGothic) {
1344 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1347 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1348 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1349 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1353 oldMono = -10; // kludge to force recreation of animation masks
1354 oldVariant = gameInfo.variant;
1357 if(appData.monoMode != oldMono)
1360 oldMono = appData.monoMode;
1365 ParseIcsTextColors ()
1366 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1367 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1368 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1369 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1370 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1371 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1372 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1373 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1374 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1375 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1376 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1378 if (appData.colorize) {
1380 _("%s: can't parse color names; disabling colorization\n"),
1383 appData.colorize = FALSE;
1388 MakeOneColor (char *name, Pixel *color)
1390 XrmValue vFrom, vTo;
1391 if (!appData.monoMode) {
1392 vFrom.addr = (caddr_t) name;
1393 vFrom.size = strlen(name);
1394 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1395 if (vTo.addr == NULL) {
1396 appData.monoMode = True;
1399 *color = *(Pixel *) vTo.addr;
1407 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1408 int forceMono = False;
1410 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1411 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1412 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1413 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1414 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1415 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1422 { // [HGM] taken out of main
1424 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1425 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1426 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1428 if (appData.bitmapDirectory[0] != NULLCHAR) {
1432 CreateXPMBoard(appData.liteBackTextureFile, 1);
1433 CreateXPMBoard(appData.darkBackTextureFile, 0);
1437 /* Create regular pieces */
1438 if (!useImages) CreatePieces();
1443 main (int argc, char **argv)
1445 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1446 XSetWindowAttributes window_attributes;
1448 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1449 XrmValue vFrom, vTo;
1450 XtGeometryResult gres;
1453 int forceMono = False;
1455 srandom(time(0)); // [HGM] book: make random truly random
1457 setbuf(stdout, NULL);
1458 setbuf(stderr, NULL);
1461 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1462 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1466 programName = strrchr(argv[0], '/');
1467 if (programName == NULL)
1468 programName = argv[0];
1473 XtSetLanguageProc(NULL, NULL, NULL);
1474 bindtextdomain(PACKAGE, LOCALEDIR);
1475 textdomain(PACKAGE);
1479 XtAppInitialize(&appContext, "XBoard", shellOptions,
1480 XtNumber(shellOptions),
1481 &argc, argv, xboardResources, NULL, 0);
1482 appData.boardSize = "";
1483 InitAppData(ConvertToLine(argc, argv));
1485 if (p == NULL) p = "/tmp";
1486 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1487 gameCopyFilename = (char*) malloc(i);
1488 gamePasteFilename = (char*) malloc(i);
1489 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1490 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1492 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1493 clientResources, XtNumber(clientResources),
1496 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1497 static char buf[MSG_SIZ];
1498 EscapeExpand(buf, appData.firstInitString);
1499 appData.firstInitString = strdup(buf);
1500 EscapeExpand(buf, appData.secondInitString);
1501 appData.secondInitString = strdup(buf);
1502 EscapeExpand(buf, appData.firstComputerString);
1503 appData.firstComputerString = strdup(buf);
1504 EscapeExpand(buf, appData.secondComputerString);
1505 appData.secondComputerString = strdup(buf);
1508 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1511 if (chdir(chessDir) != 0) {
1512 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1518 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1519 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1520 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1521 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1524 setbuf(debugFP, NULL);
1528 if (appData.debugMode) {
1529 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1533 /* [HGM,HR] make sure board size is acceptable */
1534 if(appData.NrFiles > BOARD_FILES ||
1535 appData.NrRanks > BOARD_RANKS )
1536 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1539 /* This feature does not work; animation needs a rewrite */
1540 appData.highlightDragging = FALSE;
1544 xDisplay = XtDisplay(shellWidget);
1545 xScreen = DefaultScreen(xDisplay);
1546 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1548 gameInfo.variant = StringToVariant(appData.variant);
1549 InitPosition(FALSE);
1552 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1554 if (isdigit(appData.boardSize[0])) {
1555 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1556 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1557 &fontPxlSize, &smallLayout, &tinyLayout);
1559 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1560 programName, appData.boardSize);
1564 /* Find some defaults; use the nearest known size */
1565 SizeDefaults *szd, *nearest;
1566 int distance = 99999;
1567 nearest = szd = sizeDefaults;
1568 while (szd->name != NULL) {
1569 if (abs(szd->squareSize - squareSize) < distance) {
1571 distance = abs(szd->squareSize - squareSize);
1572 if (distance == 0) break;
1576 if (i < 2) lineGap = nearest->lineGap;
1577 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1578 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1579 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1580 if (i < 6) smallLayout = nearest->smallLayout;
1581 if (i < 7) tinyLayout = nearest->tinyLayout;
1584 SizeDefaults *szd = sizeDefaults;
1585 if (*appData.boardSize == NULLCHAR) {
1586 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1587 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1590 if (szd->name == NULL) szd--;
1591 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1593 while (szd->name != NULL &&
1594 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1595 if (szd->name == NULL) {
1596 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1597 programName, appData.boardSize);
1601 squareSize = szd->squareSize;
1602 lineGap = szd->lineGap;
1603 clockFontPxlSize = szd->clockFontPxlSize;
1604 coordFontPxlSize = szd->coordFontPxlSize;
1605 fontPxlSize = szd->fontPxlSize;
1606 smallLayout = szd->smallLayout;
1607 tinyLayout = szd->tinyLayout;
1608 // [HGM] font: use defaults from settings file if available and not overruled
1610 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1611 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1612 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1613 appData.font = fontTable[MESSAGE_FONT][squareSize];
1614 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1615 appData.coordFont = fontTable[COORD_FONT][squareSize];
1617 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1618 if (strlen(appData.pixmapDirectory) > 0) {
1619 p = ExpandPathName(appData.pixmapDirectory);
1621 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1622 appData.pixmapDirectory);
1625 if (appData.debugMode) {
1626 fprintf(stderr, _("\
1627 XBoard square size (hint): %d\n\
1628 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1630 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1631 if (appData.debugMode) {
1632 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1635 defaultLineGap = lineGap;
1636 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1638 /* [HR] height treated separately (hacked) */
1639 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1640 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1641 if (appData.showJail == 1) {
1642 /* Jail on top and bottom */
1643 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1644 XtSetArg(boardArgs[2], XtNheight,
1645 boardHeight + 2*(lineGap + squareSize));
1646 } else if (appData.showJail == 2) {
1648 XtSetArg(boardArgs[1], XtNwidth,
1649 boardWidth + 2*(lineGap + squareSize));
1650 XtSetArg(boardArgs[2], XtNheight, boardHeight);
1653 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1654 XtSetArg(boardArgs[2], XtNheight, boardHeight);
1658 * Determine what fonts to use.
1661 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1662 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1663 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1664 fontSet = CreateFontSet(appData.font);
1665 clockFontSet = CreateFontSet(appData.clockFont);
1667 /* For the coordFont, use the 0th font of the fontset. */
1668 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1669 XFontStruct **font_struct_list;
1670 XFontSetExtents *fontSize;
1671 char **font_name_list;
1672 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1673 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1674 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1675 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1676 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1679 appData.font = FindFont(appData.font, fontPxlSize);
1680 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1681 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1682 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1683 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1684 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1685 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1687 countFontID = coordFontID; // [HGM] holdings
1688 countFontStruct = coordFontStruct;
1690 xdb = XtDatabase(xDisplay);
1692 XrmPutLineResource(&xdb, "*international: True");
1693 vTo.size = sizeof(XFontSet);
1694 vTo.addr = (XtPointer) &fontSet;
1695 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1697 XrmPutStringResource(&xdb, "*font", appData.font);
1701 * Detect if there are not enough colors available and adapt.
1703 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1704 appData.monoMode = True;
1707 forceMono = MakeColors();
1710 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1712 appData.monoMode = True;
1715 if (appData.lowTimeWarning && !appData.monoMode) {
1716 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1717 vFrom.size = strlen(appData.lowTimeWarningColor);
1718 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1719 if (vTo.addr == NULL)
1720 appData.monoMode = True;
1722 lowTimeWarningColor = *(Pixel *) vTo.addr;
1725 if (appData.monoMode && appData.debugMode) {
1726 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1727 (unsigned long) XWhitePixel(xDisplay, xScreen),
1728 (unsigned long) XBlackPixel(xDisplay, xScreen));
1731 ParseIcsTextColors();
1732 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
1733 textColors[ColorNone].attr = 0;
1735 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1741 layoutName = "tinyLayout";
1742 } else if (smallLayout) {
1743 layoutName = "smallLayout";
1745 layoutName = "normalLayout";
1747 /* Outer layoutWidget is there only to provide a name for use in
1748 resources that depend on the layout style */
1750 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
1751 layoutArgs, XtNumber(layoutArgs));
1753 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
1754 formArgs, XtNumber(formArgs));
1755 XtSetArg(args[0], XtNdefaultDistance, &sep);
1756 XtGetValues(formWidget, args, 1);
1759 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
1760 XtSetArg(args[0], XtNtop, XtChainTop);
1761 XtSetArg(args[1], XtNbottom, XtChainTop);
1762 XtSetArg(args[2], XtNright, XtChainLeft);
1763 XtSetValues(menuBarWidget, args, 3);
1765 widgetList[j++] = whiteTimerWidget =
1766 XtCreateWidget("whiteTime", labelWidgetClass,
1767 formWidget, timerArgs, XtNumber(timerArgs));
1769 XtSetArg(args[0], XtNfontSet, clockFontSet);
1771 XtSetArg(args[0], XtNfont, clockFontStruct);
1773 XtSetArg(args[1], XtNtop, XtChainTop);
1774 XtSetArg(args[2], XtNbottom, XtChainTop);
1775 XtSetValues(whiteTimerWidget, args, 3);
1777 widgetList[j++] = blackTimerWidget =
1778 XtCreateWidget("blackTime", labelWidgetClass,
1779 formWidget, timerArgs, XtNumber(timerArgs));
1781 XtSetArg(args[0], XtNfontSet, clockFontSet);
1783 XtSetArg(args[0], XtNfont, clockFontStruct);
1785 XtSetArg(args[1], XtNtop, XtChainTop);
1786 XtSetArg(args[2], XtNbottom, XtChainTop);
1787 XtSetValues(blackTimerWidget, args, 3);
1789 if (appData.titleInWindow) {
1790 widgetList[j++] = titleWidget =
1791 XtCreateWidget("title", labelWidgetClass, formWidget,
1792 titleArgs, XtNumber(titleArgs));
1793 XtSetArg(args[0], XtNtop, XtChainTop);
1794 XtSetArg(args[1], XtNbottom, XtChainTop);
1795 XtSetValues(titleWidget, args, 2);
1798 if (appData.showButtonBar) {
1799 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
1800 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
1801 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
1802 XtSetArg(args[2], XtNtop, XtChainTop);
1803 XtSetArg(args[3], XtNbottom, XtChainTop);
1804 XtSetValues(buttonBarWidget, args, 4);
1807 widgetList[j++] = messageWidget =
1808 XtCreateWidget("message", labelWidgetClass, formWidget,
1809 messageArgs, XtNumber(messageArgs));
1810 XtSetArg(args[0], XtNtop, XtChainTop);
1811 XtSetArg(args[1], XtNbottom, XtChainTop);
1812 XtSetValues(messageWidget, args, 2);
1814 widgetList[j++] = boardWidget =
1815 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
1816 XtNumber(boardArgs));
1818 XtManageChildren(widgetList, j);
1820 timerWidth = (boardWidth - sep) / 2;
1821 XtSetArg(args[0], XtNwidth, timerWidth);
1822 XtSetValues(whiteTimerWidget, args, 1);
1823 XtSetValues(blackTimerWidget, args, 1);
1825 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1826 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1827 XtGetValues(whiteTimerWidget, args, 2);
1829 if (appData.showButtonBar) {
1830 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1831 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1832 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
1836 * formWidget uses these constraints but they are stored
1840 XtSetArg(args[i], XtNfromHoriz, 0); i++;
1841 XtSetValues(menuBarWidget, args, i);
1842 if (appData.titleInWindow) {
1845 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1846 XtSetValues(whiteTimerWidget, args, i);
1848 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1849 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1850 XtSetValues(blackTimerWidget, args, i);
1852 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1853 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
1854 XtSetValues(titleWidget, args, i);
1856 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1857 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1858 XtSetValues(messageWidget, args, i);
1859 if (appData.showButtonBar) {
1861 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1862 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1863 XtSetValues(buttonBarWidget, args, i);
1867 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1868 XtSetValues(whiteTimerWidget, args, i);
1870 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1871 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1872 XtSetValues(blackTimerWidget, args, i);
1874 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
1875 XtSetValues(titleWidget, args, i);
1877 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1878 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1879 XtSetValues(messageWidget, args, i);
1880 if (appData.showButtonBar) {
1882 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1883 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1884 XtSetValues(buttonBarWidget, args, i);
1889 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1890 XtSetValues(whiteTimerWidget, args, i);
1892 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1893 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1894 XtSetValues(blackTimerWidget, args, i);
1896 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1897 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1898 XtSetValues(messageWidget, args, i);
1899 if (appData.showButtonBar) {
1901 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1902 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1903 XtSetValues(buttonBarWidget, args, i);
1907 XtSetArg(args[0], XtNfromVert, messageWidget);
1908 XtSetArg(args[1], XtNtop, XtChainTop);
1909 XtSetArg(args[2], XtNbottom, XtChainBottom);
1910 XtSetArg(args[3], XtNleft, XtChainLeft);
1911 XtSetArg(args[4], XtNright, XtChainRight);
1912 XtSetValues(boardWidget, args, 5);
1914 XtRealizeWidget(shellWidget);
1917 XtSetArg(args[0], XtNx, wpMain.x);
1918 XtSetArg(args[1], XtNy, wpMain.y);
1919 XtSetValues(shellWidget, args, 2);
1923 * Correct the width of the message and title widgets.
1924 * It is not known why some systems need the extra fudge term.
1925 * The value "2" is probably larger than needed.
1927 XawFormDoLayout(formWidget, False);
1929 #define WIDTH_FUDGE 2
1931 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1932 XtSetArg(args[i], XtNheight, &h); i++;
1933 XtGetValues(messageWidget, args, i);
1934 if (appData.showButtonBar) {
1936 XtSetArg(args[i], XtNwidth, &w); i++;
1937 XtGetValues(buttonBarWidget, args, i);
1938 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1940 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
1943 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1944 if (gres != XtGeometryYes && appData.debugMode) {
1945 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1946 programName, gres, w, h, wr, hr);
1949 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
1950 /* The size used for the child widget in layout lags one resize behind
1951 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
1953 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1954 if (gres != XtGeometryYes && appData.debugMode) {
1955 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1956 programName, gres, w, h, wr, hr);
1959 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
1960 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
1961 XtSetArg(args[1], XtNright, XtChainRight);
1962 XtSetValues(messageWidget, args, 2);
1964 if (appData.titleInWindow) {
1966 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1967 XtSetArg(args[i], XtNheight, &h); i++;
1968 XtGetValues(titleWidget, args, i);
1970 w = boardWidth - 2*bor;
1972 XtSetArg(args[0], XtNwidth, &w);
1973 XtGetValues(menuBarWidget, args, 1);
1974 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1977 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1978 if (gres != XtGeometryYes && appData.debugMode) {
1980 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1981 programName, gres, w, h, wr, hr);
1984 XawFormDoLayout(formWidget, True);
1986 xBoardWindow = XtWindow(boardWidget);
1988 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1989 // not need to go into InitDrawingSizes().
1993 * Create X checkmark bitmap and initialize option menu checks.
1995 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1996 checkmark_bits, checkmark_width, checkmark_height);
2002 ReadBitmap(&wIconPixmap, "icon_white.bm",
2003 icon_white_bits, icon_white_width, icon_white_height);
2004 ReadBitmap(&bIconPixmap, "icon_black.bm",
2005 icon_black_bits, icon_black_width, icon_black_height);
2006 iconPixmap = wIconPixmap;
2008 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2009 XtSetValues(shellWidget, args, i);
2012 * Create a cursor for the board widget.
2014 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2015 XChangeWindowAttributes(xDisplay, xBoardWindow,
2016 CWCursor, &window_attributes);
2019 * Inhibit shell resizing.
2021 shellArgs[0].value = (XtArgVal) &w;
2022 shellArgs[1].value = (XtArgVal) &h;
2023 XtGetValues(shellWidget, shellArgs, 2);
2024 shellArgs[4].value = shellArgs[2].value = w;
2025 shellArgs[5].value = shellArgs[3].value = h;
2026 XtSetValues(shellWidget, &shellArgs[2], 4);
2027 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2028 marginH = h - boardHeight;
2030 CatchDeleteWindow(shellWidget, "QuitProc");
2038 if (appData.animate || appData.animateDragging)
2041 XtAugmentTranslations(formWidget,
2042 XtParseTranslationTable(globalTranslations));
2043 XtAugmentTranslations(boardWidget,
2044 XtParseTranslationTable(boardTranslations));
2045 XtAugmentTranslations(whiteTimerWidget,
2046 XtParseTranslationTable(whiteTranslations));
2047 XtAugmentTranslations(blackTimerWidget,
2048 XtParseTranslationTable(blackTranslations));
2050 /* Why is the following needed on some versions of X instead
2051 * of a translation? */
2052 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2053 (XtEventHandler) EventProc, NULL);
2055 XtAddEventHandler(formWidget, KeyPressMask, False,
2056 (XtEventHandler) MoveTypeInProc, NULL);
2057 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
2058 (XtEventHandler) EventProc, NULL);
2060 /* [AS] Restore layout */
2061 if( wpMoveHistory.visible ) {
2065 if( wpEvalGraph.visible )
2070 if( wpEngineOutput.visible ) {
2071 EngineOutputPopUp();
2076 if (errorExitStatus == -1) {
2077 if (appData.icsActive) {
2078 /* We now wait until we see "login:" from the ICS before
2079 sending the logon script (problems with timestamp otherwise) */
2080 /*ICSInitScript();*/
2081 if (appData.icsInputBox) ICSInputBoxPopUp();
2085 signal(SIGWINCH, TermSizeSigHandler);
2087 signal(SIGINT, IntSigHandler);
2088 signal(SIGTERM, IntSigHandler);
2089 if (*appData.cmailGameName != NULLCHAR) {
2090 signal(SIGUSR1, CmailSigHandler);
2094 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2096 // XtSetKeyboardFocus(shellWidget, formWidget);
2097 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2099 XtAppMainLoop(appContext);
2100 if (appData.debugMode) fclose(debugFP); // [DM] debug
2104 static Boolean noEcho;
2109 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2110 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2112 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2113 unlink(gameCopyFilename);
2114 unlink(gamePasteFilename);
2115 if(noEcho) EchoOn();
2119 TermSizeSigHandler (int sig)
2125 IntSigHandler (int sig)
2131 CmailSigHandler (int sig)
2136 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2138 /* Activate call-back function CmailSigHandlerCallBack() */
2139 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2141 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2145 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
2148 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2150 /**** end signal code ****/
2156 /* try to open the icsLogon script, either in the location given
2157 * or in the users HOME directory
2164 f = fopen(appData.icsLogon, "r");
2167 homedir = getenv("HOME");
2168 if (homedir != NULL)
2170 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2171 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2172 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2173 f = fopen(buf, "r");
2178 ProcessICSInitScript(f);
2180 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2193 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
2194 #define HISTORY_SIZE 64
2195 static char *history[HISTORY_SIZE];
2196 int histIn = 0, histP = 0;
2199 SaveInHistory (char *cmd)
2201 if (history[histIn] != NULL) {
2202 free(history[histIn]);
2203 history[histIn] = NULL;
2205 if (*cmd == NULLCHAR) return;
2206 history[histIn] = StrSave(cmd);
2207 histIn = (histIn + 1) % HISTORY_SIZE;
2208 if (history[histIn] != NULL) {
2209 free(history[histIn]);
2210 history[histIn] = NULL;
2216 PrevInHistory (char *cmd)
2219 if (histP == histIn) {
2220 if (history[histIn] != NULL) free(history[histIn]);
2221 history[histIn] = StrSave(cmd);
2223 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
2224 if (newhp == histIn || history[newhp] == NULL) return NULL;
2226 return history[histP];
2232 if (histP == histIn) return NULL;
2233 histP = (histP + 1) % HISTORY_SIZE;
2234 return history[histP];
2236 // end of borrowed code
2238 #define Abs(n) ((n)<0 ? -(n) : (n))
2242 InsertPxlSize (char *pattern, int targetPxlSize)
2244 char *base_fnt_lst, strInt[12], *p, *q;
2245 int alternatives, i, len, strIntLen;
2248 * Replace the "*" (if present) in the pixel-size slot of each
2249 * alternative with the targetPxlSize.
2253 while ((p = strchr(p, ',')) != NULL) {
2257 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
2258 strIntLen = strlen(strInt);
2259 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
2263 while (alternatives--) {
2264 char *comma = strchr(p, ',');
2265 for (i=0; i<14; i++) {
2266 char *hyphen = strchr(p, '-');
2268 if (comma && hyphen > comma) break;
2269 len = hyphen + 1 - p;
2270 if (i == 7 && *p == '*' && len == 2) {
2272 memcpy(q, strInt, strIntLen);
2282 len = comma + 1 - p;
2289 return base_fnt_lst;
2293 CreateFontSet (char *base_fnt_lst)
2296 char **missing_list;
2300 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
2301 &missing_list, &missing_count, &def_string);
2302 if (appData.debugMode) {
2304 XFontStruct **font_struct_list;
2305 char **font_name_list;
2306 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
2308 fprintf(debugFP, " got list %s, locale %s\n",
2309 XBaseFontNameListOfFontSet(fntSet),
2310 XLocaleOfFontSet(fntSet));
2311 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
2312 for (i = 0; i < count; i++) {
2313 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
2316 for (i = 0; i < missing_count; i++) {
2317 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
2320 if (fntSet == NULL) {
2321 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
2326 #else // not ENABLE_NLS
2328 * Find a font that matches "pattern" that is as close as
2329 * possible to the targetPxlSize. Prefer fonts that are k
2330 * pixels smaller to fonts that are k pixels larger. The
2331 * pattern must be in the X Consortium standard format,
2332 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2333 * The return value should be freed with XtFree when no
2337 FindFont (char *pattern, int targetPxlSize)
2339 char **fonts, *p, *best, *scalable, *scalableTail;
2340 int i, j, nfonts, minerr, err, pxlSize;
2342 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2344 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2345 programName, pattern);
2352 for (i=0; i<nfonts; i++) {
2355 if (*p != '-') continue;
2357 if (*p == NULLCHAR) break;
2358 if (*p++ == '-') j++;
2360 if (j < 7) continue;
2363 scalable = fonts[i];
2366 err = pxlSize - targetPxlSize;
2367 if (Abs(err) < Abs(minerr) ||
2368 (minerr > 0 && err < 0 && -err == minerr)) {
2374 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2375 /* If the error is too big and there is a scalable font,
2376 use the scalable font. */
2377 int headlen = scalableTail - scalable;
2378 p = (char *) XtMalloc(strlen(scalable) + 10);
2379 while (isdigit(*scalableTail)) scalableTail++;
2380 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2382 p = (char *) XtMalloc(strlen(best) + 2);
2383 safeStrCpy(p, best, strlen(best)+1 );
2385 if (appData.debugMode) {
2386 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2387 pattern, targetPxlSize, p);
2389 XFreeFontNames(fonts);
2396 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
2397 // must be called before all non-first callse to CreateGCs()
2398 XtReleaseGC(shellWidget, highlineGC);
2399 XtReleaseGC(shellWidget, lightSquareGC);
2400 XtReleaseGC(shellWidget, darkSquareGC);
2401 XtReleaseGC(shellWidget, lineGC);
2402 if (appData.monoMode) {
2403 if (DefaultDepth(xDisplay, xScreen) == 1) {
2404 XtReleaseGC(shellWidget, wbPieceGC);
2406 XtReleaseGC(shellWidget, bwPieceGC);
2409 XtReleaseGC(shellWidget, prelineGC);
2410 XtReleaseGC(shellWidget, jailSquareGC);
2411 XtReleaseGC(shellWidget, wdPieceGC);
2412 XtReleaseGC(shellWidget, wlPieceGC);
2413 XtReleaseGC(shellWidget, wjPieceGC);
2414 XtReleaseGC(shellWidget, bdPieceGC);
2415 XtReleaseGC(shellWidget, blPieceGC);
2416 XtReleaseGC(shellWidget, bjPieceGC);
2421 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
2423 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2424 | GCBackground | GCFunction | GCPlaneMask;
2425 gc_values->foreground = foreground;
2426 gc_values->background = background;
2427 return XtGetGC(shellWidget, value_mask, gc_values);
2431 CreateGCs (int redo)
2433 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2434 | GCBackground | GCFunction | GCPlaneMask;
2435 XGCValues gc_values;
2437 Pixel white = XWhitePixel(xDisplay, xScreen);
2438 Pixel black = XBlackPixel(xDisplay, xScreen);
2440 gc_values.plane_mask = AllPlanes;
2441 gc_values.line_width = lineGap;
2442 gc_values.line_style = LineSolid;
2443 gc_values.function = GXcopy;
2446 DeleteGCs(); // called a second time; clean up old GCs first
2447 } else { // [HGM] grid and font GCs created on first call only
2448 coordGC = CreateOneGC(&gc_values, black, white);
2449 XSetFont(xDisplay, coordGC, coordFontID);
2451 // [HGM] make font for holdings counts (white on black)
2452 countGC = CreateOneGC(&gc_values, white, black);
2453 XSetFont(xDisplay, countGC, countFontID);
2455 lineGC = CreateOneGC(&gc_values, black, black);
2457 if (appData.monoMode) {
2459 highlineGC = CreateOneGC(&gc_values, white, white);
2460 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
2461 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
2463 if (DefaultDepth(xDisplay, xScreen) == 1) {
2464 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2465 gc_values.function = GXcopyInverted;
2466 copyInvertedGC = CreateOneGC(&gc_values, black, white);
2467 gc_values.function = GXcopy;
2468 if (XBlackPixel(xDisplay, xScreen) == 1) {
2469 bwPieceGC = darkSquareGC;
2470 wbPieceGC = copyInvertedGC;
2472 bwPieceGC = copyInvertedGC;
2473 wbPieceGC = lightSquareGC;
2478 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
2479 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
2480 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
2481 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
2482 jailSquareGC = CreateOneGC(&gc_values, jailSquareColor, jailSquareColor);
2483 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
2484 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
2485 wjPieceGC = CreateOneGC(&gc_values, whitePieceColor, jailSquareColor);
2486 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
2487 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
2488 bjPieceGC = CreateOneGC(&gc_values, blackPieceColor, jailSquareColor);
2493 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
2501 fp = fopen(filename, "rb");
2503 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
2510 for (y=0; y<h; ++y) {
2511 for (x=0; x<h; ++x) {
2516 XPutPixel(xim, x, y, blackPieceColor);
2518 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2521 XPutPixel(xim, x, y, darkSquareColor);
2523 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2526 XPutPixel(xim, x, y, whitePieceColor);
2528 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2531 XPutPixel(xim, x, y, lightSquareColor);
2533 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2541 /* create Pixmap of piece */
2542 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2544 XPutImage(xDisplay, *dest, lightSquareGC, xim,
2547 /* create Pixmap of clipmask
2548 Note: We assume the white/black pieces have the same
2549 outline, so we make only 6 masks. This is okay
2550 since the XPM clipmask routines do the same. */
2552 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2554 XPutImage(xDisplay, temp, lightSquareGC, xmask,
2557 /* now create the 1-bit version */
2558 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2561 values.foreground = 1;
2562 values.background = 0;
2564 /* Don't use XtGetGC, not read only */
2565 maskGC = XCreateGC(xDisplay, *mask,
2566 GCForeground | GCBackground, &values);
2567 XCopyPlane(xDisplay, temp, *mask, maskGC,
2568 0, 0, squareSize, squareSize, 0, 0, 1);
2569 XFreePixmap(xDisplay, temp);
2574 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
2582 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
2587 /* The XSynchronize calls were copied from CreatePieces.
2588 Not sure if needed, but can't hurt */
2589 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2592 /* temp needed by loadXIM() */
2593 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2594 0, 0, ss, ss, AllPlanes, XYPixmap);
2596 if (strlen(appData.pixmapDirectory) == 0) {
2600 if (appData.monoMode) {
2601 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
2605 fprintf(stderr, _("\nLoading XIMs...\n"));
2607 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2608 fprintf(stderr, "%d", piece+1);
2609 for (kind=0; kind<4; kind++) {
2610 fprintf(stderr, ".");
2611 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2612 ExpandPathName(appData.pixmapDirectory),
2613 piece <= (int) WhiteKing ? "" : "w",
2614 pieceBitmapNames[piece],
2616 ximPieceBitmap[kind][piece] =
2617 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2618 0, 0, ss, ss, AllPlanes, XYPixmap);
2619 if (appData.debugMode)
2620 fprintf(stderr, _("(File:%s:) "), buf);
2621 loadXIM(ximPieceBitmap[kind][piece],
2623 &(xpmPieceBitmap2[kind][piece]),
2624 &(ximMaskPm2[piece]));
2625 if(piece <= (int)WhiteKing)
2626 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2628 fprintf(stderr," ");
2630 /* Load light and dark squares */
2631 /* If the LSQ and DSQ pieces don't exist, we will
2632 draw them with solid squares. */
2633 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2634 if (access(buf, 0) != 0) {
2638 fprintf(stderr, _("light square "));
2640 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2641 0, 0, ss, ss, AllPlanes, XYPixmap);
2642 if (appData.debugMode)
2643 fprintf(stderr, _("(File:%s:) "), buf);
2645 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2646 fprintf(stderr, _("dark square "));
2647 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2648 ExpandPathName(appData.pixmapDirectory), ss);
2649 if (appData.debugMode)
2650 fprintf(stderr, _("(File:%s:) "), buf);
2652 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2653 0, 0, ss, ss, AllPlanes, XYPixmap);
2654 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2655 xpmJailSquare = xpmLightSquare;
2657 fprintf(stderr, _("Done.\n"));
2659 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2662 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2666 CreateXPMBoard (char *s, int kind)
2670 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2671 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2672 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2678 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2679 // thisroutine has to be called t free the old piece pixmaps
2681 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2682 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2684 XFreePixmap(xDisplay, xpmLightSquare);
2685 XFreePixmap(xDisplay, xpmDarkSquare);
2694 u_int ss = squareSize;
2696 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2697 XpmColorSymbol symbols[4];
2698 static int redo = False;
2700 if(redo) FreeXPMPieces(); else redo = 1;
2702 /* The XSynchronize calls were copied from CreatePieces.
2703 Not sure if needed, but can't hurt */
2704 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2706 /* Setup translations so piece colors match square colors */
2707 symbols[0].name = "light_piece";
2708 symbols[0].value = appData.whitePieceColor;
2709 symbols[1].name = "dark_piece";
2710 symbols[1].value = appData.blackPieceColor;
2711 symbols[2].name = "light_square";
2712 symbols[2].value = appData.lightSquareColor;
2713 symbols[3].name = "dark_square";
2714 symbols[3].value = appData.darkSquareColor;
2716 attr.valuemask = XpmColorSymbols;
2717 attr.colorsymbols = symbols;
2718 attr.numsymbols = 4;
2720 if (appData.monoMode) {
2721 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2725 if (strlen(appData.pixmapDirectory) == 0) {
2726 XpmPieces* pieces = builtInXpms;
2729 while (pieces->size != squareSize && pieces->size) pieces++;
2730 if (!pieces->size) {
2731 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2734 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2735 for (kind=0; kind<4; kind++) {
2737 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2738 pieces->xpm[piece][kind],
2739 &(xpmPieceBitmap2[kind][piece]),
2740 NULL, &attr)) != 0) {
2741 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2745 if(piece <= (int) WhiteKing)
2746 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2750 xpmJailSquare = xpmLightSquare;
2754 fprintf(stderr, _("\nLoading XPMs...\n"));
2757 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2758 fprintf(stderr, "%d ", piece+1);
2759 for (kind=0; kind<4; kind++) {
2760 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2761 ExpandPathName(appData.pixmapDirectory),
2762 piece > (int) WhiteKing ? "w" : "",
2763 pieceBitmapNames[piece],
2765 if (appData.debugMode) {
2766 fprintf(stderr, _("(File:%s:) "), buf);
2768 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2769 &(xpmPieceBitmap2[kind][piece]),
2770 NULL, &attr)) != 0) {
2771 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2772 // [HGM] missing: read of unorthodox piece failed; substitute King.
2773 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2774 ExpandPathName(appData.pixmapDirectory),
2776 if (appData.debugMode) {
2777 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2779 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2780 &(xpmPieceBitmap2[kind][piece]),
2784 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2789 if(piece <= (int) WhiteKing)
2790 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2793 /* Load light and dark squares */
2794 /* If the LSQ and DSQ pieces don't exist, we will
2795 draw them with solid squares. */
2796 fprintf(stderr, _("light square "));
2797 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2798 if (access(buf, 0) != 0) {
2802 if (appData.debugMode)
2803 fprintf(stderr, _("(File:%s:) "), buf);
2805 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2806 &xpmLightSquare, NULL, &attr)) != 0) {
2807 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2810 fprintf(stderr, _("dark square "));
2811 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2812 ExpandPathName(appData.pixmapDirectory), ss);
2813 if (appData.debugMode) {
2814 fprintf(stderr, _("(File:%s:) "), buf);
2816 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2817 &xpmDarkSquare, NULL, &attr)) != 0) {
2818 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2822 xpmJailSquare = xpmLightSquare;
2823 fprintf(stderr, _("Done.\n"));
2825 oldVariant = -1; // kludge to force re-makig of animation masks
2826 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2829 #endif /* HAVE_LIBXPM */
2832 /* No built-in bitmaps */
2837 u_int ss = squareSize;
2839 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2842 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2843 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2844 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2845 pieceBitmapNames[piece],
2846 ss, kind == SOLID ? 's' : 'o');
2847 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2848 if(piece <= (int)WhiteKing)
2849 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2853 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2857 /* With built-in bitmaps */
2861 BuiltInBits* bib = builtInBits;
2864 u_int ss = squareSize;
2866 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2869 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2871 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2872 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2873 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2874 pieceBitmapNames[piece],
2875 ss, kind == SOLID ? 's' : 'o');
2876 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2877 bib->bits[kind][piece], ss, ss);
2878 if(piece <= (int)WhiteKing)
2879 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2883 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2889 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2894 char msg[MSG_SIZ], fullname[MSG_SIZ];
2896 if (*appData.bitmapDirectory != NULLCHAR) {
2897 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2898 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2899 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2900 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2901 &w, &h, pm, &x_hot, &y_hot);
2902 fprintf(stderr, "load %s\n", name);
2903 if (errcode != BitmapSuccess) {
2905 case BitmapOpenFailed:
2906 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2908 case BitmapFileInvalid:
2909 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2911 case BitmapNoMemory:
2912 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2916 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2920 fprintf(stderr, _("%s: %s...using built-in\n"),
2922 } else if (w != wreq || h != hreq) {
2924 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2925 programName, fullname, w, h, wreq, hreq);
2931 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2941 if (lineGap == 0) return;
2943 /* [HR] Split this into 2 loops for non-square boards. */
2945 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2946 gridSegments[i].x1 = 0;
2947 gridSegments[i].x2 =
2948 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2949 gridSegments[i].y1 = gridSegments[i].y2
2950 = lineGap / 2 + (i * (squareSize + lineGap));
2953 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2954 gridSegments[j + i].y1 = 0;
2955 gridSegments[j + i].y2 =
2956 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2957 gridSegments[j + i].x1 = gridSegments[j + i].x2
2958 = lineGap / 2 + (j * (squareSize + lineGap));
2962 int nrOfMenuItems = 7;
2963 Widget menuWidget[150];
2964 MenuListItem menuItemList[150] = {
2965 { "LoadNextGameProc", LoadNextGameProc },
2966 { "LoadPrevGameProc", LoadPrevGameProc },
2967 { "ReloadGameProc", ReloadGameProc },
2968 { "ReloadPositionProc", ReloadPositionProc },
2969 #ifndef OPTIONSDIALOG
2970 { "AlwaysQueenProc", AlwaysQueenProc },
2971 { "AnimateDraggingProc", AnimateDraggingProc },
2972 { "AnimateMovingProc", AnimateMovingProc },
2973 { "AutoflagProc", AutoflagProc },
2974 { "AutoflipProc", AutoflipProc },
2975 { "BlindfoldProc", BlindfoldProc },
2976 { "FlashMovesProc", FlashMovesProc },
2978 { "HighlightDraggingProc", HighlightDraggingProc },
2980 { "HighlightLastMoveProc", HighlightLastMoveProc },
2981 // { "IcsAlarmProc", IcsAlarmProc },
2982 { "MoveSoundProc", MoveSoundProc },
2983 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
2984 { "PopupExitMessageProc", PopupExitMessageProc },
2985 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
2986 // { "PremoveProc", PremoveProc },
2987 { "ShowCoordsProc", ShowCoordsProc },
2988 { "ShowThinkingProc", ShowThinkingProc },
2989 { "HideThinkingProc", HideThinkingProc },
2990 { "TestLegalityProc", TestLegalityProc },
2992 { "AboutGameProc", AboutGameEvent },
2993 { "DebugProc", DebugProc },
2994 { "NothingProc", NothingProc },
2999 MarkMenuItem (char *menuRef, int state)
3001 int nr = MenuToNumber(menuRef);
3004 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
3005 XtSetValues(menuWidget[nr], args, 1);
3010 EnableMenuItem (char *menuRef, int state)
3012 int nr = MenuToNumber(menuRef);
3013 if(nr >= 0) XtSetSensitive(menuWidget[nr], state);
3017 EnableButtonBar (int state)
3019 XtSetSensitive(buttonBarWidget, state);
3024 SetMenuEnables (Enables *enab)
3026 while (enab->name != NULL) {
3027 EnableMenuItem(enab->name, enab->value);
3033 Equal(char *p, char *s)
3034 { // compare strings skipping spaces in second
3036 if(*s == ' ') { s++; continue; }
3037 if(*s++ != *p++) return 0;
3043 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3044 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
3046 if(*nprms == 0) return;
3047 for(i=0; menuItemList[i].name; i++) {
3048 if(Equal(prms[0], menuItemList[i].name)) {
3049 (menuItemList[i].proc) ();
3056 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
3058 MenuProc *proc = (MenuProc *) addr;
3064 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
3066 RecentEngineEvent((int) (intptr_t) addr);
3069 // some stuff that must remain in front-end
3070 static Widget mainBar, currentMenu;
3071 static int wtot, nr = 0, widths[10];
3074 AppendMenuItem (char *text, char *name, MenuProc *action)
3081 XtSetArg(args[j], XtNleftMargin, 20); j++;
3082 XtSetArg(args[j], XtNrightMargin, 20); j++;
3084 if (strcmp(text, "----") == 0) {
3085 entry = XtCreateManagedWidget(text, smeLineObjectClass,
3086 currentMenu, args, j);
3088 XtSetArg(args[j], XtNlabel, XtNewString(_(text)));
3089 entry = XtCreateManagedWidget(name, smeBSBObjectClass,
3090 currentMenu, args, j+1);
3091 XtAddCallback(entry, XtNcallback,
3092 (XtCallbackProc) (strcmp(name, "recent") ? MenuBarSelect : MenuEngineSelect),
3094 menuWidget[nrOfMenuItems] = entry;
3099 CreateMenuButton (char *name, Menu *mb)
3100 { // create menu button on main bar, and shell for pull-down list
3106 XtSetArg(args[j], XtNmenuName, XtNewString(name)); j++;
3107 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3108 XtSetArg(args[j], XtNborderWidth, 0); j++;
3109 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3111 currentMenu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3114 XtSetArg(args[j], XtNwidth, &w); j++;
3115 XtGetValues(mb->subMenu, args, j);
3116 wtot += mb->textWidth = widths[nr++] = w;
3120 CreateMenuBar (Menu *mb, int boardWidth)
3124 char menuName[MSG_SIZ];
3128 // create bar itself
3130 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3131 XtSetArg(args[j], XtNvSpace, 0); j++;
3132 XtSetArg(args[j], XtNborderWidth, 0); j++;
3133 mainBar = XtCreateWidget("menuBar", boxWidgetClass,
3134 formWidget, args, j);
3136 CreateMainMenus(mb); // put menus in bar according to description in back-end
3138 // size buttons to make menu bar fit, clipping menu names where necessary
3139 while(wtot > boardWidth - 40) {
3141 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
3145 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
3147 XtSetArg(args[j], XtNwidth, widths[i]); j++;
3148 XtSetValues(ma[i].subMenu, args, j);
3155 CreateButtonBar (MenuItem *mi)
3158 Widget button, buttonBar;
3162 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3164 XtSetArg(args[j], XtNhSpace, 0); j++;
3166 XtSetArg(args[j], XtNborderWidth, 0); j++;
3167 XtSetArg(args[j], XtNvSpace, 0); j++;
3168 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3169 formWidget, args, j);
3171 while (mi->string != NULL) {
3174 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3175 XtSetArg(args[j], XtNborderWidth, 0); j++;
3177 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3178 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3179 buttonBar, args, j);
3180 XtAddCallback(button, XtNcallback,
3181 (XtCallbackProc) MenuBarSelect,
3182 (caddr_t) mi->proc);
3189 CreatePieceMenu (char *name, int color)
3194 ChessSquare selection;
3196 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3197 boardWidget, args, 0);
3199 for (i = 0; i < PIECE_MENU_SIZE; i++) {
3200 String item = pieceMenuStrings[color][i];
3202 if (strcmp(item, "----") == 0) {
3203 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3206 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3207 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3209 selection = pieceMenuTranslation[color][i];
3210 XtAddCallback(entry, XtNcallback,
3211 (XtCallbackProc) PieceMenuSelect,
3212 (caddr_t) selection);
3213 if (selection == WhitePawn || selection == BlackPawn) {
3214 XtSetArg(args[0], XtNpopupOnEntry, entry);
3215 XtSetValues(menu, args, 1);
3228 ChessSquare selection;
3230 whitePieceMenu = CreatePieceMenu("menuW", 0);
3231 blackPieceMenu = CreatePieceMenu("menuB", 1);
3233 if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
3234 XtRegisterGrabAction(PieceMenuPopup, True,
3235 (unsigned)(ButtonPressMask|ButtonReleaseMask),
3236 GrabModeAsync, GrabModeAsync);
3238 XtSetArg(args[0], XtNlabel, _("Drop"));
3239 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3240 boardWidget, args, 1);
3241 for (i = 0; i < DROP_MENU_SIZE; i++) {
3242 String item = dropMenuStrings[i];
3244 if (strcmp(item, "----") == 0) {
3245 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3248 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3249 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3251 selection = dropMenuTranslation[i];
3252 XtAddCallback(entry, XtNcallback,
3253 (XtCallbackProc) DropMenuSelect,
3254 (caddr_t) selection);
3268 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3269 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3270 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3271 dmEnables[i].piece);
3272 XtSetSensitive(entry, p != NULL || !appData.testLegality
3273 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3274 && !appData.icsActive));
3276 while (p && *p++ == dmEnables[i].piece) count++;
3277 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3279 XtSetArg(args[j], XtNlabel, label); j++;
3280 XtSetValues(entry, args, j);
3285 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
3287 String whichMenu; int menuNr = -2;
3288 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3289 if (event->type == ButtonRelease)
3290 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3291 else if (event->type == ButtonPress)
3292 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3294 case 0: whichMenu = params[0]; break;
3295 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3297 case -1: if (errorUp) ErrorPopDown();
3300 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3304 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3306 if (pmFromX < 0 || pmFromY < 0) return;
3307 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3311 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3313 if (pmFromX < 0 || pmFromY < 0) return;
3314 DropMenuEvent(piece, pmFromX, pmFromY);
3318 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3320 shiftKey = prms[0][0] & 1;
3325 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3327 shiftKey = prms[0][0] & 1;
3333 do_flash_delay (unsigned long msec)
3339 DrawBorder (int x, int y, int type)
3343 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
3345 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3346 squareSize+lineGap, squareSize+lineGap);
3350 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
3352 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
3353 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
3355 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
3356 if(textureW[kind] < W*squareSize)
3357 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
3359 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
3360 if(textureH[kind] < H*squareSize)
3361 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
3363 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
3368 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
3369 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
3371 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
3372 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
3373 squareSize, squareSize, x*fac, y*fac);
3375 if (useImages && useImageSqs) {
3379 pm = xpmLightSquare;
3384 case 2: /* neutral */
3389 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3390 squareSize, squareSize, x*fac, y*fac);
3400 case 2: /* neutral */
3405 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
3410 I split out the routines to draw a piece so that I could
3411 make a generic flash routine.
3414 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3416 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3417 switch (square_color) {
3419 case 2: /* neutral */
3421 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3422 ? *pieceToOutline(piece)
3423 : *pieceToSolid(piece),
3424 dest, bwPieceGC, 0, 0,
3425 squareSize, squareSize, x, y);
3428 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3429 ? *pieceToSolid(piece)
3430 : *pieceToOutline(piece),
3431 dest, wbPieceGC, 0, 0,
3432 squareSize, squareSize, x, y);
3438 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3440 switch (square_color) {
3442 case 2: /* neutral */
3444 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3445 ? *pieceToOutline(piece)
3446 : *pieceToSolid(piece),
3447 dest, bwPieceGC, 0, 0,
3448 squareSize, squareSize, x, y, 1);
3451 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3452 ? *pieceToSolid(piece)
3453 : *pieceToOutline(piece),
3454 dest, wbPieceGC, 0, 0,
3455 squareSize, squareSize, x, y, 1);
3461 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3463 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
3464 switch (square_color) {
3466 XCopyPlane(xDisplay, *pieceToSolid(piece),
3467 dest, (int) piece < (int) BlackPawn
3468 ? wlPieceGC : blPieceGC, 0, 0,
3469 squareSize, squareSize, x, y, 1);
3472 XCopyPlane(xDisplay, *pieceToSolid(piece),
3473 dest, (int) piece < (int) BlackPawn
3474 ? wdPieceGC : bdPieceGC, 0, 0,
3475 squareSize, squareSize, x, y, 1);
3477 case 2: /* neutral */
3479 XCopyPlane(xDisplay, *pieceToSolid(piece),
3480 dest, (int) piece < (int) BlackPawn
3481 ? wjPieceGC : bjPieceGC, 0, 0,
3482 squareSize, squareSize, x, y, 1);
3488 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3490 int kind, p = piece;
3492 switch (square_color) {
3494 case 2: /* neutral */
3496 if ((int)piece < (int) BlackPawn) {
3504 if ((int)piece < (int) BlackPawn) {
3512 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
3513 if(useTexture & square_color+1) {
3514 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
3515 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
3516 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
3517 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
3518 XSetClipMask(xDisplay, wlPieceGC, None);
3519 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
3521 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3522 dest, wlPieceGC, 0, 0,
3523 squareSize, squareSize, x, y);
3526 typedef void (*DrawFunc)();
3531 if (appData.monoMode) {
3532 if (DefaultDepth(xDisplay, xScreen) == 1) {
3533 return monoDrawPiece_1bit;
3535 return monoDrawPiece;
3539 return colorDrawPieceImage;
3541 return colorDrawPiece;
3546 DrawDot (int marker, int x, int y, int r)
3548 if(appData.monoMode) {
3549 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
3550 x, y, r, r, 0, 64*360);
3551 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
3552 x, y, r, r, 0, 64*360);
3554 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
3555 x, y, r, r, 0, 64*360);
3559 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
3560 { // basic front-end board-draw function: takes care of everything that can be in square:
3561 // piece, background, coordinate/count, marker dot
3562 int direction, font_ascent, font_descent;
3563 XCharStruct overall;
3566 if (piece == EmptySquare) {
3567 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
3569 drawfunc = ChooseDrawFunc();
3570 drawfunc(piece, square_color, x, y, xBoardWindow);
3573 if(align) { // square carries inscription (coord or piece count)
3575 GC hGC = align < 3 ? coordGC : countGC;
3576 // first calculate where it goes
3577 XTextExtents(countFontStruct, string, 1, &direction,
3578 &font_ascent, &font_descent, &overall);
3580 xx += squareSize - overall.width - 2;
3581 yy += squareSize - font_descent - 1;
3582 } else if (align == 2) {
3583 xx += 2, yy += font_ascent + 1;
3584 } else if (align == 3) {
3585 xx += squareSize - overall.width - 2;
3586 yy += font_ascent + 1;
3587 } else if (align == 4) {
3588 xx += 2, yy += font_ascent + 1;
3591 if (appData.monoMode) {
3592 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3594 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3598 if(marker) { // print fat marker dot, if requested
3599 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
3604 FlashDelay (int flash_delay)
3606 XSync(xDisplay, False);
3607 if(flash_delay) do_flash_delay(flash_delay);
3611 Fraction (int x, int start, int stop)
3613 double f = ((double) x - start)/(stop - start);
3614 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
3618 static WindowPlacement wpNew;
3621 CoDrag (Widget sh, WindowPlacement *wp)
3624 int j=0, touch=0, fudge = 2;
3625 GetActualPlacement(sh, wp);
3626 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
3627 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
3628 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
3629 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
3630 if(!touch ) return; // only windows that touch co-move
3631 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
3632 int heightInc = wpNew.height - wpMain.height;
3633 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3634 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3635 wp->y += fracTop * heightInc;
3636 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
3637 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
3638 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
3639 int widthInc = wpNew.width - wpMain.width;
3640 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3641 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3642 wp->y += fracLeft * widthInc;
3643 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
3644 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
3646 wp->x += wpNew.x - wpMain.x;
3647 wp->y += wpNew.y - wpMain.y;
3648 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
3649 if(touch == 3) wp->y += wpNew.height - wpMain.height;
3650 XtSetArg(args[j], XtNx, wp->x); j++;
3651 XtSetArg(args[j], XtNy, wp->y); j++;
3652 XtSetValues(sh, args, j);
3655 static XtIntervalId delayedDragID = 0;
3660 GetActualPlacement(shellWidget, &wpNew);
3661 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
3662 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
3663 return; // false alarm
3664 if(EngineOutputIsUp()) CoDrag(engineOutputShell, &wpEngineOutput);
3665 if(MoveHistoryIsUp()) CoDrag(shells[7], &wpMoveHistory);
3666 if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
3667 if(GameListIsUp()) CoDrag(gameListShell, &wpGameList);
3669 DrawPosition(True, NULL);
3670 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
3677 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
3679 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
3682 /* Why is this needed on some versions of X? */
3684 EventProc (Widget widget, caddr_t unused, XEvent *event)
3686 if (!XtIsRealized(widget))
3688 switch (event->type) {
3689 case ConfigureNotify: // main window is being dragged: drag attached windows with it
3690 if(appData.useStickyWindows)
3691 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
3694 if (event->xexpose.count > 0) return; /* no clipping is done */
3695 DrawPosition(True, NULL);
3696 if(twoBoards) { // [HGM] dual: draw other board in other orientation
3697 flipView = !flipView; partnerUp = !partnerUp;
3698 DrawPosition(True, NULL);
3699 flipView = !flipView; partnerUp = !partnerUp;
3703 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
3710 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
3712 DrawSeekAxis (int x, int y, int xTo, int yTo)
3714 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
3718 DrawSeekBackground (int left, int top, int right, int bottom)
3720 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
3724 DrawSeekText (char *buf, int x, int y)
3726 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
3730 DrawSeekDot (int x, int y, int colorNr)
3732 int square = colorNr & 0x80;
3735 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
3737 XFillRectangle(xDisplay, xBoardWindow, color,
3738 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3740 XFillArc(xDisplay, xBoardWindow, color,
3741 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
3745 DrawGrid (int second)
3747 XDrawSegments(xDisplay, xBoardWindow, lineGC,
3748 second ? secondSegments : // [HGM] dual
3749 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
3754 * event handler for redrawing the board
3757 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3759 DrawPosition(True, NULL);
3764 * event handler for parsing user moves
3766 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
3767 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
3768 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
3769 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
3770 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
3771 // and at the end FinishMove() to perform the move after optional promotion popups.
3772 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
3774 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3776 if (w != boardWidget || errorExitStatus != -1) return;
3777 if(nprms) shiftKey = !strcmp(prms[0], "1");
3780 if (event->type == ButtonPress) {
3781 XtPopdown(promotionShell);
3782 XtDestroyWidget(promotionShell);
3783 promotionUp = False;
3791 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
3792 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
3793 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
3797 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
3799 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
3800 DragPieceMove(event->xmotion.x, event->xmotion.y);
3804 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3805 { // [HGM] pv: walk PV
3806 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3809 static int savedIndex; /* gross that this is global */
3812 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3815 XawTextPosition index, dummy;
3818 XawTextGetSelectionPos(w, &index, &dummy);
3819 XtSetArg(arg, XtNstring, &val);
3820 XtGetValues(w, &arg, 1);
3821 ReplaceComment(savedIndex, val);
3822 if(savedIndex != currentMove) ToNrEvent(savedIndex);
3823 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3827 EditCommentPopUp (int index, char *title, char *text)
3830 if (text == NULL) text = "";
3831 NewCommentPopup(title, text, index);
3840 extern Option boxOptions[];
3850 edit = boxOptions[0].handle;
3852 XtSetArg(args[j], XtNstring, &val); j++;
3853 XtGetValues(edit, args, j);
3855 SendMultiLineToICS(val);
3856 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
3857 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
3861 ICSInputBoxPopDown ()
3867 CommentPopUp (char *title, char *text)
3869 savedIndex = currentMove; // [HGM] vari
3870 NewCommentPopup(title, text, currentMove);
3879 static char *openName;
3885 (void) (*fileProc)(openFP, 0, openName);
3889 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
3891 fileProc = proc; /* I can't see a way not */
3892 fileOpenMode = openMode; /* to use globals here */
3893 { // [HGM] use file-selector dialog stolen from Ghostview
3894 int index; // this is not supported yet
3895 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
3896 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
3897 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
3898 ScheduleDelayedEvent(&DelayedLoad, 50);
3905 if (!filenameUp) return;
3906 XtPopdown(fileNameShell);
3907 XtDestroyWidget(fileNameShell);
3913 FileNameCallback (Widget w, XtPointer client_data, XtPointer call_data)
3918 XtSetArg(args[0], XtNlabel, &name);
3919 XtGetValues(w, args, 1);
3921 if (strcmp(name, _("cancel")) == 0) {
3926 FileNameAction(w, NULL, NULL, NULL);
3930 FileNameAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3938 name = XawDialogGetValueString(w = XtParent(w));
3940 if ((name != NULL) && (*name != NULLCHAR)) {
3941 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
3942 XtPopdown(w = XtParent(XtParent(w)));
3946 p = strrchr(buf, ' ');
3953 fullname = ExpandPathName(buf);
3955 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
3958 f = fopen(fullname, fileOpenMode);
3960 DisplayError(_("Failed to open file"), errno);
3962 (void) (*fileProc)(f, index, buf);
3969 XtPopdown(w = XtParent(XtParent(w)));
3979 Widget dialog, layout;
3981 Dimension bw_width, pw_width;
3983 char *PromoChars = "wglcqrbnkac+=\0";
3986 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3987 XtGetValues(boardWidget, args, j);
3990 XtSetArg(args[j], XtNresizable, True); j++;
3991 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
3993 XtCreatePopupShell("Promotion", transientShellWidgetClass,
3994 shellWidget, args, j);
3996 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
3997 layoutArgs, XtNumber(layoutArgs));
4000 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
4001 XtSetArg(args[j], XtNborderWidth, 0); j++;
4002 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
4005 if(gameInfo.variant != VariantShogi) {
4006 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
4007 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
4008 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
4009 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
4010 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
4012 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
4013 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
4014 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
4015 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
4017 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
4018 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
4019 gameInfo.variant == VariantGiveaway) {
4020 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
4022 if(gameInfo.variant == VariantCapablanca ||
4023 gameInfo.variant == VariantGothic ||
4024 gameInfo.variant == VariantCapaRandom) {
4025 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
4026 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
4028 } else // [HGM] shogi
4030 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
4031 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
4033 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
4035 XtRealizeWidget(promotionShell);
4036 CatchDeleteWindow(promotionShell, "PromotionPopDown");
4039 XtSetArg(args[j], XtNwidth, &pw_width); j++;
4040 XtGetValues(promotionShell, args, j);
4042 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
4043 lineGap + squareSize/3 +
4044 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
4045 0 : 6*(squareSize + lineGap)), &x, &y);
4048 XtSetArg(args[j], XtNx, x); j++;
4049 XtSetArg(args[j], XtNy, y); j++;
4050 XtSetValues(promotionShell, args, j);
4052 XtPopup(promotionShell, XtGrabNone);
4060 if (!promotionUp) return;
4061 XtPopdown(promotionShell);
4062 XtDestroyWidget(promotionShell);
4063 promotionUp = False;
4067 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
4069 int promoChar = * (const char *) client_data;
4073 if (fromX == -1) return;
4080 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
4082 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
4083 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
4089 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
4091 dialogError = errorUp = False;
4092 XtPopdown(w = XtParent(XtParent(XtParent(w))));
4094 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
4101 if (!errorUp) return;
4102 dialogError = errorUp = False;
4103 XtPopdown(errorShell);
4104 XtDestroyWidget(errorShell);
4105 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
4109 ErrorPopUp (char *title, char *label, int modal)
4112 Widget dialog, layout;
4116 Dimension bw_width, pw_width;
4117 Dimension pw_height;
4121 XtSetArg(args[i], XtNresizable, True); i++;
4122 XtSetArg(args[i], XtNtitle, title); i++;
4124 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
4125 shellUp[0] ? (dialogError = modal = TRUE, shells[0]) : shellWidget, args, i);
4127 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
4128 layoutArgs, XtNumber(layoutArgs));
4131 XtSetArg(args[i], XtNlabel, label); i++;
4132 XtSetArg(args[i], XtNborderWidth, 0); i++;
4133 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
4136 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
4138 XtRealizeWidget(errorShell);
4139 CatchDeleteWindow(errorShell, "ErrorPopDown");
4142 XtSetArg(args[i], XtNwidth, &bw_width); i++;
4143 XtGetValues(boardWidget, args, i);
4145 XtSetArg(args[i], XtNwidth, &pw_width); i++;
4146 XtSetArg(args[i], XtNheight, &pw_height); i++;
4147 XtGetValues(errorShell, args, i);
4150 /* This code seems to tickle an X bug if it is executed too soon
4151 after xboard starts up. The coordinates get transformed as if
4152 the main window was positioned at (0, 0).
4154 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
4155 0 - pw_height + squareSize / 3, &x, &y);
4157 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
4158 RootWindowOfScreen(XtScreen(boardWidget)),
4159 (bw_width - pw_width) / 2,
4160 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
4164 if (y < 0) y = 0; /*avoid positioning top offscreen*/
4167 XtSetArg(args[i], XtNx, x); i++;
4168 XtSetArg(args[i], XtNy, y); i++;
4169 XtSetValues(errorShell, args, i);
4172 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
4175 /* Disable all user input other than deleting the window */
4176 static int frozen = 0;
4182 /* Grab by a widget that doesn't accept input */
4183 XtAddGrab(messageWidget, TRUE, FALSE);
4187 /* Undo a FreezeUI */
4191 if (!frozen) return;
4192 XtRemoveGrab(messageWidget);
4200 static int oldPausing = FALSE;
4201 static GameMode oldmode = (GameMode) -1;
4204 if (!boardWidget || !XtIsRealized(boardWidget)) return;
4206 if (pausing != oldPausing) {
4207 oldPausing = pausing;
4208 MarkMenuItem("Pause", pausing);
4210 if (appData.showButtonBar) {
4211 /* Always toggle, don't set. Previous code messes up when
4212 invoked while the button is pressed, as releasing it
4213 toggles the state again. */
4216 XtSetArg(args[0], XtNbackground, &oldbg);
4217 XtSetArg(args[1], XtNforeground, &oldfg);
4218 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
4220 XtSetArg(args[0], XtNbackground, oldfg);
4221 XtSetArg(args[1], XtNforeground, oldbg);
4223 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
4227 wname = ModeToWidgetName(oldmode);
4228 if (wname != NULL) {
4229 MarkMenuItem(wname, False);
4231 wname = ModeToWidgetName(gameMode);
4232 if (wname != NULL) {
4233 MarkMenuItem(wname, True);
4236 MarkMenuItem("Machine Match", matchMode && matchGame < appData.matchGames);
4238 /* Maybe all the enables should be handled here, not just this one */
4239 EnableMenuItem("Training", gameMode == Training || gameMode == PlayFromGameFile);
4244 * Button/menu procedures
4247 LoadGamePopUp (FILE *f, int gameNumber, char *title)
4249 cmailMsgLoaded = FALSE;
4250 if (gameNumber == 0) {
4251 int error = GameListBuild(f);
4253 DisplayError(_("Cannot build game list"), error);
4254 } else if (!ListEmpty(&gameList) &&
4255 ((ListGame *) gameList.tailPred)->number > 1) {
4256 GameListPopUp(f, title);
4262 return LoadGame(f, gameNumber, title, FALSE);
4265 /* this variable is shared between CopyPositionProc and SendPositionSelection */
4266 char *selected_fen_position=NULL;
4269 SendPositionSelection (Widget w, Atom *selection, Atom *target,
4270 Atom *type_return, XtPointer *value_return,
4271 unsigned long *length_return, int *format_return)
4273 char *selection_tmp;
4275 if (!selected_fen_position) return False; /* should never happen */
4276 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4277 /* note: since no XtSelectionDoneProc was registered, Xt will
4278 * automatically call XtFree on the value returned. So have to
4279 * make a copy of it allocated with XtMalloc */
4280 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
4281 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
4283 *value_return=selection_tmp;
4284 *length_return=strlen(selection_tmp);
4285 *type_return=*target;
4286 *format_return = 8; /* bits per byte */
4288 } else if (*target == XA_TARGETS(xDisplay)) {
4289 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4290 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4291 targets_tmp[1] = XA_STRING;
4292 *value_return = targets_tmp;
4293 *type_return = XA_ATOM;
4296 // This code leads to a read of value_return out of bounds on 64-bit systems.
4297 // Other code which I have seen always sets *format_return to 32 independent of
4298 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
4299 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
4300 *format_return = 8 * sizeof(Atom);
4301 if (*format_return > 32) {
4302 *length_return *= *format_return / 32;
4303 *format_return = 32;
4306 *format_return = 32;
4314 /* note: when called from menu all parameters are NULL, so no clue what the
4315 * Widget which was clicked on was, or what the click event was
4321 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4322 * have a notion of a position that is selected but not copied.
4323 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4325 if(gameMode == EditPosition) EditPositionDone(TRUE);
4326 if (selected_fen_position) free(selected_fen_position);
4327 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
4328 if (!selected_fen_position) return;
4329 XtOwnSelection(menuBarWidget, XA_PRIMARY,
4331 SendPositionSelection,
4332 NULL/* lose_ownership_proc */ ,
4333 NULL/* transfer_done_proc */);
4334 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4336 SendPositionSelection,
4337 NULL/* lose_ownership_proc */ ,
4338 NULL/* transfer_done_proc */);
4341 /* function called when the data to Paste is ready */
4343 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
4344 Atom *type, XtPointer value, unsigned long *len, int *format)
4347 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
4348 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
4349 EditPositionPasteFEN(fenstr);
4353 /* called when Paste Position button is pressed,
4354 * all parameters will be NULL */
4356 PastePositionProc ()
4358 XtGetSelectionValue(menuBarWidget,
4359 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4360 /* (XtSelectionCallbackProc) */ PastePositionCB,
4361 NULL, /* client_data passed to PastePositionCB */
4363 /* better to use the time field from the event that triggered the
4364 * call to this function, but that isn't trivial to get
4372 SendGameSelection (Widget w, Atom *selection, Atom *target,
4373 Atom *type_return, XtPointer *value_return,
4374 unsigned long *length_return, int *format_return)
4376 char *selection_tmp;
4378 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4379 FILE* f = fopen(gameCopyFilename, "r");
4382 if (f == NULL) return False;
4386 selection_tmp = XtMalloc(len + 1);
4387 count = fread(selection_tmp, 1, len, f);
4390 XtFree(selection_tmp);
4393 selection_tmp[len] = NULLCHAR;
4394 *value_return = selection_tmp;
4395 *length_return = len;
4396 *type_return = *target;
4397 *format_return = 8; /* bits per byte */
4399 } else if (*target == XA_TARGETS(xDisplay)) {
4400 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4401 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4402 targets_tmp[1] = XA_STRING;
4403 *value_return = targets_tmp;
4404 *type_return = XA_ATOM;
4407 // This code leads to a read of value_return out of bounds on 64-bit systems.
4408 // Other code which I have seen always sets *format_return to 32 independent of
4409 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
4410 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
4411 *format_return = 8 * sizeof(Atom);
4412 if (*format_return > 32) {
4413 *length_return *= *format_return / 32;
4414 *format_return = 32;
4417 *format_return = 32;
4429 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4430 * have a notion of a game that is selected but not copied.
4431 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4433 XtOwnSelection(menuBarWidget, XA_PRIMARY,
4436 NULL/* lose_ownership_proc */ ,
4437 NULL/* transfer_done_proc */);
4438 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4441 NULL/* lose_ownership_proc */ ,
4442 NULL/* transfer_done_proc */);
4445 /* note: when called from menu all parameters are NULL, so no clue what the
4446 * Widget which was clicked on was, or what the click event was
4448 /* function called when the data to Paste is ready */
4450 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
4451 Atom *type, XtPointer value, unsigned long *len, int *format)
4454 if (value == NULL || *len == 0) {
4455 return; /* nothing had been selected to copy */
4457 f = fopen(gamePasteFilename, "w");
4459 DisplayError(_("Can't open temp file"), errno);
4462 fwrite(value, 1, *len, f);
4465 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
4468 /* called when Paste Game button is pressed,
4469 * all parameters will be NULL */
4473 XtGetSelectionValue(menuBarWidget,
4474 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4475 /* (XtSelectionCallbackProc) */ PasteGameCB,
4476 NULL, /* client_data passed to PasteGameCB */
4478 /* better to use the time field from the event that triggered the
4479 * call to this function, but that isn't trivial to get
4488 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4494 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4495 { // [HGM] input: let up-arrow recall previous line from history
4502 if (!shellUp[4]) return;
4503 edit = boxOptions[0].handle;
4505 XtSetArg(args[j], XtNstring, &val); j++;
4506 XtGetValues(edit, args, j);
4507 val = PrevInHistory(val);
4508 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4509 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4511 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
4512 XawTextReplace(edit, 0, 0, &t);
4513 XawTextSetInsertionPoint(edit, 9999);
4518 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4519 { // [HGM] input: let down-arrow recall next line from history
4524 if (!shellUp[4]) return;
4525 edit = boxOptions[0].handle;
4526 val = NextInHistory();
4527 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4528 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4530 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
4531 XawTextReplace(edit, 0, 0, &t);
4532 XawTextSetInsertionPoint(edit, 9999);
4537 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4539 if (shellUp[4] == True)
4544 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4546 if (!TempBackwardActive) {
4547 TempBackwardActive = True;
4553 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4555 /* Check to see if triggered by a key release event for a repeating key.
4556 * If so the next queued event will be a key press of the same key at the same time */
4557 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
4559 XPeekEvent(xDisplay, &next);
4560 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
4561 next.xkey.keycode == event->xkey.keycode)
4565 TempBackwardActive = False;
4569 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4570 { // called as key binding
4573 if (nprms && *nprms > 0)
4577 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
4582 DisplayMessage (char *message, char *extMessage)
4584 /* display a message in the message widget */
4593 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
4598 message = extMessage;
4602 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
4604 /* need to test if messageWidget already exists, since this function
4605 can also be called during the startup, if for example a Xresource
4606 is not set up correctly */
4609 XtSetArg(arg, XtNlabel, message);
4610 XtSetValues(messageWidget, &arg, 1);
4617 DisplayTitle (char *text)
4621 char title[MSG_SIZ];
4624 if (text == NULL) text = "";
4626 if (appData.titleInWindow) {
4628 XtSetArg(args[i], XtNlabel, text); i++;
4629 XtSetValues(titleWidget, args, i);
4632 if (*text != NULLCHAR) {
4633 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
4634 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
4635 } else if (appData.icsActive) {
4636 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
4637 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
4638 } else if (appData.cmailGameName[0] != NULLCHAR) {
4639 snprintf(icon, sizeof(icon), "%s", "CMail");
4640 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
4642 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
4643 } else if (gameInfo.variant == VariantGothic) {
4644 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
4645 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
4648 } else if (gameInfo.variant == VariantFalcon) {
4649 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
4650 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
4652 } else if (appData.noChessProgram) {
4653 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
4654 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
4656 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
4657 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
4660 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
4661 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
4662 XtSetValues(shellWidget, args, i);
4663 XSync(xDisplay, False);
4668 DisplayError (String message, int error)
4673 if (appData.debugMode || appData.matchMode) {
4674 fprintf(stderr, "%s: %s\n", programName, message);
4677 if (appData.debugMode || appData.matchMode) {
4678 fprintf(stderr, "%s: %s: %s\n",
4679 programName, message, strerror(error));
4681 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
4684 ErrorPopUp(_("Error"), message, FALSE);
4689 DisplayMoveError (String message)
4693 DrawPosition(FALSE, NULL);
4694 if (appData.debugMode || appData.matchMode) {
4695 fprintf(stderr, "%s: %s\n", programName, message);
4697 if (appData.popupMoveErrors) {
4698 ErrorPopUp(_("Error"), message, FALSE);
4700 DisplayMessage(message, "");
4706 DisplayFatalError (String message, int error, int status)
4710 errorExitStatus = status;
4712 fprintf(stderr, "%s: %s\n", programName, message);
4714 fprintf(stderr, "%s: %s: %s\n",
4715 programName, message, strerror(error));
4716 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
4719 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
4720 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
4727 DisplayInformation (String message)
4730 ErrorPopUp(_("Information"), message, TRUE);
4734 DisplayNote (String message)
4737 ErrorPopUp(_("Note"), message, FALSE);
4741 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
4747 DisplayIcsInteractionTitle (String message)
4749 if (oldICSInteractionTitle == NULL) {
4750 /* Magic to find the old window title, adapted from vim */
4751 char *wina = getenv("WINDOWID");
4753 Window win = (Window) atoi(wina);
4754 Window root, parent, *children;
4755 unsigned int nchildren;
4756 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4758 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4759 if (!XQueryTree(xDisplay, win, &root, &parent,
4760 &children, &nchildren)) break;
4761 if (children) XFree((void *)children);
4762 if (parent == root || parent == 0) break;
4765 XSetErrorHandler(oldHandler);
4767 if (oldICSInteractionTitle == NULL) {
4768 oldICSInteractionTitle = "xterm";
4771 printf("\033]0;%s\007", message);
4775 char pendingReplyPrefix[MSG_SIZ];
4776 ProcRef pendingReplyPR;
4779 AskQuestionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4782 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
4786 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
4790 AskQuestionPopDown ()
4792 if (!askQuestionUp) return;
4793 XtPopdown(askQuestionShell);
4794 XtDestroyWidget(askQuestionShell);
4795 askQuestionUp = False;
4799 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4805 reply = XawDialogGetValueString(w = XtParent(w));
4806 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
4807 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
4808 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
4809 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
4810 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
4811 AskQuestionPopDown();
4813 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
4817 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
4822 XtSetArg(args[0], XtNlabel, &name);
4823 XtGetValues(w, args, 1);
4825 if (strcmp(name, _("cancel")) == 0) {
4826 AskQuestionPopDown();
4828 AskQuestionReplyAction(w, NULL, NULL, NULL);
4833 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
4836 Widget popup, layout, dialog, edit;
4842 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
4843 pendingReplyPR = pr;
4846 XtSetArg(args[i], XtNresizable, True); i++;
4847 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
4848 askQuestionShell = popup =
4849 XtCreatePopupShell(title, transientShellWidgetClass,
4850 shellWidget, args, i);
4853 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
4854 layoutArgs, XtNumber(layoutArgs));
4857 XtSetArg(args[i], XtNlabel, question); i++;
4858 XtSetArg(args[i], XtNvalue, ""); i++;
4859 XtSetArg(args[i], XtNborderWidth, 0); i++;
4860 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
4863 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
4864 (XtPointer) dialog);
4865 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
4866 (XtPointer) dialog);
4868 XtRealizeWidget(popup);
4869 CatchDeleteWindow(popup, "AskQuestionPopDown");
4871 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
4872 &x, &y, &win_x, &win_y, &mask);
4874 XtSetArg(args[0], XtNx, x - 10);
4875 XtSetArg(args[1], XtNy, y - 30);
4876 XtSetValues(popup, args, 2);
4878 XtPopup(popup, XtGrabExclusive);
4879 askQuestionUp = True;
4881 edit = XtNameToWidget(dialog, "*value");
4882 XtSetKeyboardFocus(popup, edit);
4887 PlaySound (char *name)
4889 if (*name == NULLCHAR) {
4891 } else if (strcmp(name, "$") == 0) {
4892 putc(BELLCHAR, stderr);
4895 char *prefix = "", *sep = "";
4896 if(appData.soundProgram[0] == NULLCHAR) return;
4897 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
4898 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
4906 PlaySound(appData.soundMove);
4912 PlaySound(appData.soundIcsWin);
4918 PlaySound(appData.soundIcsLoss);
4924 PlaySound(appData.soundIcsDraw);
4928 PlayIcsUnfinishedSound ()
4930 PlaySound(appData.soundIcsUnfinished);
4936 PlaySound(appData.soundIcsAlarm);
4942 PlaySound(appData.soundTell);
4948 system("stty echo");
4955 system("stty -echo");
4960 RunCommand (char *buf)
4966 Colorize (ColorClass cc, int continuation)
4969 int count, outCount, error;
4971 if (textColors[(int)cc].bg > 0) {
4972 if (textColors[(int)cc].fg > 0) {
4973 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
4974 textColors[(int)cc].fg, textColors[(int)cc].bg);
4976 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
4977 textColors[(int)cc].bg);
4980 if (textColors[(int)cc].fg > 0) {
4981 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
4982 textColors[(int)cc].fg);
4984 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
4987 count = strlen(buf);
4988 outCount = OutputToProcess(NoProc, buf, count, &error);
4989 if (outCount < count) {
4990 DisplayFatalError(_("Error writing to display"), error, 1);
4993 if (continuation) return;
4996 PlaySound(appData.soundShout);
4999 PlaySound(appData.soundSShout);
5002 PlaySound(appData.soundChannel1);
5005 PlaySound(appData.soundChannel);
5008 PlaySound(appData.soundKibitz);
5011 PlaySound(appData.soundTell);
5013 case ColorChallenge:
5014 PlaySound(appData.soundChallenge);
5017 PlaySound(appData.soundRequest);
5020 PlaySound(appData.soundSeek);
5032 return getpwuid(getuid())->pw_name;
5036 ExpandPathName (char *path)
5038 static char static_buf[4*MSG_SIZ];
5039 char *d, *s, buf[4*MSG_SIZ];
5045 while (*s && isspace(*s))
5054 if (*(s+1) == '/') {
5055 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
5059 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
5060 { char *p; if(p = strchr(buf, '/')) *p = 0; }
5061 pwd = getpwnam(buf);
5064 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
5068 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
5069 strcat(d, strchr(s+1, '/'));
5073 safeStrCpy(d, s, 4*MSG_SIZ );
5081 static char host_name[MSG_SIZ];
5083 #if HAVE_GETHOSTNAME
5084 gethostname(host_name, MSG_SIZ);
5086 #else /* not HAVE_GETHOSTNAME */
5087 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
5088 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
5090 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
5092 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
5093 #endif /* not HAVE_GETHOSTNAME */
5096 XtIntervalId delayedEventTimerXID = 0;
5097 DelayedEventCallback delayedEventCallback = 0;
5102 delayedEventTimerXID = 0;
5103 delayedEventCallback();
5107 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
5109 if(delayedEventTimerXID && delayedEventCallback == cb)
5110 // [HGM] alive: replace, rather than add or flush identical event
5111 XtRemoveTimeOut(delayedEventTimerXID);
5112 delayedEventCallback = cb;
5113 delayedEventTimerXID =
5114 XtAppAddTimeOut(appContext, millisec,
5115 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
5118 DelayedEventCallback
5121 if (delayedEventTimerXID) {
5122 return delayedEventCallback;
5129 CancelDelayedEvent ()
5131 if (delayedEventTimerXID) {
5132 XtRemoveTimeOut(delayedEventTimerXID);
5133 delayedEventTimerXID = 0;
5137 XtIntervalId loadGameTimerXID = 0;
5140 LoadGameTimerRunning ()
5142 return loadGameTimerXID != 0;
5146 StopLoadGameTimer ()
5148 if (loadGameTimerXID != 0) {
5149 XtRemoveTimeOut(loadGameTimerXID);
5150 loadGameTimerXID = 0;
5158 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
5160 loadGameTimerXID = 0;
5165 StartLoadGameTimer (long millisec)
5168 XtAppAddTimeOut(appContext, millisec,
5169 (XtTimerCallbackProc) LoadGameTimerCallback,
5173 XtIntervalId analysisClockXID = 0;
5176 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
5178 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
5179 || appData.icsEngineAnalyze) { // [DM]
5180 AnalysisPeriodicEvent(0);
5181 StartAnalysisClock();
5186 StartAnalysisClock ()
5189 XtAppAddTimeOut(appContext, 2000,
5190 (XtTimerCallbackProc) AnalysisClockCallback,
5194 XtIntervalId clockTimerXID = 0;
5197 ClockTimerRunning ()
5199 return clockTimerXID != 0;
5205 if (clockTimerXID != 0) {
5206 XtRemoveTimeOut(clockTimerXID);
5215 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
5222 StartClockTimer (long millisec)
5225 XtAppAddTimeOut(appContext, millisec,
5226 (XtTimerCallbackProc) ClockTimerCallback,
5231 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
5236 /* check for low time warning */
5237 Pixel foregroundOrWarningColor = timerForegroundPixel;
5240 appData.lowTimeWarning &&
5241 (timer / 1000) < appData.icsAlarmTime)
5242 foregroundOrWarningColor = lowTimeWarningColor;
5244 if (appData.clockMode) {
5245 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
5246 XtSetArg(args[0], XtNlabel, buf);
5248 snprintf(buf, MSG_SIZ, "%s ", color);
5249 XtSetArg(args[0], XtNlabel, buf);
5254 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
5255 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
5257 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
5258 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
5261 XtSetValues(w, args, 3);
5265 DisplayWhiteClock (long timeRemaining, int highlight)
5269 if(appData.noGUI) return;
5270 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
5271 if (highlight && iconPixmap == bIconPixmap) {
5272 iconPixmap = wIconPixmap;
5273 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
5274 XtSetValues(shellWidget, args, 1);
5279 DisplayBlackClock (long timeRemaining, int highlight)
5283 if(appData.noGUI) return;
5284 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
5285 if (highlight && iconPixmap == wIconPixmap) {
5286 iconPixmap = bIconPixmap;
5287 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
5288 XtSetValues(shellWidget, args, 1);
5307 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
5311 int to_prog[2], from_prog[2];
5315 if (appData.debugMode) {
5316 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
5319 /* We do NOT feed the cmdLine to the shell; we just
5320 parse it into blank-separated arguments in the
5321 most simple-minded way possible.
5324 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
5327 while(*p == ' ') p++;
5329 if(*p == '"' || *p == '\'')
5330 p = strchr(++argv[i-1], *p);
5331 else p = strchr(p, ' ');
5332 if (p == NULL) break;
5337 SetUpChildIO(to_prog, from_prog);
5339 if ((pid = fork()) == 0) {
5341 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
5342 close(to_prog[1]); // first close the unused pipe ends
5343 close(from_prog[0]);
5344 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
5345 dup2(from_prog[1], 1);
5346 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
5347 close(from_prog[1]); // and closing again loses one of the pipes!
5348 if(fileno(stderr) >= 2) // better safe than sorry...
5349 dup2(1, fileno(stderr)); /* force stderr to the pipe */
5351 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
5356 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
5358 execvp(argv[0], argv);
5360 /* If we get here, exec failed */
5365 /* Parent process */
5367 close(from_prog[1]);
5369 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5372 cp->fdFrom = from_prog[0];
5373 cp->fdTo = to_prog[1];
5378 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
5380 AlarmCallBack (int n)
5386 DestroyChildProcess (ProcRef pr, int signalType)
5388 ChildProc *cp = (ChildProc *) pr;
5390 if (cp->kind != CPReal) return;
5392 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
5393 signal(SIGALRM, AlarmCallBack);
5395 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
5396 kill(cp->pid, SIGKILL); // kill it forcefully
5397 wait((int *) 0); // and wait again
5401 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
5403 /* Process is exiting either because of the kill or because of
5404 a quit command sent by the backend; either way, wait for it to die.
5413 InterruptChildProcess (ProcRef pr)
5415 ChildProc *cp = (ChildProc *) pr;
5417 if (cp->kind != CPReal) return;
5418 (void) kill(cp->pid, SIGINT); /* stop it thinking */
5422 OpenTelnet (char *host, char *port, ProcRef *pr)
5424 char cmdLine[MSG_SIZ];
5426 if (port[0] == NULLCHAR) {
5427 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
5429 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
5431 return StartChildProcess(cmdLine, "", pr);
5435 OpenTCP (char *host, char *port, ProcRef *pr)
5438 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
5439 #else /* !OMIT_SOCKETS */
5440 struct addrinfo hints;
5441 struct addrinfo *ais, *ai;
5446 memset(&hints, 0, sizeof(hints));
5447 hints.ai_family = AF_UNSPEC;
5448 hints.ai_socktype = SOCK_STREAM;
5450 error = getaddrinfo(host, port, &hints, &ais);
5452 /* a getaddrinfo error is not an errno, so can't return it */
5453 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
5454 host, port, gai_strerror(error));
5458 for (ai = ais; ai != NULL; ai = ai->ai_next) {
5459 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
5463 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
5476 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5482 #endif /* !OMIT_SOCKETS */
5488 OpenCommPort (char *name, ProcRef *pr)
5493 fd = open(name, 2, 0);
5494 if (fd < 0) return errno;
5496 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5507 OpenLoopback (ProcRef *pr)
5512 SetUpChildIO(to, from);
5514 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5517 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
5525 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
5527 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
5531 #define INPUT_SOURCE_BUF_SIZE 8192
5540 char buf[INPUT_SOURCE_BUF_SIZE];
5545 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
5547 InputSource *is = (InputSource *) closure;
5552 if (is->lineByLine) {
5553 count = read(is->fd, is->unused,
5554 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
5556 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
5559 is->unused += count;
5561 while (p < is->unused) {
5562 q = memchr(p, '\n', is->unused - p);
5563 if (q == NULL) break;
5565 (is->func)(is, is->closure, p, q - p, 0);
5569 while (p < is->unused) {
5574 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
5579 (is->func)(is, is->closure, is->buf, count, error);
5584 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
5587 ChildProc *cp = (ChildProc *) pr;
5589 is = (InputSource *) calloc(1, sizeof(InputSource));
5590 is->lineByLine = lineByLine;
5594 is->fd = fileno(stdin);
5596 is->kind = cp->kind;
5597 is->fd = cp->fdFrom;
5600 is->unused = is->buf;
5603 is->xid = XtAppAddInput(appContext, is->fd,
5604 (XtPointer) (XtInputReadMask),
5605 (XtInputCallbackProc) DoInputCallback,
5607 is->closure = closure;
5608 return (InputSourceRef) is;
5612 RemoveInputSource (InputSourceRef isr)
5614 InputSource *is = (InputSource *) isr;
5616 if (is->xid == 0) return;
5617 XtRemoveInput(is->xid);
5622 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
5624 static int line = 0;
5625 ChildProc *cp = (ChildProc *) pr;
5630 if (appData.noJoin || !appData.useInternalWrap)
5631 outCount = fwrite(message, 1, count, stdout);
5634 int width = get_term_width();
5635 int len = wrap(NULL, message, count, width, &line);
5636 char *msg = malloc(len);
5640 outCount = fwrite(message, 1, count, stdout);
5643 dbgchk = wrap(msg, message, count, width, &line);
5644 if (dbgchk != len && appData.debugMode)
5645 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
5646 outCount = fwrite(msg, 1, dbgchk, stdout);
5652 outCount = write(cp->fdTo, message, count);
5662 /* Output message to process, with "ms" milliseconds of delay
5663 between each character. This is needed when sending the logon
5664 script to ICC, which for some reason doesn't like the
5665 instantaneous send. */
5667 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
5669 ChildProc *cp = (ChildProc *) pr;
5674 r = write(cp->fdTo, message++, 1);
5687 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
5689 /* Masks for XPM pieces. Black and white pieces can have
5690 different shapes, but in the interest of retaining my
5691 sanity pieces must have the same outline on both light
5692 and dark squares, and all pieces must use the same
5693 background square colors/images. */
5695 static int xpmDone = 0;
5696 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
5697 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
5700 CreateAnimMasks (int pieceDepth)
5706 unsigned long plane;
5709 /* Need a bitmap just to get a GC with right depth */
5710 buf = XCreatePixmap(xDisplay, xBoardWindow,
5712 values.foreground = 1;
5713 values.background = 0;
5714 /* Don't use XtGetGC, not read only */
5715 maskGC = XCreateGC(xDisplay, buf,
5716 GCForeground | GCBackground, &values);
5717 XFreePixmap(xDisplay, buf);
5719 buf = XCreatePixmap(xDisplay, xBoardWindow,
5720 squareSize, squareSize, pieceDepth);
5721 values.foreground = XBlackPixel(xDisplay, xScreen);
5722 values.background = XWhitePixel(xDisplay, xScreen);
5723 bufGC = XCreateGC(xDisplay, buf,
5724 GCForeground | GCBackground, &values);
5726 for (piece = WhitePawn; piece <= BlackKing; piece++) {
5727 /* Begin with empty mask */
5728 if(!xpmDone) // [HGM] pieces: keep using existing
5729 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
5730 squareSize, squareSize, 1);
5731 XSetFunction(xDisplay, maskGC, GXclear);
5732 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
5733 0, 0, squareSize, squareSize);
5735 /* Take a copy of the piece */
5740 XSetFunction(xDisplay, bufGC, GXcopy);
5741 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
5743 0, 0, squareSize, squareSize, 0, 0);
5745 /* XOR the background (light) over the piece */
5746 XSetFunction(xDisplay, bufGC, GXxor);
5748 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
5749 0, 0, squareSize, squareSize, 0, 0);
5751 XSetForeground(xDisplay, bufGC, lightSquareColor);
5752 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
5755 /* We now have an inverted piece image with the background
5756 erased. Construct mask by just selecting all the non-zero
5757 pixels - no need to reconstruct the original image. */
5758 XSetFunction(xDisplay, maskGC, GXor);
5760 /* Might be quicker to download an XImage and create bitmap
5761 data from it rather than this N copies per piece, but it
5762 only takes a fraction of a second and there is a much
5763 longer delay for loading the pieces. */
5764 for (n = 0; n < pieceDepth; n ++) {
5765 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
5766 0, 0, squareSize, squareSize,
5772 XFreePixmap(xDisplay, buf);
5773 XFreeGC(xDisplay, bufGC);
5774 XFreeGC(xDisplay, maskGC);
5778 InitAnimState (AnimNr anr, XWindowAttributes *info)
5783 /* Each buffer is square size, same depth as window */
5784 animBufs[anr+4] = xBoardWindow;
5785 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
5786 squareSize, squareSize, info->depth);
5787 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
5788 squareSize, squareSize, info->depth);
5790 /* Create a plain GC for blitting */
5791 mask = GCForeground | GCBackground | GCFunction |
5792 GCPlaneMask | GCGraphicsExposures;
5793 values.foreground = XBlackPixel(xDisplay, xScreen);
5794 values.background = XWhitePixel(xDisplay, xScreen);
5795 values.function = GXcopy;
5796 values.plane_mask = AllPlanes;
5797 values.graphics_exposures = False;
5798 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
5800 /* Piece will be copied from an existing context at
5801 the start of each new animation/drag. */
5802 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
5804 /* Outline will be a read-only copy of an existing */
5805 animGCs[anr+4] = None;
5811 XWindowAttributes info;
5813 if (xpmDone && gameInfo.variant == oldVariant) return;
5814 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
5815 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
5817 InitAnimState(Game, &info);
5818 InitAnimState(Player, &info);
5820 /* For XPM pieces, we need bitmaps to use as masks. */
5822 CreateAnimMasks(info.depth), xpmDone = 1;
5827 static Boolean frameWaiting;
5830 FrameAlarm (int sig)
5832 frameWaiting = False;
5833 /* In case System-V style signals. Needed?? */
5834 signal(SIGALRM, FrameAlarm);
5838 FrameDelay (int time)
5840 struct itimerval delay;
5842 XSync(xDisplay, False);
5845 frameWaiting = True;
5846 signal(SIGALRM, FrameAlarm);
5847 delay.it_interval.tv_sec =
5848 delay.it_value.tv_sec = time / 1000;
5849 delay.it_interval.tv_usec =
5850 delay.it_value.tv_usec = (time % 1000) * 1000;
5851 setitimer(ITIMER_REAL, &delay, NULL);
5852 while (frameWaiting) pause();
5853 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
5854 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
5855 setitimer(ITIMER_REAL, &delay, NULL);
5862 FrameDelay (int time)
5864 XSync(xDisplay, False);
5866 usleep(time * 1000);
5872 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
5876 /* Bitmap for piece being moved. */
5877 if (appData.monoMode) {
5878 *mask = *pieceToSolid(piece);
5879 } else if (useImages) {
5881 *mask = xpmMask[piece];
5883 *mask = ximMaskPm[piece];
5886 *mask = *pieceToSolid(piece);
5889 /* GC for piece being moved. Square color doesn't matter, but
5890 since it gets modified we make a copy of the original. */
5892 if (appData.monoMode)
5897 if (appData.monoMode)
5902 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
5904 /* Outline only used in mono mode and is not modified */
5906 *outline = bwPieceGC;
5908 *outline = wbPieceGC;
5912 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
5917 /* Draw solid rectangle which will be clipped to shape of piece */
5918 XFillRectangle(xDisplay, dest, clip,
5919 0, 0, squareSize, squareSize);
5920 if (appData.monoMode)
5921 /* Also draw outline in contrasting color for black
5922 on black / white on white cases */
5923 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
5924 0, 0, squareSize, squareSize, 0, 0, 1);
5926 /* Copy the piece */
5931 if(appData.upsideDown && flipView) kind ^= 2;
5932 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
5934 0, 0, squareSize, squareSize,
5940 InsertPiece (AnimNr anr, ChessSquare piece)
5942 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
5946 DrawBlank (AnimNr anr, int x, int y, int startColor)
5948 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
5951 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
5952 int srcX, int srcY, int width, int height, int destX, int destY)
5954 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
5955 srcX, srcY, width, height, destX, destY);
5959 SetDragPiece (AnimNr anr, ChessSquare piece)
5962 /* The piece will be drawn using its own bitmap as a matte */
5963 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
5964 XSetClipMask(xDisplay, animGCs[anr+2], mask);
5967 #include <sys/ioctl.h>
5971 int fd, default_width;
5974 default_width = 79; // this is FICS default anyway...
5976 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
5978 if (!ioctl(fd, TIOCGSIZE, &win))
5979 default_width = win.ts_cols;
5980 #elif defined(TIOCGWINSZ)
5982 if (!ioctl(fd, TIOCGWINSZ, &win))
5983 default_width = win.ws_col;
5985 return default_width;
5991 static int old_width = 0;
5992 int new_width = get_term_width();
5994 if (old_width != new_width)
5995 ics_printf("set width %d\n", new_width);
5996 old_width = new_width;
6000 NotifyFrontendLogin ()
6005 /* [AS] Arrow highlighting support */
6008 DrawPolygon (Pnt arrow[], int nr)
6012 for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
6013 XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
6014 if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
6018 UpdateLogos (int displ)
6020 return; // no logos in XBoard yet