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 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>
65 # if HAVE_SYS_SOCKET_H
66 # include <sys/socket.h>
67 # include <netinet/in.h>
69 # else /* not HAVE_SYS_SOCKET_H */
70 # if HAVE_LAN_SOCKET_H
71 # include <lan/socket.h>
73 # include <lan/netdb.h>
74 # else /* not HAVE_LAN_SOCKET_H */
75 # define OMIT_SOCKETS 1
76 # endif /* not HAVE_LAN_SOCKET_H */
77 # endif /* not HAVE_SYS_SOCKET_H */
78 #endif /* !OMIT_SOCKETS */
83 #else /* not STDC_HEADERS */
84 extern char *getenv();
87 # else /* not HAVE_STRING_H */
89 # endif /* not HAVE_STRING_H */
90 #endif /* not STDC_HEADERS */
93 # include <sys/fcntl.h>
94 #else /* not HAVE_SYS_FCNTL_H */
97 # endif /* HAVE_FCNTL_H */
98 #endif /* not HAVE_SYS_FCNTL_H */
100 #if HAVE_SYS_SYSTEMINFO_H
101 # include <sys/systeminfo.h>
102 #endif /* HAVE_SYS_SYSTEMINFO_H */
104 #if TIME_WITH_SYS_TIME
105 # include <sys/time.h>
109 # include <sys/time.h>
120 # include <sys/wait.h>
125 # define NAMLEN(dirent) strlen((dirent)->d_name)
126 # define HAVE_DIR_STRUCT
128 # define dirent direct
129 # define NAMLEN(dirent) (dirent)->d_namlen
131 # include <sys/ndir.h>
132 # define HAVE_DIR_STRUCT
135 # include <sys/dir.h>
136 # define HAVE_DIR_STRUCT
140 # define HAVE_DIR_STRUCT
144 #include <X11/Intrinsic.h>
145 #include <X11/StringDefs.h>
146 #include <X11/Shell.h>
147 #include <X11/cursorfont.h>
148 #include <X11/Xatom.h>
149 #include <X11/Xmu/Atoms.h>
151 #include <X11/Xaw3d/Dialog.h>
152 #include <X11/Xaw3d/Form.h>
153 #include <X11/Xaw3d/List.h>
154 #include <X11/Xaw3d/Label.h>
155 #include <X11/Xaw3d/SimpleMenu.h>
156 #include <X11/Xaw3d/SmeBSB.h>
157 #include <X11/Xaw3d/SmeLine.h>
158 #include <X11/Xaw3d/Box.h>
159 #include <X11/Xaw3d/MenuButton.h>
160 #include <X11/Xaw3d/Text.h>
161 #include <X11/Xaw3d/AsciiText.h>
163 #include <X11/Xaw/Dialog.h>
164 #include <X11/Xaw/Form.h>
165 #include <X11/Xaw/List.h>
166 #include <X11/Xaw/Label.h>
167 #include <X11/Xaw/SimpleMenu.h>
168 #include <X11/Xaw/SmeBSB.h>
169 #include <X11/Xaw/SmeLine.h>
170 #include <X11/Xaw/Box.h>
171 #include <X11/Xaw/MenuButton.h>
172 #include <X11/Xaw/Text.h>
173 #include <X11/Xaw/AsciiText.h>
176 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
181 #include "pixmaps/pixmaps.h"
182 #define IMAGE_EXT "xpm"
184 #define IMAGE_EXT "xim"
185 #include "bitmaps/bitmaps.h"
190 #include <gdk-pixbuf/gdk-pixbuf.h>
192 #include "bitmaps/icon_white.bm"
193 #include "bitmaps/icon_black.bm"
194 #include "bitmaps/checkmark.bm"
196 #include "frontend.h"
201 #include "xgamelist.h"
202 #include "xhistory.h"
203 #include "xedittags.h"
205 #include "callback.h"
206 #include "interface.h"
208 // must be moved to xengineoutput.h
210 void EngineOutputProc P((Widget w, XEvent *event,
211 String *prms, Cardinal *nprms));
212 void EvalGraphProc P((Widget w, XEvent *event,
213 String *prms, Cardinal *nprms));
220 #define usleep(t) _sleep2(((t)+500)/1000)
224 # define _(s) gettext (s)
225 # define N_(s) gettext_noop (s)
248 int main P((int argc, char **argv));
249 RETSIGTYPE CmailSigHandler P((int sig));
250 RETSIGTYPE IntSigHandler P((int sig));
251 RETSIGTYPE TermSizeSigHandler P((int sig));
252 void CreateGCs P((void));
253 void CreateXIMPieces P((void));
254 void CreateXPMPieces P((void));
255 void CreatePieces P((void));
256 void CreatePieceMenus P((void));
257 Widget CreateMenuBar P((Menu *mb));
258 char *FindFont P((char *pattern, int targetPxlSize));
259 void PieceMenuPopup P((Widget w, XEvent *event,
260 String *params, Cardinal *num_params));
261 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
262 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
263 int EventToSquare P((int x, int limit));
264 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
265 void AnimateUserMove P((Widget w, XEvent * event,
266 String * params, Cardinal * nParams));
267 void HandlePV P((Widget w, XEvent * event,
268 String * params, Cardinal * nParams));
269 void CommentPopUp P((char *title, char *label));
270 void CommentPopDown P((void));
271 void CommentCallback P((Widget w, XtPointer client_data,
272 XtPointer call_data));
273 void ICSInputBoxPopUp P((void));
274 void ICSInputBoxPopDown P((void));
275 void AskQuestionReplyAction P((Widget w, XEvent *event,
276 String *prms, Cardinal *nprms));
277 void AskQuestionProc P((Widget w, XEvent *event,
278 String *prms, Cardinal *nprms));
279 void AskQuestionPopDown P((void));
280 void PromotionPopDown P((void));
281 void PromotionCallback P((Widget w, XtPointer client_data,
282 XtPointer call_data));
283 void EditCommentPopDown P((void));
284 void EditCommentCallback P((Widget w, XtPointer client_data,
285 XtPointer call_data));
286 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
287 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
289 void PastePositionProc P((Widget w, XEvent *event, String *prms,
291 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
292 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
293 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
294 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
296 void EditCommentProc P((Widget w, XEvent *event,
297 String *prms, Cardinal *nprms));
298 void IcsInputBoxProc P((Widget w, XEvent *event,
299 String *prms, Cardinal *nprms));
300 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
301 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
302 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
303 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
304 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
305 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
306 void Iconify P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
307 void DisplayMove P((int moveNumber));
308 void DisplayTitle P((char *title));
309 void ICSInitScript P((void));
310 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
311 void ErrorPopUp P((char *title, char *text, int modal));
312 void ErrorPopDown P((void));
313 static char *ExpandPathName P((char *path));
314 static void CreateAnimVars P((void));
315 static void DragPieceMove P((int x, int y));
316 static void DrawDragPiece P((void));
317 char *ModeToWidgetName P((GameMode mode));
318 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
319 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
320 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
321 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
322 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
323 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
324 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
325 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
326 void GameListOptionsPopDown P(());
327 void ShufflePopDown P(());
328 void EnginePopDown P(());
329 void UciPopDown P(());
330 void TimeControlPopDown P(());
331 void NewVariantPopDown P(());
332 void SettingsPopDown P(());
333 void SetMenuEnables P((Enables *enab));
334 void update_ics_width P(());
335 int get_term_width P(());
336 int CopyMemoProc P(());
338 * XBoard depends on Xt R4 or higher
340 int xtVersion = XtSpecificationRelease;
345 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
346 jailSquareColor, highlightSquareColor, premoveHighlightColor;
347 Pixel lowTimeWarningColor;
349 #define LINE_TYPE_NORMAL 0
350 #define LINE_TYPE_HIGHLIGHT 1
351 #define LINE_TYPE_PRE 2
354 GC lightSquareGC, darkSquareGC, jailSquareGC, wdPieceGC, wlPieceGC,
355 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC,
356 wjPieceGC, bjPieceGC;
357 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
358 Widget layoutWidget, formWidget, boardWidget, messageWidget,
359 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
360 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
361 menuBarWidget, editShell, errorShell, analysisShell,
362 ICSInputShell, fileNameShell, askQuestionShell;
364 Widget evalGraphShell, gameListShell;
365 //XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
366 //XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
368 Font clockFontID, coordFontID, countFontID;
369 XFontStruct *clockFontStruct, *coordFontStruct, *countFontStruct;
370 XtAppContext appContext;
372 char *oldICSInteractionTitle;
376 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
378 Position commentX = -1, commentY = -1;
379 Dimension commentW, commentH;
380 typedef unsigned int BoardSize;
382 Boolean chessProgram;
384 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
385 int squareSize, smallLayout = 0, tinyLayout = 0,
386 marginW, marginH, // [HGM] for run-time resizing
387 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
388 ICSInputBoxUp = False, askQuestionUp = False,
389 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
390 editUp = False, errorUp = False, errorExitStatus = -1, lineGap;
391 Pixel timerForegroundPixel, timerBackgroundPixel;
392 Pixel buttonForegroundPixel, buttonBackgroundPixel;
393 char *chessDir, *programName, *programVersion,
394 *gameCopyFilename, *gamePasteFilename;
395 Boolean alwaysOnTop = False;
396 Boolean saveSettingsOnExit;
397 char *settingsFileName;
398 char *icsTextMenuString;
400 char *firstChessProgramNames;
401 char *secondChessProgramNames;
403 WindowPlacement wpMain;
404 WindowPlacement wpConsole;
405 WindowPlacement wpComment;
406 WindowPlacement wpMoveHistory;
407 WindowPlacement wpEvalGraph;
408 WindowPlacement wpEngineOutput;
409 WindowPlacement wpGameList;
410 WindowPlacement wpTags;
414 Pixmap pieceBitmap[2][(int)BlackPawn];
415 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
416 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
417 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
418 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
419 int useImages=0, useImageSqs;
420 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
421 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
422 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
423 XImage *ximLightSquare, *ximDarkSquare;
426 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
427 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
429 #define White(piece) ((int)(piece) < (int)BlackPawn)
431 /* Variables for doing smooth animation. This whole thing
432 would be much easier if the board was double-buffered,
433 but that would require a fairly major rewrite. */
438 GC blitGC, pieceGC, outlineGC;
439 XPoint startSquare, prevFrame, mouseDelta;
443 int startBoardX, startBoardY;
446 /* There can be two pieces being animated at once: a player
447 can begin dragging a piece before the remote opponent has moved. */
449 static AnimState game, player;
451 /* Bitmaps for use as masks when drawing XPM pieces.
452 Need one for each black and white piece. */
453 static Pixmap xpmMask[BlackKing + 1];
455 /* This magic number is the number of intermediate frames used
456 in each half of the animation. For short moves it's reduced
457 by 1. The total number of frames will be factor * 2 + 1. */
460 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
462 Enables icsEnables[] = {
463 { "menuFile.Mail Move", False },
464 { "menuFile.Reload CMail Message", False },
465 { "menuMode.Machine Black", False },
466 { "menuMode.Machine White", False },
467 { "menuMode.Analysis Mode", False },
468 { "menuMode.Analyze File", False },
469 { "menuMode.Two Machines", False },
471 { "menuHelp.Hint", False },
472 { "menuHelp.Book", False },
473 { "menuStep.Move Now", False },
474 { "menuOptions.Periodic Updates", False },
475 { "menuOptions.Hide Thinking", False },
476 { "menuOptions.Ponder Next Move", False },
481 Enables ncpEnables[] = {
482 { "menuFile.Mail Move", False },
483 { "menuFile.Reload CMail Message", False },
484 { "menuMode.Machine White", False },
485 { "menuMode.Machine Black", False },
486 { "menuMode.Analysis Mode", False },
487 { "menuMode.Analyze File", False },
488 { "menuMode.Two Machines", False },
489 { "menuMode.ICS Client", False },
490 { "menuMode.ICS Input Box", False },
492 { "menuStep.Revert", False },
493 { "menuStep.Move Now", False },
494 { "menuStep.Retract Move", False },
495 { "menuOptions.Auto Comment", False },
496 { "menuOptions.Auto Flag", False },
497 { "menuOptions.Auto Flip View", False },
498 { "menuOptions.Auto Observe", False },
499 { "menuOptions.Auto Raise Board", False },
500 { "menuOptions.Get Move List", False },
501 { "menuOptions.ICS Alarm", False },
502 { "menuOptions.Move Sound", False },
503 { "menuOptions.Quiet Play", False },
504 { "menuOptions.Hide Thinking", False },
505 { "menuOptions.Periodic Updates", False },
506 { "menuOptions.Ponder Next Move", False },
507 { "menuHelp.Hint", False },
508 { "menuHelp.Book", False },
512 Enables gnuEnables[] = {
513 { "menuMode.ICS Client", False },
514 { "menuMode.ICS Input Box", False },
515 { "menuAction.Accept", False },
516 { "menuAction.Decline", False },
517 { "menuAction.Rematch", False },
518 { "menuAction.Adjourn", False },
519 { "menuAction.Stop Examining", False },
520 { "menuAction.Stop Observing", False },
521 { "menuStep.Revert", False },
522 { "menuOptions.Auto Comment", False },
523 { "menuOptions.Auto Observe", False },
524 { "menuOptions.Auto Raise Board", False },
525 { "menuOptions.Get Move List", False },
526 { "menuOptions.Premove", False },
527 { "menuOptions.Quiet Play", False },
529 /* The next two options rely on SetCmailMode being called *after* */
530 /* SetGNUMode so that when GNU is being used to give hints these */
531 /* menu options are still available */
533 { "menuFile.Mail Move", False },
534 { "menuFile.Reload CMail Message", False },
538 Enables cmailEnables[] = {
540 { "menuAction.Call Flag", False },
541 { "menuAction.Draw", True },
542 { "menuAction.Adjourn", False },
543 { "menuAction.Abort", False },
544 { "menuAction.Stop Observing", False },
545 { "menuAction.Stop Examining", False },
546 { "menuFile.Mail Move", True },
547 { "menuFile.Reload CMail Message", True },
551 Enables trainingOnEnables[] = {
552 { "menuMode.Edit Comment", False },
553 { "menuMode.Pause", False },
554 { "menuStep.Forward", False },
555 { "menuStep.Backward", False },
556 { "menuStep.Forward to End", False },
557 { "menuStep.Back to Start", False },
558 { "menuStep.Move Now", False },
559 { "menuStep.Truncate Game", False },
563 Enables trainingOffEnables[] = {
564 { "menuMode.Edit Comment", True },
565 { "menuMode.Pause", True },
566 { "menuStep.Forward", True },
567 { "menuStep.Backward", True },
568 { "menuStep.Forward to End", True },
569 { "menuStep.Back to Start", True },
570 { "menuStep.Move Now", True },
571 { "menuStep.Truncate Game", True },
575 Enables machineThinkingEnables[] = {
576 { "menuFile.Load Game", False },
577 { "menuFile.Load Next Game", False },
578 { "menuFile.Load Previous Game", False },
579 { "menuFile.Reload Same Game", False },
580 { "menuFile.Paste Game", False },
581 { "menuFile.Load Position", False },
582 { "menuFile.Load Next Position", False },
583 { "menuFile.Load Previous Position", False },
584 { "menuFile.Reload Same Position", False },
585 { "menuFile.Paste Position", False },
586 { "menuMode.Machine White", False },
587 { "menuMode.Machine Black", False },
588 { "menuMode.Two Machines", False },
589 { "menuStep.Retract Move", False },
593 Enables userThinkingEnables[] = {
594 { "menuFile.Load Game", True },
595 { "menuFile.Load Next Game", True },
596 { "menuFile.Load Previous Game", True },
597 { "menuFile.Reload Same Game", True },
598 { "menuFile.Paste Game", True },
599 { "menuFile.Load Position", True },
600 { "menuFile.Load Next Position", True },
601 { "menuFile.Load Previous Position", True },
602 { "menuFile.Reload Same Position", True },
603 { "menuFile.Paste Position", True },
604 { "menuMode.Machine White", True },
605 { "menuMode.Machine Black", True },
606 { "menuMode.Two Machines", True },
607 { "menuStep.Retract Move", True },
613 MenuItem fileMenu[] = {
614 {N_("New Shuffle Game ..."), ShuffleMenuProc},
615 {N_("New Variant ..."), NewVariantProc}, // [HGM] variant: not functional yet
616 // {"----", NothingProc},
617 // {N_("Save Game"), SaveGameProc},
618 // {"----", NothingProc},
619 {N_("Copy Game"), CopyGameProc},
620 {N_("Paste Game"), PasteGameProc},
621 // {"----", NothingProc},
622 // {N_("Load Position"), LoadPositionProc},
623 // {N_("Load Next Position"), LoadNextPositionProc},
624 // {N_("Load Previous Position"), LoadPrevPositionProc},
625 // {N_("Reload Same Position"), ReloadPositionProc},
626 // {N_("Save Position"), SavePositionProc},
627 // {"----", NothingProc},
628 {N_("Copy Position"), CopyPositionProc},
629 {N_("Paste Position"), PastePositionProc},
630 // {"----", NothingProc},
631 {N_("Mail Move"), MailMoveProc},
632 {N_("Reload CMail Message"), ReloadCmailMsgProc},
633 // {"----", NothingProc},
637 MenuItem modeMenu[] = {
638 // {N_("Machine White"), MachineWhiteProc},
639 // {N_("Machine Black"), MachineBlackProc},
640 // {N_("Two Machines"), TwoMachinesProc},
641 // {N_("Analysis Mode"), AnalyzeModeProc},
642 // {N_("Analyze File"), AnalyzeFileProc },
643 // {N_("ICS Client"), IcsClientProc},
644 // {N_("Edit Game"), EditGameProc},
645 // {N_("Edit Position"), EditPositionProc},
646 // {N_("Training"), TrainingProc},
647 // {"----", NothingProc},
648 {N_("Show Engine Output"), EngineOutputProc},
649 {N_("Show Evaluation Graph"), EvalGraphProc},
650 {N_("Show Game List"), ShowGameListProc},
651 // {"Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
652 // {"----", NothingProc},
653 // {N_("Edit Tags"), EditTagsProc},
654 {N_("Edit Comment"), EditCommentProc},
655 {N_("ICS Input Box"), IcsInputBoxProc},
659 MenuItem optionsMenu[] = {
660 // {N_("Flip View"), FlipViewProc},
661 // {"----", NothingProc},
662 {N_("Adjudications ..."), EngineMenuProc},
663 {N_("General Settings ..."), UciMenuProc},
664 {N_("Engine #1 Settings ..."), FirstSettingsProc},
665 {N_("Engine #2 Settings ..."), SecondSettingsProc},
666 {N_("Time Control ..."), TimeControlProc},
667 {N_("Game List ..."), GameListOptionsPopUp},
668 {"----", NothingProc},
669 // {N_("Always Queen"), AlwaysQueenProc},
670 // {N_("Animate Dragging"), AnimateDraggingProc},
671 // {N_("Animate Moving"), AnimateMovingProc},
672 // {N_("Auto Comment"), AutocommProc},
673 // {N_("Auto Flag"), AutoflagProc},
674 // {N_("Auto Flip View"), AutoflipProc},
675 // {N_("Auto Observe"), AutobsProc},
676 // {N_("Auto Raise Board"), AutoraiseProc},
677 // {N_("Auto Save"), AutosaveProc},
678 // {N_("Blindfold"), BlindfoldProc},
679 // {N_("Flash Moves"), FlashMovesProc},
680 // {N_("Get Move List"), GetMoveListProc},
682 // {N_("Highlight Dragging"), HighlightDraggingProc},
684 // {N_("Highlight Last Move"), HighlightLastMoveProc},
685 // {N_("Move Sound"), MoveSoundProc},
686 // {N_("ICS Alarm"), IcsAlarmProc},
687 // {N_("Old Save Style"), OldSaveStyleProc},
688 // {N_("Periodic Updates"), PeriodicUpdatesProc},
689 // {N_("Ponder Next Move"), PonderNextMoveProc},
690 // {N_("Popup Exit Message"), PopupExitMessageProc},
691 // {N_("Popup Move Errors"), PopupMoveErrorsProc},
692 // {N_("Premove"), PremoveProc},
693 // {N_("Quiet Play"), QuietPlayProc},
694 // {N_("Hide Thinking"), HideThinkingProc},
695 // {N_("Test Legality"), TestLegalityProc},
696 // {N_("Show Coords"), ShowCoordsProc},
697 {"----", NothingProc},
698 {N_("Save Settings Now"), SaveSettingsProc},
699 {N_("Save Settings on Exit"), SaveOnExitProc},
704 {N_("File"), fileMenu},
705 {N_("Mode"), modeMenu},
706 {N_("Options"), optionsMenu},
710 #define PIECE_MENU_SIZE 18
711 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
712 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
713 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
714 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
715 N_("Empty square"), N_("Clear board") },
716 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
717 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
718 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
719 N_("Empty square"), N_("Clear board") }
721 /* must be in same order as PieceMenuStrings! */
722 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
723 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
724 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
725 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
726 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
727 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
728 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
729 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
730 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
733 #define DROP_MENU_SIZE 6
734 String dropMenuStrings[DROP_MENU_SIZE] = {
735 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
737 /* must be in same order as PieceMenuStrings! */
738 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
739 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
740 WhiteRook, WhiteQueen
748 DropMenuEnables dmEnables[] = {
757 { XtNborderWidth, 0 },
758 { XtNdefaultDistance, 0 },
762 { XtNborderWidth, 0 },
763 { XtNresizable, (XtArgVal) True },
767 { XtNborderWidth, 0 },
772 XtResource clientResources[] = {
773 { "flashCount", "flashCount", XtRInt, sizeof(int),
774 XtOffset(AppDataPtr, flashCount), XtRImmediate,
775 (XtPointer) FLASH_COUNT },
778 XtActionsRec boardActions[] = {
779 // { "HandleUserMove", HandleUserMove },
780 { "AnimateUserMove", AnimateUserMove },
781 // { "FileNameAction", FileNameAction },
782 { "HandlePV", HandlePV },
783 { "UnLoadPV", UnLoadPV },
784 { "AskQuestionProc", AskQuestionProc },
785 { "AskQuestionReplyAction", AskQuestionReplyAction },
786 { "PieceMenuPopup", PieceMenuPopup },
787 // { "WhiteClock", WhiteClock },
788 // { "BlackClock", BlackClock },
789 { "Iconify", Iconify },
790 { "LoadSelectedProc", LoadSelectedProc },
791 // { "LoadPositionProc", LoadPositionProc },
792 // { "LoadNextPositionProc", LoadNextPositionProc },
793 // { "LoadPrevPositionProc", LoadPrevPositionProc },
794 // { "ReloadPositionProc", ReloadPositionProc },
795 { "SetFilterProc", SetFilterProc },
796 { "CopyPositionProc", CopyPositionProc },
797 { "PastePositionProc", PastePositionProc },
798 { "CopyGameProc", CopyGameProc },
799 { "PasteGameProc", PasteGameProc },
800 // { "SaveGameProc", SaveGameProc },
801 // { "SavePositionProc", SavePositionProc },
802 { "MailMoveProc", MailMoveProc },
803 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
804 // { "MachineWhiteProc", MachineWhiteProc },
805 // { "MachineBlackProc", MachineBlackProc },
806 // { "AnalysisModeProc", AnalyzeModeProc },
807 // { "AnalyzeFileProc", AnalyzeFileProc },
808 // { "TwoMachinesProc", TwoMachinesProc },
809 // { "IcsClientProc", IcsClientProc },
810 // { "EditGameProc", EditGameProc },
811 // { "EditPositionProc", EditPositionProc },
812 // { "TrainingProc", EditPositionProc },
813 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
814 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
815 { "ShowGameListProc", ShowGameListProc },
816 // { "ShowMoveListProc", HistoryShowProc},
817 // { "EditTagsProc", EditCommentProc },
818 { "EditCommentProc", EditCommentProc },
819 // { "IcsAlarmProc", IcsAlarmProc },
820 { "IcsInputBoxProc", IcsInputBoxProc },
821 // { "AcceptProc", AcceptProc },
822 // { "DeclineProc", DeclineProc },
823 // { "RematchProc", RematchProc },
824 // { "CallFlagProc", CallFlagProc },
825 // { "DrawProc", DrawProc },
826 // { "AdjournProc", AdjournProc },
827 // { "AbortProc", AbortProc },
828 // { "ResignProc", ResignProc },
829 // { "AdjuWhiteProc", AdjuWhiteProc },
830 // { "AdjuBlackProc", AdjuBlackProc },
831 // { "AdjuDrawProc", AdjuDrawProc },
832 { "EnterKeyProc", EnterKeyProc },
833 // { "StopObservingProc", StopObservingProc },
834 // { "StopExaminingProc", StopExaminingProc },
835 // { "BackwardProc", BackwardProc },
836 // { "ForwardProc", ForwardProc },
837 // { "ToStartProc", ToStartProc },
838 // { "ToEndProc", ToEndProc },
839 // { "RevertProc", RevertProc },
840 // { "TruncateGameProc", TruncateGameProc },
841 // { "MoveNowProc", MoveNowProc },
842 // { "RetractMoveProc", RetractMoveProc },
843 // { "AlwaysQueenProc", AlwaysQueenProc },
844 // { "AnimateDraggingProc", AnimateDraggingProc },
845 // { "AnimateMovingProc", AnimateMovingProc },
846 // { "AutoflagProc", AutoflagProc },
847 // { "AutoflipProc", AutoflipProc },
848 // { "AutobsProc", AutobsProc },
849 // { "AutoraiseProc", AutoraiseProc },
850 // { "AutosaveProc", AutosaveProc },
851 // { "BlindfoldProc", BlindfoldProc },
852 // { "FlashMovesProc", FlashMovesProc },
853 // { "FlipViewProc", FlipViewProc },
854 // { "GetMoveListProc", GetMoveListProc },
856 // { "HighlightDraggingProc", HighlightDraggingProc },
858 // { "HighlightLastMoveProc", HighlightLastMoveProc },
859 // { "IcsAlarmProc", IcsAlarmProc },
860 // { "MoveSoundProc", MoveSoundProc },
861 // { "OldSaveStyleProc", OldSaveStyleProc },
862 // { "PeriodicUpdatesProc", PeriodicUpdatesProc },
863 // { "PonderNextMoveProc", PonderNextMoveProc },
864 // { "PopupExitMessageProc", PopupExitMessageProc },
865 // { "PopupMoveErrorsProc", PopupMoveErrorsProc },
866 // { "PremoveProc", PremoveProc },
867 // { "QuietPlayProc", QuietPlayProc },
868 // { "ShowThinkingProc", ShowThinkingProc },
869 // { "HideThinkingProc", HideThinkingProc },
870 // { "TestLegalityProc", TestLegalityProc },
871 { "SaveSettingsProc", SaveSettingsProc },
872 { "SaveOnExitProc", SaveOnExitProc },
873 // { "InfoProc", InfoProc },
874 // { "ManProc", ManProc },
875 // { "HintProc", HintProc },
876 // { "BookProc", BookProc },
877 { "AboutGameProc", AboutGameProc },
878 { "DebugProc", DebugProc },
879 { "NothingProc", NothingProc },
880 { "CommentPopDown", (XtActionProc) CommentPopDown },
881 { "EditCommentPopDown", (XtActionProc) EditCommentPopDown },
882 { "TagsPopDown", (XtActionProc) TagsPopDown },
883 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
884 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
885 // { "FileNamePopDown", (XtActionProc) FileNamePopDown },
886 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
887 { "GameListPopDown", (XtActionProc) GameListPopDown },
888 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
889 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
890 // { "HistoryPopDown", (XtActionProc) HistoryPopDown },
891 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
892 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
893 { "ShufflePopDown", (XtActionProc) ShufflePopDown },
894 { "EnginePopDown", (XtActionProc) EnginePopDown },
895 { "UciPopDown", (XtActionProc) UciPopDown },
896 { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
897 { "NewVariantPopDown", (XtActionProc) NewVariantPopDown },
898 { "SettingsPopDown", (XtActionProc) SettingsPopDown },
899 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
902 //char globalTranslations[] =
903 // ":<Key>R: ResignProc() \n \
904 // :<Key>r: ResetProc() \n \
905 // :<Key>g: LoadGameProc() \n \
906 // :<Key>N: LoadNextGameProc() \n \
907 // :<Key>P: LoadPrevGameProc() \n \
908 // :<Key>Q: QuitProc() \n \
909 // :<Key>F: ToEndProc() \n \
910 // :<Key>f: ForwardProc() \n \
911 // :<Key>B: ToStartProc() \n \
912 // :<Key>b: BackwardProc() \n \
913 // :<Key>p: PauseProc() \n \
914 // :<Key>d: DrawProc() \n \
915 // :<Key>t: CallFlagProc() \n \
916 // :<Key>i: Iconify() \n \
917 // :<Key>c: Iconify() \n \
918 // :<Key>v: FlipViewProc() \n \
919 // <KeyDown>Control_L: BackwardProc() \n \
920 // <KeyUp>Control_L: ForwardProc() \n \
921 // <KeyDown>Control_R: BackwardProc() \n \
922 // <KeyUp>Control_R: ForwardProc() \n \
923 // Shift<Key>1: AskQuestionProc(\"Direct command\", \
924 // \"Send to chess program:\",,1) \n \
925 // Shift<Key>2: AskQuestionProc(\"Direct command\", \
926 // \"Send to second chess program:\",,2) \n";
928 //char boardTranslations[] =
929 // "<Btn1Down>: HandleUserMove() \n \
930 // <Btn1Up>: HandleUserMove() \n \
931 // <Btn1Motion>: AnimateUserMove() \n \
932 // <Btn3Motion>: HandlePV() \n \
933 // <Btn3Up>: UnLoadPV() \n \
934 // Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
935 // PieceMenuPopup(menuB) \n \
936 // Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
937 // PieceMenuPopup(menuW) \n \
938 // Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
939 // PieceMenuPopup(menuW) \n \
940 // Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
941 // PieceMenuPopup(menuB) \n";
943 //char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
944 //char blackTranslations[] = "<BtnDown>: BlackClock()\n";
946 char ICSInputTranslations[] =
947 "<Key>Return: EnterKeyProc() \n";
949 String xboardResources[] = {
950 // "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
951 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
952 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
956 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
957 "magenta", "cyan", "white" };
961 TextColors textColors[(int)NColorClasses];
963 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
965 parse_color(str, which)
969 char *p, buf[100], *d;
972 if (strlen(str) > 99) /* watch bounds on buf */
977 for (i=0; i<which; ++i) {
984 /* Could be looking at something like:
986 .. in which case we want to stop on a comma also */
987 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
991 return -1; /* Use default for empty field */
994 if (which == 2 || isdigit(*p))
997 while (*p && isalpha(*p))
1002 for (i=0; i<8; ++i) {
1003 if (!StrCaseCmp(buf, cnames[i]))
1004 return which? (i+40) : (i+30);
1006 if (!StrCaseCmp(buf, "default")) return -1;
1008 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1013 parse_cpair(cc, str)
1017 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1018 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1023 /* bg and attr are optional */
1024 textColors[(int)cc].bg = parse_color(str, 1);
1025 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1026 textColors[(int)cc].attr = 0;
1035 /* this should raise the board to the top */
1036 gtk_window_present(GTK_WINDOW(GUI_Window));
1040 //---------------------------------------------------------------------------------------------------------
1041 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1044 #define CW_USEDEFAULT (1<<31)
1045 #define ICS_TEXT_MENU_SIZE 90
1046 #define DEBUG_FILE "xboard.debug"
1047 #define SetCurrentDirectory chdir
1048 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1052 // these two must some day move to frontend.h, when they are implemented
1053 Boolean GameListIsUp();
1055 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1058 // front-end part of option handling
1060 // [HGM] This platform-dependent table provides the location for storing the color info
1061 extern char *crWhite, * crBlack;
1065 &appData.whitePieceColor,
1066 &appData.blackPieceColor,
1067 &appData.lightSquareColor,
1068 &appData.darkSquareColor,
1069 &appData.highlightSquareColor,
1070 &appData.premoveHighlightColor,
1071 &appData.lowTimeWarningColor,
1082 // [HGM] font: keep a font for each square size, even non-stndard ones
1083 #define NUM_SIZES 18
1084 #define MAX_SIZE 130
1085 Boolean fontSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1086 char *fontTable[NUM_FONTS][MAX_SIZE];
1089 ParseFont(char *name, int number)
1090 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1092 if(sscanf(name, "size%d:", &size)) {
1093 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1094 // defer processing it until we know if it matches our board size
1095 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1096 fontTable[number][size] = strdup(strchr(name, ':')+1);
1097 fontValid[number][size] = True;
1102 case 0: // CLOCK_FONT
1103 appData.clockFont = strdup(name);
1105 case 1: // MESSAGE_FONT
1106 appData.font = strdup(name);
1108 case 2: // COORD_FONT
1109 appData.coordFont = strdup(name);
1114 fontSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1119 { // only 2 fonts currently
1120 appData.clockFont = CLOCK_FONT_NAME;
1121 appData.coordFont = COORD_FONT_NAME;
1122 appData.font = DEFAULT_FONT_NAME;
1127 { // no-op, until we identify the code for this already in XBoard and move it here
1131 ParseColor(int n, char *name)
1132 { // in XBoard, just copy the color-name string
1133 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1137 ParseTextAttribs(ColorClass cc, char *s)
1139 (&appData.colorShout)[cc] = strdup(s);
1143 ParseBoardSize(void *addr, char *name)
1145 appData.boardSize = strdup(name);
1150 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1154 SetCommPortDefaults()
1155 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1158 // [HGM] args: these three cases taken out to stay in front-end
1160 SaveFontArg(FILE *f, ArgDescriptor *ad)
1162 char *name, buf[MSG_SIZ];
1163 int i, n = (int)ad->argLoc;
1165 case 0: // CLOCK_FONT
1166 name = appData.clockFont;
1168 case 1: // MESSAGE_FONT
1169 name = appData.font;
1171 case 2: // COORD_FONT
1172 name = appData.coordFont;
1177 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1178 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1179 fontTable[n][squareSize] = strdup(name);
1180 fontValid[n][squareSize] = True;
1183 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1184 fprintf(f, OPTCHAR "%s" SEPCHAR "size%d:%s\n", ad->argName, i, fontTable[n][i]);
1189 { // nothing to do, as the sounds are at all times represented by their text-string names already
1193 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1194 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1195 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)ad->argLoc]);
1199 SaveColor(FILE *f, ArgDescriptor *ad)
1200 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1201 if(colorVariable[(int)ad->argLoc])
1202 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)ad->argLoc]);
1206 SaveBoardSize(FILE *f, char *name, void *addr)
1207 { // wrapper to shield back-end from BoardSize & sizeInfo
1208 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1212 ParseCommPortSettings(char *s)
1213 { // no such option in XBoard (yet)
1216 extern Widget engineOutputShell;
1217 extern Widget tagsShell, editTagsShell;
1220 GetActualPlacement(Widget wg, WindowPlacement *wp)
1230 XtSetArg(args[i], XtNx, &x); i++;
1231 XtSetArg(args[i], XtNy, &y); i++;
1232 XtSetArg(args[i], XtNwidth, &w); i++;
1233 XtSetArg(args[i], XtNheight, &h); i++;
1234 XtGetValues(wg, args, i);
1243 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1244 // In XBoard this will have to wait until awareness of window parameters is implemented
1246 // GetActualPlacement(shellWidget, &wpMain);
1247 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1248 // if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1249 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1250 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1251 if(commentShell) GetActualPlacement(commentShell, &wpComment);
1252 else GetActualPlacement(editShell, &wpComment);
1253 if(tagsShell) GetActualPlacement(tagsShell, &wpTags);
1254 else GetActualPlacement(editTagsShell, &wpTags);
1258 PrintCommPortSettings(FILE *f, char *name)
1259 { // This option does not exist in XBoard
1263 MySearchPath(char *installDir, char *name, char *fullname)
1264 { // just append installDir and name. Perhaps ExpandPath should be used here?
1265 name = ExpandPathName(name);
1266 if(name && name[0] == '/') strcpy(fullname, name); else {
1267 sprintf(fullname, "%s%c%s", installDir, '/', name);
1273 MyGetFullPathName(char *name, char *fullname)
1274 { // should use ExpandPath?
1275 name = ExpandPathName(name);
1276 strcpy(fullname, name);
1281 EnsureOnScreen(int *x, int *y, int minX, int minY)
1288 { // [HGM] args: allows testing if main window is realized from back-end
1289 return xBoardWindow != 0;
1293 PopUpStartupDialog()
1294 { // start menu not implemented in XBoard
1297 ConvertToLine(int argc, char **argv)
1299 static char line[128*1024], buf[1024];
1303 for(i=1; i<argc; i++) {
1304 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1305 && argv[i][0] != '{' )
1306 sprintf(buf, "{%s} ", argv[i]);
1307 else sprintf(buf, "%s ", argv[i]);
1310 line[strlen(line)-1] = NULLCHAR;
1314 //--------------------------------------------------------------------------------------------
1317 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1320 #define BoardSize int
1321 void InitDrawingSizes(BoardSize boardSize, int flags)
1322 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1323 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1325 XtGeometryResult gres;
1328 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1329 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1331 timerWidth = (boardWidth - sep) / 2;
1333 if (appData.titleInWindow)
1338 w = boardWidth - 2*bor;
1342 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1346 if(!formWidget) return;
1349 * Inhibit shell resizing.
1352 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1355 for(i=0; i<4; i++) {
1357 for(p=0; p<=(int)WhiteKing; p++)
1358 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1359 if(gameInfo.variant == VariantShogi) {
1360 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1361 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1362 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1363 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1364 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1367 if(gameInfo.variant == VariantGothic) {
1368 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1372 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1373 for(p=0; p<=(int)WhiteKing; p++)
1374 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1375 if(gameInfo.variant == VariantShogi) {
1376 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1377 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1378 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1379 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1380 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1383 if(gameInfo.variant == VariantGothic) {
1384 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1390 for(i=0; i<2; i++) {
1392 for(p=0; p<=(int)WhiteKing; p++)
1393 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1394 if(gameInfo.variant == VariantShogi) {
1395 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1396 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1397 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1398 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1399 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1402 if(gameInfo.variant == VariantGothic) {
1403 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1414 void EscapeExpand(char *p, char *q)
1415 { // [HGM] initstring: routine to shape up string arguments
1419 case 'n': p[-1] = '\n'; break;
1420 case 'r': p[-1] = '\r'; break;
1421 case 't': p[-1] = '\t'; break;
1422 case '\\': p[-1] = '\\'; break;
1423 case 0: *p = 0; return;
1424 default: p[-1] = q[-1]; break;
1433 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1434 XSetWindowAttributes window_attributes;
1436 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1437 XrmValue vFrom, vTo;
1438 XtGeometryResult gres;
1441 int forceMono = False;
1443 srandom(time(0)); // [HGM] book: make random truly random
1445 setbuf(stdout, NULL);
1446 setbuf(stderr, NULL);
1449 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1450 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1454 programName = strrchr(argv[0], '/');
1455 if (programName == NULL)
1456 programName = argv[0];
1461 XtSetLanguageProc(NULL, NULL, NULL);
1462 bindtextdomain(PACKAGE, LOCALEDIR);
1463 textdomain(PACKAGE);
1467 gtk_init (&argc, &argv);
1469 /* parse glade file to build widgets */
1471 builder = gtk_builder_new ();
1472 GError *gtkerror=NULL;
1473 if(!gtk_builder_add_from_file (builder, "gtk-interface.xml", >kerror))
1476 printf ("Error: %d %s\n",gtkerror->code,gtkerror->message);
1479 /* test if everything worked ok */
1481 GUI_Window = GTK_WIDGET (gtk_builder_get_object (builder, "MainWindow"));
1482 if(!GUI_Window) printf("Error: gtk_builder didn't work (MainWindow)!\n");
1484 GUI_Aspect = GTK_WIDGET (gtk_builder_get_object (builder, "Aspectframe"));
1485 if(!GUI_Aspect) printf("Error: gtk_builder didn't work (Aspectframe)!\n");
1487 GUI_Menubar = GTK_WIDGET (gtk_builder_get_object (builder, "MenuBar"));
1488 if(!GUI_Menubar) printf("Error: gtk_builder didn't work (MenuBar)!\n");
1489 GUI_Timer = GTK_WIDGET (gtk_builder_get_object (builder, "Timer"));
1490 if(!GUI_Timer) printf("Error: gtk_builder didn't work (Timer)!\n");
1491 GUI_Buttonbar = GTK_WIDGET (gtk_builder_get_object (builder, "ButtonBar"));
1492 if(!GUI_Buttonbar) printf("Error: gtk_builder didn't work (ButtonBar)!\n");
1493 GUI_Board = GTK_WIDGET (gtk_builder_get_object (builder, "Board"));
1494 if(!GUI_Board) printf("Error: gtk_builder didn't work (Board)!\n");
1496 GUI_Whiteclock = GTK_WIDGET (gtk_builder_get_object (builder, "WhiteClock"));
1497 if(!GUI_Whiteclock) printf("Error: gtk_builder didn't work (WhiteClock)!\n");
1499 GUI_Blackclock = GTK_WIDGET (gtk_builder_get_object (builder, "BlackClock"));
1500 if(!GUI_Blackclock) printf("Error: gtk_builder didn't work (BlackClock)!\n");
1502 /* GTK lists stores*/
1503 LIST_MoveHistory = GTK_LIST_STORE (gtk_builder_get_object (builder, "MoveHistoryStore"));
1504 if(!LIST_MoveHistory) printf("Error: gtk_builder didn't work (MoveHistoryStore)!\n");
1506 LIST_GameList = GTK_LIST_STORE (gtk_builder_get_object (builder, "GameListStore"));
1507 if(!LIST_GameList) printf("Error: gtk_builder didn't work (GameListStore)!\n");
1509 /* EditTags window */
1510 GUI_EditTags = GTK_WIDGET (gtk_builder_get_object (builder, "EditTags"));
1511 if(!GUI_EditTags) printf("Error: gtk_builder didn't work (EditTags)!\n");
1513 GUI_EditTagsTextArea = GTK_WIDGET (gtk_builder_get_object (builder, "EditTagsTextArea"));
1514 if(!GUI_EditTagsTextArea) printf("Error: gtk_builder didn't work(EditTagsTextArea)!\n");
1516 /* move history and game list windows */
1517 GUI_History = GTK_WIDGET (gtk_builder_get_object (builder, "MoveHistory"));
1518 if(!GUI_History) printf("Error: gtk_builder didn't work (MoveHistory)!\n");
1520 TREE_History = GTK_TREE_VIEW (gtk_builder_get_object (builder, "MoveHistoryView"));
1521 if(!TREE_History) printf("Error: gtk_builder didn't work (MoveHistoryView)!\n");
1523 GUI_GameList = GTK_WIDGET (gtk_builder_get_object (builder, "GameList"));
1524 if(!GUI_GameList) printf("Error: gtk_builder didn't work (GameList)!\n");
1526 TREE_Game = GTK_TREE_VIEW (gtk_builder_get_object (builder, "GameListView"));
1527 if(!TREE_Game) printf("Error: gtk_builder didn't work (GameListView)!\n");
1530 /* connect lists to views */
1531 gtk_tree_view_set_model(TREE_History, GTK_TREE_MODEL(LIST_MoveHistory));
1532 gtk_tree_view_set_model(TREE_Game, GTK_TREE_MODEL(LIST_GameList));
1534 gtk_builder_connect_signals (builder, NULL);
1536 // don't unref the builder, since we use it to get references to widgets
1537 // g_object_unref (G_OBJECT (builder));
1539 /* end parse glade file */
1541 appData.boardSize = "";
1542 InitAppData(ConvertToLine(argc, argv));
1545 if (p == NULL) p = "/tmp";
1546 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1547 gameCopyFilename = (char*) malloc(i);
1548 gamePasteFilename = (char*) malloc(i);
1549 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1550 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1552 // XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1553 // clientResources, XtNumber(clientResources),
1556 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1557 static char buf[MSG_SIZ];
1558 EscapeExpand(buf, appData.initString);
1559 appData.initString = strdup(buf);
1560 EscapeExpand(buf, appData.secondInitString);
1561 appData.secondInitString = strdup(buf);
1562 EscapeExpand(buf, appData.firstComputerString);
1563 appData.firstComputerString = strdup(buf);
1564 EscapeExpand(buf, appData.secondComputerString);
1565 appData.secondComputerString = strdup(buf);
1568 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1571 if (chdir(chessDir) != 0) {
1572 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1578 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1579 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1580 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1581 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1584 setbuf(debugFP, NULL);
1589 /* This feature does not work; animation needs a rewrite */
1590 appData.highlightDragging = FALSE;
1594 gameInfo.variant = StringToVariant(appData.variant);
1595 InitPosition(FALSE);
1600 clockFontPxlSize = 20;
1601 coordFontPxlSize = 20;
1607 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1608 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1611 * Determine what fonts to use.
1613 // appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1614 // clockFontID = XLoadFont(xDisplay, appData.clockFont);
1615 // clockFontStruct = XQueryFont(xDisplay, clockFontID);
1616 // appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1617 // coordFontID = XLoadFont(xDisplay, appData.coordFont);
1618 // coordFontStruct = XQueryFont(xDisplay, coordFontID);
1619 // appData.font = FindFont(appData.font, fontPxlSize);
1620 // countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
1621 // countFontStruct = XQueryFont(xDisplay, countFontID);
1622 // appData.font = FindFont(appData.font, fontPxlSize);
1624 // xdb = XtDatabase(xDisplay);
1625 // XrmPutStringResource(&xdb, "*font", appData.font);
1628 * Detect if there are not enough colors available and adapt.
1630 // if (DefaultDepth(xDisplay, xScreen) <= 2) {
1631 // appData.monoMode = True;
1634 if (!appData.monoMode) {
1635 vFrom.addr = (caddr_t) appData.lightSquareColor;
1636 vFrom.size = strlen(appData.lightSquareColor);
1637 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1638 if (vTo.addr == NULL) {
1639 appData.monoMode = True;
1642 lightSquareColor = *(Pixel *) vTo.addr;
1645 if (!appData.monoMode) {
1646 vFrom.addr = (caddr_t) appData.darkSquareColor;
1647 vFrom.size = strlen(appData.darkSquareColor);
1648 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1649 if (vTo.addr == NULL) {
1650 appData.monoMode = True;
1653 darkSquareColor = *(Pixel *) vTo.addr;
1656 if (!appData.monoMode) {
1657 vFrom.addr = (caddr_t) appData.whitePieceColor;
1658 vFrom.size = strlen(appData.whitePieceColor);
1659 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1660 if (vTo.addr == NULL) {
1661 appData.monoMode = True;
1664 whitePieceColor = *(Pixel *) vTo.addr;
1667 if (!appData.monoMode) {
1668 vFrom.addr = (caddr_t) appData.blackPieceColor;
1669 vFrom.size = strlen(appData.blackPieceColor);
1670 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1671 if (vTo.addr == NULL) {
1672 appData.monoMode = True;
1675 blackPieceColor = *(Pixel *) vTo.addr;
1679 if (!appData.monoMode) {
1680 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1681 vFrom.size = strlen(appData.highlightSquareColor);
1682 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1683 if (vTo.addr == NULL) {
1684 appData.monoMode = True;
1687 highlightSquareColor = *(Pixel *) vTo.addr;
1691 if (!appData.monoMode) {
1692 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1693 vFrom.size = strlen(appData.premoveHighlightColor);
1694 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1695 if (vTo.addr == NULL) {
1696 appData.monoMode = True;
1699 premoveHighlightColor = *(Pixel *) vTo.addr;
1704 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1707 if (appData.bitmapDirectory == NULL ||
1708 appData.bitmapDirectory[0] == NULLCHAR)
1709 appData.bitmapDirectory = DEF_BITMAP_DIR;
1712 if (appData.lowTimeWarning && !appData.monoMode) {
1713 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1714 vFrom.size = strlen(appData.lowTimeWarningColor);
1715 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1716 if (vTo.addr == NULL)
1717 appData.monoMode = True;
1719 lowTimeWarningColor = *(Pixel *) vTo.addr;
1722 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1723 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1724 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1725 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1726 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1727 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1728 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1729 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1730 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1731 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1733 if (appData.colorize) {
1735 _("%s: can't parse color names; disabling colorization\n"),
1738 appData.colorize = FALSE;
1740 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
1741 textColors[ColorNone].attr = 0;
1743 // XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1749 layoutName = "tinyLayout";
1750 } else if (smallLayout) {
1751 layoutName = "smallLayout";
1753 layoutName = "normalLayout";
1756 if (appData.titleInWindow) {
1757 /* todo check what this appdata does */
1760 if (appData.showButtonBar) {
1761 /* TODO hide button bar if requested */
1765 if (appData.titleInWindow)
1770 if (appData.showButtonBar)
1777 if (appData.showButtonBar)
1787 /* set some checkboxes in the menu according to appData */
1789 if (appData.alwaysPromoteToQueen)
1790 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Always Queen")),TRUE);
1792 if (appData.animateDragging)
1793 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Animate Dragging")),TRUE);
1795 if (appData.animate)
1796 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Animate Moving")),TRUE);
1798 if (appData.autoComment)
1799 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Comment")),TRUE);
1801 if (appData.autoCallFlag)
1802 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Flag")),TRUE);
1804 if (appData.autoFlipView)
1805 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Flip View")),TRUE);
1807 if (appData.autoObserve)
1808 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Observe")),TRUE);
1810 if (appData.autoRaiseBoard)
1811 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Raise Board")),TRUE);
1813 if (appData.autoSaveGames)
1814 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Save")),TRUE);
1816 if (appData.saveGameFile[0] != NULLCHAR)
1818 /* Can't turn this off from menu */
1819 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Save")),TRUE);
1820 gtk_action_set_sensitive(GTK_ACTION (gtk_builder_get_object (builder, "menuOptions.Auto Save")),FALSE);
1823 if (appData.blindfold)
1824 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Blindfold")),TRUE);
1826 if (appData.flashCount > 0)
1827 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Flash Moves")),TRUE);
1829 if (appData.getMoveList)
1830 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Get Move List")),TRUE);
1833 if (appData.highlightDragging)
1834 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Highlight Dragging")),TRUE);
1837 if (appData.highlightLastMove)
1838 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Highlight Last Move")),TRUE);
1840 if (appData.icsAlarm)
1841 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.ICS Alarm")),TRUE);
1843 if (appData.ringBellAfterMoves)
1844 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Move Sound")),TRUE);
1846 if (appData.oldSaveStyle)
1847 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Old Save Style")),TRUE);
1849 if (appData.periodicUpdates)
1850 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Periodic Updates")),TRUE);
1852 if (appData.ponderNextMove)
1853 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Ponder Next Move")),TRUE);
1855 if (appData.popupExitMessage)
1856 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Popup Exit Message")),TRUE);
1858 if (appData.popupMoveErrors)
1859 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Popup Move Errors")),TRUE);
1861 if (appData.premove)
1862 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Premove")),TRUE);
1864 if (appData.quietPlay)
1865 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Quit Play")),TRUE);
1867 if (appData.showCoords)
1868 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Show Coords")),TRUE);
1870 if (appData.showThinking)
1871 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Hide Thinking")),TRUE);
1873 if (appData.testLegality)
1874 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Test Legality")),TRUE);
1877 // if (saveSettingsOnExit) {
1878 // XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
1883 /* end setting check boxes */
1885 /* load square colors */
1886 SVGLightSquare = load_pixbuf("svg/LightSquare.svg",squareSize);
1887 SVGDarkSquare = load_pixbuf("svg/DarkSquare.svg",squareSize);
1888 SVGNeutralSquare = load_pixbuf("svg/NeutralSquare.svg",squareSize);
1890 /* use two icons to indicate if it is white's or black's turn */
1891 WhiteIcon = load_pixbuf("svg/icon_white.svg",0);
1892 BlackIcon = load_pixbuf("svg/icon_black.svg",0);
1893 WindowIcon = WhiteIcon;
1894 gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
1897 /* realize window */
1898 gtk_widget_show (GUI_Window);
1900 /* recalc boardsize */
1905 if (appData.animate || appData.animateDragging)
1908 /* [AS] Restore layout */
1909 if( wpMoveHistory.visible ) {
1913 if( wpEvalGraph.visible )
1918 if( wpEngineOutput.visible ) {
1919 EngineOutputPopUp();
1924 if (errorExitStatus == -1) {
1925 if (appData.icsActive) {
1926 /* We now wait until we see "login:" from the ICS before
1927 sending the logon script (problems with timestamp otherwise) */
1928 /*ICSInitScript();*/
1929 if (appData.icsInputBox) ICSInputBoxPopUp();
1933 signal(SIGWINCH, TermSizeSigHandler);
1935 signal(SIGINT, IntSigHandler);
1936 signal(SIGTERM, IntSigHandler);
1937 if (*appData.cmailGameName != NULLCHAR) {
1938 signal(SIGUSR1, CmailSigHandler);
1941 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1945 * Create a cursor for the board widget.
1946 * (This needs to be called after the window has been created to have access to board-window)
1949 BoardCursor = gdk_cursor_new(GDK_HAND2);
1950 gdk_window_set_cursor(GUI_Board->window, BoardCursor);
1951 gdk_cursor_destroy(BoardCursor);
1956 if (appData.debugMode) fclose(debugFP); // [DM] debug
1963 if (appData.icsActive && oldICSInteractionTitle != NULL) {
1964 DisplayIcsInteractionTitle(oldICSInteractionTitle);
1966 if (saveSettingsOnExit) SaveSettings(settingsFileName);
1967 unlink(gameCopyFilename);
1968 unlink(gamePasteFilename);
1971 RETSIGTYPE TermSizeSigHandler(int sig)
1984 CmailSigHandler(sig)
1990 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1992 /* Activate call-back function CmailSigHandlerCallBack() */
1993 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1995 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1999 CmailSigHandlerCallBack(isr, closure, message, count, error)
2007 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2009 /**** end signal code ****/
2019 f = fopen(appData.icsLogon, "r");
2025 strcat(buf, appData.icsLogon);
2026 f = fopen(buf, "r");
2030 ProcessICSInitScript(f);
2037 EditCommentPopDown();
2047 if (!menuBarWidget) return;
2048 w = XtNameToWidget(menuBarWidget, "menuStep.Revert");
2050 DisplayError("menuStep.Revert", 0);
2052 XtSetSensitive(w, !grey);
2057 SetMenuEnables(enab)
2062 if (!builder) return;
2063 while (enab->name != NULL) {
2064 o = gtk_builder_get_object(builder, enab->name);
2065 if(GTK_IS_WIDGET(o))
2066 gtk_widget_set_sensitive(GTK_WIDGET (o),enab->value);
2069 if(GTK_IS_ACTION(o))
2070 gtk_action_set_sensitive(GTK_ACTION (o),enab->value);
2072 DisplayError(enab->name, 0);
2080 SetMenuEnables(icsEnables);
2083 if (appData.zippyPlay && !appData.noChessProgram) /* [DM] icsEngineAnalyze */
2084 {}; // XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2091 SetMenuEnables(ncpEnables);
2097 SetMenuEnables(gnuEnables);
2103 SetMenuEnables(cmailEnables);
2109 SetMenuEnables(trainingOnEnables);
2110 if (appData.showButtonBar) {
2111 // XtSetSensitive(buttonBarWidget, False);
2117 SetTrainingModeOff()
2119 SetMenuEnables(trainingOffEnables);
2120 if (appData.showButtonBar) {
2121 // XtSetSensitive(buttonBarWidget, True);
2126 SetUserThinkingEnables()
2128 if (appData.noChessProgram) return;
2129 SetMenuEnables(userThinkingEnables);
2133 SetMachineThinkingEnables()
2135 if (appData.noChessProgram) return;
2136 SetMenuEnables(machineThinkingEnables);
2138 case MachinePlaysBlack:
2139 case MachinePlaysWhite:
2140 case TwoMachinesPlay:
2141 // XtSetSensitive(XtNameToWidget(menuBarWidget,
2142 // ModeToWidgetName(gameMode)), True);
2149 #define Abs(n) ((n)<0 ? -(n) : (n))
2152 * Find a font that matches "pattern" that is as close as
2153 * possible to the targetPxlSize. Prefer fonts that are k
2154 * pixels smaller to fonts that are k pixels larger. The
2155 * pattern must be in the X Consortium standard format,
2156 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2157 * The return value should be freed with XtFree when no
2160 char *FindFont(pattern, targetPxlSize)
2164 char **fonts, *p, *best, *scalable, *scalableTail;
2165 int i, j, nfonts, minerr, err, pxlSize;
2168 char **missing_list;
2170 char *def_string, *base_fnt_lst, strInt[3];
2172 XFontStruct **fnt_list;
2174 base_fnt_lst = calloc(1, strlen(pattern) + 3);
2175 sprintf(strInt, "%d", targetPxlSize);
2176 p = strstr(pattern, "--");
2177 strncpy(base_fnt_lst, pattern, p - pattern + 2);
2178 strcat(base_fnt_lst, strInt);
2179 strcat(base_fnt_lst, strchr(p + 2, '-'));
2181 if ((fntSet = XCreateFontSet(xDisplay,
2185 &def_string)) == NULL) {
2187 fprintf(stderr, _("Unable to create font set.\n"));
2191 nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
2193 // fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2194 // if (nfonts < 1) {
2195 // fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2196 // programName, pattern);
2204 for (i=0; i<nfonts; i++) {
2207 if (*p != '-') continue;
2209 if (*p == NULLCHAR) break;
2210 if (*p++ == '-') j++;
2212 if (j < 7) continue;
2215 scalable = fonts[i];
2218 err = pxlSize - targetPxlSize;
2219 if (Abs(err) < Abs(minerr) ||
2220 (minerr > 0 && err < 0 && -err == minerr)) {
2226 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2227 /* If the error is too big and there is a scalable font,
2228 use the scalable font. */
2229 int headlen = scalableTail - scalable;
2230 p = (char *) XtMalloc(strlen(scalable) + 10);
2231 while (isdigit(*scalableTail)) scalableTail++;
2232 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2234 p = (char *) XtMalloc(strlen(best) + 1);
2237 if (appData.debugMode) {
2238 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2239 pattern, targetPxlSize, p);
2242 if (missing_count > 0)
2243 XFreeStringList(missing_list);
2244 // XFreeFontSet(xDisplay, fntSet);
2246 XFreeFontNames(fonts);
2253 /* GCs are not needed anymore for GTK just left them in here for the moment, since there is a lot of X-code still around that's wants them*/
2262 for(i=0;i<MAXPIECES;i++)
2266 g_free(SVGpieces[i]);
2273 SVGLightSquare = load_pixbuf("svg/LightSquare.svg",squareSize);
2274 SVGDarkSquare = load_pixbuf("svg/DarkSquare.svg",squareSize);
2275 SVGNeutralSquare = load_pixbuf("svg/NeutralSquare.svg",squareSize);
2278 /* get some defaults going */
2279 for(i=WhitePawn; i<DemotePiece+1; i++)
2280 SVGpieces[i] = load_pixbuf("svg/NeutralSquare.svg",squareSize);
2282 SVGpieces[WhitePawn] = load_pixbuf("svg/WhitePawn.svg",squareSize);
2283 SVGpieces[WhiteKnight] = load_pixbuf("svg/WhiteKnight.svg",squareSize);
2284 SVGpieces[WhiteBishop] = load_pixbuf("svg/WhiteBishop.svg",squareSize);
2285 SVGpieces[WhiteRook] = load_pixbuf("svg/WhiteRook.svg",squareSize);
2286 SVGpieces[WhiteQueen] = load_pixbuf("svg/WhiteQueen.svg",squareSize);
2287 SVGpieces[WhiteKing] = load_pixbuf("svg/WhiteKing.svg",squareSize);
2289 SVGpieces[BlackPawn] = load_pixbuf("svg/BlackPawn.svg",squareSize);
2290 SVGpieces[BlackKnight] = load_pixbuf("svg/BlackKnight.svg",squareSize);
2291 SVGpieces[BlackBishop] = load_pixbuf("svg/BlackBishop.svg",squareSize);
2292 SVGpieces[BlackRook] = load_pixbuf("svg/BlackRook.svg",squareSize);
2293 SVGpieces[BlackQueen] = load_pixbuf("svg/BlackQueen.svg",squareSize);
2294 SVGpieces[BlackKing] = load_pixbuf("svg/BlackKing.svg",squareSize);
2300 static void MenuBarSelect(w, addr, index)
2305 XtActionProc proc = (XtActionProc) addr;
2307 (proc)(NULL, NULL, NULL, NULL);
2310 void CreateMenuBarPopup(parent, name, mb)
2320 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2323 XtSetArg(args[j], XtNleftMargin, 20); j++;
2324 XtSetArg(args[j], XtNrightMargin, 20); j++;
2326 while (mi->string != NULL) {
2327 if (strcmp(mi->string, "----") == 0) {
2328 entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
2331 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
2332 entry = XtCreateManagedWidget(mi->string, smeBSBObjectClass,
2334 XtAddCallback(entry, XtNcallback,
2335 (XtCallbackProc) MenuBarSelect,
2336 (caddr_t) mi->proc);
2347 Widget anchor, menuBar;
2349 char menuName[MSG_SIZ];
2352 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2353 XtSetArg(args[j], XtNvSpace, 0); j++;
2354 XtSetArg(args[j], XtNborderWidth, 0); j++;
2355 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
2356 formWidget, args, j);
2358 while (mb->name != NULL) {
2359 strcpy(menuName, "menu");
2360 strcat(menuName, mb->name);
2362 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
2365 shortName[0] = _(mb->name)[0];
2366 shortName[1] = NULLCHAR;
2367 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
2370 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
2373 XtSetArg(args[j], XtNborderWidth, 0); j++;
2374 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
2376 CreateMenuBarPopup(menuBar, menuName, mb);
2384 CreatePieceMenu(name, color)
2391 ChessSquare selection;
2393 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2394 boardWidget, args, 0);
2396 for (i = 0; i < PIECE_MENU_SIZE; i++) {
2397 String item = pieceMenuStrings[color][i];
2399 if (strcmp(item, "----") == 0) {
2400 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2403 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2404 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2406 selection = pieceMenuTranslation[color][i];
2407 XtAddCallback(entry, XtNcallback,
2408 (XtCallbackProc) PieceMenuSelect,
2409 (caddr_t) selection);
2410 if (selection == WhitePawn || selection == BlackPawn) {
2411 XtSetArg(args[0], XtNpopupOnEntry, entry);
2412 XtSetValues(menu, args, 1);
2425 ChessSquare selection;
2427 // whitePieceMenu = CreatePieceMenu("menuW", 0);
2428 // blackPieceMenu = CreatePieceMenu("menuB", 1);
2430 // XtRegisterGrabAction(PieceMenuPopup, True,
2431 // (unsigned)(ButtonPressMask|ButtonReleaseMask),
2432 // GrabModeAsync, GrabModeAsync);
2434 // XtSetArg(args[0], XtNlabel, _("Drop"));
2435 // dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
2436 // boardWidget, args, 1);
2437 // for (i = 0; i < DROP_MENU_SIZE; i++) {
2438 // String item = dropMenuStrings[i];
2440 // if (strcmp(item, "----") == 0) {
2441 // entry = XtCreateManagedWidget(item, smeLineObjectClass,
2442 // dropMenu, NULL, 0);
2444 // XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2445 // entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2446 // dropMenu, args, 1);
2447 // selection = dropMenuTranslation[i];
2448 // XtAddCallback(entry, XtNcallback,
2449 // (XtCallbackProc) DropMenuSelect,
2450 // (caddr_t) selection);
2464 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2465 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2466 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2467 dmEnables[i].piece);
2468 XtSetSensitive(entry, p != NULL || !appData.testLegality
2469 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2470 && !appData.icsActive));
2472 while (p && *p++ == dmEnables[i].piece) count++;
2473 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2475 XtSetArg(args[j], XtNlabel, label); j++;
2476 XtSetValues(entry, args, j);
2481 PieceMenuPopup(w, event, params, num_params)
2485 Cardinal *num_params;
2487 String whichMenu; int menuNr;
2488 if (event->type == ButtonRelease)
2489 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
2490 else if (event->type == ButtonPress)
2491 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
2493 case 0: whichMenu = params[0]; break;
2494 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
2496 case -1: if (errorUp) ErrorPopDown();
2499 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
2503 PieceMenuSelect(w, piece, junk)
2508 if (pmFromX < 0 || pmFromY < 0) return;
2509 EditPositionMenuEvent(piece, pmFromX, pmFromY);
2513 DropMenuSelect(w, piece, junk)
2518 if (pmFromX < 0 || pmFromY < 0) return;
2519 DropMenuEvent(piece, pmFromX, pmFromY);
2523 * If the user selects on a border boundary, return -1; if off the board,
2524 * return -2. Otherwise map the event coordinate to the square.
2527 EventToSquare(x, limit)
2535 if ((x % (squareSize + lineGap)) >= squareSize)
2537 x /= (squareSize + lineGap);
2544 do_flash_delay(msec)
2551 drawHighlight(file, rank, line_type)
2552 int file, rank, line_type;
2557 if (lineGap == 0 || appData.blindfold) return;
2561 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
2562 (squareSize + lineGap);
2563 y = lineGap/2 + rank * (squareSize + lineGap);
2567 x = lineGap/2 + file * (squareSize + lineGap);
2568 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
2569 (squareSize + lineGap);
2573 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2575 /* draw the highlight */
2576 cairo_move_to (cr, x, y);
2577 cairo_rel_line_to (cr, 0,squareSize+lineGap);
2578 cairo_rel_line_to (cr, squareSize+lineGap,0);
2579 cairo_rel_line_to (cr, 0,-squareSize-lineGap);
2580 cairo_close_path (cr);
2582 cairo_set_line_width (cr, lineGap);
2585 /* TODO: use appdata colors */
2586 case LINE_TYPE_HIGHLIGHT:
2587 cairo_set_source_rgba (cr, 1, 1, 0, 1.0);
2590 cairo_set_source_rgba (cr, 1, 0, 0, 1.0);
2592 case LINE_TYPE_NORMAL:
2594 cairo_set_source_rgba (cr, 0, 1, 0, 1.0);
2605 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
2606 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
2609 SetHighlights(fromX, fromY, toX, toY)
2610 int fromX, fromY, toX, toY;
2612 if (hi1X != fromX || hi1Y != fromY)
2614 if (hi1X >= 0 && hi1Y >= 0)
2616 drawHighlight(hi1X, hi1Y, LINE_TYPE_NORMAL);
2619 if (hi2X != toX || hi2Y != toY)
2621 if (hi2X >= 0 && hi2Y >= 0)
2623 drawHighlight(hi2X, hi2Y, LINE_TYPE_NORMAL);
2626 if (hi1X != fromX || hi1Y != fromY)
2628 if (fromX >= 0 && fromY >= 0)
2630 drawHighlight(fromX, fromY, LINE_TYPE_HIGHLIGHT);
2633 if (hi2X != toX || hi2Y != toY)
2635 if (toX >= 0 && toY >= 0)
2637 drawHighlight(toX, toY, LINE_TYPE_HIGHLIGHT);
2651 SetHighlights(-1, -1, -1, -1);
2656 SetPremoveHighlights(fromX, fromY, toX, toY)
2657 int fromX, fromY, toX, toY;
2659 if (pm1X != fromX || pm1Y != fromY)
2661 if (pm1X >= 0 && pm1Y >= 0)
2663 drawHighlight(pm1X, pm1Y, LINE_TYPE_NORMAL);
2665 if (fromX >= 0 && fromY >= 0)
2667 drawHighlight(fromX, fromY, LINE_TYPE_PRE);
2670 if (pm2X != toX || pm2Y != toY)
2672 if (pm2X >= 0 && pm2Y >= 0)
2674 drawHighlight(pm2X, pm2Y, LINE_TYPE_NORMAL);
2676 if (toX >= 0 && toY >= 0)
2678 drawHighlight(toX, toY, LINE_TYPE_PRE);
2691 ClearPremoveHighlights()
2693 SetPremoveHighlights(-1, -1, -1, -1);
2696 void BlankSquare(x, y, color, piece, dest)
2709 pb = SVGLightSquare;
2711 case 2: /* neutral */
2713 pb = SVGNeutralSquare;
2716 gdk_draw_pixbuf(GDK_WINDOW(GUI_Board->window),NULL,pb,0,0,x,y,-1,-1, GDK_RGB_DITHER_NORMAL, 0, 0);
2721 DrawPiece(piece, square_color, x, y, dest)
2723 int square_color, x, y;
2726 /* redraw background, since piece might be transparent in some areas */
2727 BlankSquare(x,y,square_color,piece,dest);
2730 gdk_draw_pixbuf(GDK_WINDOW(GUI_Board->window),NULL,
2731 GDK_PIXBUF(SVGpieces[piece]),0,0,x,y,-1,-1,
2732 GDK_RGB_DITHER_NORMAL, 0, 0);
2736 /* [HR] determine square color depending on chess variant. */
2737 static int SquareColor(row, column)
2742 if (gameInfo.variant == VariantXiangqi) {
2743 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
2745 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
2747 } else if (row <= 4) {
2753 square_color = ((column + row) % 2) == 1;
2756 /* [hgm] holdings: next line makes all holdings squares light */
2757 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
2759 return square_color;
2762 void DrawSquare(row, column, piece, do_flash)
2763 int row, column, do_flash;
2766 int square_color, x, y;
2771 /* Calculate delay in milliseconds (2-delays per complete flash) */
2772 flash_delay = 500 / appData.flashRate;
2774 /* calculate x and y coordinates from row and column */
2777 x = lineGap + ((BOARD_WIDTH-1)-column) *
2778 (squareSize + lineGap);
2779 y = lineGap + row * (squareSize + lineGap);
2783 x = lineGap + column * (squareSize + lineGap);
2784 y = lineGap + ((BOARD_HEIGHT-1)-row) *
2785 (squareSize + lineGap);
2788 square_color = SquareColor(row, column);
2790 // [HGM] holdings: blank out area between board and holdings
2791 if ( column == BOARD_LEFT-1 || column == BOARD_RGHT
2792 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
2793 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) )
2795 BlankSquare(x, y, 2, EmptySquare, xBoardWindow);
2797 // [HGM] print piece counts next to holdings
2798 string[1] = NULLCHAR;
2801 cairo_text_extents_t extents;
2806 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2808 string[0] = '0' + piece;
2810 /* TODO this has to go into the font-selection */
2811 cairo_select_font_face (cr, "Sans",
2812 CAIRO_FONT_SLANT_NORMAL,
2813 CAIRO_FONT_WEIGHT_NORMAL);
2815 // switch (event->type) {
2817 // if (event->xexpose.count > 0) return; /* no clipping is done */
2818 // XDrawPosition(widget, True, NULL);
2820 // case MotionNotify:
2821 // if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
2828 cairo_set_font_size (cr, 12.0);
2829 cairo_text_extents (cr, string, &extents);
2831 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) )
2833 xpos= x + squareSize - extents.width - 2;
2834 ypos= y + extents.y_bearing + 1;
2836 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1)
2839 ypos = y + extents.y_bearing + 1;
2842 /* TODO mono mode? */
2843 cairo_move_to (cr, xpos, ypos);
2844 cairo_text_path (cr, string);
2845 cairo_set_source_rgb (cr, 1.0, 1.0, 1);
2846 cairo_fill_preserve (cr);
2847 cairo_set_source_rgb (cr, 0, 0, 0);
2848 cairo_set_line_width (cr, 0.1);
2857 /* square on the board */
2858 if (piece == EmptySquare || appData.blindfold)
2860 BlankSquare(x, y, square_color, piece, xBoardWindow);
2864 if (do_flash && appData.flashCount > 0)
2866 for (i=0; i<appData.flashCount; ++i)
2869 DrawPiece(piece, square_color, x, y, xBoardWindow);
2870 do_flash_delay(flash_delay);
2872 BlankSquare(x, y, square_color, piece, xBoardWindow);
2873 do_flash_delay(flash_delay);
2876 DrawPiece(piece, square_color, x, y, xBoardWindow);
2880 /* show coordinates if necessary */
2881 if(appData.showCoords)
2883 cairo_text_extents_t extents;
2887 /* TODO this has to go into the font-selection */
2888 cairo_select_font_face (cr, "Sans",
2889 CAIRO_FONT_SLANT_NORMAL,
2890 CAIRO_FONT_WEIGHT_NORMAL);
2891 cairo_set_font_size (cr, 12.0);
2893 string[1] = NULLCHAR;
2896 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2898 if (row == (flipView ? BOARD_HEIGHT-1 : 0) &&
2899 column >= BOARD_LEFT && column < BOARD_RGHT)
2901 string[0] = 'a' + column - BOARD_LEFT;
2902 cairo_text_extents (cr, string, &extents);
2904 xpos = x + squareSize - extents.width - 2;
2905 ypos = y + squareSize - extents.height - extents.y_bearing - 1;
2907 if (appData.monoMode)
2914 cairo_move_to (cr, xpos, ypos);
2915 cairo_text_path (cr, string);
2916 cairo_set_source_rgb (cr, 0.0, 0.0, 0);
2917 cairo_fill_preserve (cr);
2918 cairo_set_source_rgb (cr, 0, 1.0, 0);
2919 cairo_set_line_width (cr, 0.1);
2922 if ( column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT))
2925 string[0] = ONE + row;
2926 cairo_text_extents (cr, string, &extents);
2929 ypos = y + extents.height + 1;
2931 if (appData.monoMode)
2938 cairo_move_to (cr, xpos, ypos);
2939 cairo_text_path (cr, string);
2940 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
2941 cairo_fill_preserve (cr);
2942 cairo_set_source_rgb (cr, 0, 0, 1.0);
2943 cairo_set_line_width (cr, 0.1);
2955 /* Returns 1 if there are "too many" differences between b1 and b2
2956 (i.e. more than 1 move was made) */
2957 static int too_many_diffs(b1, b2)
2963 for (i=0; i<BOARD_HEIGHT; ++i) {
2964 for (j=0; j<BOARD_WIDTH; ++j) {
2965 if (b1[i][j] != b2[i][j]) {
2966 if (++c > 4) /* Castling causes 4 diffs */
2975 /* Matrix describing castling maneuvers */
2976 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
2977 static int castling_matrix[4][5] = {
2978 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
2979 { 0, 7, 4, 5, 6 }, /* 0-0, white */
2980 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
2981 { 7, 7, 4, 5, 6 } /* 0-0, black */
2984 /* Checks whether castling occurred. If it did, *rrow and *rcol
2985 are set to the destination (row,col) of the rook that moved.
2987 Returns 1 if castling occurred, 0 if not.
2989 Note: Only handles a max of 1 castling move, so be sure
2990 to call too_many_diffs() first.
2992 static int check_castle_draw(newb, oldb, rrow, rcol)
2999 /* For each type of castling... */
3000 for (i=0; i<4; ++i) {
3001 r = castling_matrix[i];
3003 /* Check the 4 squares involved in the castling move */
3005 for (j=1; j<=4; ++j) {
3006 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
3013 /* All 4 changed, so it must be a castling move */
3022 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
3024 DrawSeekAxis( int x, int y, int xTo, int yTo )
3026 // XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
3030 DrawSeekBackground( int left, int top, int right, int bottom )
3032 // XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
3036 DrawSeekText(char *buf, int x, int y)
3038 // XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
3042 DrawSeekDot(int x, int y, int colorNr)
3044 int square = colorNr & 0x80;
3047 // color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
3049 // XFillRectangle(xDisplay, xBoardWindow, color,
3050 // x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3052 // XFillArc(xDisplay, xBoardWindow, color,
3053 // x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
3056 static int damage[BOARD_RANKS][BOARD_FILES];
3059 * event handler for redrawing the board
3061 void DrawPosition( repaint, board)
3062 /*Boolean*/int repaint;
3066 static int lastFlipView = 0;
3067 static int lastBoardValid = 0;
3068 static Board lastBoard;
3071 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
3073 if (board == NULL) {
3074 if (!lastBoardValid) return;
3077 if (!lastBoardValid || lastFlipView != flipView) {
3078 // XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
3079 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flip View"),
3084 * It would be simpler to clear the window with XClearWindow()
3085 * but this causes a very distracting flicker.
3088 if (!repaint && lastBoardValid && lastFlipView == flipView)
3090 /* If too much changes (begin observing new game, etc.), don't
3092 do_flash = too_many_diffs(board, lastBoard) ? 0 : 1;
3094 /* Special check for castling so we don't flash both the king
3095 and the rook (just flash the king). */
3098 if (check_castle_draw(board, lastBoard, &rrow, &rcol))
3100 /* Draw rook with NO flashing. King will be drawn flashing later */
3101 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
3102 lastBoard[rrow][rcol] = board[rrow][rcol];
3106 /* First pass -- Draw (newly) empty squares and repair damage.
3107 This prevents you from having a piece show up twice while it
3108 is flashing on its new square */
3109 for (i = 0; i < BOARD_HEIGHT; i++)
3110 for (j = 0; j < BOARD_WIDTH; j++)
3111 if ((board[i][j] != lastBoard[i][j] && board[i][j] == EmptySquare)
3114 DrawSquare(i, j, board[i][j], 0);
3115 damage[i][j] = False;
3118 /* Second pass -- Draw piece(s) in new position and flash them */
3119 for (i = 0; i < BOARD_HEIGHT; i++)
3120 for (j = 0; j < BOARD_WIDTH; j++)
3121 if (board[i][j] != lastBoard[i][j])
3123 DrawSquare(i, j, board[i][j], do_flash);
3135 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
3137 cairo_set_line_width (cr, lineGap);
3139 /* TODO: use appdata colors */
3140 cairo_set_source_rgba (cr, 0, 1, 0, 1.0);
3144 for (i = 0; i < BOARD_HEIGHT + 1; i++)
3147 x2 = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3148 y1 = y2 = lineGap / 2 + (i * (squareSize + lineGap));
3150 cairo_move_to (cr, x1, y1);
3151 cairo_rel_line_to (cr, x2,0);
3155 for (j = 0; j < BOARD_WIDTH + 1; j++)
3158 y2 = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3159 x1 = x2 = lineGap / 2 + (j * (squareSize + lineGap));
3161 cairo_move_to (cr, x1, y1);
3162 cairo_rel_line_to (cr, 0, y2);
3171 for (i = 0; i < BOARD_HEIGHT; i++)
3172 for (j = 0; j < BOARD_WIDTH; j++)
3174 DrawSquare(i, j, board[i][j], 0);
3175 damage[i][j] = False;
3179 CopyBoard(lastBoard, board);
3181 lastFlipView = flipView;
3183 /* Draw highlights */
3184 if (pm1X >= 0 && pm1Y >= 0)
3186 drawHighlight(pm1X, pm1Y, LINE_TYPE_PRE);
3188 if (pm2X >= 0 && pm2Y >= 0)
3190 drawHighlight(pm2X, pm2Y, LINE_TYPE_PRE);
3192 if (hi1X >= 0 && hi1Y >= 0)
3194 drawHighlight(hi1X, hi1Y, LINE_TYPE_HIGHLIGHT);
3196 if (hi2X >= 0 && hi2Y >= 0)
3198 drawHighlight(hi2X, hi2Y, LINE_TYPE_HIGHLIGHT);
3201 /* If piece being dragged around board, must redraw that too */
3207 void AnimateUserMove (Widget w, XEvent * event,
3208 String * params, Cardinal * nParams)
3210 DragPieceMove(event->xmotion.x, event->xmotion.y);
3213 void HandlePV (Widget w, XEvent * event,
3214 String * params, Cardinal * nParams)
3215 { // [HGM] pv: walk PV
3216 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3219 Widget CommentCreate(name, text, mutable, callback, lines)
3221 int /*Boolean*/ mutable;
3222 XtCallbackProc callback;
3226 Widget shell, layout, form, edit, b_ok, b_cancel, b_clear, b_close, b_edit;
3231 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3232 XtGetValues(boardWidget, args, j);
3235 XtSetArg(args[j], XtNresizable, True); j++;
3238 // XtCreatePopupShell(name, topLevelShellWidgetClass,
3239 // shellWidget, args, j);
3242 // XtCreatePopupShell(name, transientShellWidgetClass,
3243 // shellWidget, args, j);
3246 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
3247 layoutArgs, XtNumber(layoutArgs));
3249 XtCreateManagedWidget("form", formWidgetClass, layout,
3250 formArgs, XtNumber(formArgs));
3254 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
3255 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
3257 XtSetArg(args[j], XtNstring, text); j++;
3258 XtSetArg(args[j], XtNtop, XtChainTop); j++;
3259 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3260 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3261 XtSetArg(args[j], XtNright, XtChainRight); j++;
3262 XtSetArg(args[j], XtNresizable, True); j++;
3263 XtSetArg(args[j], XtNwidth, bw_width); j++; /*force wider than buttons*/
3264 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
3265 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
3266 XtSetArg(args[j], XtNautoFill, True); j++;
3267 XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
3269 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
3273 XtSetArg(args[j], XtNfromVert, edit); j++;
3274 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3275 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3276 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3277 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3279 XtCreateManagedWidget(_("ok"), commandWidgetClass, form, args, j);
3280 XtAddCallback(b_ok, XtNcallback, callback, (XtPointer) 0);
3283 XtSetArg(args[j], XtNfromVert, edit); j++;
3284 XtSetArg(args[j], XtNfromHoriz, b_ok); j++;
3285 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3286 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3287 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3288 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3290 XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
3291 XtAddCallback(b_cancel, XtNcallback, callback, (XtPointer) 0);
3294 XtSetArg(args[j], XtNfromVert, edit); j++;
3295 XtSetArg(args[j], XtNfromHoriz, b_cancel); j++;
3296 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3297 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3298 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3299 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3301 XtCreateManagedWidget(_("clear"), commandWidgetClass, form, args, j);
3302 XtAddCallback(b_clear, XtNcallback, callback, (XtPointer) 0);
3305 XtSetArg(args[j], XtNfromVert, edit); j++;
3306 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3307 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3308 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3309 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3311 XtCreateManagedWidget(_("close"), commandWidgetClass, form, args, j);
3312 XtAddCallback(b_close, XtNcallback, callback, (XtPointer) 0);
3315 XtSetArg(args[j], XtNfromVert, edit); j++;
3316 XtSetArg(args[j], XtNfromHoriz, b_close); j++;
3317 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3318 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3319 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3320 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3322 XtCreateManagedWidget(_("edit"), commandWidgetClass, form, args, j);
3323 XtAddCallback(b_edit, XtNcallback, callback, (XtPointer) 0);
3326 XtRealizeWidget(shell);
3328 if (commentX == -1) {
3331 Dimension pw_height;
3332 Dimension ew_height;
3335 XtSetArg(args[j], XtNheight, &ew_height); j++;
3336 XtGetValues(edit, args, j);
3339 XtSetArg(args[j], XtNheight, &pw_height); j++;
3340 XtGetValues(shell, args, j);
3341 commentH = pw_height + (lines - 1) * ew_height;
3342 commentW = bw_width - 16;
3344 // XSync(xDisplay, False);
3346 /* This code seems to tickle an X bug if it is executed too soon
3347 after xboard starts up. The coordinates get transformed as if
3348 the main window was positioned at (0, 0).
3350 // XtTranslateCoords(shellWidget,
3351 // (bw_width - commentW) / 2, 0 - commentH / 2,
3352 // &commentX, &commentY);
3354 // XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
3355 // RootWindowOfScreen(XtScreen(shellWidget)),
3356 // (bw_width - commentW) / 2, 0 - commentH / 2,
3357 // &xx, &yy, &junk);
3361 if (commentY < 0) commentY = 0; /*avoid positioning top offscreen*/
3364 if(wpComment.width > 0) {
3365 commentX = wpComment.x;
3366 commentY = wpComment.y;
3367 commentW = wpComment.width;
3368 commentH = wpComment.height;
3372 XtSetArg(args[j], XtNheight, commentH); j++;
3373 XtSetArg(args[j], XtNwidth, commentW); j++;
3374 XtSetArg(args[j], XtNx, commentX); j++;
3375 XtSetArg(args[j], XtNy, commentY); j++;
3376 XtSetValues(shell, args, j);
3377 XtSetKeyboardFocus(shell, edit);
3382 /* Used for analysis window and ICS input window */
3383 Widget MiscCreate(name, text, mutable, callback, lines)
3385 int /*Boolean*/ mutable;
3386 XtCallbackProc callback;
3390 Widget shell, layout, form, edit;
3392 Dimension bw_width, pw_height, ew_height, w, h;
3398 XtSetArg(args[j], XtNresizable, True); j++;
3401 // XtCreatePopupShell(name, topLevelShellWidgetClass,
3402 // shellWidget, args, j);
3405 // XtCreatePopupShell(name, transientShellWidgetClass,
3406 // shellWidget, args, j);
3409 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
3410 layoutArgs, XtNumber(layoutArgs));
3412 XtCreateManagedWidget("form", formWidgetClass, layout,
3413 formArgs, XtNumber(formArgs));
3417 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
3418 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
3420 XtSetArg(args[j], XtNstring, text); j++;
3421 XtSetArg(args[j], XtNtop, XtChainTop); j++;
3422 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3423 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3424 XtSetArg(args[j], XtNright, XtChainRight); j++;
3425 XtSetArg(args[j], XtNresizable, True); j++;
3426 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
3427 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
3428 XtSetArg(args[j], XtNautoFill, True); j++;
3429 XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
3431 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
3433 XtRealizeWidget(shell);
3436 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3437 XtGetValues(boardWidget, args, j);
3440 XtSetArg(args[j], XtNheight, &ew_height); j++;
3441 XtGetValues(edit, args, j);
3444 XtSetArg(args[j], XtNheight, &pw_height); j++;
3445 XtGetValues(shell, args, j);
3446 h = pw_height + (lines - 1) * ew_height;
3449 // XSync(xDisplay, False);
3451 /* This code seems to tickle an X bug if it is executed too soon
3452 after xboard starts up. The coordinates get transformed as if
3453 the main window was positioned at (0, 0).
3455 // XtTranslateCoords(shellWidget, (bw_width - w) / 2, 0 - h / 2, &x, &y);
3457 // XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
3458 // RootWindowOfScreen(XtScreen(shellWidget)),
3459 // (bw_width - w) / 2, 0 - h / 2, &xx, &yy, &junk);
3463 if (y < 0) y = 0; /*avoid positioning top offscreen*/
3466 XtSetArg(args[j], XtNheight, h); j++;
3467 XtSetArg(args[j], XtNwidth, w); j++;
3468 XtSetArg(args[j], XtNx, x); j++;
3469 XtSetArg(args[j], XtNy, y); j++;
3470 XtSetValues(shell, args, j);
3476 static int savedIndex; /* gross that this is global */
3478 void EditCommentPopUp(index, title, text)
3487 if (text == NULL) text = "";
3489 if (editShell == NULL) {
3491 CommentCreate(title, text, True, EditCommentCallback, 4);
3492 XtRealizeWidget(editShell);
3493 // CatchDeleteWindow(editShell, "EditCommentPopDown");
3495 edit = XtNameToWidget(editShell, "*form.text");
3497 XtSetArg(args[j], XtNstring, text); j++;
3498 XtSetValues(edit, args, j);
3500 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
3501 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
3502 XtSetValues(editShell, args, j);
3505 XtPopup(editShell, XtGrabNone);
3509 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
3510 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
3514 void EditCommentCallback(w, client_data, call_data)
3516 XtPointer client_data, call_data;
3524 XtSetArg(args[j], XtNlabel, &name); j++;
3525 XtGetValues(w, args, j);
3527 if (strcmp(name, _("ok")) == 0) {
3528 edit = XtNameToWidget(editShell, "*form.text");
3530 XtSetArg(args[j], XtNstring, &val); j++;
3531 XtGetValues(edit, args, j);
3532 ReplaceComment(savedIndex, val);
3533 EditCommentPopDown();
3534 } else if (strcmp(name, _("cancel")) == 0) {
3535 EditCommentPopDown();
3536 } else if (strcmp(name, _("clear")) == 0) {
3537 edit = XtNameToWidget(editShell, "*form.text");
3538 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
3539 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
3543 void EditCommentPopDown()
3548 if (!editUp) return;
3550 XtSetArg(args[j], XtNx, &commentX); j++;
3551 XtSetArg(args[j], XtNy, &commentY); j++;
3552 XtSetArg(args[j], XtNheight, &commentH); j++;
3553 XtSetArg(args[j], XtNwidth, &commentW); j++;
3554 XtGetValues(editShell, args, j);
3555 XtPopdown(editShell);
3558 XtSetArg(args[j], XtNleftBitmap, None); j++;
3559 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
3563 void ICSInputBoxPopUp()
3568 char *title = _("ICS Input");
3571 if (ICSInputShell == NULL) {
3572 ICSInputShell = MiscCreate(title, "", True, NULL, 1);
3573 tr = XtParseTranslationTable(ICSInputTranslations);
3574 edit = XtNameToWidget(ICSInputShell, "*form.text");
3575 XtOverrideTranslations(edit, tr);
3576 XtRealizeWidget(ICSInputShell);
3577 // CatchDeleteWindow(ICSInputShell, "ICSInputBoxPopDown");
3580 edit = XtNameToWidget(ICSInputShell, "*form.text");
3582 XtSetArg(args[j], XtNstring, ""); j++;
3583 XtSetValues(edit, args, j);
3585 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
3586 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
3587 XtSetValues(ICSInputShell, args, j);
3590 XtPopup(ICSInputShell, XtGrabNone);
3591 XtSetKeyboardFocus(ICSInputShell, edit);
3593 ICSInputBoxUp = True;
3595 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
3596 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
3600 void ICSInputSendText()
3607 edit = XtNameToWidget(ICSInputShell, "*form.text");
3609 XtSetArg(args[j], XtNstring, &val); j++;
3610 XtGetValues(edit, args, j);
3611 SendMultiLineToICS(val);
3612 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
3613 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
3616 void ICSInputBoxPopDown()
3621 if (!ICSInputBoxUp) return;
3623 XtPopdown(ICSInputShell);
3624 ICSInputBoxUp = False;
3626 XtSetArg(args[j], XtNleftBitmap, None); j++;
3627 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
3631 void CommentPopUp(title, text)
3638 if (commentShell == NULL) {
3640 CommentCreate(title, text, False, CommentCallback, 4);
3641 XtRealizeWidget(commentShell);
3642 // CatchDeleteWindow(commentShell, "CommentPopDown");
3644 edit = XtNameToWidget(commentShell, "*form.text");
3646 XtSetArg(args[j], XtNstring, text); j++;
3647 XtSetValues(edit, args, j);
3649 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
3650 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
3651 XtSetValues(commentShell, args, j);
3654 XtPopup(commentShell, XtGrabNone);
3655 // XSync(xDisplay, False);
3660 void CommentCallback(w, client_data, call_data)
3662 XtPointer client_data, call_data;
3669 XtSetArg(args[j], XtNlabel, &name); j++;
3670 XtGetValues(w, args, j);
3672 if (strcmp(name, _("close")) == 0) {
3674 } else if (strcmp(name, _("edit")) == 0) {
3681 void CommentPopDown()
3686 if (!commentUp) return;
3688 XtSetArg(args[j], XtNx, &commentX); j++;
3689 XtSetArg(args[j], XtNy, &commentY); j++;
3690 XtSetArg(args[j], XtNwidth, &commentW); j++;
3691 XtSetArg(args[j], XtNheight, &commentH); j++;
3692 XtGetValues(commentShell, args, j);
3693 XtPopdown(commentShell);
3694 // XSync(xDisplay, False);
3698 void PromotionPopUp()
3701 Widget dialog, layout;
3703 Dimension bw_width, pw_width;
3707 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3708 XtGetValues(boardWidget, args, j);
3711 XtSetArg(args[j], XtNresizable, True); j++;
3712 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
3714 // XtCreatePopupShell("Promotion", transientShellWidgetClass,
3715 // shellWidget, args, j);
3717 // XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
3718 // layoutArgs, XtNumber(layoutArgs));
3721 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
3722 XtSetArg(args[j], XtNborderWidth, 0); j++;
3723 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
3726 if(gameInfo.variant != VariantShogi) {
3727 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
3728 (XtPointer) dialog);
3729 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
3730 (XtPointer) dialog);
3731 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
3732 (XtPointer) dialog);
3733 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
3734 (XtPointer) dialog);
3735 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
3736 gameInfo.variant == VariantGiveaway) {
3737 XawDialogAddButton(dialog, _("King"), PromotionCallback,
3738 (XtPointer) dialog);
3740 if(gameInfo.variant == VariantCapablanca ||
3741 gameInfo.variant == VariantGothic ||
3742 gameInfo.variant == VariantCapaRandom) {
3743 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
3744 (XtPointer) dialog);
3745 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
3746 (XtPointer) dialog);
3748 } else // [HGM] shogi
3750 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
3751 (XtPointer) dialog);
3752 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
3753 (XtPointer) dialog);
3755 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
3756 (XtPointer) dialog);
3758 XtRealizeWidget(promotionShell);
3759 // CatchDeleteWindow(promotionShell, "PromotionPopDown");
3762 XtSetArg(args[j], XtNwidth, &pw_width); j++;
3763 XtGetValues(promotionShell, args, j);
3765 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3766 lineGap + squareSize/3 +
3767 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
3768 0 : 6*(squareSize + lineGap)), &x, &y);
3771 XtSetArg(args[j], XtNx, x); j++;
3772 XtSetArg(args[j], XtNy, y); j++;
3773 XtSetValues(promotionShell, args, j);
3775 XtPopup(promotionShell, XtGrabNone);
3780 void PromotionPopDown()
3782 if (!promotionUp) return;
3783 XtPopdown(promotionShell);
3784 XtDestroyWidget(promotionShell);
3785 promotionUp = False;
3788 void PromotionCallback(w, client_data, call_data)
3790 XtPointer client_data, call_data;
3796 XtSetArg(args[0], XtNlabel, &name);
3797 XtGetValues(w, args, 1);
3801 if (fromX == -1) return;
3803 if (strcmp(name, _("cancel")) == 0) {
3807 } else if (strcmp(name, _("Knight")) == 0) {
3809 } else if (strcmp(name, _("Promote")) == 0) {
3811 } else if (strcmp(name, _("Defer")) == 0) {
3814 promoChar = ToLower(name[0]);
3817 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
3819 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
3820 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3825 void ErrorCallback(w, client_data, call_data)
3827 XtPointer client_data, call_data;
3830 XtPopdown(w = XtParent(XtParent(XtParent(w))));
3832 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3838 if (!errorUp) return;
3842 gtk_widget_destroy(GTK_WIDGET(GUI_Error));
3844 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3849 void ErrorPopUp(title, label, modal)
3850 char *title, *label;
3853 GUI_Error = gtk_message_dialog_new(GTK_WINDOW(GUI_Window),
3854 GTK_DIALOG_DESTROY_WITH_PARENT,
3859 gtk_window_set_title(GTK_WINDOW(GUI_Error),(gchar *) title);
3862 gtk_dialog_run(GTK_DIALOG(GUI_Error));
3863 gtk_widget_destroy(GTK_WIDGET(GUI_Error));
3867 g_signal_connect_swapped (GUI_Error, "response",
3868 G_CALLBACK (ErrorPopDownProc),
3871 gtk_widget_show(GTK_WIDGET(GUI_Error));
3877 /* Disable all user input other than deleting the window */
3878 static int frozen = 0;
3882 /* Grab by a widget that doesn't accept input */
3883 // XtAddGrab(messageWidget, TRUE, FALSE);
3887 /* Undo a FreezeUI */
3890 if (!frozen) return;
3891 // XtRemoveGrab(messageWidget);
3895 char *ModeToWidgetName(mode)
3899 case BeginningOfGame:
3900 if (appData.icsActive)
3901 return "menuMode.ICS Client";
3902 else if (appData.noChessProgram ||
3903 *appData.cmailGameName != NULLCHAR)
3904 return "menuMode.Edit Game";
3906 return "menuMode.Machine Black";
3907 case MachinePlaysBlack:
3908 return "menuMode.Machine Black";
3909 case MachinePlaysWhite:
3910 return "menuMode.Machine White";
3912 return "menuMode.Analysis Mode";
3914 return "menuMode.Analyze File";
3915 case TwoMachinesPlay:
3916 return "menuMode.Two Machines";
3918 return "menuMode.Edit Game";
3919 case PlayFromGameFile:
3920 return "menuFile.Load Game";
3922 return "menuMode.Edit Position";
3924 return "menuMode.Training";
3925 case IcsPlayingWhite:
3926 case IcsPlayingBlack:
3930 return "menuMode.ICS Client";
3937 void ModeHighlight()
3939 static int oldPausing = FALSE;
3940 static GameMode oldmode = (GameMode) -1;
3943 // todo this toggling of the pause button doesn't seem to work?
3944 // e.g. select pause from buttonbar doesn't activate menumode.pause
3946 // if (!boardWidget || !XtIsRealized(boardWidget)) return;
3948 if (pausing != oldPausing) {
3949 oldPausing = pausing;
3950 gtk_button_set_relief(GTK_BUTTON (gtk_builder_get_object (builder, "menuMode.Pause")),pausing?GTK_RELIEF_NORMAL:GTK_RELIEF_NONE);
3951 /* toggle background color in showbuttonbar */
3952 if (appData.showButtonBar) {
3954 gtk_button_pressed(GTK_BUTTON (gtk_builder_get_object (builder, "buttonbar.Pause")));
3956 gtk_button_released(GTK_BUTTON (gtk_builder_get_object (builder, "buttonbar.Pause")));
3961 // probably not needed anymore
3962 // wname = ModeToWidgetName(oldmode);
3964 // gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, wname)),True);
3968 /* Maybe all the enables should be handled here, not just this one */
3969 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuMode.Training")),
3970 gameMode == Training || gameMode == PlayFromGameFile);
3975 * Button/menu procedures
3978 int LoadGamePopUp(f, gameNumber, title)
3983 cmailMsgLoaded = FALSE;
3985 if (gameNumber == 0)
3987 int error = GameListBuild(f);
3991 DisplayError(_("Cannot build game list"), error);
3993 else if (!ListEmpty(&gameList)
3994 && ((ListGame *) gameList.tailPred)->number > 1)
3996 /* we need an answer which game to load, so let's make it modal for a while*/
3997 gtk_window_set_modal(GTK_WINDOW(GUI_GameList) , TRUE);
3998 GameListPopUp(f, title);
3999 gtk_window_set_modal(GTK_WINDOW(GUI_GameList) , FALSE);
4008 return LoadGame(f, gameNumber, title, FALSE);
4011 void ReloadCmailMsgProc(w, event, prms, nprms)
4017 ReloadCmailMsgEvent(FALSE);
4020 void MailMoveProc(w, event, prms, nprms)
4029 /* this variable is shared between CopyPositionProc and SendPositionSelection */
4030 char *selected_fen_position=NULL;
4033 SendPositionSelection(Widget w, Atom *selection, Atom *target,
4034 Atom *type_return, XtPointer *value_return,
4035 unsigned long *length_return, int *format_return)
4037 char *selection_tmp;
4039 if (!selected_fen_position) return False; /* should never happen */
4040 // if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4041 // /* note: since no XtSelectionDoneProc was registered, Xt will
4042 // * automatically call XtFree on the value returned. So have to
4043 // * make a copy of it allocated with XtMalloc */
4044 // selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
4045 // strcpy(selection_tmp, selected_fen_position);
4047 // *value_return=selection_tmp;
4048 // *length_return=strlen(selection_tmp);
4049 // *type_return=*target;
4050 // *format_return = 8; /* bits per byte */
4052 // } else if (*target == XA_TARGETS(xDisplay)) {
4053 // Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4054 // targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4055 // targets_tmp[1] = XA_STRING;
4056 // *value_return = targets_tmp;
4057 // *type_return = XA_ATOM;
4058 // *length_return = 2;
4059 // *format_return = 8 * sizeof(Atom);
4060 // if (*format_return > 32) {
4061 // *length_return *= *format_return / 32;
4062 // *format_return = 32;
4070 /* note: when called from menu all parameters are NULL, so no clue what the
4071 * Widget which was clicked on was, or what the click event was
4073 void CopyPositionProc(w, event, prms, nprms)
4080 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4081 * have a notion of a position that is selected but not copied.
4082 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4084 if(gameMode == EditPosition) EditPositionDone(TRUE);
4085 if (selected_fen_position) free(selected_fen_position);
4086 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
4087 if (!selected_fen_position) return;
4088 // XtOwnSelection(menuBarWidget, XA_PRIMARY,
4090 // SendPositionSelection,
4091 // NULL/* lose_ownership_proc */ ,
4092 // NULL/* transfer_done_proc */);
4093 // XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4095 // SendPositionSelection,
4096 // NULL/* lose_ownership_proc */ ,
4097 // NULL/* transfer_done_proc */);
4100 /* function called when the data to Paste is ready */
4102 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
4103 Atom *type, XtPointer value, unsigned long *len, int *format)
4106 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
4107 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
4108 EditPositionPasteFEN(fenstr);
4112 /* called when Paste Position button is pressed,
4113 * all parameters will be NULL */
4114 void PastePositionProc(w, event, prms, nprms)
4120 // XtGetSelectionValue(menuBarWidget,
4121 // appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4122 // /* (XtSelectionCallbackProc) */ PastePositionCB,
4123 // NULL, /* client_data passed to PastePositionCB */
4125 // /* better to use the time field from the event that triggered the
4126 // * call to this function, but that isn't trivial to get
4134 SendGameSelection(Widget w, Atom *selection, Atom *target,
4135 Atom *type_return, XtPointer *value_return,
4136 unsigned long *length_return, int *format_return)
4138 char *selection_tmp;
4140 // if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4141 // FILE* f = fopen(gameCopyFilename, "r");
4144 // if (f == NULL) return False;
4148 // selection_tmp = XtMalloc(len + 1);
4149 // count = fread(selection_tmp, 1, len, f);
4150 // if (len != count) {
4151 // XtFree(selection_tmp);
4154 // selection_tmp[len] = NULLCHAR;
4155 // *value_return = selection_tmp;
4156 // *length_return = len;
4157 // *type_return = *target;
4158 // *format_return = 8; /* bits per byte */
4160 // } else if (*target == XA_TARGETS(xDisplay)) {
4161 // Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4162 // targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4163 // targets_tmp[1] = XA_STRING;
4164 // *value_return = targets_tmp;
4165 // *type_return = XA_ATOM;
4166 // *length_return = 2;
4167 // *format_return = 8 * sizeof(Atom);
4168 // if (*format_return > 32) {
4169 // *length_return *= *format_return / 32;
4170 // *format_return = 32;
4178 /* note: when called from menu all parameters are NULL, so no clue what the
4179 * Widget which was clicked on was, or what the click event was
4181 void CopyGameProc(w, event, prms, nprms)
4189 ret = SaveGameToFile(gameCopyFilename, FALSE);
4193 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4194 * have a notion of a game that is selected but not copied.
4195 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4197 // XtOwnSelection(menuBarWidget, XA_PRIMARY,
4199 // SendGameSelection,
4200 // NULL/* lose_ownership_proc */ ,
4201 // NULL/* transfer_done_proc */);
4202 // XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4204 // SendGameSelection,
4205 // NULL/* lose_ownership_proc */ ,
4206 // NULL/* transfer_done_proc */);
4209 /* function called when the data to Paste is ready */
4211 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
4212 Atom *type, XtPointer value, unsigned long *len, int *format)
4215 if (value == NULL || *len == 0) {
4216 return; /* nothing had been selected to copy */
4218 f = fopen(gamePasteFilename, "w");
4220 DisplayError(_("Can't open temp file"), errno);
4223 fwrite(value, 1, *len, f);
4226 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
4229 /* called when Paste Game button is pressed,
4230 * all parameters will be NULL */
4231 void PasteGameProc(w, event, prms, nprms)
4237 // XtGetSelectionValue(menuBarWidget,
4238 // appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4239 // /* (XtSelectionCallbackProc) */ PasteGameCB,
4240 // NULL, /* client_data passed to PasteGameCB */
4242 // /* better to use the time field from the event that triggered the
4243 // * call to this function, but that isn't trivial to get
4250 void SaveOnExitProc(w, event, prms, nprms)
4258 saveSettingsOnExit = !saveSettingsOnExit;
4260 if (saveSettingsOnExit) {
4261 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
4263 XtSetArg(args[0], XtNleftBitmap, None);
4265 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
4269 void SaveSettingsProc(w, event, prms, nprms)
4275 SaveSettings(settingsFileName);
4281 SaveGameProc(NULL, NULL);
4286 void EditCommentProc(w, event, prms, nprms)
4293 EditCommentPopDown();
4299 void IcsInputBoxProc(w, event, prms, nprms)
4305 if (ICSInputBoxUp) {
4306 ICSInputBoxPopDown();
4313 void EnterKeyProc(w, event, prms, nprms)
4319 if (ICSInputBoxUp == True)
4324 void DebugProc(w, event, prms, nprms)
4330 appData.debugMode = !appData.debugMode;
4333 void AboutGameProc(w, event, prms, nprms)
4342 void NothingProc(w, event, prms, nprms)
4351 void Iconify(w, event, prms, nprms)
4359 // fromX = fromY = -1;
4360 // XtSetArg(args[0], XtNiconic, True);
4361 // XtSetValues(shellWidget, args, 1);
4364 void DisplayMessage(message, extMessage)
4365 gchar *message, *extMessage;
4372 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
4375 message = extMessage;
4378 gtk_label_set_text( GTK_LABEL(gtk_builder_get_object (builder, "Messages")),message);
4383 void DisplayTitle(text)
4386 gchar title[MSG_SIZ];
4388 if (text == NULL) text = "";
4390 if (appData.titleInWindow)
4395 if (*text != NULLCHAR)
4397 strcpy(title, text);
4399 else if (appData.icsActive)
4401 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
4403 else if (appData.cmailGameName[0] != NULLCHAR)
4405 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
4407 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
4409 else if (gameInfo.variant == VariantGothic)
4411 strcpy(title, GOTHIC);
4415 else if (gameInfo.variant == VariantFalcon)
4417 strcpy(title, FALCON);
4420 else if (appData.noChessProgram)
4422 strcpy(title, programName);
4426 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
4428 gtk_window_set_title(GTK_WINDOW(GUI_Window),title);
4434 void DisplayError(message, error)
4441 if (appData.debugMode || appData.matchMode) {
4442 fprintf(stderr, "%s: %s\n", programName, message);
4445 if (appData.debugMode || appData.matchMode) {
4446 fprintf(stderr, "%s: %s: %s\n",
4447 programName, message, strerror(error));
4449 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
4452 ErrorPopUp(_("Error"), message, FALSE);
4456 void DisplayMoveError(message)
4461 DrawPosition(FALSE, NULL);
4462 if (appData.debugMode || appData.matchMode) {
4463 fprintf(stderr, "%s: %s\n", programName, message);
4465 if (appData.popupMoveErrors) {
4466 ErrorPopUp(_("Error"), message, FALSE);
4468 DisplayMessage(message, "");
4473 void DisplayFatalError(message, error, status)
4479 errorExitStatus = status;
4481 fprintf(stderr, "%s: %s\n", programName, message);
4483 fprintf(stderr, "%s: %s: %s\n",
4484 programName, message, strerror(error));
4485 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
4488 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
4489 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
4495 void DisplayInformation(message)
4499 ErrorPopUp(_("Information"), message, TRUE);
4502 void DisplayNote(message)
4506 ErrorPopUp(_("Note"), message, FALSE);
4510 NullXErrorCheck(dpy, error_event)
4512 XErrorEvent *error_event;
4517 void DisplayIcsInteractionTitle(message)
4520 if (oldICSInteractionTitle == NULL) {
4521 /* Magic to find the old window title, adapted from vim */
4522 char *wina = getenv("WINDOWID");
4524 Window win = (Window) atoi(wina);
4525 Window root, parent, *children;
4526 unsigned int nchildren;
4527 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4529 // if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4530 // if (!XQueryTree(xDisplay, win, &root, &parent,
4531 // &children, &nchildren)) break;
4532 // if (children) XFree((void *)children);
4533 // if (parent == root || parent == 0) break;
4536 XSetErrorHandler(oldHandler);
4538 if (oldICSInteractionTitle == NULL) {
4539 oldICSInteractionTitle = "xterm";
4542 printf("\033]0;%s\007", message);
4546 char pendingReplyPrefix[MSG_SIZ];
4547 ProcRef pendingReplyPR;
4549 void AskQuestionProc(w, event, prms, nprms)
4556 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
4560 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
4563 void AskQuestionPopDown()
4565 if (!askQuestionUp) return;
4566 XtPopdown(askQuestionShell);
4567 XtDestroyWidget(askQuestionShell);
4568 askQuestionUp = False;
4571 void AskQuestionReplyAction(w, event, prms, nprms)
4581 reply = XawDialogGetValueString(w = XtParent(w));
4582 strcpy(buf, pendingReplyPrefix);
4583 if (*buf) strcat(buf, " ");
4586 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
4587 AskQuestionPopDown();
4589 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
4592 void AskQuestionCallback(w, client_data, call_data)
4594 XtPointer client_data, call_data;
4599 XtSetArg(args[0], XtNlabel, &name);
4600 XtGetValues(w, args, 1);
4602 if (strcmp(name, _("cancel")) == 0) {
4603 AskQuestionPopDown();
4605 AskQuestionReplyAction(w, NULL, NULL, NULL);
4609 void AskQuestion(title, question, replyPrefix, pr)
4610 char *title, *question, *replyPrefix;
4614 Widget popup, layout, dialog, edit;
4620 strcpy(pendingReplyPrefix, replyPrefix);
4621 pendingReplyPR = pr;
4624 XtSetArg(args[i], XtNresizable, True); i++;
4625 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
4626 // askQuestionShell = popup =
4627 // XtCreatePopupShell(title, transientShellWidgetClass,
4628 // shellWidget, args, i);
4631 // XtCreateManagedWidget(layoutName, formWidgetClass, popup,
4632 // layoutArgs, XtNumber(layoutArgs));
4635 XtSetArg(args[i], XtNlabel, question); i++;
4636 XtSetArg(args[i], XtNvalue, ""); i++;
4637 XtSetArg(args[i], XtNborderWidth, 0); i++;
4638 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
4641 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
4642 (XtPointer) dialog);
4643 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
4644 (XtPointer) dialog);
4646 XtRealizeWidget(popup);
4647 // CatchDeleteWindow(popup, "AskQuestionPopDown");
4649 // XQueryPointer(xDisplay, xBoardWindow, &root, &child,
4650 // &x, &y, &win_x, &win_y, &mask);
4652 // XtSetArg(args[0], XtNx, x - 10);
4653 // XtSetArg(args[1], XtNy, y - 30);
4654 // XtSetValues(popup, args, 2);
4656 // XtPopup(popup, XtGrabExclusive);
4657 // askQuestionUp = True;
4659 // edit = XtNameToWidget(dialog, "*value");
4660 // XtSetKeyboardFocus(popup, edit);
4668 if (*name == NULLCHAR) {
4670 } else if (strcmp(name, "$") == 0) {
4671 putc(BELLCHAR, stderr);
4674 snprintf(buf, sizeof(buf), "%s '%s' &", appData.soundProgram, name);
4682 PlaySound(appData.soundMove);
4688 PlaySound(appData.soundIcsWin);
4694 PlaySound(appData.soundIcsLoss);
4700 PlaySound(appData.soundIcsDraw);
4704 PlayIcsUnfinishedSound()
4706 PlaySound(appData.soundIcsUnfinished);
4712 PlaySound(appData.soundIcsAlarm);
4718 system("stty echo");
4724 system("stty -echo");
4728 Colorize(cc, continuation)
4733 int count, outCount, error;
4735 if (textColors[(int)cc].bg > 0) {
4736 if (textColors[(int)cc].fg > 0) {
4737 sprintf(buf, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
4738 textColors[(int)cc].fg, textColors[(int)cc].bg);
4740 sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
4741 textColors[(int)cc].bg);
4744 if (textColors[(int)cc].fg > 0) {
4745 sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
4746 textColors[(int)cc].fg);
4748 sprintf(buf, "\033[0;%dm", textColors[(int)cc].attr);
4751 count = strlen(buf);
4752 outCount = OutputToProcess(NoProc, buf, count, &error);
4753 if (outCount < count) {
4754 DisplayFatalError(_("Error writing to display"), error, 1);
4757 if (continuation) return;
4760 PlaySound(appData.soundShout);
4763 PlaySound(appData.soundSShout);
4766 PlaySound(appData.soundChannel1);
4769 PlaySound(appData.soundChannel);
4772 PlaySound(appData.soundKibitz);
4775 PlaySound(appData.soundTell);
4777 case ColorChallenge:
4778 PlaySound(appData.soundChallenge);
4781 PlaySound(appData.soundRequest);
4784 PlaySound(appData.soundSeek);
4795 return getpwuid(getuid())->pw_name;
4798 static char *ExpandPathName(path)
4801 static char static_buf[2000];
4802 char *d, *s, buf[2000];
4808 while (*s && isspace(*s))
4817 if (*(s+1) == '/') {
4818 strcpy(d, getpwuid(getuid())->pw_dir);
4823 *strchr(buf, '/') = 0;
4824 pwd = getpwnam(buf);
4827 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
4831 strcpy(d, pwd->pw_dir);
4832 strcat(d, strchr(s+1, '/'));
4843 static char host_name[MSG_SIZ];
4845 #if HAVE_GETHOSTNAME
4846 gethostname(host_name, MSG_SIZ);
4848 #else /* not HAVE_GETHOSTNAME */
4849 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
4850 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
4852 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4854 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4855 #endif /* not HAVE_GETHOSTNAME */
4858 guint delayedEventTimerTag = 0;
4859 DelayedEventCallback delayedEventCallback = 0;
4862 FireDelayedEvent(data)
4866 g_source_remove(delayedEventTimerTag);
4867 delayedEventTimerTag = 0;
4870 delayedEventCallback();
4876 ScheduleDelayedEvent(cb, millisec)
4877 DelayedEventCallback cb; guint millisec;
4879 if(delayedEventTimerTag && delayedEventCallback == cb)
4880 // [HGM] alive: replace, rather than add or flush identical event
4881 g_source_remove(delayedEventTimerTag);
4882 delayedEventCallback = cb;
4883 delayedEventTimerTag = g_timeout_add(millisec,(GSourceFunc) FireDelayedEvent, NULL);
4887 DelayedEventCallback
4890 if (delayedEventTimerTag)
4892 return delayedEventCallback;
4901 CancelDelayedEvent()
4903 if (delayedEventTimerTag)
4905 g_source_remove(delayedEventTimerTag);
4906 delayedEventTimerTag = 0;
4912 guint loadGameTimerTag = 0;
4914 int LoadGameTimerRunning()
4916 return loadGameTimerTag != 0;
4919 int StopLoadGameTimer()
4921 if (loadGameTimerTag != 0) {
4922 g_source_remove(loadGameTimerTag);
4923 loadGameTimerTag = 0;
4931 LoadGameTimerCallback(data)
4935 g_source_remove(loadGameTimerTag);
4936 loadGameTimerTag = 0;
4943 StartLoadGameTimer(millisec)
4947 g_timeout_add( millisec, (GSourceFunc) LoadGameTimerCallback, NULL);
4951 guint analysisClockTag = 0;
4954 AnalysisClockCallback(data)
4957 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
4958 || appData.icsEngineAnalyze)
4960 AnalysisPeriodicEvent(0);
4961 return 1; /* keep on going */
4963 return 0; /* stop timer */
4967 StartAnalysisClock()
4970 g_timeout_add( 2000,(GSourceFunc) AnalysisClockCallback, NULL);
4974 guint clockTimerTag = 0;
4976 int ClockTimerRunning()
4978 return clockTimerTag != 0;
4981 int StopClockTimer()
4983 if (clockTimerTag != 0)
4985 g_source_remove(clockTimerTag);
4996 ClockTimerCallback(data)
5000 g_source_remove(clockTimerTag);
5008 StartClockTimer(millisec)
5011 clockTimerTag = g_timeout_add(millisec,(GSourceFunc) ClockTimerCallback,NULL);
5016 DisplayTimerLabel(w, color, timer, highlight)
5025 if (appData.clockMode) {
5026 sprintf(buf, "%s: %s", color, TimeString(timer));
5028 sprintf(buf, "%s ", color);
5030 gtk_label_set_text(GTK_LABEL(w),buf);
5032 /* check for low time warning */
5033 // Pixel foregroundOrWarningColor = timerForegroundPixel;
5036 // appData.lowTimeWarning &&
5037 // (timer / 1000) < appData.icsAlarmTime)
5038 // foregroundOrWarningColor = lowTimeWarningColor;
5040 // if (appData.clockMode) {
5041 // sprintf(buf, "%s: %s", color, TimeString(timer));
5042 // XtSetArg(args[0], XtNlabel, buf);
5044 // sprintf(buf, "%s ", color);
5045 // XtSetArg(args[0], XtNlabel, buf);
5050 // XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
5051 // XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
5053 // XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
5054 // XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
5057 // XtSetValues(w, args, 3);
5062 DisplayWhiteClock(timeRemaining, highlight)
5066 if(appData.noGUI) return;
5068 DisplayTimerLabel(GUI_Whiteclock, _("White"), timeRemaining, highlight);
5069 if (highlight && WindowIcon == BlackIcon)
5071 WindowIcon = WhiteIcon;
5072 gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
5077 DisplayBlackClock(timeRemaining, highlight)
5081 if(appData.noGUI) return;
5083 DisplayTimerLabel(GUI_Blackclock, _("Black"), timeRemaining, highlight);
5084 if (highlight && WindowIcon == WhiteIcon)
5086 WindowIcon = BlackIcon;
5087 gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
5105 int StartChildProcess(cmdLine, dir, pr)
5112 int to_prog[2], from_prog[2];
5116 if (appData.debugMode) {
5117 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
5120 /* We do NOT feed the cmdLine to the shell; we just
5121 parse it into blank-separated arguments in the
5122 most simple-minded way possible.
5125 strcpy(buf, cmdLine);
5128 while(*p == ' ') p++;
5130 if(*p == '"' || *p == '\'')
5131 p = strchr(++argv[i-1], *p);
5132 else p = strchr(p, ' ');
5133 if (p == NULL) break;
5138 SetUpChildIO(to_prog, from_prog);
5140 if ((pid = fork()) == 0) {
5142 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
5143 close(to_prog[1]); // first close the unused pipe ends
5144 close(from_prog[0]);
5145 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
5146 dup2(from_prog[1], 1);
5147 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
5148 close(from_prog[1]); // and closing again loses one of the pipes!
5149 if(fileno(stderr) >= 2) // better safe than sorry...
5150 dup2(1, fileno(stderr)); /* force stderr to the pipe */
5152 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
5157 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
5159 execvp(argv[0], argv);
5161 /* If we get here, exec failed */
5166 /* Parent process */
5168 close(from_prog[1]);
5170 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5173 cp->fdFrom = from_prog[0];
5174 cp->fdTo = to_prog[1];
5179 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
5180 static RETSIGTYPE AlarmCallBack(int n)
5186 DestroyChildProcess(pr, signalType)
5190 ChildProc *cp = (ChildProc *) pr;
5192 if (cp->kind != CPReal) return;
5194 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
5195 signal(SIGALRM, AlarmCallBack);
5197 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
5198 kill(cp->pid, SIGKILL); // kill it forcefully
5199 wait((int *) 0); // and wait again
5203 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
5205 /* Process is exiting either because of the kill or because of
5206 a quit command sent by the backend; either way, wait for it to die.
5215 InterruptChildProcess(pr)
5218 ChildProc *cp = (ChildProc *) pr;
5220 if (cp->kind != CPReal) return;
5221 (void) kill(cp->pid, SIGINT); /* stop it thinking */
5224 int OpenTelnet(host, port, pr)
5229 char cmdLine[MSG_SIZ];
5231 if (port[0] == NULLCHAR) {
5232 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
5234 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
5236 return StartChildProcess(cmdLine, "", pr);
5239 int OpenTCP(host, port, pr)
5245 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
5246 #else /* !OMIT_SOCKETS */
5248 struct sockaddr_in sa;
5250 unsigned short uport;
5253 if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
5257 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
5258 sa.sin_family = AF_INET;
5259 sa.sin_addr.s_addr = INADDR_ANY;
5260 uport = (unsigned short) 0;
5261 sa.sin_port = htons(uport);
5262 if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
5266 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
5267 if (!(hp = gethostbyname(host))) {
5269 if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
5270 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
5271 hp->h_addrtype = AF_INET;
5273 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
5274 hp->h_addr_list[0] = (char *) malloc(4);
5275 hp->h_addr_list[0][0] = b0;
5276 hp->h_addr_list[0][1] = b1;
5277 hp->h_addr_list[0][2] = b2;
5278 hp->h_addr_list[0][3] = b3;
5283 sa.sin_family = hp->h_addrtype;
5284 uport = (unsigned short) atoi(port);
5285 sa.sin_port = htons(uport);
5286 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
5288 if (connect(s, (struct sockaddr *) &sa,
5289 sizeof(struct sockaddr_in)) < 0) {
5293 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5300 #endif /* !OMIT_SOCKETS */
5305 int OpenCommPort(name, pr)
5312 fd = open(name, 2, 0);
5313 if (fd < 0) return errno;
5315 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5325 int OpenLoopback(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 */
5343 int OpenRcmd(host, user, cmd, pr)
5344 char *host, *user, *cmd;
5347 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
5351 #define INPUT_SOURCE_BUF_SIZE 8192
5360 char buf[INPUT_SOURCE_BUF_SIZE];
5365 DoInputCallback(io,cond,data)
5370 /* read input from one of the input source (for example a chess program, ICS, etc).
5371 * and call a function that will handle the input
5374 int count; /* how many bytes did we read */
5378 /* All information (callback function, file descriptor, etc) is
5379 * saved in an InputSource structure
5381 InputSource *is = (InputSource *) data;
5385 count = read(is->fd, is->unused,
5386 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
5390 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
5393 is->unused += count;
5395 /* break input into lines and call the callback function on each
5398 while (p < is->unused)
5400 q = memchr(p, '\n', is->unused - p);
5401 if (q == NULL) break;
5403 (is->func)(is, is->closure, p, q - p, 0);
5406 /* remember not yet used part of the buffer */
5408 while (p < is->unused)
5416 /* read maximum length of input buffer and send the whole buffer
5417 * to the callback function
5419 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
5424 (is->func)(is, is->closure, is->buf, count, error);
5430 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
5437 GIOChannel *channel;
5438 ChildProc *cp = (ChildProc *) pr;
5440 is = (InputSource *) calloc(1, sizeof(InputSource));
5441 is->lineByLine = lineByLine;
5445 is->fd = fileno(stdin);
5447 is->kind = cp->kind;
5448 is->fd = cp->fdFrom;
5451 is->unused = is->buf;
5455 // is->xid = XtAppAddInput(appContext, is->fd,
5456 // (XtPointer) (XtInputReadMask),
5457 // (XtInputCallbackProc) DoInputCallback,
5461 /* TODO: will this work on windows?*/
5463 channel = g_io_channel_unix_new(is->fd);
5464 g_io_channel_set_close_on_unref (channel, TRUE);
5465 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
5466 is->closure = closure;
5467 return (InputSourceRef) is;
5471 RemoveInputSource(isr)
5474 InputSource *is = (InputSource *) isr;
5476 if (is->sid == 0) return;
5477 g_source_remove(is->sid);
5482 int OutputToProcess(pr, message, count, outError)
5488 static int line = 0;
5489 ChildProc *cp = (ChildProc *) pr;
5494 if (appData.noJoin || !appData.useInternalWrap)
5495 outCount = fwrite(message, 1, count, stdout);
5498 int width = get_term_width();
5499 int len = wrap(NULL, message, count, width, &line);
5500 char *msg = malloc(len);
5504 outCount = fwrite(message, 1, count, stdout);
5507 dbgchk = wrap(msg, message, count, width, &line);
5508 if (dbgchk != len && appData.debugMode)
5509 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
5510 outCount = fwrite(msg, 1, dbgchk, stdout);
5516 outCount = write(cp->fdTo, message, count);
5526 /* Output message to process, with "ms" milliseconds of delay
5527 between each character. This is needed when sending the logon
5528 script to ICC, which for some reason doesn't like the
5529 instantaneous send. */
5530 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
5537 ChildProc *cp = (ChildProc *) pr;
5542 r = write(cp->fdTo, message++, 1);
5555 /**** Animation code by Hugh Fisher, DCS, ANU.
5557 Known problem: if a window overlapping the board is
5558 moved away while a piece is being animated underneath,
5559 the newly exposed area won't be updated properly.
5560 I can live with this.
5562 Known problem: if you look carefully at the animation
5563 of pieces in mono mode, they are being drawn as solid
5564 shapes without interior detail while moving. Fixing
5565 this would be a major complication for minimal return.
5568 /* Masks for XPM pieces. Black and white pieces can have
5569 different shapes, but in the interest of retaining my
5570 sanity pieces must have the same outline on both light
5571 and dark squares, and all pieces must use the same
5572 background square colors/images. */
5574 static int xpmDone = 0;
5577 CreateAnimMasks (pieceDepth)
5584 unsigned long plane;
5587 /* just return for gtk at the moment */
5590 /* Need a bitmap just to get a GC with right depth */
5591 // buf = XCreatePixmap(xDisplay, xBoardWindow,
5593 values.foreground = 1;
5594 values.background = 0;
5595 /* Don't use XtGetGC, not read only */
5596 // maskGC = XCreateGC(xDisplay, buf,
5597 // GCForeground | GCBackground, &values);
5598 // XFreePixmap(xDisplay, buf);
5600 // buf = XCreatePixmap(xDisplay, xBoardWindow,
5601 // squareSize, squareSize, pieceDepth);
5602 // values.foreground = XBlackPixel(xDisplay, xScreen);
5603 // values.background = XWhitePixel(xDisplay, xScreen);
5604 // bufGC = XCreateGC(xDisplay, buf,
5605 // GCForeground | GCBackground, &values);
5607 for (piece = WhitePawn; piece <= BlackKing; piece++) {
5608 /* Begin with empty mask */
5609 // if(!xpmDone) // [HGM] pieces: keep using existing
5610 // xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
5611 // squareSize, squareSize, 1);
5612 // XSetFunction(xDisplay, maskGC, GXclear);
5613 // XFillRectangle(xDisplay, xpmMask[piece], maskGC,
5614 // 0, 0, squareSize, squareSize);
5616 /* Take a copy of the piece */
5621 // XSetFunction(xDisplay, bufGC, GXcopy);
5622 // XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
5624 // 0, 0, squareSize, squareSize, 0, 0);
5626 /* XOR the background (light) over the piece */
5627 // XSetFunction(xDisplay, bufGC, GXxor);
5629 // XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
5630 // 0, 0, squareSize, squareSize, 0, 0);
5632 // XSetForeground(xDisplay, bufGC, lightSquareColor);
5633 // XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
5636 /* We now have an inverted piece image with the background
5637 erased. Construct mask by just selecting all the non-zero
5638 pixels - no need to reconstruct the original image. */
5639 // XSetFunction(xDisplay, maskGC, GXor);
5641 /* Might be quicker to download an XImage and create bitmap
5642 data from it rather than this N copies per piece, but it
5643 only takes a fraction of a second and there is a much
5644 longer delay for loading the pieces. */
5645 // for (n = 0; n < pieceDepth; n ++) {
5646 // XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
5647 // 0, 0, squareSize, squareSize,
5649 // plane = plane << 1;
5653 // XFreePixmap(xDisplay, buf);
5654 // XFreeGC(xDisplay, bufGC);
5655 // XFreeGC(xDisplay, maskGC);
5659 InitAnimState (anim, info)
5661 XWindowAttributes * info;
5666 /* Each buffer is square size, same depth as window */
5667 // anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
5668 // squareSize, squareSize, info->depth);
5669 // anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
5670 // squareSize, squareSize, info->depth);
5672 // /* Create a plain GC for blitting */
5673 // mask = GCForeground | GCBackground | GCFunction |
5674 // GCPlaneMask | GCGraphicsExposures;
5675 // values.foreground = XBlackPixel(xDisplay, xScreen);
5676 // values.background = XWhitePixel(xDisplay, xScreen);
5677 // values.function = GXcopy;
5678 // values.plane_mask = AllPlanes;
5679 // values.graphics_exposures = False;
5680 // anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
5682 // /* Piece will be copied from an existing context at
5683 // the start of each new animation/drag. */
5684 // anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
5686 // /* Outline will be a read-only copy of an existing */
5687 // anim->outlineGC = None;
5693 static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
5694 XWindowAttributes info;
5696 /* for gtk at the moment just ... */
5699 if (xpmDone && gameInfo.variant == old) return;
5700 if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet
5701 // XGetWindowAttributes(xDisplay, xBoardWindow, &info);
5703 // InitAnimState(&game, &info);
5704 // InitAnimState(&player, &info);
5706 /* For XPM pieces, we need bitmaps to use as masks. */
5708 // CreateAnimMasks(info.depth);
5714 static Boolean frameWaiting;
5716 static RETSIGTYPE FrameAlarm (sig)
5719 frameWaiting = False;
5720 /* In case System-V style signals. Needed?? */
5721 signal(SIGALRM, FrameAlarm);
5728 struct itimerval delay;
5730 XSync(xDisplay, False);
5733 frameWaiting = True;
5734 signal(SIGALRM, FrameAlarm);
5735 delay.it_interval.tv_sec =
5736 delay.it_value.tv_sec = time / 1000;
5737 delay.it_interval.tv_usec =
5738 delay.it_value.tv_usec = (time % 1000) * 1000;
5739 setitimer(ITIMER_REAL, &delay, NULL);
5740 while (frameWaiting) pause();
5741 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
5742 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
5743 setitimer(ITIMER_REAL, &delay, NULL);
5753 // XSync(xDisplay, False);
5755 usleep(time * 1000);
5760 /* Convert board position to corner of screen rect and color */
5763 ScreenSquare(column, row, pt, color)
5764 int column; int row; XPoint * pt; int * color;
5767 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
5768 pt->y = lineGap + row * (squareSize + lineGap);
5770 pt->x = lineGap + column * (squareSize + lineGap);
5771 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
5773 *color = SquareColor(row, column);
5776 /* Convert window coords to square */
5779 BoardSquare(x, y, column, row)
5780 int x; int y; int * column; int * row;
5782 *column = EventToSquare(x, BOARD_WIDTH);
5783 if (flipView && *column >= 0)
5784 *column = BOARD_WIDTH - 1 - *column;
5785 *row = EventToSquare(y, BOARD_HEIGHT);
5786 if (!flipView && *row >= 0)
5787 *row = BOARD_HEIGHT - 1 - *row;
5792 #undef Max /* just in case */
5794 #define Max(a, b) ((a) > (b) ? (a) : (b))
5795 #define Min(a, b) ((a) < (b) ? (a) : (b))
5798 SetRect(rect, x, y, width, height)
5799 XRectangle * rect; int x; int y; int width; int height;
5803 rect->width = width;
5804 rect->height = height;
5807 /* Test if two frames overlap. If they do, return
5808 intersection rect within old and location of
5809 that rect within new. */
5812 Intersect(old, new, size, area, pt)
5813 XPoint * old; XPoint * new;
5814 int size; XRectangle * area; XPoint * pt;
5816 if (old->x > new->x + size || new->x > old->x + size ||
5817 old->y > new->y + size || new->y > old->y + size) {
5820 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
5821 size - abs(old->x - new->x), size - abs(old->y - new->y));
5822 pt->x = Max(old->x - new->x, 0);
5823 pt->y = Max(old->y - new->y, 0);
5828 /* For two overlapping frames, return the rect(s)
5829 in the old that do not intersect with the new. */
5832 CalcUpdateRects(old, new, size, update, nUpdates)
5833 XPoint * old; XPoint * new; int size;
5834 XRectangle update[]; int * nUpdates;
5838 /* If old = new (shouldn't happen) then nothing to draw */
5839 if (old->x == new->x && old->y == new->y) {
5843 /* Work out what bits overlap. Since we know the rects
5844 are the same size we don't need a full intersect calc. */
5846 /* Top or bottom edge? */
5847 if (new->y > old->y) {
5848 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
5850 } else if (old->y > new->y) {
5851 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
5852 size, old->y - new->y);
5855 /* Left or right edge - don't overlap any update calculated above. */
5856 if (new->x > old->x) {
5857 SetRect(&(update[count]), old->x, Max(new->y, old->y),
5858 new->x - old->x, size - abs(new->y - old->y));
5860 } else if (old->x > new->x) {
5861 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
5862 old->x - new->x, size - abs(new->y - old->y));
5869 /* Generate a series of frame coords from start->mid->finish.
5870 The movement rate doubles until the half way point is
5871 reached, then halves back down to the final destination,
5872 which gives a nice slow in/out effect. The algorithmn
5873 may seem to generate too many intermediates for short
5874 moves, but remember that the purpose is to attract the
5875 viewers attention to the piece about to be moved and
5876 then to where it ends up. Too few frames would be less
5880 Tween(start, mid, finish, factor, frames, nFrames)
5881 XPoint * start; XPoint * mid;
5882 XPoint * finish; int factor;
5883 XPoint frames[]; int * nFrames;
5885 int fraction, n, count;
5889 /* Slow in, stepping 1/16th, then 1/8th, ... */
5891 for (n = 0; n < factor; n++)
5893 for (n = 0; n < factor; n++) {
5894 frames[count].x = start->x + (mid->x - start->x) / fraction;
5895 frames[count].y = start->y + (mid->y - start->y) / fraction;
5897 fraction = fraction / 2;
5901 frames[count] = *mid;
5904 /* Slow out, stepping 1/2, then 1/4, ... */
5906 for (n = 0; n < factor; n++) {
5907 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
5908 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
5910 fraction = fraction * 2;
5915 /* Draw a piece on the screen without disturbing what's there */
5918 SelectGCMask(piece, clip, outline, mask)
5919 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
5923 /* Bitmap for piece being moved. */
5924 if (appData.monoMode) {
5925 *mask = *pieceToSolid(piece);
5926 } else if (useImages) {
5928 *mask = xpmMask[piece];
5930 *mask = ximMaskPm[piece];
5933 *mask = *pieceToSolid(piece);
5936 /* GC for piece being moved. Square color doesn't matter, but
5937 since it gets modified we make a copy of the original. */
5939 if (appData.monoMode)
5944 if (appData.monoMode)
5949 // XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
5951 /* Outline only used in mono mode and is not modified */
5953 *outline = bwPieceGC;
5955 *outline = wbPieceGC;
5959 OverlayPiece(piece, clip, outline, dest)
5960 ChessSquare piece; GC clip; GC outline; Drawable dest;
5965 /* Draw solid rectangle which will be clipped to shape of piece */
5966 // XFillRectangle(xDisplay, dest, clip,
5967 // 0, 0, squareSize, squareSize)
5969 if (appData.monoMode)
5970 /* Also draw outline in contrasting color for black
5971 on black / white on white cases */
5972 // XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
5973 // 0, 0, squareSize, squareSize, 0, 0, 1)
5976 /* Copy the piece */
5981 // XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
5983 // 0, 0, squareSize, squareSize,
5988 /* Animate the movement of a single piece */
5991 BeginAnimation(anim, piece, startColor, start)
5999 /* The old buffer is initialised with the start square (empty) */
6000 BlankSquare(0, 0, startColor, EmptySquare, anim->saveBuf);
6001 anim->prevFrame = *start;
6003 /* The piece will be drawn using its own bitmap as a matte */
6004 // SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
6005 // XSetClipMask(xDisplay, anim->pieceGC, mask);
6009 AnimationFrame(anim, frame, piece)
6014 XRectangle updates[4];
6019 /* Save what we are about to draw into the new buffer */
6020 // XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
6021 // frame->x, frame->y, squareSize, squareSize,
6024 /* Erase bits of the previous frame */
6025 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
6026 /* Where the new frame overlapped the previous,
6027 the contents in newBuf are wrong. */
6028 // XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
6029 // overlap.x, overlap.y,
6030 // overlap.width, overlap.height,
6032 /* Repaint the areas in the old that don't overlap new */
6033 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
6034 for (i = 0; i < count; i++)
6035 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6036 // updates[i].x - anim->prevFrame.x,
6037 // updates[i].y - anim->prevFrame.y,
6038 // updates[i].width, updates[i].height,
6039 // updates[i].x, updates[i].y)
6042 /* Easy when no overlap */
6043 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6044 // 0, 0, squareSize, squareSize,
6045 // anim->prevFrame.x, anim->prevFrame.y);
6048 /* Save this frame for next time round */
6049 // XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
6050 // 0, 0, squareSize, squareSize,
6052 anim->prevFrame = *frame;
6054 /* Draw piece over original screen contents, not current,
6055 and copy entire rect. Wipes out overlapping piece images. */
6056 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
6057 // XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
6058 // 0, 0, squareSize, squareSize,
6059 // frame->x, frame->y);
6063 EndAnimation (anim, finish)
6067 XRectangle updates[4];
6072 /* The main code will redraw the final square, so we
6073 only need to erase the bits that don't overlap. */
6074 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
6075 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
6076 for (i = 0; i < count; i++)
6077 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6078 // updates[i].x - anim->prevFrame.x,
6079 // updates[i].y - anim->prevFrame.y,
6080 // updates[i].width, updates[i].height,
6081 // updates[i].x, updates[i].y)
6084 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6085 // 0, 0, squareSize, squareSize,
6086 // anim->prevFrame.x, anim->prevFrame.y);
6091 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
6093 ChessSquare piece; int startColor;
6094 XPoint * start; XPoint * finish;
6095 XPoint frames[]; int nFrames;
6099 BeginAnimation(anim, piece, startColor, start);
6100 for (n = 0; n < nFrames; n++) {
6101 AnimationFrame(anim, &(frames[n]), piece);
6102 FrameDelay(appData.animSpeed);
6104 EndAnimation(anim, finish);
6107 /* Main control logic for deciding what to animate and how */
6110 AnimateMove(board, fromX, fromY, toX, toY)
6119 XPoint start, finish, mid;
6120 XPoint frames[kFactor * 2 + 1];
6121 int nFrames, startColor, endColor;
6123 /* Are we animating? */
6124 if (!appData.animate || appData.blindfold)
6127 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
6128 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
6129 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
6131 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
6132 piece = board[fromY][fromX];
6133 if (piece >= EmptySquare) return;
6138 hop = (piece == WhiteKnight || piece == BlackKnight);
6141 if (appData.debugMode) {
6142 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
6143 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
6144 piece, fromX, fromY, toX, toY); }
6146 ScreenSquare(fromX, fromY, &start, &startColor);
6147 ScreenSquare(toX, toY, &finish, &endColor);
6150 /* Knight: make diagonal movement then straight */
6151 if (abs(toY - fromY) < abs(toX - fromX)) {
6152 mid.x = start.x + (finish.x - start.x) / 2;
6156 mid.y = start.y + (finish.y - start.y) / 2;
6159 mid.x = start.x + (finish.x - start.x) / 2;
6160 mid.y = start.y + (finish.y - start.y) / 2;
6163 /* Don't use as many frames for very short moves */
6164 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
6165 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
6167 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
6168 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
6170 /* Be sure end square is redrawn */
6171 damage[toY][toX] = True;
6175 DragPieceBegin(x, y)
6178 int boardX, boardY, color;
6181 /* Are we animating? */
6182 if (!appData.animateDragging || appData.blindfold)
6185 /* Figure out which square we start in and the
6186 mouse position relative to top left corner. */
6187 BoardSquare(x, y, &boardX, &boardY);
6188 player.startBoardX = boardX;
6189 player.startBoardY = boardY;
6190 ScreenSquare(boardX, boardY, &corner, &color);
6191 player.startSquare = corner;
6192 player.startColor = color;
6193 /* As soon as we start dragging, the piece will jump slightly to
6194 be centered over the mouse pointer. */
6195 player.mouseDelta.x = squareSize/2;
6196 player.mouseDelta.y = squareSize/2;
6197 /* Initialise animation */
6198 player.dragPiece = PieceForSquare(boardX, boardY);
6200 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
6201 player.dragActive = True;
6202 BeginAnimation(&player, player.dragPiece, color, &corner);
6203 /* Mark this square as needing to be redrawn. Note that
6204 we don't remove the piece though, since logically (ie
6205 as seen by opponent) the move hasn't been made yet. */
6206 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
6207 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
6208 // XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
6209 // corner.x, corner.y, squareSize, squareSize,
6210 // 0, 0); // [HGM] zh: unstack in stead of grab
6211 damage[boardY][boardX] = True;
6213 player.dragActive = False;
6223 /* Are we animating? */
6224 if (!appData.animateDragging || appData.blindfold)
6228 if (! player.dragActive)
6230 /* Move piece, maintaining same relative position
6231 of mouse within square */
6232 corner.x = x - player.mouseDelta.x;
6233 corner.y = y - player.mouseDelta.y;
6234 AnimationFrame(&player, &corner, player.dragPiece);
6236 if (appData.highlightDragging) {
6238 BoardSquare(x, y, &boardX, &boardY);
6239 SetHighlights(fromX, fromY, boardX, boardY);
6248 int boardX, boardY, color;
6251 /* Are we animating? */
6252 if (!appData.animateDragging || appData.blindfold)
6256 if (! player.dragActive)
6258 /* Last frame in sequence is square piece is
6259 placed on, which may not match mouse exactly. */
6260 BoardSquare(x, y, &boardX, &boardY);
6261 ScreenSquare(boardX, boardY, &corner, &color);
6262 EndAnimation(&player, &corner);
6264 /* Be sure end square is redrawn */
6265 damage[boardY][boardX] = True;
6267 /* This prevents weird things happening with fast successive
6268 clicks which on my Sun at least can cause motion events
6269 without corresponding press/release. */
6270 player.dragActive = False;
6273 /* Handle expose event while piece being dragged */
6278 if (!player.dragActive || appData.blindfold)
6281 /* What we're doing: logically, the move hasn't been made yet,
6282 so the piece is still in it's original square. But visually
6283 it's being dragged around the board. So we erase the square
6284 that the piece is on and draw it at the last known drag point. */
6285 BlankSquare(player.startSquare.x, player.startSquare.y,
6286 player.startColor, EmptySquare, xBoardWindow);
6287 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
6288 damage[player.startBoardY][player.startBoardX] = TRUE;
6291 #include <sys/ioctl.h>
6292 int get_term_width()
6294 int fd, default_width;
6297 default_width = 79; // this is FICS default anyway...
6299 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
6301 if (!ioctl(fd, TIOCGSIZE, &win))
6302 default_width = win.ts_cols;
6303 #elif defined(TIOCGWINSZ)
6305 if (!ioctl(fd, TIOCGWINSZ, &win))
6306 default_width = win.ws_col;
6308 return default_width;
6311 void update_ics_width()
6313 static int old_width = 0;
6314 int new_width = get_term_width();
6316 if (old_width != new_width)
6317 ics_printf("set width %d\n", new_width);
6318 old_width = new_width;
6321 void NotifyFrontendLogin()