2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
66 # if HAVE_SYS_SOCKET_H
67 # include <sys/socket.h>
68 # include <netinet/in.h>
70 # else /* not HAVE_SYS_SOCKET_H */
71 # if HAVE_LAN_SOCKET_H
72 # include <lan/socket.h>
74 # include <lan/netdb.h>
75 # else /* not HAVE_LAN_SOCKET_H */
76 # define OMIT_SOCKETS 1
77 # endif /* not HAVE_LAN_SOCKET_H */
78 # endif /* not HAVE_SYS_SOCKET_H */
79 #endif /* !OMIT_SOCKETS */
84 #else /* not STDC_HEADERS */
85 extern char *getenv();
88 # else /* not HAVE_STRING_H */
90 # endif /* not HAVE_STRING_H */
91 #endif /* not STDC_HEADERS */
94 # include <sys/fcntl.h>
95 #else /* not HAVE_SYS_FCNTL_H */
98 # endif /* HAVE_FCNTL_H */
99 #endif /* not HAVE_SYS_FCNTL_H */
101 #if HAVE_SYS_SYSTEMINFO_H
102 # include <sys/systeminfo.h>
103 #endif /* HAVE_SYS_SYSTEMINFO_H */
105 #if TIME_WITH_SYS_TIME
106 # include <sys/time.h>
110 # include <sys/time.h>
121 # include <sys/wait.h>
126 # define NAMLEN(dirent) strlen((dirent)->d_name)
127 # define HAVE_DIR_STRUCT
129 # define dirent direct
130 # define NAMLEN(dirent) (dirent)->d_namlen
132 # include <sys/ndir.h>
133 # define HAVE_DIR_STRUCT
136 # include <sys/dir.h>
137 # define HAVE_DIR_STRUCT
141 # define HAVE_DIR_STRUCT
149 #include <X11/Intrinsic.h>
150 #include <X11/StringDefs.h>
151 #include <X11/Shell.h>
152 #include <X11/cursorfont.h>
153 #include <X11/Xatom.h>
154 #include <X11/Xmu/Atoms.h>
156 #include <X11/Xaw3d/Dialog.h>
157 #include <X11/Xaw3d/Form.h>
158 #include <X11/Xaw3d/List.h>
159 #include <X11/Xaw3d/Label.h>
160 #include <X11/Xaw3d/SimpleMenu.h>
161 #include <X11/Xaw3d/SmeBSB.h>
162 #include <X11/Xaw3d/SmeLine.h>
163 #include <X11/Xaw3d/Box.h>
164 #include <X11/Xaw3d/MenuButton.h>
165 #include <X11/Xaw3d/Text.h>
166 #include <X11/Xaw3d/AsciiText.h>
168 #include <X11/Xaw/Dialog.h>
169 #include <X11/Xaw/Form.h>
170 #include <X11/Xaw/List.h>
171 #include <X11/Xaw/Label.h>
172 #include <X11/Xaw/SimpleMenu.h>
173 #include <X11/Xaw/SmeBSB.h>
174 #include <X11/Xaw/SmeLine.h>
175 #include <X11/Xaw/Box.h>
176 #include <X11/Xaw/MenuButton.h>
177 #include <X11/Xaw/Text.h>
178 #include <X11/Xaw/AsciiText.h>
181 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
186 #include "pixmaps/pixmaps.h"
187 #define IMAGE_EXT "xpm"
189 #define IMAGE_EXT "xim"
190 #include "bitmaps/bitmaps.h"
193 #include "bitmaps/icon_white.bm"
194 #include "bitmaps/icon_black.bm"
195 #include "bitmaps/checkmark.bm"
197 #include "frontend.h"
199 #include "backendz.h"
203 #include "xgamelist.h"
204 #include "xhistory.h"
205 #include "xedittags.h"
216 #define usleep(t) _sleep2(((t)+500)/1000)
220 # define _(s) gettext (s)
221 # define N_(s) gettext_noop (s)
227 int main P((int argc, char **argv));
228 FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
229 char *init_path, char *filter, char *mode, int (*show_entry)(), char **name_return));
230 RETSIGTYPE CmailSigHandler P((int sig));
231 RETSIGTYPE IntSigHandler P((int sig));
232 RETSIGTYPE TermSizeSigHandler P((int sig));
233 static void CreateGCs P((int redo));
234 static void CreateAnyPieces P((void));
235 void CreateXIMPieces P((void));
236 void CreateXPMPieces P((void));
237 void CreateXPMBoard P((char *s, int n));
238 void CreatePieces P((void));
239 void CreatePieceMenus P((void));
240 Widget CreateMenuBar P((Menu *mb, int boardWidth));
241 Widget CreateButtonBar P ((MenuItem *mi));
243 char *InsertPxlSize P((char *pattern, int targetPxlSize));
244 XFontSet CreateFontSet P((char *base_fnt_lst));
246 char *FindFont P((char *pattern, int targetPxlSize));
248 void PieceMenuPopup P((Widget w, XEvent *event,
249 String *params, Cardinal *num_params));
250 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
251 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
252 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
253 u_int wreq, u_int hreq));
254 void CreateGrid P((void));
255 int EventToSquare P((int x, int limit));
256 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
257 void DelayedDrag P((void));
258 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
259 void HandleUserMove P((Widget w, XEvent *event,
260 String *prms, Cardinal *nprms));
261 void AnimateUserMove P((Widget w, XEvent * event,
262 String * params, Cardinal * nParams));
263 void HandlePV P((Widget w, XEvent * event,
264 String * params, Cardinal * nParams));
265 void SelectPV P((Widget w, XEvent * event,
266 String * params, Cardinal * nParams));
267 void StopPV P((Widget w, XEvent * event,
268 String * params, Cardinal * nParams));
269 void WhiteClock P((Widget w, XEvent *event,
270 String *prms, Cardinal *nprms));
271 void BlackClock P((Widget w, XEvent *event,
272 String *prms, Cardinal *nprms));
273 void DrawPositionProc P((Widget w, XEvent *event,
274 String *prms, Cardinal *nprms));
275 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
277 void CommentClick P((Widget w, XEvent * event,
278 String * params, Cardinal * nParams));
279 void CommentPopUp P((char *title, char *label));
280 void CommentPopDown P((void));
281 void ICSInputBoxPopUp P((void));
282 void ICSInputBoxPopDown P((void));
283 void FileNamePopUp P((char *label, char *def, char *filter,
284 FileProc proc, char *openMode));
285 void FileNamePopDown P((void));
286 void FileNameCallback P((Widget w, XtPointer client_data,
287 XtPointer call_data));
288 void FileNameAction P((Widget w, XEvent *event,
289 String *prms, Cardinal *nprms));
290 void AskQuestionReplyAction P((Widget w, XEvent *event,
291 String *prms, Cardinal *nprms));
292 void AskQuestionProc P((Widget w, XEvent *event,
293 String *prms, Cardinal *nprms));
294 void AskQuestionPopDown P((void));
295 void PromotionPopDown P((void));
296 void PromotionCallback P((Widget w, XtPointer client_data,
297 XtPointer call_data));
298 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
299 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
300 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
301 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
302 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
303 static 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 ICSInitScript P((void));
310 static char *ExpandPathName P((char *path));
311 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
312 void GameListOptionsPopDown P(());
313 void GenericPopDown P(());
314 void update_ics_width P(());
315 int get_term_width P(());
316 int CopyMemoProc P(());
317 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
318 Boolean IsDrawArrowEnabled P(());
321 * XBoard depends on Xt R4 or higher
323 int xtVersion = XtSpecificationRelease;
328 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
329 jailSquareColor, highlightSquareColor, premoveHighlightColor;
330 Pixel lowTimeWarningColor;
331 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
332 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
333 wjPieceGC, bjPieceGC, prelineGC, countGC;
334 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
335 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
336 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
337 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
338 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
339 ICSInputShell, fileNameShell, askQuestionShell;
340 Widget historyShell, evalGraphShell, gameListShell;
341 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
342 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
343 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
345 XFontSet fontSet, clockFontSet;
348 XFontStruct *clockFontStruct;
350 Font coordFontID, countFontID;
351 XFontStruct *coordFontStruct, *countFontStruct;
352 XtAppContext appContext;
354 char *oldICSInteractionTitle;
358 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
360 Position commentX = -1, commentY = -1;
361 Dimension commentW, commentH;
362 typedef unsigned int BoardSize;
364 Boolean chessProgram;
366 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
367 int smallLayout = 0, tinyLayout = 0,
368 marginW, marginH, // [HGM] for run-time resizing
369 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
370 ICSInputBoxUp = False, askQuestionUp = False,
371 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
372 errorUp = False, errorExitStatus = -1, defaultLineGap;
373 Dimension textHeight;
374 Pixel timerForegroundPixel, timerBackgroundPixel;
375 Pixel buttonForegroundPixel, buttonBackgroundPixel;
376 char *chessDir, *programName, *programVersion;
377 Boolean alwaysOnTop = False;
378 char *icsTextMenuString;
380 char *firstChessProgramNames;
381 char *secondChessProgramNames;
383 WindowPlacement wpMain;
384 WindowPlacement wpConsole;
385 WindowPlacement wpComment;
386 WindowPlacement wpMoveHistory;
387 WindowPlacement wpEvalGraph;
388 WindowPlacement wpEngineOutput;
389 WindowPlacement wpGameList;
390 WindowPlacement wpTags;
395 Pixmap pieceBitmap[2][(int)BlackPawn];
396 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
397 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
398 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
399 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
400 Pixmap xpmBoardBitmap[2];
401 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
402 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
403 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
404 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
405 XImage *ximLightSquare, *ximDarkSquare;
408 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
409 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
411 #define White(piece) ((int)(piece) < (int)BlackPawn)
413 /* Bitmaps for use as masks when drawing XPM pieces.
414 Need one for each black and white piece. */
415 static Pixmap xpmMask[BlackKing + 1];
417 /* This magic number is the number of intermediate frames used
418 in each half of the animation. For short moves it's reduced
419 by 1. The total number of frames will be factor * 2 + 1. */
422 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
424 #define PAUSE_BUTTON "P"
425 MenuItem buttonBar[] = {
426 {"<<", "<<", ToStartEvent},
427 {"<", "<", BackwardEvent},
428 {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseEvent},
429 {">", ">", ForwardEvent},
430 {">>", ">>", ToEndEvent},
434 #define PIECE_MENU_SIZE 18
435 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
436 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
437 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
438 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
439 N_("Empty square"), N_("Clear board") },
440 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
441 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
442 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
443 N_("Empty square"), N_("Clear board") }
445 /* must be in same order as pieceMenuStrings! */
446 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
447 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
448 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
449 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
450 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
451 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
452 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
453 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
454 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
457 #define DROP_MENU_SIZE 6
458 String dropMenuStrings[DROP_MENU_SIZE] = {
459 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
461 /* must be in same order as dropMenuStrings! */
462 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
463 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
464 WhiteRook, WhiteQueen
472 DropMenuEnables dmEnables[] = {
490 { XtNborderWidth, 0 },
491 { XtNdefaultDistance, 0 },
495 { XtNborderWidth, 0 },
496 { XtNresizable, (XtArgVal) True },
500 { XtNborderWidth, 0 },
506 { XtNjustify, (XtArgVal) XtJustifyRight },
507 { XtNlabel, (XtArgVal) "..." },
508 { XtNresizable, (XtArgVal) True },
509 { XtNresize, (XtArgVal) False }
512 Arg messageArgs[] = {
513 { XtNjustify, (XtArgVal) XtJustifyLeft },
514 { XtNlabel, (XtArgVal) "..." },
515 { XtNresizable, (XtArgVal) True },
516 { XtNresize, (XtArgVal) False }
520 { XtNborderWidth, 0 },
521 { XtNjustify, (XtArgVal) XtJustifyLeft }
524 XtResource clientResources[] = {
525 { "flashCount", "flashCount", XtRInt, sizeof(int),
526 XtOffset(AppDataPtr, flashCount), XtRImmediate,
527 (XtPointer) FLASH_COUNT },
530 XrmOptionDescRec shellOptions[] = {
531 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
532 { "-flash", "flashCount", XrmoptionNoArg, "3" },
533 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
536 XtActionsRec boardActions[] = {
537 { "DrawPosition", DrawPositionProc },
538 { "HandleUserMove", HandleUserMove },
539 { "AnimateUserMove", AnimateUserMove },
540 { "HandlePV", HandlePV },
541 { "SelectPV", SelectPV },
542 { "StopPV", StopPV },
543 { "FileNameAction", FileNameAction },
544 { "AskQuestionProc", AskQuestionProc },
545 { "AskQuestionReplyAction", AskQuestionReplyAction },
546 { "PieceMenuPopup", PieceMenuPopup },
547 { "WhiteClock", WhiteClock },
548 { "BlackClock", BlackClock },
549 { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
550 { "QuitProc", QuitWrapper },
551 { "ManProc", ManInner },
552 { "TempBackwardProc", TempBackwardProc },
553 { "TempForwardProc", TempForwardProc },
554 { "CommentClick", (XtActionProc) CommentClick },
555 { "CommentPopDown", (XtActionProc) CommentPopDown },
556 { "TagsPopDown", (XtActionProc) TagsPopDown },
557 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
558 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
559 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
560 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
561 { "GameListPopDown", (XtActionProc) GameListPopDown },
562 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
563 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
564 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
565 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
566 { "GenericPopDown", (XtActionProc) GenericPopDown },
567 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
568 { "SelectMove", (XtActionProc) SelectMove },
569 { "LoadSelectedProc", LoadSelectedProc },
570 { "SetFilterProc", SetFilterProc },
571 { "TypeInProc", TypeInProc },
572 { "EnterKeyProc", EnterKeyProc },
573 { "UpKeyProc", UpKeyProc },
574 { "DownKeyProc", DownKeyProc },
577 char globalTranslations[] =
578 ":<Key>F9: MenuItem(ResignProc) \n \
579 :Ctrl<Key>n: MenuItem(NewGame) \n \
580 :Meta<Key>V: MenuItem(NewVariant) \n \
581 :Ctrl<Key>o: MenuItem(LoadGame) \n \
582 :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
583 :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
584 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
585 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
586 :Ctrl<Key>s: MenuItem(SaveGame) \n \
587 :Ctrl<Key>c: MenuItem(CopyGame) \n \
588 :Ctrl<Key>v: MenuItem(PasteGame) \n \
589 :Ctrl<Key>O: MenuItem(LoadPosition) \n \
590 :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
591 :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
592 :Ctrl<Key>S: MenuItem(SavePosition) \n \
593 :Ctrl<Key>C: MenuItem(CopyPosition) \n \
594 :Ctrl<Key>V: MenuItem(PastePosition) \n \
595 :Ctrl<Key>q: MenuItem(Exit) \n \
596 :Ctrl<Key>w: MenuItem(MachineWhite) \n \
597 :Ctrl<Key>b: MenuItem(MachineBlack) \n \
598 :Ctrl<Key>t: MenuItem(TwoMachines) \n \
599 :Ctrl<Key>a: MenuItem(AnalysisMode) \n \
600 :Ctrl<Key>g: MenuItem(AnalyzeFile) \n \
601 :Ctrl<Key>e: MenuItem(EditGame) \n \
602 :Ctrl<Key>E: MenuItem(EditPosition) \n \
603 :Meta<Key>O: MenuItem(ShowEngineOutput) \n \
604 :Meta<Key>E: MenuItem(ShowEvaluationGraph) \n \
605 :Meta<Key>G: MenuItem(ShowGameList) \n \
606 :Meta<Key>H: MenuItem(ShowMoveHistory) \n \
607 :<Key>Pause: MenuItem(Pause) \n \
608 :<Key>F3: MenuItem(Accept) \n \
609 :<Key>F4: MenuItem(Decline) \n \
610 :<Key>F12: MenuItem(Rematch) \n \
611 :<Key>F5: MenuItem(CallFlag) \n \
612 :<Key>F6: MenuItem(Draw) \n \
613 :<Key>F7: MenuItem(Adjourn) \n \
614 :<Key>F8: MenuItem(Abort) \n \
615 :<Key>F10: MenuItem(StopObserving) \n \
616 :<Key>F11: MenuItem(StopExamining) \n \
617 :Ctrl<Key>d: MenuItem(DebugProc) \n \
618 :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
619 :Meta<Key>End: MenuItem(ToEnd) \n \
620 :Meta<Key>Right: MenuItem(Forward) \n \
621 :Meta<Key>Home: MenuItem(ToStart) \n \
622 :Meta<Key>Left: MenuItem(Backward) \n \
623 :<Key>Left: MenuItem(Backward) \n \
624 :<Key>Right: MenuItem(Forward) \n \
625 :<Key>Home: MenuItem(Revert) \n \
626 :<Key>End: MenuItem(TruncateGame) \n \
627 :Ctrl<Key>m: MenuItem(MoveNow) \n \
628 :Ctrl<Key>x: MenuItem(RetractMove) \n \
629 :Meta<Key>J: MenuItem(Adjudications) \n \
630 :Meta<Key>U: MenuItem(CommonEngine) \n \
631 :Meta<Key>T: MenuItem(TimeControl) \n \
632 :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
633 #ifndef OPTIONSDIALOG
635 :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
636 :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
637 :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
638 :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
639 :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
642 :<Key>F1: MenuItem(Manual) \n \
643 :<Key>F2: MenuItem(FlipView) \n \
644 :<KeyDown>Return: TempBackwardProc() \n \
645 :<KeyUp>Return: TempForwardProc() \n";
647 char boardTranslations[] =
648 "<Btn1Down>: HandleUserMove(0) \n \
649 Shift<Btn1Up>: HandleUserMove(1) \n \
650 <Btn1Up>: HandleUserMove(0) \n \
651 <Btn1Motion>: AnimateUserMove() \n \
652 <Btn3Motion>: HandlePV() \n \
653 <Btn2Motion>: HandlePV() \n \
654 <Btn3Up>: PieceMenuPopup(menuB) \n \
655 <Btn2Up>: PieceMenuPopup(menuB) \n \
656 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
657 PieceMenuPopup(menuB) \n \
658 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
659 PieceMenuPopup(menuW) \n \
660 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
661 PieceMenuPopup(menuW) \n \
662 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
663 PieceMenuPopup(menuB) \n";
665 char whiteTranslations[] =
666 "Shift<BtnDown>: WhiteClock(1)\n \
667 <BtnDown>: WhiteClock(0)\n";
668 char blackTranslations[] =
669 "Shift<BtnDown>: BlackClock(1)\n \
670 <BtnDown>: BlackClock(0)\n";
672 char ICSInputTranslations[] =
673 "<Key>Up: UpKeyProc() \n "
674 "<Key>Down: DownKeyProc() \n "
675 "<Key>Return: EnterKeyProc() \n";
677 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
678 // as the widget is destroyed before the up-click can call extend-end
679 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
681 String xboardResources[] = {
682 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
683 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
684 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
689 /* Max possible square size */
690 #define MAXSQSIZE 256
692 static int xpm_avail[MAXSQSIZE];
694 #ifdef HAVE_DIR_STRUCT
696 /* Extract piece size from filename */
698 xpm_getsize (char *name, int len, char *ext)
706 if ((p=strchr(name, '.')) == NULL ||
707 StrCaseCmp(p+1, ext) != 0)
713 while (*p && isdigit(*p))
720 /* Setup xpm_avail */
722 xpm_getavail (char *dirname, char *ext)
728 for (i=0; i<MAXSQSIZE; ++i)
731 if (appData.debugMode)
732 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
734 dir = opendir(dirname);
737 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
738 programName, dirname);
742 while ((ent=readdir(dir)) != NULL) {
743 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
744 if (i > 0 && i < MAXSQSIZE)
754 xpm_print_avail (FILE *fp, char *ext)
758 fprintf(fp, _("Available `%s' sizes:\n"), ext);
759 for (i=1; i<MAXSQSIZE; ++i) {
765 /* Return XPM piecesize closest to size */
767 xpm_closest_to (char *dirname, int size, char *ext)
770 int sm_diff = MAXSQSIZE;
774 xpm_getavail(dirname, ext);
776 if (appData.debugMode)
777 xpm_print_avail(stderr, ext);
779 for (i=1; i<MAXSQSIZE; ++i) {
782 diff = (diff<0) ? -diff : diff;
783 if (diff < sm_diff) {
791 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
797 #else /* !HAVE_DIR_STRUCT */
798 /* If we are on a system without a DIR struct, we can't
799 read the directory, so we can't collect a list of
800 filenames, etc., so we can't do any size-fitting. */
802 xpm_closest_to (char *dirname, int size, char *ext)
805 Warning: No DIR structure found on this system --\n\
806 Unable to autosize for XPM/XIM pieces.\n\
807 Please report this error to %s.\n\
808 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
811 #endif /* HAVE_DIR_STRUCT */
813 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
814 "magenta", "cyan", "white" };
818 TextColors textColors[(int)NColorClasses];
820 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
822 parse_color (char *str, int which)
824 char *p, buf[100], *d;
827 if (strlen(str) > 99) /* watch bounds on buf */
832 for (i=0; i<which; ++i) {
839 /* Could be looking at something like:
841 .. in which case we want to stop on a comma also */
842 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
846 return -1; /* Use default for empty field */
849 if (which == 2 || isdigit(*p))
852 while (*p && isalpha(*p))
857 for (i=0; i<8; ++i) {
858 if (!StrCaseCmp(buf, cnames[i]))
859 return which? (i+40) : (i+30);
861 if (!StrCaseCmp(buf, "default")) return -1;
863 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
868 parse_cpair (ColorClass cc, char *str)
870 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
871 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
876 /* bg and attr are optional */
877 textColors[(int)cc].bg = parse_color(str, 1);
878 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
879 textColors[(int)cc].attr = 0;
885 /* Arrange to catch delete-window events */
886 Atom wm_delete_window;
888 CatchDeleteWindow (Widget w, String procname)
891 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
892 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
893 XtAugmentTranslations(w, XtParseTranslationTable(buf));
900 XtSetArg(args[0], XtNiconic, False);
901 XtSetValues(shellWidget, args, 1);
903 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
906 //---------------------------------------------------------------------------------------------------------
907 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
910 #define CW_USEDEFAULT (1<<31)
911 #define ICS_TEXT_MENU_SIZE 90
912 #define DEBUG_FILE "xboard.debug"
913 #define SetCurrentDirectory chdir
914 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
918 // these two must some day move to frontend.h, when they are implemented
919 Boolean GameListIsUp();
921 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
924 // front-end part of option handling
926 // [HGM] This platform-dependent table provides the location for storing the color info
927 extern char *crWhite, * crBlack;
931 &appData.whitePieceColor,
932 &appData.blackPieceColor,
933 &appData.lightSquareColor,
934 &appData.darkSquareColor,
935 &appData.highlightSquareColor,
936 &appData.premoveHighlightColor,
937 &appData.lowTimeWarningColor,
948 // [HGM] font: keep a font for each square size, even non-stndard ones
951 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
952 char *fontTable[NUM_FONTS][MAX_SIZE];
955 ParseFont (char *name, int number)
956 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
958 if(sscanf(name, "size%d:", &size)) {
959 // [HGM] font: font is meant for specific boardSize (likely from settings file);
960 // defer processing it until we know if it matches our board size
961 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
962 fontTable[number][size] = strdup(strchr(name, ':')+1);
963 fontValid[number][size] = True;
968 case 0: // CLOCK_FONT
969 appData.clockFont = strdup(name);
971 case 1: // MESSAGE_FONT
972 appData.font = strdup(name);
974 case 2: // COORD_FONT
975 appData.coordFont = strdup(name);
980 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
985 { // only 2 fonts currently
986 appData.clockFont = CLOCK_FONT_NAME;
987 appData.coordFont = COORD_FONT_NAME;
988 appData.font = DEFAULT_FONT_NAME;
993 { // no-op, until we identify the code for this already in XBoard and move it here
997 ParseColor (int n, char *name)
998 { // in XBoard, just copy the color-name string
999 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1003 ParseTextAttribs (ColorClass cc, char *s)
1005 (&appData.colorShout)[cc] = strdup(s);
1009 ParseBoardSize (void *addr, char *name)
1011 appData.boardSize = strdup(name);
1016 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1020 SetCommPortDefaults ()
1021 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1024 // [HGM] args: these three cases taken out to stay in front-end
1026 SaveFontArg (FILE *f, ArgDescriptor *ad)
1029 int i, n = (int)(intptr_t)ad->argLoc;
1031 case 0: // CLOCK_FONT
1032 name = appData.clockFont;
1034 case 1: // MESSAGE_FONT
1035 name = appData.font;
1037 case 2: // COORD_FONT
1038 name = appData.coordFont;
1043 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1044 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1045 fontTable[n][squareSize] = strdup(name);
1046 fontValid[n][squareSize] = True;
1049 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1050 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1055 { // nothing to do, as the sounds are at all times represented by their text-string names already
1059 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
1060 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1061 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1065 SaveColor (FILE *f, ArgDescriptor *ad)
1066 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1067 if(colorVariable[(int)(intptr_t)ad->argLoc])
1068 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1072 SaveBoardSize (FILE *f, char *name, void *addr)
1073 { // wrapper to shield back-end from BoardSize & sizeInfo
1074 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1078 ParseCommPortSettings (char *s)
1079 { // no such option in XBoard (yet)
1082 extern Widget engineOutputShell;
1086 GetActualPlacement (Widget wg, WindowPlacement *wp)
1091 XWindowAttributes winAt;
1098 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
1099 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
1100 wp->x = rx - winAt.x;
1101 wp->y = ry - winAt.y;
1102 wp->height = winAt.height;
1103 wp->width = winAt.width;
1104 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
1109 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1110 // In XBoard this will have to wait until awareness of window parameters is implemented
1111 GetActualPlacement(shellWidget, &wpMain);
1112 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1113 if(MoveHistoryIsUp()) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
1114 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1115 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1116 if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
1117 if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
1121 PrintCommPortSettings (FILE *f, char *name)
1122 { // This option does not exist in XBoard
1126 MySearchPath (char *installDir, char *name, char *fullname)
1127 { // just append installDir and name. Perhaps ExpandPath should be used here?
1128 name = ExpandPathName(name);
1129 if(name && name[0] == '/')
1130 safeStrCpy(fullname, name, MSG_SIZ );
1132 sprintf(fullname, "%s%c%s", installDir, '/', name);
1138 MyGetFullPathName (char *name, char *fullname)
1139 { // should use ExpandPath?
1140 name = ExpandPathName(name);
1141 safeStrCpy(fullname, name, MSG_SIZ );
1146 EnsureOnScreen (int *x, int *y, int minX, int minY)
1153 { // [HGM] args: allows testing if main window is realized from back-end
1154 return xBoardWindow != 0;
1158 PopUpStartupDialog ()
1159 { // start menu not implemented in XBoard
1163 ConvertToLine (int argc, char **argv)
1165 static char line[128*1024], buf[1024];
1169 for(i=1; i<argc; i++)
1171 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1172 && argv[i][0] != '{' )
1173 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1175 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1176 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1179 line[strlen(line)-1] = NULLCHAR;
1183 //--------------------------------------------------------------------------------------------
1186 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1188 #define BoardSize int
1190 InitDrawingSizes (BoardSize boardSize, int flags)
1191 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1192 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1194 XtGeometryResult gres;
1196 static Dimension oldWidth, oldHeight;
1197 static VariantClass oldVariant;
1198 static int oldDual = -1, oldMono = -1;
1200 if(!formWidget) return;
1202 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1203 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1204 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1206 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1208 * Enable shell resizing.
1210 shellArgs[0].value = (XtArgVal) &w;
1211 shellArgs[1].value = (XtArgVal) &h;
1212 XtGetValues(shellWidget, shellArgs, 2);
1214 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1215 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1216 XtSetValues(shellWidget, &shellArgs[2], 4);
1218 XtSetArg(args[0], XtNdefaultDistance, &sep);
1219 XtGetValues(formWidget, args, 1);
1221 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1223 hOffset = boardWidth + 10;
1224 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1225 secondSegments[i] = gridSegments[i];
1226 secondSegments[i].x1 += hOffset;
1227 secondSegments[i].x2 += hOffset;
1230 XtSetArg(args[0], XtNwidth, boardWidth);
1231 XtSetArg(args[1], XtNheight, boardHeight);
1232 XtSetValues(boardWidget, args, 2);
1234 timerWidth = (boardWidth - sep) / 2;
1235 XtSetArg(args[0], XtNwidth, timerWidth);
1236 XtSetValues(whiteTimerWidget, args, 1);
1237 XtSetValues(blackTimerWidget, args, 1);
1239 XawFormDoLayout(formWidget, False);
1241 if (appData.titleInWindow) {
1243 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1244 XtSetArg(args[i], XtNheight, &h); i++;
1245 XtGetValues(titleWidget, args, i);
1247 w = boardWidth - 2*bor;
1249 XtSetArg(args[0], XtNwidth, &w);
1250 XtGetValues(menuBarWidget, args, 1);
1251 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1254 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1255 if (gres != XtGeometryYes && appData.debugMode) {
1257 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1258 programName, gres, w, h, wr, hr);
1262 XawFormDoLayout(formWidget, True);
1265 * Inhibit shell resizing.
1267 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1268 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1269 shellArgs[4].value = shellArgs[2].value = w;
1270 shellArgs[5].value = shellArgs[3].value = h;
1271 XtSetValues(shellWidget, &shellArgs[0], 6);
1273 XSync(xDisplay, False);
1277 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1280 if(gameInfo.variant != oldVariant) { // and only if variant changed
1283 for(i=0; i<4; i++) {
1285 for(p=0; p<=(int)WhiteKing; p++)
1286 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1287 if(gameInfo.variant == VariantShogi) {
1288 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1289 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1290 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1291 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1292 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1295 if(gameInfo.variant == VariantGothic) {
1296 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1299 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1300 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1301 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1304 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1305 for(p=0; p<=(int)WhiteKing; p++)
1306 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1307 if(gameInfo.variant == VariantShogi) {
1308 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1309 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1310 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1311 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1312 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1315 if(gameInfo.variant == VariantGothic) {
1316 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1319 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1320 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1321 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1326 for(i=0; i<2; i++) {
1328 for(p=0; p<=(int)WhiteKing; p++)
1329 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1330 if(gameInfo.variant == VariantShogi) {
1331 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1332 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1333 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1334 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1335 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1338 if(gameInfo.variant == VariantGothic) {
1339 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1342 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1343 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1344 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1348 oldMono = -10; // kludge to force recreation of animation masks
1349 oldVariant = gameInfo.variant;
1352 if(appData.monoMode != oldMono)
1355 oldMono = appData.monoMode;
1360 ParseIcsTextColors ()
1361 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1362 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1363 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1364 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1365 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1366 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1367 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1368 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1369 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1370 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1371 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1373 if (appData.colorize) {
1375 _("%s: can't parse color names; disabling colorization\n"),
1378 appData.colorize = FALSE;
1383 MakeOneColor (char *name, Pixel *color)
1385 XrmValue vFrom, vTo;
1386 if (!appData.monoMode) {
1387 vFrom.addr = (caddr_t) name;
1388 vFrom.size = strlen(name);
1389 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1390 if (vTo.addr == NULL) {
1391 appData.monoMode = True;
1394 *color = *(Pixel *) vTo.addr;
1402 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1403 int forceMono = False;
1405 forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1406 forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1407 forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1408 forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1409 forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1410 forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1417 { // [HGM] taken out of main
1419 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1420 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1421 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1423 if (appData.bitmapDirectory[0] != NULLCHAR) {
1427 CreateXPMBoard(appData.liteBackTextureFile, 1);
1428 CreateXPMBoard(appData.darkBackTextureFile, 0);
1432 /* Create regular pieces */
1433 if (!useImages) CreatePieces();
1438 InitDrawingParams ()
1440 MakeColors(); CreateGCs(True);
1445 main (int argc, char **argv)
1447 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1448 XSetWindowAttributes window_attributes;
1450 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1451 XrmValue vFrom, vTo;
1452 XtGeometryResult gres;
1455 int forceMono = False;
1457 srandom(time(0)); // [HGM] book: make random truly random
1459 setbuf(stdout, NULL);
1460 setbuf(stderr, NULL);
1463 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1464 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1468 programName = strrchr(argv[0], '/');
1469 if (programName == NULL)
1470 programName = argv[0];
1475 XtSetLanguageProc(NULL, NULL, NULL);
1476 bindtextdomain(PACKAGE, LOCALEDIR);
1477 textdomain(PACKAGE);
1481 XtAppInitialize(&appContext, "XBoard", shellOptions,
1482 XtNumber(shellOptions),
1483 &argc, argv, xboardResources, NULL, 0);
1484 appData.boardSize = "";
1485 InitAppData(ConvertToLine(argc, argv));
1487 if (p == NULL) p = "/tmp";
1488 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1489 gameCopyFilename = (char*) malloc(i);
1490 gamePasteFilename = (char*) malloc(i);
1491 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1492 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1494 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1495 clientResources, XtNumber(clientResources),
1498 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1499 static char buf[MSG_SIZ];
1500 EscapeExpand(buf, appData.firstInitString);
1501 appData.firstInitString = strdup(buf);
1502 EscapeExpand(buf, appData.secondInitString);
1503 appData.secondInitString = strdup(buf);
1504 EscapeExpand(buf, appData.firstComputerString);
1505 appData.firstComputerString = strdup(buf);
1506 EscapeExpand(buf, appData.secondComputerString);
1507 appData.secondComputerString = strdup(buf);
1510 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1513 if (chdir(chessDir) != 0) {
1514 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1520 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1521 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1522 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1523 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1526 setbuf(debugFP, NULL);
1530 if (appData.debugMode) {
1531 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1535 /* [HGM,HR] make sure board size is acceptable */
1536 if(appData.NrFiles > BOARD_FILES ||
1537 appData.NrRanks > BOARD_RANKS )
1538 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1541 /* This feature does not work; animation needs a rewrite */
1542 appData.highlightDragging = FALSE;
1546 xDisplay = XtDisplay(shellWidget);
1547 xScreen = DefaultScreen(xDisplay);
1548 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1550 gameInfo.variant = StringToVariant(appData.variant);
1551 InitPosition(FALSE);
1554 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1556 if (isdigit(appData.boardSize[0])) {
1557 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1558 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1559 &fontPxlSize, &smallLayout, &tinyLayout);
1561 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1562 programName, appData.boardSize);
1566 /* Find some defaults; use the nearest known size */
1567 SizeDefaults *szd, *nearest;
1568 int distance = 99999;
1569 nearest = szd = sizeDefaults;
1570 while (szd->name != NULL) {
1571 if (abs(szd->squareSize - squareSize) < distance) {
1573 distance = abs(szd->squareSize - squareSize);
1574 if (distance == 0) break;
1578 if (i < 2) lineGap = nearest->lineGap;
1579 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1580 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1581 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1582 if (i < 6) smallLayout = nearest->smallLayout;
1583 if (i < 7) tinyLayout = nearest->tinyLayout;
1586 SizeDefaults *szd = sizeDefaults;
1587 if (*appData.boardSize == NULLCHAR) {
1588 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1589 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1592 if (szd->name == NULL) szd--;
1593 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1595 while (szd->name != NULL &&
1596 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1597 if (szd->name == NULL) {
1598 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1599 programName, appData.boardSize);
1603 squareSize = szd->squareSize;
1604 lineGap = szd->lineGap;
1605 clockFontPxlSize = szd->clockFontPxlSize;
1606 coordFontPxlSize = szd->coordFontPxlSize;
1607 fontPxlSize = szd->fontPxlSize;
1608 smallLayout = szd->smallLayout;
1609 tinyLayout = szd->tinyLayout;
1610 // [HGM] font: use defaults from settings file if available and not overruled
1612 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1613 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1614 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1615 appData.font = fontTable[MESSAGE_FONT][squareSize];
1616 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1617 appData.coordFont = fontTable[COORD_FONT][squareSize];
1619 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1620 if (strlen(appData.pixmapDirectory) > 0) {
1621 p = ExpandPathName(appData.pixmapDirectory);
1623 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1624 appData.pixmapDirectory);
1627 if (appData.debugMode) {
1628 fprintf(stderr, _("\
1629 XBoard square size (hint): %d\n\
1630 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1632 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1633 if (appData.debugMode) {
1634 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1637 defaultLineGap = lineGap;
1638 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1640 /* [HR] height treated separately (hacked) */
1641 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1642 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1643 if (appData.showJail == 1) {
1644 /* Jail on top and bottom */
1645 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1646 XtSetArg(boardArgs[2], XtNheight,
1647 boardHeight + 2*(lineGap + squareSize));
1648 } else if (appData.showJail == 2) {
1650 XtSetArg(boardArgs[1], XtNwidth,
1651 boardWidth + 2*(lineGap + squareSize));
1652 XtSetArg(boardArgs[2], XtNheight, boardHeight);
1655 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1656 XtSetArg(boardArgs[2], XtNheight, boardHeight);
1660 * Determine what fonts to use.
1663 appData.font = InsertPxlSize(appData.font, fontPxlSize);
1664 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1665 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1666 fontSet = CreateFontSet(appData.font);
1667 clockFontSet = CreateFontSet(appData.clockFont);
1669 /* For the coordFont, use the 0th font of the fontset. */
1670 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1671 XFontStruct **font_struct_list;
1672 XFontSetExtents *fontSize;
1673 char **font_name_list;
1674 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1675 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1676 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1677 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1678 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1681 appData.font = FindFont(appData.font, fontPxlSize);
1682 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1683 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1684 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1685 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1686 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1687 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1689 countFontID = coordFontID; // [HGM] holdings
1690 countFontStruct = coordFontStruct;
1692 xdb = XtDatabase(xDisplay);
1694 XrmPutLineResource(&xdb, "*international: True");
1695 vTo.size = sizeof(XFontSet);
1696 vTo.addr = (XtPointer) &fontSet;
1697 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1699 XrmPutStringResource(&xdb, "*font", appData.font);
1703 * Detect if there are not enough colors available and adapt.
1705 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1706 appData.monoMode = True;
1709 forceMono = MakeColors();
1712 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1714 appData.monoMode = True;
1717 if (appData.lowTimeWarning && !appData.monoMode) {
1718 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1719 vFrom.size = strlen(appData.lowTimeWarningColor);
1720 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1721 if (vTo.addr == NULL)
1722 appData.monoMode = True;
1724 lowTimeWarningColor = *(Pixel *) vTo.addr;
1727 if (appData.monoMode && appData.debugMode) {
1728 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1729 (unsigned long) XWhitePixel(xDisplay, xScreen),
1730 (unsigned long) XBlackPixel(xDisplay, xScreen));
1733 ParseIcsTextColors();
1734 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
1735 textColors[ColorNone].attr = 0;
1737 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1743 layoutName = "tinyLayout";
1744 } else if (smallLayout) {
1745 layoutName = "smallLayout";
1747 layoutName = "normalLayout";
1749 /* Outer layoutWidget is there only to provide a name for use in
1750 resources that depend on the layout style */
1752 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
1753 layoutArgs, XtNumber(layoutArgs));
1755 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
1756 formArgs, XtNumber(formArgs));
1757 XtSetArg(args[0], XtNdefaultDistance, &sep);
1758 XtGetValues(formWidget, args, 1);
1761 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
1762 XtSetArg(args[0], XtNtop, XtChainTop);
1763 XtSetArg(args[1], XtNbottom, XtChainTop);
1764 XtSetArg(args[2], XtNright, XtChainLeft);
1765 XtSetValues(menuBarWidget, args, 3);
1767 widgetList[j++] = whiteTimerWidget =
1768 XtCreateWidget("whiteTime", labelWidgetClass,
1769 formWidget, timerArgs, XtNumber(timerArgs));
1771 XtSetArg(args[0], XtNfontSet, clockFontSet);
1773 XtSetArg(args[0], XtNfont, clockFontStruct);
1775 XtSetArg(args[1], XtNtop, XtChainTop);
1776 XtSetArg(args[2], XtNbottom, XtChainTop);
1777 XtSetValues(whiteTimerWidget, args, 3);
1779 widgetList[j++] = blackTimerWidget =
1780 XtCreateWidget("blackTime", labelWidgetClass,
1781 formWidget, timerArgs, XtNumber(timerArgs));
1783 XtSetArg(args[0], XtNfontSet, clockFontSet);
1785 XtSetArg(args[0], XtNfont, clockFontStruct);
1787 XtSetArg(args[1], XtNtop, XtChainTop);
1788 XtSetArg(args[2], XtNbottom, XtChainTop);
1789 XtSetValues(blackTimerWidget, args, 3);
1791 if (appData.titleInWindow) {
1792 widgetList[j++] = titleWidget =
1793 XtCreateWidget("title", labelWidgetClass, formWidget,
1794 titleArgs, XtNumber(titleArgs));
1795 XtSetArg(args[0], XtNtop, XtChainTop);
1796 XtSetArg(args[1], XtNbottom, XtChainTop);
1797 XtSetValues(titleWidget, args, 2);
1800 if (appData.showButtonBar) {
1801 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
1802 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
1803 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
1804 XtSetArg(args[2], XtNtop, XtChainTop);
1805 XtSetArg(args[3], XtNbottom, XtChainTop);
1806 XtSetValues(buttonBarWidget, args, 4);
1809 widgetList[j++] = messageWidget =
1810 XtCreateWidget("message", labelWidgetClass, formWidget,
1811 messageArgs, XtNumber(messageArgs));
1812 XtSetArg(args[0], XtNtop, XtChainTop);
1813 XtSetArg(args[1], XtNbottom, XtChainTop);
1814 XtSetValues(messageWidget, args, 2);
1816 widgetList[j++] = boardWidget =
1817 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
1818 XtNumber(boardArgs));
1820 XtManageChildren(widgetList, j);
1822 timerWidth = (boardWidth - sep) / 2;
1823 XtSetArg(args[0], XtNwidth, timerWidth);
1824 XtSetValues(whiteTimerWidget, args, 1);
1825 XtSetValues(blackTimerWidget, args, 1);
1827 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1828 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1829 XtGetValues(whiteTimerWidget, args, 2);
1831 if (appData.showButtonBar) {
1832 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1833 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1834 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
1838 * formWidget uses these constraints but they are stored
1842 XtSetArg(args[i], XtNfromHoriz, 0); i++;
1843 XtSetValues(menuBarWidget, args, i);
1844 if (appData.titleInWindow) {
1847 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1848 XtSetValues(whiteTimerWidget, args, i);
1850 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1851 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1852 XtSetValues(blackTimerWidget, args, i);
1854 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1855 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
1856 XtSetValues(titleWidget, args, i);
1858 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1859 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1860 XtSetValues(messageWidget, args, i);
1861 if (appData.showButtonBar) {
1863 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1864 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1865 XtSetValues(buttonBarWidget, args, i);
1869 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1870 XtSetValues(whiteTimerWidget, args, i);
1872 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1873 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1874 XtSetValues(blackTimerWidget, args, i);
1876 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
1877 XtSetValues(titleWidget, args, i);
1879 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1880 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1881 XtSetValues(messageWidget, args, i);
1882 if (appData.showButtonBar) {
1884 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1885 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1886 XtSetValues(buttonBarWidget, args, i);
1891 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1892 XtSetValues(whiteTimerWidget, args, i);
1894 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1895 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1896 XtSetValues(blackTimerWidget, args, i);
1898 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1899 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1900 XtSetValues(messageWidget, args, i);
1901 if (appData.showButtonBar) {
1903 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1904 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1905 XtSetValues(buttonBarWidget, args, i);
1909 XtSetArg(args[0], XtNfromVert, messageWidget);
1910 XtSetArg(args[1], XtNtop, XtChainTop);
1911 XtSetArg(args[2], XtNbottom, XtChainBottom);
1912 XtSetArg(args[3], XtNleft, XtChainLeft);
1913 XtSetArg(args[4], XtNright, XtChainRight);
1914 XtSetValues(boardWidget, args, 5);
1916 XtRealizeWidget(shellWidget);
1919 XtSetArg(args[0], XtNx, wpMain.x);
1920 XtSetArg(args[1], XtNy, wpMain.y);
1921 XtSetValues(shellWidget, args, 2);
1925 * Correct the width of the message and title widgets.
1926 * It is not known why some systems need the extra fudge term.
1927 * The value "2" is probably larger than needed.
1929 XawFormDoLayout(formWidget, False);
1931 #define WIDTH_FUDGE 2
1933 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1934 XtSetArg(args[i], XtNheight, &h); i++;
1935 XtGetValues(messageWidget, args, i);
1936 if (appData.showButtonBar) {
1938 XtSetArg(args[i], XtNwidth, &w); i++;
1939 XtGetValues(buttonBarWidget, args, i);
1940 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1942 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
1945 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1946 if (gres != XtGeometryYes && appData.debugMode) {
1947 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1948 programName, gres, w, h, wr, hr);
1951 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
1952 /* The size used for the child widget in layout lags one resize behind
1953 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
1955 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1956 if (gres != XtGeometryYes && appData.debugMode) {
1957 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1958 programName, gres, w, h, wr, hr);
1961 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
1962 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
1963 XtSetArg(args[1], XtNright, XtChainRight);
1964 XtSetValues(messageWidget, args, 2);
1966 if (appData.titleInWindow) {
1968 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1969 XtSetArg(args[i], XtNheight, &h); i++;
1970 XtGetValues(titleWidget, args, i);
1972 w = boardWidth - 2*bor;
1974 XtSetArg(args[0], XtNwidth, &w);
1975 XtGetValues(menuBarWidget, args, 1);
1976 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1979 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1980 if (gres != XtGeometryYes && appData.debugMode) {
1982 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1983 programName, gres, w, h, wr, hr);
1986 XawFormDoLayout(formWidget, True);
1988 xBoardWindow = XtWindow(boardWidget);
1990 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1991 // not need to go into InitDrawingSizes().
1995 * Create X checkmark bitmap and initialize option menu checks.
1997 ReadBitmap(&xMarkPixmap, "checkmark.bm",
1998 checkmark_bits, checkmark_width, checkmark_height);
2004 ReadBitmap(&wIconPixmap, "icon_white.bm",
2005 icon_white_bits, icon_white_width, icon_white_height);
2006 ReadBitmap(&bIconPixmap, "icon_black.bm",
2007 icon_black_bits, icon_black_width, icon_black_height);
2008 iconPixmap = wIconPixmap;
2010 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2011 XtSetValues(shellWidget, args, i);
2014 * Create a cursor for the board widget.
2016 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2017 XChangeWindowAttributes(xDisplay, xBoardWindow,
2018 CWCursor, &window_attributes);
2021 * Inhibit shell resizing.
2023 shellArgs[0].value = (XtArgVal) &w;
2024 shellArgs[1].value = (XtArgVal) &h;
2025 XtGetValues(shellWidget, shellArgs, 2);
2026 shellArgs[4].value = shellArgs[2].value = w;
2027 shellArgs[5].value = shellArgs[3].value = h;
2028 XtSetValues(shellWidget, &shellArgs[2], 4);
2029 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2030 marginH = h - boardHeight;
2032 CatchDeleteWindow(shellWidget, "QuitProc");
2040 if (appData.animate || appData.animateDragging)
2043 XtAugmentTranslations(formWidget,
2044 XtParseTranslationTable(globalTranslations));
2045 XtAugmentTranslations(boardWidget,
2046 XtParseTranslationTable(boardTranslations));
2047 XtAugmentTranslations(whiteTimerWidget,
2048 XtParseTranslationTable(whiteTranslations));
2049 XtAugmentTranslations(blackTimerWidget,
2050 XtParseTranslationTable(blackTranslations));
2052 /* Why is the following needed on some versions of X instead
2053 * of a translation? */
2054 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2055 (XtEventHandler) EventProc, NULL);
2057 XtAddEventHandler(formWidget, KeyPressMask, False,
2058 (XtEventHandler) MoveTypeInProc, NULL);
2059 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
2060 (XtEventHandler) EventProc, NULL);
2062 /* [AS] Restore layout */
2063 if( wpMoveHistory.visible ) {
2067 if( wpEvalGraph.visible )
2072 if( wpEngineOutput.visible ) {
2073 EngineOutputPopUp();
2078 if (errorExitStatus == -1) {
2079 if (appData.icsActive) {
2080 /* We now wait until we see "login:" from the ICS before
2081 sending the logon script (problems with timestamp otherwise) */
2082 /*ICSInitScript();*/
2083 if (appData.icsInputBox) ICSInputBoxPopUp();
2087 signal(SIGWINCH, TermSizeSigHandler);
2089 signal(SIGINT, IntSigHandler);
2090 signal(SIGTERM, IntSigHandler);
2091 if (*appData.cmailGameName != NULLCHAR) {
2092 signal(SIGUSR1, CmailSigHandler);
2096 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2098 // XtSetKeyboardFocus(shellWidget, formWidget);
2099 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2101 XtAppMainLoop(appContext);
2102 if (appData.debugMode) fclose(debugFP); // [DM] debug
2106 static Boolean noEcho;
2111 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2112 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2114 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2115 unlink(gameCopyFilename);
2116 unlink(gamePasteFilename);
2117 if(noEcho) EchoOn();
2121 TermSizeSigHandler (int sig)
2127 IntSigHandler (int sig)
2133 CmailSigHandler (int sig)
2138 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2140 /* Activate call-back function CmailSigHandlerCallBack() */
2141 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2143 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2147 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
2150 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2152 /**** end signal code ****/
2158 /* try to open the icsLogon script, either in the location given
2159 * or in the users HOME directory
2166 f = fopen(appData.icsLogon, "r");
2169 homedir = getenv("HOME");
2170 if (homedir != NULL)
2172 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2173 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2174 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2175 f = fopen(buf, "r");
2180 ProcessICSInitScript(f);
2182 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2195 #define Abs(n) ((n)<0 ? -(n) : (n))
2199 InsertPxlSize (char *pattern, int targetPxlSize)
2201 char *base_fnt_lst, strInt[12], *p, *q;
2202 int alternatives, i, len, strIntLen;
2205 * Replace the "*" (if present) in the pixel-size slot of each
2206 * alternative with the targetPxlSize.
2210 while ((p = strchr(p, ',')) != NULL) {
2214 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
2215 strIntLen = strlen(strInt);
2216 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
2220 while (alternatives--) {
2221 char *comma = strchr(p, ',');
2222 for (i=0; i<14; i++) {
2223 char *hyphen = strchr(p, '-');
2225 if (comma && hyphen > comma) break;
2226 len = hyphen + 1 - p;
2227 if (i == 7 && *p == '*' && len == 2) {
2229 memcpy(q, strInt, strIntLen);
2239 len = comma + 1 - p;
2246 return base_fnt_lst;
2250 CreateFontSet (char *base_fnt_lst)
2253 char **missing_list;
2257 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
2258 &missing_list, &missing_count, &def_string);
2259 if (appData.debugMode) {
2261 XFontStruct **font_struct_list;
2262 char **font_name_list;
2263 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
2265 fprintf(debugFP, " got list %s, locale %s\n",
2266 XBaseFontNameListOfFontSet(fntSet),
2267 XLocaleOfFontSet(fntSet));
2268 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
2269 for (i = 0; i < count; i++) {
2270 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
2273 for (i = 0; i < missing_count; i++) {
2274 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
2277 if (fntSet == NULL) {
2278 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
2283 #else // not ENABLE_NLS
2285 * Find a font that matches "pattern" that is as close as
2286 * possible to the targetPxlSize. Prefer fonts that are k
2287 * pixels smaller to fonts that are k pixels larger. The
2288 * pattern must be in the X Consortium standard format,
2289 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2290 * The return value should be freed with XtFree when no
2294 FindFont (char *pattern, int targetPxlSize)
2296 char **fonts, *p, *best, *scalable, *scalableTail;
2297 int i, j, nfonts, minerr, err, pxlSize;
2299 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2301 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2302 programName, pattern);
2309 for (i=0; i<nfonts; i++) {
2312 if (*p != '-') continue;
2314 if (*p == NULLCHAR) break;
2315 if (*p++ == '-') j++;
2317 if (j < 7) continue;
2320 scalable = fonts[i];
2323 err = pxlSize - targetPxlSize;
2324 if (Abs(err) < Abs(minerr) ||
2325 (minerr > 0 && err < 0 && -err == minerr)) {
2331 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2332 /* If the error is too big and there is a scalable font,
2333 use the scalable font. */
2334 int headlen = scalableTail - scalable;
2335 p = (char *) XtMalloc(strlen(scalable) + 10);
2336 while (isdigit(*scalableTail)) scalableTail++;
2337 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2339 p = (char *) XtMalloc(strlen(best) + 2);
2340 safeStrCpy(p, best, strlen(best)+1 );
2342 if (appData.debugMode) {
2343 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2344 pattern, targetPxlSize, p);
2346 XFreeFontNames(fonts);
2353 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
2354 // must be called before all non-first callse to CreateGCs()
2355 XtReleaseGC(shellWidget, highlineGC);
2356 XtReleaseGC(shellWidget, lightSquareGC);
2357 XtReleaseGC(shellWidget, darkSquareGC);
2358 XtReleaseGC(shellWidget, lineGC);
2359 if (appData.monoMode) {
2360 if (DefaultDepth(xDisplay, xScreen) == 1) {
2361 XtReleaseGC(shellWidget, wbPieceGC);
2363 XtReleaseGC(shellWidget, bwPieceGC);
2366 XtReleaseGC(shellWidget, prelineGC);
2367 XtReleaseGC(shellWidget, jailSquareGC);
2368 XtReleaseGC(shellWidget, wdPieceGC);
2369 XtReleaseGC(shellWidget, wlPieceGC);
2370 XtReleaseGC(shellWidget, wjPieceGC);
2371 XtReleaseGC(shellWidget, bdPieceGC);
2372 XtReleaseGC(shellWidget, blPieceGC);
2373 XtReleaseGC(shellWidget, bjPieceGC);
2378 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
2380 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2381 | GCBackground | GCFunction | GCPlaneMask;
2382 gc_values->foreground = foreground;
2383 gc_values->background = background;
2384 return XtGetGC(shellWidget, value_mask, gc_values);
2388 CreateGCs (int redo)
2390 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2391 | GCBackground | GCFunction | GCPlaneMask;
2392 XGCValues gc_values;
2394 Pixel white = XWhitePixel(xDisplay, xScreen);
2395 Pixel black = XBlackPixel(xDisplay, xScreen);
2397 gc_values.plane_mask = AllPlanes;
2398 gc_values.line_width = lineGap;
2399 gc_values.line_style = LineSolid;
2400 gc_values.function = GXcopy;
2403 DeleteGCs(); // called a second time; clean up old GCs first
2404 } else { // [HGM] grid and font GCs created on first call only
2405 coordGC = CreateOneGC(&gc_values, black, white);
2406 XSetFont(xDisplay, coordGC, coordFontID);
2408 // [HGM] make font for holdings counts (white on black)
2409 countGC = CreateOneGC(&gc_values, white, black);
2410 XSetFont(xDisplay, countGC, countFontID);
2412 lineGC = CreateOneGC(&gc_values, black, black);
2414 if (appData.monoMode) {
2416 highlineGC = CreateOneGC(&gc_values, white, white);
2417 lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
2418 darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
2420 if (DefaultDepth(xDisplay, xScreen) == 1) {
2421 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2422 gc_values.function = GXcopyInverted;
2423 copyInvertedGC = CreateOneGC(&gc_values, black, white);
2424 gc_values.function = GXcopy;
2425 if (XBlackPixel(xDisplay, xScreen) == 1) {
2426 bwPieceGC = darkSquareGC;
2427 wbPieceGC = copyInvertedGC;
2429 bwPieceGC = copyInvertedGC;
2430 wbPieceGC = lightSquareGC;
2435 highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
2436 prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
2437 lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
2438 darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
2439 jailSquareGC = CreateOneGC(&gc_values, jailSquareColor, jailSquareColor);
2440 wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
2441 wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
2442 wjPieceGC = CreateOneGC(&gc_values, whitePieceColor, jailSquareColor);
2443 bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
2444 blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
2445 bjPieceGC = CreateOneGC(&gc_values, blackPieceColor, jailSquareColor);
2450 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
2458 fp = fopen(filename, "rb");
2460 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
2467 for (y=0; y<h; ++y) {
2468 for (x=0; x<h; ++x) {
2473 XPutPixel(xim, x, y, blackPieceColor);
2475 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2478 XPutPixel(xim, x, y, darkSquareColor);
2480 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2483 XPutPixel(xim, x, y, whitePieceColor);
2485 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2488 XPutPixel(xim, x, y, lightSquareColor);
2490 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2498 /* create Pixmap of piece */
2499 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2501 XPutImage(xDisplay, *dest, lightSquareGC, xim,
2504 /* create Pixmap of clipmask
2505 Note: We assume the white/black pieces have the same
2506 outline, so we make only 6 masks. This is okay
2507 since the XPM clipmask routines do the same. */
2509 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2511 XPutImage(xDisplay, temp, lightSquareGC, xmask,
2514 /* now create the 1-bit version */
2515 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2518 values.foreground = 1;
2519 values.background = 0;
2521 /* Don't use XtGetGC, not read only */
2522 maskGC = XCreateGC(xDisplay, *mask,
2523 GCForeground | GCBackground, &values);
2524 XCopyPlane(xDisplay, temp, *mask, maskGC,
2525 0, 0, squareSize, squareSize, 0, 0, 1);
2526 XFreePixmap(xDisplay, temp);
2531 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
2539 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
2544 /* The XSynchronize calls were copied from CreatePieces.
2545 Not sure if needed, but can't hurt */
2546 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2549 /* temp needed by loadXIM() */
2550 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2551 0, 0, ss, ss, AllPlanes, XYPixmap);
2553 if (strlen(appData.pixmapDirectory) == 0) {
2557 if (appData.monoMode) {
2558 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
2562 fprintf(stderr, _("\nLoading XIMs...\n"));
2564 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2565 fprintf(stderr, "%d", piece+1);
2566 for (kind=0; kind<4; kind++) {
2567 fprintf(stderr, ".");
2568 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2569 ExpandPathName(appData.pixmapDirectory),
2570 piece <= (int) WhiteKing ? "" : "w",
2571 pieceBitmapNames[piece],
2573 ximPieceBitmap[kind][piece] =
2574 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2575 0, 0, ss, ss, AllPlanes, XYPixmap);
2576 if (appData.debugMode)
2577 fprintf(stderr, _("(File:%s:) "), buf);
2578 loadXIM(ximPieceBitmap[kind][piece],
2580 &(xpmPieceBitmap2[kind][piece]),
2581 &(ximMaskPm2[piece]));
2582 if(piece <= (int)WhiteKing)
2583 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2585 fprintf(stderr," ");
2587 /* Load light and dark squares */
2588 /* If the LSQ and DSQ pieces don't exist, we will
2589 draw them with solid squares. */
2590 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2591 if (access(buf, 0) != 0) {
2595 fprintf(stderr, _("light square "));
2597 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2598 0, 0, ss, ss, AllPlanes, XYPixmap);
2599 if (appData.debugMode)
2600 fprintf(stderr, _("(File:%s:) "), buf);
2602 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2603 fprintf(stderr, _("dark square "));
2604 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2605 ExpandPathName(appData.pixmapDirectory), ss);
2606 if (appData.debugMode)
2607 fprintf(stderr, _("(File:%s:) "), buf);
2609 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2610 0, 0, ss, ss, AllPlanes, XYPixmap);
2611 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2612 xpmJailSquare = xpmLightSquare;
2614 fprintf(stderr, _("Done.\n"));
2616 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2619 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2623 CreateXPMBoard (char *s, int kind)
2627 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2628 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2629 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2635 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2636 // thisroutine has to be called t free the old piece pixmaps
2638 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2639 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2641 XFreePixmap(xDisplay, xpmLightSquare);
2642 XFreePixmap(xDisplay, xpmDarkSquare);
2651 u_int ss = squareSize;
2653 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2654 XpmColorSymbol symbols[4];
2655 static int redo = False;
2657 if(redo) FreeXPMPieces(); else redo = 1;
2659 /* The XSynchronize calls were copied from CreatePieces.
2660 Not sure if needed, but can't hurt */
2661 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2663 /* Setup translations so piece colors match square colors */
2664 symbols[0].name = "light_piece";
2665 symbols[0].value = appData.whitePieceColor;
2666 symbols[1].name = "dark_piece";
2667 symbols[1].value = appData.blackPieceColor;
2668 symbols[2].name = "light_square";
2669 symbols[2].value = appData.lightSquareColor;
2670 symbols[3].name = "dark_square";
2671 symbols[3].value = appData.darkSquareColor;
2673 attr.valuemask = XpmColorSymbols;
2674 attr.colorsymbols = symbols;
2675 attr.numsymbols = 4;
2677 if (appData.monoMode) {
2678 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2682 if (strlen(appData.pixmapDirectory) == 0) {
2683 XpmPieces* pieces = builtInXpms;
2686 while (pieces->size != squareSize && pieces->size) pieces++;
2687 if (!pieces->size) {
2688 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2691 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2692 for (kind=0; kind<4; kind++) {
2694 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2695 pieces->xpm[piece][kind],
2696 &(xpmPieceBitmap2[kind][piece]),
2697 NULL, &attr)) != 0) {
2698 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2702 if(piece <= (int) WhiteKing)
2703 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2707 xpmJailSquare = xpmLightSquare;
2711 fprintf(stderr, _("\nLoading XPMs...\n"));
2714 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2715 fprintf(stderr, "%d ", piece+1);
2716 for (kind=0; kind<4; kind++) {
2717 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2718 ExpandPathName(appData.pixmapDirectory),
2719 piece > (int) WhiteKing ? "w" : "",
2720 pieceBitmapNames[piece],
2722 if (appData.debugMode) {
2723 fprintf(stderr, _("(File:%s:) "), buf);
2725 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2726 &(xpmPieceBitmap2[kind][piece]),
2727 NULL, &attr)) != 0) {
2728 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2729 // [HGM] missing: read of unorthodox piece failed; substitute King.
2730 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2731 ExpandPathName(appData.pixmapDirectory),
2733 if (appData.debugMode) {
2734 fprintf(stderr, _("(Replace by File:%s:) "), buf);
2736 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2737 &(xpmPieceBitmap2[kind][piece]),
2741 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2746 if(piece <= (int) WhiteKing)
2747 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2750 /* Load light and dark squares */
2751 /* If the LSQ and DSQ pieces don't exist, we will
2752 draw them with solid squares. */
2753 fprintf(stderr, _("light square "));
2754 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2755 if (access(buf, 0) != 0) {
2759 if (appData.debugMode)
2760 fprintf(stderr, _("(File:%s:) "), buf);
2762 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2763 &xpmLightSquare, NULL, &attr)) != 0) {
2764 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2767 fprintf(stderr, _("dark square "));
2768 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2769 ExpandPathName(appData.pixmapDirectory), ss);
2770 if (appData.debugMode) {
2771 fprintf(stderr, _("(File:%s:) "), buf);
2773 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2774 &xpmDarkSquare, NULL, &attr)) != 0) {
2775 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2779 xpmJailSquare = xpmLightSquare;
2780 fprintf(stderr, _("Done.\n"));
2782 oldVariant = -1; // kludge to force re-makig of animation masks
2783 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2786 #endif /* HAVE_LIBXPM */
2789 /* No built-in bitmaps */
2794 u_int ss = squareSize;
2796 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2799 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2800 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2801 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2802 pieceBitmapNames[piece],
2803 ss, kind == SOLID ? 's' : 'o');
2804 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2805 if(piece <= (int)WhiteKing)
2806 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2810 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2814 /* With built-in bitmaps */
2818 BuiltInBits* bib = builtInBits;
2821 u_int ss = squareSize;
2823 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2826 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2828 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2829 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2830 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2831 pieceBitmapNames[piece],
2832 ss, kind == SOLID ? 's' : 'o');
2833 ReadBitmap(&pieceBitmap2[kind][piece], buf,
2834 bib->bits[kind][piece], ss, ss);
2835 if(piece <= (int)WhiteKing)
2836 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2840 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2846 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2851 char msg[MSG_SIZ], fullname[MSG_SIZ];
2853 if (*appData.bitmapDirectory != NULLCHAR) {
2854 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2855 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2856 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2857 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2858 &w, &h, pm, &x_hot, &y_hot);
2859 fprintf(stderr, "load %s\n", name);
2860 if (errcode != BitmapSuccess) {
2862 case BitmapOpenFailed:
2863 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2865 case BitmapFileInvalid:
2866 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2868 case BitmapNoMemory:
2869 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2873 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2877 fprintf(stderr, _("%s: %s...using built-in\n"),
2879 } else if (w != wreq || h != hreq) {
2881 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2882 programName, fullname, w, h, wreq, hreq);
2888 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2898 if (lineGap == 0) return;
2900 /* [HR] Split this into 2 loops for non-square boards. */
2902 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2903 gridSegments[i].x1 = 0;
2904 gridSegments[i].x2 =
2905 lineGap + BOARD_WIDTH * (squareSize + lineGap);
2906 gridSegments[i].y1 = gridSegments[i].y2
2907 = lineGap / 2 + (i * (squareSize + lineGap));
2910 for (j = 0; j < BOARD_WIDTH + 1; j++) {
2911 gridSegments[j + i].y1 = 0;
2912 gridSegments[j + i].y2 =
2913 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2914 gridSegments[j + i].x1 = gridSegments[j + i].x2
2915 = lineGap / 2 + (j * (squareSize + lineGap));
2919 int nrOfMenuItems = 7;
2920 Widget menuWidget[150];
2921 MenuListItem menuItemList[150] = {
2922 { "LoadNextGameProc", LoadNextGameProc },
2923 { "LoadPrevGameProc", LoadPrevGameProc },
2924 { "ReloadGameProc", ReloadGameProc },
2925 { "ReloadPositionProc", ReloadPositionProc },
2926 #ifndef OPTIONSDIALOG
2927 { "AlwaysQueenProc", AlwaysQueenProc },
2928 { "AnimateDraggingProc", AnimateDraggingProc },
2929 { "AnimateMovingProc", AnimateMovingProc },
2930 { "AutoflagProc", AutoflagProc },
2931 { "AutoflipProc", AutoflipProc },
2932 { "BlindfoldProc", BlindfoldProc },
2933 { "FlashMovesProc", FlashMovesProc },
2935 { "HighlightDraggingProc", HighlightDraggingProc },
2937 { "HighlightLastMoveProc", HighlightLastMoveProc },
2938 // { "IcsAlarmProc", IcsAlarmProc },
2939 { "MoveSoundProc", MoveSoundProc },
2940 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
2941 { "PopupExitMessageProc", PopupExitMessageProc },
2942 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
2943 // { "PremoveProc", PremoveProc },
2944 { "ShowCoordsProc", ShowCoordsProc },
2945 { "ShowThinkingProc", ShowThinkingProc },
2946 { "HideThinkingProc", HideThinkingProc },
2947 { "TestLegalityProc", TestLegalityProc },
2949 { "AboutGameProc", AboutGameEvent },
2950 { "DebugProc", DebugProc },
2951 { "NothingProc", NothingProc },
2956 MarkMenuItem (char *menuRef, int state)
2958 int nr = MenuToNumber(menuRef);
2961 XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2962 XtSetValues(menuWidget[nr], args, 1);
2967 EnableMenuItem (char *menuRef, int state)
2969 int nr = MenuToNumber(menuRef);
2970 if(nr >= 0) XtSetSensitive(menuWidget[nr], state);
2974 EnableButtonBar (int state)
2976 XtSetSensitive(buttonBarWidget, state);
2981 SetMenuEnables (Enables *enab)
2983 while (enab->name != NULL) {
2984 EnableMenuItem(enab->name, enab->value);
2990 Equal(char *p, char *s)
2991 { // compare strings skipping spaces in second
2993 if(*s == ' ') { s++; continue; }
2994 if(*s++ != *p++) return 0;
3000 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3001 { // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
3003 if(*nprms == 0) return;
3004 for(i=0; menuItemList[i].name; i++) {
3005 if(Equal(prms[0], menuItemList[i].name)) {
3006 (menuItemList[i].proc) ();
3013 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
3015 MenuProc *proc = (MenuProc *) addr;
3021 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
3023 RecentEngineEvent((int) (intptr_t) addr);
3026 // some stuff that must remain in front-end
3027 static Widget mainBar, currentMenu;
3028 static int wtot, nr = 0, widths[10];
3031 AppendMenuItem (char *text, char *name, MenuProc *action)
3038 XtSetArg(args[j], XtNleftMargin, 20); j++;
3039 XtSetArg(args[j], XtNrightMargin, 20); j++;
3041 if (strcmp(text, "----") == 0) {
3042 entry = XtCreateManagedWidget(text, smeLineObjectClass,
3043 currentMenu, args, j);
3045 XtSetArg(args[j], XtNlabel, XtNewString(_(text)));
3046 entry = XtCreateManagedWidget(name, smeBSBObjectClass,
3047 currentMenu, args, j+1);
3048 XtAddCallback(entry, XtNcallback,
3049 (XtCallbackProc) (strcmp(name, "recent") ? MenuBarSelect : MenuEngineSelect),
3051 menuWidget[nrOfMenuItems] = entry;
3056 CreateMenuButton (char *name, Menu *mb)
3057 { // create menu button on main bar, and shell for pull-down list
3063 XtSetArg(args[j], XtNmenuName, XtNewString(name)); j++;
3064 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3065 XtSetArg(args[j], XtNborderWidth, 0); j++;
3066 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3068 currentMenu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3071 XtSetArg(args[j], XtNwidth, &w); j++;
3072 XtGetValues(mb->subMenu, args, j);
3073 wtot += mb->textWidth = widths[nr++] = w;
3077 CreateMenuBar (Menu *mb, int boardWidth)
3081 char menuName[MSG_SIZ];
3085 // create bar itself
3087 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3088 XtSetArg(args[j], XtNvSpace, 0); j++;
3089 XtSetArg(args[j], XtNborderWidth, 0); j++;
3090 mainBar = XtCreateWidget("menuBar", boxWidgetClass,
3091 formWidget, args, j);
3093 CreateMainMenus(mb); // put menus in bar according to description in back-end
3095 // size buttons to make menu bar fit, clipping menu names where necessary
3096 while(wtot > boardWidth - 40) {
3098 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
3102 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
3104 XtSetArg(args[j], XtNwidth, widths[i]); j++;
3105 XtSetValues(ma[i].subMenu, args, j);
3112 CreateButtonBar (MenuItem *mi)
3115 Widget button, buttonBar;
3119 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3121 XtSetArg(args[j], XtNhSpace, 0); j++;
3123 XtSetArg(args[j], XtNborderWidth, 0); j++;
3124 XtSetArg(args[j], XtNvSpace, 0); j++;
3125 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3126 formWidget, args, j);
3128 while (mi->string != NULL) {
3131 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3132 XtSetArg(args[j], XtNborderWidth, 0); j++;
3134 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3135 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3136 buttonBar, args, j);
3137 XtAddCallback(button, XtNcallback,
3138 (XtCallbackProc) MenuBarSelect,
3139 (caddr_t) mi->proc);
3146 CreatePieceMenu (char *name, int color)
3151 ChessSquare selection;
3153 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3154 boardWidget, args, 0);
3156 for (i = 0; i < PIECE_MENU_SIZE; i++) {
3157 String item = pieceMenuStrings[color][i];
3159 if (strcmp(item, "----") == 0) {
3160 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3163 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3164 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3166 selection = pieceMenuTranslation[color][i];
3167 XtAddCallback(entry, XtNcallback,
3168 (XtCallbackProc) PieceMenuSelect,
3169 (caddr_t) selection);
3170 if (selection == WhitePawn || selection == BlackPawn) {
3171 XtSetArg(args[0], XtNpopupOnEntry, entry);
3172 XtSetValues(menu, args, 1);
3185 ChessSquare selection;
3187 whitePieceMenu = CreatePieceMenu("menuW", 0);
3188 blackPieceMenu = CreatePieceMenu("menuB", 1);
3190 if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
3191 XtRegisterGrabAction(PieceMenuPopup, True,
3192 (unsigned)(ButtonPressMask|ButtonReleaseMask),
3193 GrabModeAsync, GrabModeAsync);
3195 XtSetArg(args[0], XtNlabel, _("Drop"));
3196 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3197 boardWidget, args, 1);
3198 for (i = 0; i < DROP_MENU_SIZE; i++) {
3199 String item = dropMenuStrings[i];
3201 if (strcmp(item, "----") == 0) {
3202 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3205 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3206 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3208 selection = dropMenuTranslation[i];
3209 XtAddCallback(entry, XtNcallback,
3210 (XtCallbackProc) DropMenuSelect,
3211 (caddr_t) selection);
3225 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3226 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3227 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3228 dmEnables[i].piece);
3229 XtSetSensitive(entry, p != NULL || !appData.testLegality
3230 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3231 && !appData.icsActive));
3233 while (p && *p++ == dmEnables[i].piece) count++;
3234 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3236 XtSetArg(args[j], XtNlabel, label); j++;
3237 XtSetValues(entry, args, j);
3242 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
3244 String whichMenu; int menuNr = -2;
3245 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3246 if (event->type == ButtonRelease)
3247 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3248 else if (event->type == ButtonPress)
3249 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3251 case 0: whichMenu = params[0]; break;
3252 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3254 case -1: if (errorUp) ErrorPopDown();
3257 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3261 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3263 if (pmFromX < 0 || pmFromY < 0) return;
3264 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3268 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3270 if (pmFromX < 0 || pmFromY < 0) return;
3271 DropMenuEvent(piece, pmFromX, pmFromY);
3275 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3277 shiftKey = prms[0][0] & 1;
3282 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3284 shiftKey = prms[0][0] & 1;
3290 do_flash_delay (unsigned long msec)
3296 DrawBorder (int x, int y, int type)
3300 if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
3302 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3303 squareSize+lineGap, squareSize+lineGap);
3307 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
3309 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
3310 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
3312 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
3313 if(textureW[kind] < W*squareSize)
3314 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
3316 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
3317 if(textureH[kind] < H*squareSize)
3318 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
3320 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
3325 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
3326 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
3328 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
3329 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
3330 squareSize, squareSize, x*fac, y*fac);
3332 if (useImages && useImageSqs) {
3336 pm = xpmLightSquare;
3341 case 2: /* neutral */
3346 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3347 squareSize, squareSize, x*fac, y*fac);
3357 case 2: /* neutral */
3362 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
3367 I split out the routines to draw a piece so that I could
3368 make a generic flash routine.
3371 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3373 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3374 switch (square_color) {
3376 case 2: /* neutral */
3378 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3379 ? *pieceToOutline(piece)
3380 : *pieceToSolid(piece),
3381 dest, bwPieceGC, 0, 0,
3382 squareSize, squareSize, x, y);
3385 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3386 ? *pieceToSolid(piece)
3387 : *pieceToOutline(piece),
3388 dest, wbPieceGC, 0, 0,
3389 squareSize, squareSize, x, y);
3395 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3397 switch (square_color) {
3399 case 2: /* neutral */
3401 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3402 ? *pieceToOutline(piece)
3403 : *pieceToSolid(piece),
3404 dest, bwPieceGC, 0, 0,
3405 squareSize, squareSize, x, y, 1);
3408 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3409 ? *pieceToSolid(piece)
3410 : *pieceToOutline(piece),
3411 dest, wbPieceGC, 0, 0,
3412 squareSize, squareSize, x, y, 1);
3418 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3420 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
3421 switch (square_color) {
3423 XCopyPlane(xDisplay, *pieceToSolid(piece),
3424 dest, (int) piece < (int) BlackPawn
3425 ? wlPieceGC : blPieceGC, 0, 0,
3426 squareSize, squareSize, x, y, 1);
3429 XCopyPlane(xDisplay, *pieceToSolid(piece),
3430 dest, (int) piece < (int) BlackPawn
3431 ? wdPieceGC : bdPieceGC, 0, 0,
3432 squareSize, squareSize, x, y, 1);
3434 case 2: /* neutral */
3436 XCopyPlane(xDisplay, *pieceToSolid(piece),
3437 dest, (int) piece < (int) BlackPawn
3438 ? wjPieceGC : bjPieceGC, 0, 0,
3439 squareSize, squareSize, x, y, 1);
3445 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3447 int kind, p = piece;
3449 switch (square_color) {
3451 case 2: /* neutral */
3453 if ((int)piece < (int) BlackPawn) {
3461 if ((int)piece < (int) BlackPawn) {
3469 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
3470 if(useTexture & square_color+1) {
3471 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
3472 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
3473 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
3474 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
3475 XSetClipMask(xDisplay, wlPieceGC, None);
3476 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
3478 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3479 dest, wlPieceGC, 0, 0,
3480 squareSize, squareSize, x, y);
3483 typedef void (*DrawFunc)();
3488 if (appData.monoMode) {
3489 if (DefaultDepth(xDisplay, xScreen) == 1) {
3490 return monoDrawPiece_1bit;
3492 return monoDrawPiece;
3496 return colorDrawPieceImage;
3498 return colorDrawPiece;
3503 DrawDot (int marker, int x, int y, int r)
3505 if(appData.monoMode) {
3506 XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
3507 x, y, r, r, 0, 64*360);
3508 XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
3509 x, y, r, r, 0, 64*360);
3511 XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
3512 x, y, r, r, 0, 64*360);
3516 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
3517 { // basic front-end board-draw function: takes care of everything that can be in square:
3518 // piece, background, coordinate/count, marker dot
3519 int direction, font_ascent, font_descent;
3520 XCharStruct overall;
3523 if (piece == EmptySquare) {
3524 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
3526 drawfunc = ChooseDrawFunc();
3527 drawfunc(piece, square_color, x, y, xBoardWindow);
3530 if(align) { // square carries inscription (coord or piece count)
3532 GC hGC = align < 3 ? coordGC : countGC;
3533 // first calculate where it goes
3534 XTextExtents(countFontStruct, string, 1, &direction,
3535 &font_ascent, &font_descent, &overall);
3537 xx += squareSize - overall.width - 2;
3538 yy += squareSize - font_descent - 1;
3539 } else if (align == 2) {
3540 xx += 2, yy += font_ascent + 1;
3541 } else if (align == 3) {
3542 xx += squareSize - overall.width - 2;
3543 yy += font_ascent + 1;
3544 } else if (align == 4) {
3545 xx += 2, yy += font_ascent + 1;
3548 if (appData.monoMode) {
3549 XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3551 XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3555 if(marker) { // print fat marker dot, if requested
3556 DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
3561 FlashDelay (int flash_delay)
3563 XSync(xDisplay, False);
3564 if(flash_delay) do_flash_delay(flash_delay);
3568 Fraction (int x, int start, int stop)
3570 double f = ((double) x - start)/(stop - start);
3571 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
3575 static WindowPlacement wpNew;
3578 CoDrag (Widget sh, WindowPlacement *wp)
3581 int j=0, touch=0, fudge = 2;
3582 GetActualPlacement(sh, wp);
3583 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
3584 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
3585 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
3586 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
3587 if(!touch ) return; // only windows that touch co-move
3588 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
3589 int heightInc = wpNew.height - wpMain.height;
3590 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3591 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3592 wp->y += fracTop * heightInc;
3593 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
3594 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
3595 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
3596 int widthInc = wpNew.width - wpMain.width;
3597 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3598 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3599 wp->y += fracLeft * widthInc;
3600 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
3601 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
3603 wp->x += wpNew.x - wpMain.x;
3604 wp->y += wpNew.y - wpMain.y;
3605 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
3606 if(touch == 3) wp->y += wpNew.height - wpMain.height;
3607 XtSetArg(args[j], XtNx, wp->x); j++;
3608 XtSetArg(args[j], XtNy, wp->y); j++;
3609 XtSetValues(sh, args, j);
3612 static XtIntervalId delayedDragID = 0;
3617 GetActualPlacement(shellWidget, &wpNew);
3618 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
3619 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
3620 return; // false alarm
3621 if(EngineOutputIsUp()) CoDrag(engineOutputShell, &wpEngineOutput);
3622 if(MoveHistoryIsUp()) CoDrag(shells[HistoryDlg], &wpMoveHistory);
3623 if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
3624 if(GameListIsUp()) CoDrag(gameListShell, &wpGameList);
3626 DrawPosition(True, NULL);
3627 delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
3634 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
3636 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
3639 /* Why is this needed on some versions of X? */
3641 EventProc (Widget widget, caddr_t unused, XEvent *event)
3643 if (!XtIsRealized(widget))
3645 switch (event->type) {
3646 case ConfigureNotify: // main window is being dragged: drag attached windows with it
3647 if(appData.useStickyWindows)
3648 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
3651 if (event->xexpose.count > 0) return; /* no clipping is done */
3652 DrawPosition(True, NULL);
3653 if(twoBoards) { // [HGM] dual: draw other board in other orientation
3654 flipView = !flipView; partnerUp = !partnerUp;
3655 DrawPosition(True, NULL);
3656 flipView = !flipView; partnerUp = !partnerUp;
3660 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
3667 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
3669 DrawSeekAxis (int x, int y, int xTo, int yTo)
3671 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
3675 DrawSeekBackground (int left, int top, int right, int bottom)
3677 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
3681 DrawSeekText (char *buf, int x, int y)
3683 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
3687 DrawSeekDot (int x, int y, int colorNr)
3689 int square = colorNr & 0x80;
3692 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
3694 XFillRectangle(xDisplay, xBoardWindow, color,
3695 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3697 XFillArc(xDisplay, xBoardWindow, color,
3698 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
3702 DrawGrid (int second)
3704 XDrawSegments(xDisplay, xBoardWindow, lineGC,
3705 second ? secondSegments : // [HGM] dual
3706 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
3711 * event handler for redrawing the board
3714 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3716 DrawPosition(True, NULL);
3721 * event handler for parsing user moves
3723 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
3724 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
3725 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
3726 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
3727 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
3728 // and at the end FinishMove() to perform the move after optional promotion popups.
3729 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
3731 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3733 if (w != boardWidget || errorExitStatus != -1) return;
3734 if(nprms) shiftKey = !strcmp(prms[0], "1");
3737 if (event->type == ButtonPress) {
3738 XtPopdown(promotionShell);
3739 XtDestroyWidget(promotionShell);
3740 promotionUp = False;
3748 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
3749 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
3750 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
3754 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
3756 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
3757 DragPieceMove(event->xmotion.x, event->xmotion.y);
3761 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3762 { // [HGM] pv: walk PV
3763 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3766 static int savedIndex; /* gross that this is global */
3769 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3772 XawTextPosition index, dummy;
3775 XawTextGetSelectionPos(w, &index, &dummy);
3776 XtSetArg(arg, XtNstring, &val);
3777 XtGetValues(w, &arg, 1);
3778 ReplaceComment(savedIndex, val);
3779 if(savedIndex != currentMove) ToNrEvent(savedIndex);
3780 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3784 EditCommentPopUp (int index, char *title, char *text)
3787 if (text == NULL) text = "";
3788 NewCommentPopup(title, text, index);
3797 extern Option boxOptions[];
3800 ICSInputBoxPopDown ()
3802 PopDown(InputBoxDlg);
3806 CommentPopUp (char *title, char *text)
3808 savedIndex = currentMove; // [HGM] vari
3809 NewCommentPopup(title, text, currentMove);
3815 PopDown(CommentDlg);
3818 static char *openName;
3824 (void) (*fileProc)(openFP, 0, openName);
3828 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
3830 fileProc = proc; /* I can't see a way not */
3831 fileOpenMode = openMode; /* to use globals here */
3832 { // [HGM] use file-selector dialog stolen from Ghostview
3833 int index; // this is not supported yet
3834 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
3835 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
3836 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
3837 ScheduleDelayedEvent(&DelayedLoad, 50);
3844 if (!filenameUp) return;
3845 XtPopdown(fileNameShell);
3846 XtDestroyWidget(fileNameShell);
3852 FileNameCallback (Widget w, XtPointer client_data, XtPointer call_data)
3857 XtSetArg(args[0], XtNlabel, &name);
3858 XtGetValues(w, args, 1);
3860 if (strcmp(name, _("cancel")) == 0) {
3865 FileNameAction(w, NULL, NULL, NULL);
3869 FileNameAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3877 name = XawDialogGetValueString(w = XtParent(w));
3879 if ((name != NULL) && (*name != NULLCHAR)) {
3880 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
3881 XtPopdown(w = XtParent(XtParent(w)));
3885 p = strrchr(buf, ' ');
3892 fullname = ExpandPathName(buf);
3894 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
3897 f = fopen(fullname, fileOpenMode);
3899 DisplayError(_("Failed to open file"), errno);
3901 (void) (*fileProc)(f, index, buf);
3908 XtPopdown(w = XtParent(XtParent(w)));
3918 Widget dialog, layout;
3920 Dimension bw_width, pw_width;
3922 char *PromoChars = "wglcqrbnkac+=\0";
3925 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3926 XtGetValues(boardWidget, args, j);
3929 XtSetArg(args[j], XtNresizable, True); j++;
3930 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
3932 XtCreatePopupShell("Promotion", transientShellWidgetClass,
3933 shellWidget, args, j);
3935 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
3936 layoutArgs, XtNumber(layoutArgs));
3939 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
3940 XtSetArg(args[j], XtNborderWidth, 0); j++;
3941 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
3944 if(gameInfo.variant != VariantShogi) {
3945 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
3946 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
3947 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
3948 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
3949 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
3951 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
3952 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
3953 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
3954 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
3956 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
3957 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
3958 gameInfo.variant == VariantGiveaway) {
3959 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
3961 if(gameInfo.variant == VariantCapablanca ||
3962 gameInfo.variant == VariantGothic ||
3963 gameInfo.variant == VariantCapaRandom) {
3964 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
3965 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
3967 } else // [HGM] shogi
3969 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
3970 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
3972 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
3974 XtRealizeWidget(promotionShell);
3975 CatchDeleteWindow(promotionShell, "PromotionPopDown");
3978 XtSetArg(args[j], XtNwidth, &pw_width); j++;
3979 XtGetValues(promotionShell, args, j);
3981 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3982 lineGap + squareSize/3 +
3983 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
3984 0 : 6*(squareSize + lineGap)), &x, &y);
3987 XtSetArg(args[j], XtNx, x); j++;
3988 XtSetArg(args[j], XtNy, y); j++;
3989 XtSetValues(promotionShell, args, j);
3991 XtPopup(promotionShell, XtGrabNone);
3999 if (!promotionUp) return;
4000 XtPopdown(promotionShell);
4001 XtDestroyWidget(promotionShell);
4002 promotionUp = False;
4006 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
4008 int promoChar = * (const char *) client_data;
4012 if (fromX == -1) return;
4019 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
4021 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
4022 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
4028 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
4030 dialogError = errorUp = False;
4031 XtPopdown(w = XtParent(XtParent(XtParent(w))));
4033 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
4040 if (!errorUp) return;
4041 dialogError = errorUp = False;
4042 XtPopdown(errorShell);
4043 XtDestroyWidget(errorShell);
4044 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
4048 ErrorPopUp (char *title, char *label, int modal)
4051 Widget dialog, layout;
4055 Dimension bw_width, pw_width;
4056 Dimension pw_height;
4060 XtSetArg(args[i], XtNresizable, True); i++;
4061 XtSetArg(args[i], XtNtitle, title); i++;
4063 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
4064 shellUp[TransientDlg] ? (dialogError = modal = TRUE, shells[TransientDlg]) : shellWidget, args, i);
4066 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
4067 layoutArgs, XtNumber(layoutArgs));
4070 XtSetArg(args[i], XtNlabel, label); i++;
4071 XtSetArg(args[i], XtNborderWidth, 0); i++;
4072 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
4075 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
4077 XtRealizeWidget(errorShell);
4078 CatchDeleteWindow(errorShell, "ErrorPopDown");
4081 XtSetArg(args[i], XtNwidth, &bw_width); i++;
4082 XtGetValues(boardWidget, args, i);
4084 XtSetArg(args[i], XtNwidth, &pw_width); i++;
4085 XtSetArg(args[i], XtNheight, &pw_height); i++;
4086 XtGetValues(errorShell, args, i);
4089 /* This code seems to tickle an X bug if it is executed too soon
4090 after xboard starts up. The coordinates get transformed as if
4091 the main window was positioned at (0, 0).
4093 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
4094 0 - pw_height + squareSize / 3, &x, &y);
4096 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
4097 RootWindowOfScreen(XtScreen(boardWidget)),
4098 (bw_width - pw_width) / 2,
4099 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
4103 if (y < 0) y = 0; /*avoid positioning top offscreen*/
4106 XtSetArg(args[i], XtNx, x); i++;
4107 XtSetArg(args[i], XtNy, y); i++;
4108 XtSetValues(errorShell, args, i);
4111 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
4114 /* Disable all user input other than deleting the window */
4115 static int frozen = 0;
4121 /* Grab by a widget that doesn't accept input */
4122 XtAddGrab(messageWidget, TRUE, FALSE);
4126 /* Undo a FreezeUI */
4130 if (!frozen) return;
4131 XtRemoveGrab(messageWidget);
4139 static int oldPausing = FALSE;
4140 static GameMode oldmode = (GameMode) -1;
4143 if (!boardWidget || !XtIsRealized(boardWidget)) return;
4145 if (pausing != oldPausing) {
4146 oldPausing = pausing;
4147 MarkMenuItem("Pause", pausing);
4149 if (appData.showButtonBar) {
4150 /* Always toggle, don't set. Previous code messes up when
4151 invoked while the button is pressed, as releasing it
4152 toggles the state again. */
4155 XtSetArg(args[0], XtNbackground, &oldbg);
4156 XtSetArg(args[1], XtNforeground, &oldfg);
4157 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
4159 XtSetArg(args[0], XtNbackground, oldfg);
4160 XtSetArg(args[1], XtNforeground, oldbg);
4162 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
4166 wname = ModeToWidgetName(oldmode);
4167 if (wname != NULL) {
4168 MarkMenuItem(wname, False);
4170 wname = ModeToWidgetName(gameMode);
4171 if (wname != NULL) {
4172 MarkMenuItem(wname, True);
4175 MarkMenuItem("Machine Match", matchMode && matchGame < appData.matchGames);
4177 /* Maybe all the enables should be handled here, not just this one */
4178 EnableMenuItem("Training", gameMode == Training || gameMode == PlayFromGameFile);
4183 * Button/menu procedures
4186 LoadGamePopUp (FILE *f, int gameNumber, char *title)
4188 cmailMsgLoaded = FALSE;
4189 if (gameNumber == 0) {
4190 int error = GameListBuild(f);
4192 DisplayError(_("Cannot build game list"), error);
4193 } else if (!ListEmpty(&gameList) &&
4194 ((ListGame *) gameList.tailPred)->number > 1) {
4195 GameListPopUp(f, title);
4201 return LoadGame(f, gameNumber, title, FALSE);
4204 /* this variable is shared between CopyPositionProc and SendPositionSelection */
4205 char *selected_fen_position=NULL;
4208 SendPositionSelection (Widget w, Atom *selection, Atom *target,
4209 Atom *type_return, XtPointer *value_return,
4210 unsigned long *length_return, int *format_return)
4212 char *selection_tmp;
4214 if (!selected_fen_position) return False; /* should never happen */
4215 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4216 /* note: since no XtSelectionDoneProc was registered, Xt will
4217 * automatically call XtFree on the value returned. So have to
4218 * make a copy of it allocated with XtMalloc */
4219 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
4220 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
4222 *value_return=selection_tmp;
4223 *length_return=strlen(selection_tmp);
4224 *type_return=*target;
4225 *format_return = 8; /* bits per byte */
4227 } else if (*target == XA_TARGETS(xDisplay)) {
4228 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4229 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4230 targets_tmp[1] = XA_STRING;
4231 *value_return = targets_tmp;
4232 *type_return = XA_ATOM;
4235 // This code leads to a read of value_return out of bounds on 64-bit systems.
4236 // Other code which I have seen always sets *format_return to 32 independent of
4237 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
4238 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
4239 *format_return = 8 * sizeof(Atom);
4240 if (*format_return > 32) {
4241 *length_return *= *format_return / 32;
4242 *format_return = 32;
4245 *format_return = 32;
4253 /* note: when called from menu all parameters are NULL, so no clue what the
4254 * Widget which was clicked on was, or what the click event was
4260 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4261 * have a notion of a position that is selected but not copied.
4262 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4264 if(gameMode == EditPosition) EditPositionDone(TRUE);
4265 if (selected_fen_position) free(selected_fen_position);
4266 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
4267 if (!selected_fen_position) return;
4268 XtOwnSelection(menuBarWidget, XA_PRIMARY,
4270 SendPositionSelection,
4271 NULL/* lose_ownership_proc */ ,
4272 NULL/* transfer_done_proc */);
4273 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4275 SendPositionSelection,
4276 NULL/* lose_ownership_proc */ ,
4277 NULL/* transfer_done_proc */);
4280 /* function called when the data to Paste is ready */
4282 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
4283 Atom *type, XtPointer value, unsigned long *len, int *format)
4286 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
4287 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
4288 EditPositionPasteFEN(fenstr);
4292 /* called when Paste Position button is pressed,
4293 * all parameters will be NULL */
4295 PastePositionProc ()
4297 XtGetSelectionValue(menuBarWidget,
4298 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4299 /* (XtSelectionCallbackProc) */ PastePositionCB,
4300 NULL, /* client_data passed to PastePositionCB */
4302 /* better to use the time field from the event that triggered the
4303 * call to this function, but that isn't trivial to get
4311 SendGameSelection (Widget w, Atom *selection, Atom *target,
4312 Atom *type_return, XtPointer *value_return,
4313 unsigned long *length_return, int *format_return)
4315 char *selection_tmp;
4317 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4318 FILE* f = fopen(gameCopyFilename, "r");
4321 if (f == NULL) return False;
4325 selection_tmp = XtMalloc(len + 1);
4326 count = fread(selection_tmp, 1, len, f);
4329 XtFree(selection_tmp);
4332 selection_tmp[len] = NULLCHAR;
4333 *value_return = selection_tmp;
4334 *length_return = len;
4335 *type_return = *target;
4336 *format_return = 8; /* bits per byte */
4338 } else if (*target == XA_TARGETS(xDisplay)) {
4339 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4340 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4341 targets_tmp[1] = XA_STRING;
4342 *value_return = targets_tmp;
4343 *type_return = XA_ATOM;
4346 // This code leads to a read of value_return out of bounds on 64-bit systems.
4347 // Other code which I have seen always sets *format_return to 32 independent of
4348 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
4349 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
4350 *format_return = 8 * sizeof(Atom);
4351 if (*format_return > 32) {
4352 *length_return *= *format_return / 32;
4353 *format_return = 32;
4356 *format_return = 32;
4368 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4369 * have a notion of a game that is selected but not copied.
4370 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4372 XtOwnSelection(menuBarWidget, XA_PRIMARY,
4375 NULL/* lose_ownership_proc */ ,
4376 NULL/* transfer_done_proc */);
4377 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4380 NULL/* lose_ownership_proc */ ,
4381 NULL/* transfer_done_proc */);
4384 /* note: when called from menu all parameters are NULL, so no clue what the
4385 * Widget which was clicked on was, or what the click event was
4387 /* function called when the data to Paste is ready */
4389 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
4390 Atom *type, XtPointer value, unsigned long *len, int *format)
4393 if (value == NULL || *len == 0) {
4394 return; /* nothing had been selected to copy */
4396 f = fopen(gamePasteFilename, "w");
4398 DisplayError(_("Can't open temp file"), errno);
4401 fwrite(value, 1, *len, f);
4404 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
4407 /* called when Paste Game button is pressed,
4408 * all parameters will be NULL */
4412 XtGetSelectionValue(menuBarWidget,
4413 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4414 /* (XtSelectionCallbackProc) */ PasteGameCB,
4415 NULL, /* client_data passed to PasteGameCB */
4417 /* better to use the time field from the event that triggered the
4418 * call to this function, but that isn't trivial to get
4427 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4433 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
4435 char buf[10], keys[32];
4437 KeyCode metaL, metaR; //, ctrlL, ctrlR;
4438 int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
4439 XQueryKeymap(xDisplay,keys);
4440 metaL = XKeysymToKeycode(xDisplay, XK_Meta_L);
4441 metaR = XKeysymToKeycode(xDisplay, XK_Meta_R);
4442 // ctrlL = XKeysymToKeycode(xDisplay, XK_Control_L);
4443 // ctrlR = XKeysymToKeycode(xDisplay, XK_Control_R);
4444 if ( n == 1 && *buf >= 32 // printable
4445 && !(keys[metaL>>3]&1<<(metaL&7)) && !(keys[metaR>>3]&1<<(metaR&7)) // no alt key pressed
4446 // && !(keys[ctrlL>>3]&1<<(ctrlL&7)) && !(keys[ctrlR>>3]&1<<(ctrlR&7)) // no ctrl key pressed
4447 ) BoxAutoPopUp (buf);
4451 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4452 { // [HGM] input: let up-arrow recall previous line from history
4457 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4458 { // [HGM] input: let down-arrow recall next line from history
4463 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4469 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4471 if (!TempBackwardActive) {
4472 TempBackwardActive = True;
4478 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4480 /* Check to see if triggered by a key release event for a repeating key.
4481 * If so the next queued event will be a key press of the same key at the same time */
4482 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
4484 XPeekEvent(xDisplay, &next);
4485 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
4486 next.xkey.keycode == event->xkey.keycode)
4490 TempBackwardActive = False;
4494 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4495 { // called as key binding
4498 if (nprms && *nprms > 0)
4502 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
4507 DisplayMessage (char *message, char *extMessage)
4509 /* display a message in the message widget */
4518 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
4523 message = extMessage;
4527 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
4529 /* need to test if messageWidget already exists, since this function
4530 can also be called during the startup, if for example a Xresource
4531 is not set up correctly */
4534 XtSetArg(arg, XtNlabel, message);
4535 XtSetValues(messageWidget, &arg, 1);
4542 SetWindowTitle (char *text, char *title, char *icon)
4546 if (appData.titleInWindow) {
4548 XtSetArg(args[i], XtNlabel, text); i++;
4549 XtSetValues(titleWidget, args, i);
4552 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
4553 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
4554 XtSetValues(shellWidget, args, i);
4555 XSync(xDisplay, False);
4560 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
4566 DisplayIcsInteractionTitle (String message)
4568 if (oldICSInteractionTitle == NULL) {
4569 /* Magic to find the old window title, adapted from vim */
4570 char *wina = getenv("WINDOWID");
4572 Window win = (Window) atoi(wina);
4573 Window root, parent, *children;
4574 unsigned int nchildren;
4575 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4577 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4578 if (!XQueryTree(xDisplay, win, &root, &parent,
4579 &children, &nchildren)) break;
4580 if (children) XFree((void *)children);
4581 if (parent == root || parent == 0) break;
4584 XSetErrorHandler(oldHandler);
4586 if (oldICSInteractionTitle == NULL) {
4587 oldICSInteractionTitle = "xterm";
4590 printf("\033]0;%s\007", message);
4594 char pendingReplyPrefix[MSG_SIZ];
4595 ProcRef pendingReplyPR;
4598 AskQuestionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4601 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
4605 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
4609 AskQuestionPopDown ()
4611 if (!askQuestionUp) return;
4612 XtPopdown(askQuestionShell);
4613 XtDestroyWidget(askQuestionShell);
4614 askQuestionUp = False;
4618 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4624 reply = XawDialogGetValueString(w = XtParent(w));
4625 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
4626 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
4627 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
4628 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
4629 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
4630 AskQuestionPopDown();
4632 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
4636 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
4641 XtSetArg(args[0], XtNlabel, &name);
4642 XtGetValues(w, args, 1);
4644 if (strcmp(name, _("cancel")) == 0) {
4645 AskQuestionPopDown();
4647 AskQuestionReplyAction(w, NULL, NULL, NULL);
4652 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
4655 Widget popup, layout, dialog, edit;
4661 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
4662 pendingReplyPR = pr;
4665 XtSetArg(args[i], XtNresizable, True); i++;
4666 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
4667 askQuestionShell = popup =
4668 XtCreatePopupShell(title, transientShellWidgetClass,
4669 shellWidget, args, i);
4672 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
4673 layoutArgs, XtNumber(layoutArgs));
4676 XtSetArg(args[i], XtNlabel, question); i++;
4677 XtSetArg(args[i], XtNvalue, ""); i++;
4678 XtSetArg(args[i], XtNborderWidth, 0); i++;
4679 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
4682 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
4683 (XtPointer) dialog);
4684 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
4685 (XtPointer) dialog);
4687 XtRealizeWidget(popup);
4688 CatchDeleteWindow(popup, "AskQuestionPopDown");
4690 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
4691 &x, &y, &win_x, &win_y, &mask);
4693 XtSetArg(args[0], XtNx, x - 10);
4694 XtSetArg(args[1], XtNy, y - 30);
4695 XtSetValues(popup, args, 2);
4697 XtPopup(popup, XtGrabExclusive);
4698 askQuestionUp = True;
4700 edit = XtNameToWidget(dialog, "*value");
4701 XtSetKeyboardFocus(popup, edit);
4706 PlaySound (char *name)
4708 if (*name == NULLCHAR) {
4710 } else if (strcmp(name, "$") == 0) {
4711 putc(BELLCHAR, stderr);
4714 char *prefix = "", *sep = "";
4715 if(appData.soundProgram[0] == NULLCHAR) return;
4716 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
4717 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
4725 PlaySound(appData.soundMove);
4731 PlaySound(appData.soundIcsWin);
4737 PlaySound(appData.soundIcsLoss);
4743 PlaySound(appData.soundIcsDraw);
4747 PlayIcsUnfinishedSound ()
4749 PlaySound(appData.soundIcsUnfinished);
4755 PlaySound(appData.soundIcsAlarm);
4761 PlaySound(appData.soundTell);
4767 system("stty echo");
4774 system("stty -echo");
4779 RunCommand (char *buf)
4785 Colorize (ColorClass cc, int continuation)
4788 int count, outCount, error;
4790 if (textColors[(int)cc].bg > 0) {
4791 if (textColors[(int)cc].fg > 0) {
4792 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
4793 textColors[(int)cc].fg, textColors[(int)cc].bg);
4795 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
4796 textColors[(int)cc].bg);
4799 if (textColors[(int)cc].fg > 0) {
4800 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
4801 textColors[(int)cc].fg);
4803 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
4806 count = strlen(buf);
4807 outCount = OutputToProcess(NoProc, buf, count, &error);
4808 if (outCount < count) {
4809 DisplayFatalError(_("Error writing to display"), error, 1);
4812 if (continuation) return;
4815 PlaySound(appData.soundShout);
4818 PlaySound(appData.soundSShout);
4821 PlaySound(appData.soundChannel1);
4824 PlaySound(appData.soundChannel);
4827 PlaySound(appData.soundKibitz);
4830 PlaySound(appData.soundTell);
4832 case ColorChallenge:
4833 PlaySound(appData.soundChallenge);
4836 PlaySound(appData.soundRequest);
4839 PlaySound(appData.soundSeek);
4851 return getpwuid(getuid())->pw_name;
4855 ExpandPathName (char *path)
4857 static char static_buf[4*MSG_SIZ];
4858 char *d, *s, buf[4*MSG_SIZ];
4864 while (*s && isspace(*s))
4873 if (*(s+1) == '/') {
4874 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
4878 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
4879 { char *p; if(p = strchr(buf, '/')) *p = 0; }
4880 pwd = getpwnam(buf);
4883 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
4887 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
4888 strcat(d, strchr(s+1, '/'));
4892 safeStrCpy(d, s, 4*MSG_SIZ );
4900 static char host_name[MSG_SIZ];
4902 #if HAVE_GETHOSTNAME
4903 gethostname(host_name, MSG_SIZ);
4905 #else /* not HAVE_GETHOSTNAME */
4906 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
4907 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
4909 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4911 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4912 #endif /* not HAVE_GETHOSTNAME */
4915 XtIntervalId delayedEventTimerXID = 0;
4916 DelayedEventCallback delayedEventCallback = 0;
4921 delayedEventTimerXID = 0;
4922 delayedEventCallback();
4926 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
4928 if(delayedEventTimerXID && delayedEventCallback == cb)
4929 // [HGM] alive: replace, rather than add or flush identical event
4930 XtRemoveTimeOut(delayedEventTimerXID);
4931 delayedEventCallback = cb;
4932 delayedEventTimerXID =
4933 XtAppAddTimeOut(appContext, millisec,
4934 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
4937 DelayedEventCallback
4940 if (delayedEventTimerXID) {
4941 return delayedEventCallback;
4948 CancelDelayedEvent ()
4950 if (delayedEventTimerXID) {
4951 XtRemoveTimeOut(delayedEventTimerXID);
4952 delayedEventTimerXID = 0;
4956 XtIntervalId loadGameTimerXID = 0;
4959 LoadGameTimerRunning ()
4961 return loadGameTimerXID != 0;
4965 StopLoadGameTimer ()
4967 if (loadGameTimerXID != 0) {
4968 XtRemoveTimeOut(loadGameTimerXID);
4969 loadGameTimerXID = 0;
4977 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
4979 loadGameTimerXID = 0;
4984 StartLoadGameTimer (long millisec)
4987 XtAppAddTimeOut(appContext, millisec,
4988 (XtTimerCallbackProc) LoadGameTimerCallback,
4992 XtIntervalId analysisClockXID = 0;
4995 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
4997 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
4998 || appData.icsEngineAnalyze) { // [DM]
4999 AnalysisPeriodicEvent(0);
5000 StartAnalysisClock();
5005 StartAnalysisClock ()
5008 XtAppAddTimeOut(appContext, 2000,
5009 (XtTimerCallbackProc) AnalysisClockCallback,
5013 XtIntervalId clockTimerXID = 0;
5016 ClockTimerRunning ()
5018 return clockTimerXID != 0;
5024 if (clockTimerXID != 0) {
5025 XtRemoveTimeOut(clockTimerXID);
5034 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
5041 StartClockTimer (long millisec)
5044 XtAppAddTimeOut(appContext, millisec,
5045 (XtTimerCallbackProc) ClockTimerCallback,
5050 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
5055 /* check for low time warning */
5056 Pixel foregroundOrWarningColor = timerForegroundPixel;
5059 appData.lowTimeWarning &&
5060 (timer / 1000) < appData.icsAlarmTime)
5061 foregroundOrWarningColor = lowTimeWarningColor;
5063 if (appData.clockMode) {
5064 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
5065 XtSetArg(args[0], XtNlabel, buf);
5067 snprintf(buf, MSG_SIZ, "%s ", color);
5068 XtSetArg(args[0], XtNlabel, buf);
5073 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
5074 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
5076 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
5077 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
5080 XtSetValues(w, args, 3);
5084 DisplayWhiteClock (long timeRemaining, int highlight)
5088 if(appData.noGUI) return;
5089 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
5090 if (highlight && iconPixmap == bIconPixmap) {
5091 iconPixmap = wIconPixmap;
5092 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
5093 XtSetValues(shellWidget, args, 1);
5098 DisplayBlackClock (long timeRemaining, int highlight)
5102 if(appData.noGUI) return;
5103 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
5104 if (highlight && iconPixmap == wIconPixmap) {
5105 iconPixmap = bIconPixmap;
5106 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
5107 XtSetValues(shellWidget, args, 1);
5126 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
5130 int to_prog[2], from_prog[2];
5134 if (appData.debugMode) {
5135 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
5138 /* We do NOT feed the cmdLine to the shell; we just
5139 parse it into blank-separated arguments in the
5140 most simple-minded way possible.
5143 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
5146 while(*p == ' ') p++;
5148 if(*p == '"' || *p == '\'')
5149 p = strchr(++argv[i-1], *p);
5150 else p = strchr(p, ' ');
5151 if (p == NULL) break;
5156 SetUpChildIO(to_prog, from_prog);
5158 if ((pid = fork()) == 0) {
5160 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
5161 close(to_prog[1]); // first close the unused pipe ends
5162 close(from_prog[0]);
5163 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
5164 dup2(from_prog[1], 1);
5165 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
5166 close(from_prog[1]); // and closing again loses one of the pipes!
5167 if(fileno(stderr) >= 2) // better safe than sorry...
5168 dup2(1, fileno(stderr)); /* force stderr to the pipe */
5170 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
5175 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
5177 execvp(argv[0], argv);
5179 /* If we get here, exec failed */
5184 /* Parent process */
5186 close(from_prog[1]);
5188 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5191 cp->fdFrom = from_prog[0];
5192 cp->fdTo = to_prog[1];
5197 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
5199 AlarmCallBack (int n)
5205 DestroyChildProcess (ProcRef pr, int signalType)
5207 ChildProc *cp = (ChildProc *) pr;
5209 if (cp->kind != CPReal) return;
5211 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
5212 signal(SIGALRM, AlarmCallBack);
5214 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
5215 kill(cp->pid, SIGKILL); // kill it forcefully
5216 wait((int *) 0); // and wait again
5220 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
5222 /* Process is exiting either because of the kill or because of
5223 a quit command sent by the backend; either way, wait for it to die.
5232 InterruptChildProcess (ProcRef pr)
5234 ChildProc *cp = (ChildProc *) pr;
5236 if (cp->kind != CPReal) return;
5237 (void) kill(cp->pid, SIGINT); /* stop it thinking */
5241 OpenTelnet (char *host, char *port, ProcRef *pr)
5243 char cmdLine[MSG_SIZ];
5245 if (port[0] == NULLCHAR) {
5246 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
5248 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
5250 return StartChildProcess(cmdLine, "", pr);
5254 OpenTCP (char *host, char *port, ProcRef *pr)
5257 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
5258 #else /* !OMIT_SOCKETS */
5259 struct addrinfo hints;
5260 struct addrinfo *ais, *ai;
5265 memset(&hints, 0, sizeof(hints));
5266 hints.ai_family = AF_UNSPEC;
5267 hints.ai_socktype = SOCK_STREAM;
5269 error = getaddrinfo(host, port, &hints, &ais);
5271 /* a getaddrinfo error is not an errno, so can't return it */
5272 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
5273 host, port, gai_strerror(error));
5277 for (ai = ais; ai != NULL; ai = ai->ai_next) {
5278 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
5282 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
5295 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5301 #endif /* !OMIT_SOCKETS */
5307 OpenCommPort (char *name, ProcRef *pr)
5312 fd = open(name, 2, 0);
5313 if (fd < 0) return errno;
5315 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5326 OpenLoopback (ProcRef *pr)
5331 SetUpChildIO(to, from);
5333 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5336 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
5344 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
5346 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
5350 #define INPUT_SOURCE_BUF_SIZE 8192
5359 char buf[INPUT_SOURCE_BUF_SIZE];
5364 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
5366 InputSource *is = (InputSource *) closure;
5371 if (is->lineByLine) {
5372 count = read(is->fd, is->unused,
5373 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
5375 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
5378 is->unused += count;
5380 while (p < is->unused) {
5381 q = memchr(p, '\n', is->unused - p);
5382 if (q == NULL) break;
5384 (is->func)(is, is->closure, p, q - p, 0);
5388 while (p < is->unused) {
5393 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
5398 (is->func)(is, is->closure, is->buf, count, error);
5403 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
5406 ChildProc *cp = (ChildProc *) pr;
5408 is = (InputSource *) calloc(1, sizeof(InputSource));
5409 is->lineByLine = lineByLine;
5413 is->fd = fileno(stdin);
5415 is->kind = cp->kind;
5416 is->fd = cp->fdFrom;
5419 is->unused = is->buf;
5422 is->xid = XtAppAddInput(appContext, is->fd,
5423 (XtPointer) (XtInputReadMask),
5424 (XtInputCallbackProc) DoInputCallback,
5426 is->closure = closure;
5427 return (InputSourceRef) is;
5431 RemoveInputSource (InputSourceRef isr)
5433 InputSource *is = (InputSource *) isr;
5435 if (is->xid == 0) return;
5436 XtRemoveInput(is->xid);
5441 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
5443 static int line = 0;
5444 ChildProc *cp = (ChildProc *) pr;
5449 if (appData.noJoin || !appData.useInternalWrap)
5450 outCount = fwrite(message, 1, count, stdout);
5453 int width = get_term_width();
5454 int len = wrap(NULL, message, count, width, &line);
5455 char *msg = malloc(len);
5459 outCount = fwrite(message, 1, count, stdout);
5462 dbgchk = wrap(msg, message, count, width, &line);
5463 if (dbgchk != len && appData.debugMode)
5464 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
5465 outCount = fwrite(msg, 1, dbgchk, stdout);
5471 outCount = write(cp->fdTo, message, count);
5481 /* Output message to process, with "ms" milliseconds of delay
5482 between each character. This is needed when sending the logon
5483 script to ICC, which for some reason doesn't like the
5484 instantaneous send. */
5486 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
5488 ChildProc *cp = (ChildProc *) pr;
5493 r = write(cp->fdTo, message++, 1);
5506 /**** Animation code by Hugh Fisher, DCS, ANU. ****/
5508 /* Masks for XPM pieces. Black and white pieces can have
5509 different shapes, but in the interest of retaining my
5510 sanity pieces must have the same outline on both light
5511 and dark squares, and all pieces must use the same
5512 background square colors/images. */
5514 static int xpmDone = 0;
5515 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
5516 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
5519 CreateAnimMasks (int pieceDepth)
5525 unsigned long plane;
5528 /* Need a bitmap just to get a GC with right depth */
5529 buf = XCreatePixmap(xDisplay, xBoardWindow,
5531 values.foreground = 1;
5532 values.background = 0;
5533 /* Don't use XtGetGC, not read only */
5534 maskGC = XCreateGC(xDisplay, buf,
5535 GCForeground | GCBackground, &values);
5536 XFreePixmap(xDisplay, buf);
5538 buf = XCreatePixmap(xDisplay, xBoardWindow,
5539 squareSize, squareSize, pieceDepth);
5540 values.foreground = XBlackPixel(xDisplay, xScreen);
5541 values.background = XWhitePixel(xDisplay, xScreen);
5542 bufGC = XCreateGC(xDisplay, buf,
5543 GCForeground | GCBackground, &values);
5545 for (piece = WhitePawn; piece <= BlackKing; piece++) {
5546 /* Begin with empty mask */
5547 if(!xpmDone) // [HGM] pieces: keep using existing
5548 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
5549 squareSize, squareSize, 1);
5550 XSetFunction(xDisplay, maskGC, GXclear);
5551 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
5552 0, 0, squareSize, squareSize);
5554 /* Take a copy of the piece */
5559 XSetFunction(xDisplay, bufGC, GXcopy);
5560 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
5562 0, 0, squareSize, squareSize, 0, 0);
5564 /* XOR the background (light) over the piece */
5565 XSetFunction(xDisplay, bufGC, GXxor);
5567 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
5568 0, 0, squareSize, squareSize, 0, 0);
5570 XSetForeground(xDisplay, bufGC, lightSquareColor);
5571 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
5574 /* We now have an inverted piece image with the background
5575 erased. Construct mask by just selecting all the non-zero
5576 pixels - no need to reconstruct the original image. */
5577 XSetFunction(xDisplay, maskGC, GXor);
5579 /* Might be quicker to download an XImage and create bitmap
5580 data from it rather than this N copies per piece, but it
5581 only takes a fraction of a second and there is a much
5582 longer delay for loading the pieces. */
5583 for (n = 0; n < pieceDepth; n ++) {
5584 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
5585 0, 0, squareSize, squareSize,
5591 XFreePixmap(xDisplay, buf);
5592 XFreeGC(xDisplay, bufGC);
5593 XFreeGC(xDisplay, maskGC);
5597 InitAnimState (AnimNr anr, XWindowAttributes *info)
5602 /* Each buffer is square size, same depth as window */
5603 animBufs[anr+4] = xBoardWindow;
5604 animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
5605 squareSize, squareSize, info->depth);
5606 animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
5607 squareSize, squareSize, info->depth);
5609 /* Create a plain GC for blitting */
5610 mask = GCForeground | GCBackground | GCFunction |
5611 GCPlaneMask | GCGraphicsExposures;
5612 values.foreground = XBlackPixel(xDisplay, xScreen);
5613 values.background = XWhitePixel(xDisplay, xScreen);
5614 values.function = GXcopy;
5615 values.plane_mask = AllPlanes;
5616 values.graphics_exposures = False;
5617 animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
5619 /* Piece will be copied from an existing context at
5620 the start of each new animation/drag. */
5621 animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
5623 /* Outline will be a read-only copy of an existing */
5624 animGCs[anr+4] = None;
5630 XWindowAttributes info;
5632 if (xpmDone && gameInfo.variant == oldVariant) return;
5633 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
5634 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
5636 InitAnimState(Game, &info);
5637 InitAnimState(Player, &info);
5639 /* For XPM pieces, we need bitmaps to use as masks. */
5641 CreateAnimMasks(info.depth), xpmDone = 1;
5646 static Boolean frameWaiting;
5649 FrameAlarm (int sig)
5651 frameWaiting = False;
5652 /* In case System-V style signals. Needed?? */
5653 signal(SIGALRM, FrameAlarm);
5657 FrameDelay (int time)
5659 struct itimerval delay;
5661 XSync(xDisplay, False);
5664 frameWaiting = True;
5665 signal(SIGALRM, FrameAlarm);
5666 delay.it_interval.tv_sec =
5667 delay.it_value.tv_sec = time / 1000;
5668 delay.it_interval.tv_usec =
5669 delay.it_value.tv_usec = (time % 1000) * 1000;
5670 setitimer(ITIMER_REAL, &delay, NULL);
5671 while (frameWaiting) pause();
5672 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
5673 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
5674 setitimer(ITIMER_REAL, &delay, NULL);
5681 FrameDelay (int time)
5683 XSync(xDisplay, False);
5685 usleep(time * 1000);
5691 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
5695 /* Bitmap for piece being moved. */
5696 if (appData.monoMode) {
5697 *mask = *pieceToSolid(piece);
5698 } else if (useImages) {
5700 *mask = xpmMask[piece];
5702 *mask = ximMaskPm[piece];
5705 *mask = *pieceToSolid(piece);
5708 /* GC for piece being moved. Square color doesn't matter, but
5709 since it gets modified we make a copy of the original. */
5711 if (appData.monoMode)
5716 if (appData.monoMode)
5721 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
5723 /* Outline only used in mono mode and is not modified */
5725 *outline = bwPieceGC;
5727 *outline = wbPieceGC;
5731 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
5736 /* Draw solid rectangle which will be clipped to shape of piece */
5737 XFillRectangle(xDisplay, dest, clip,
5738 0, 0, squareSize, squareSize);
5739 if (appData.monoMode)
5740 /* Also draw outline in contrasting color for black
5741 on black / white on white cases */
5742 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
5743 0, 0, squareSize, squareSize, 0, 0, 1);
5745 /* Copy the piece */
5750 if(appData.upsideDown && flipView) kind ^= 2;
5751 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
5753 0, 0, squareSize, squareSize,
5759 InsertPiece (AnimNr anr, ChessSquare piece)
5761 OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
5765 DrawBlank (AnimNr anr, int x, int y, int startColor)
5767 BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
5770 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
5771 int srcX, int srcY, int width, int height, int destX, int destY)
5773 XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
5774 srcX, srcY, width, height, destX, destY);
5778 SetDragPiece (AnimNr anr, ChessSquare piece)
5781 /* The piece will be drawn using its own bitmap as a matte */
5782 SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
5783 XSetClipMask(xDisplay, animGCs[anr+2], mask);
5786 #include <sys/ioctl.h>
5790 int fd, default_width;
5793 default_width = 79; // this is FICS default anyway...
5795 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
5797 if (!ioctl(fd, TIOCGSIZE, &win))
5798 default_width = win.ts_cols;
5799 #elif defined(TIOCGWINSZ)
5801 if (!ioctl(fd, TIOCGWINSZ, &win))
5802 default_width = win.ws_col;
5804 return default_width;
5810 static int old_width = 0;
5811 int new_width = get_term_width();
5813 if (old_width != new_width)
5814 ics_printf("set width %d\n", new_width);
5815 old_width = new_width;
5819 NotifyFrontendLogin ()
5824 /* [AS] Arrow highlighting support */
5827 DrawPolygon (Pnt arrow[], int nr)
5831 for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
5832 XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
5833 if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
5837 UpdateLogos (int displ)
5839 return; // no logos in XBoard yet