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 },
792 // { "LoadPositionProc", LoadPositionProc },
793 // { "LoadNextPositionProc", LoadNextPositionProc },
794 // { "LoadPrevPositionProc", LoadPrevPositionProc },
795 // { "ReloadPositionProc", ReloadPositionProc },
797 { "SetFilterProc", SetFilterProc },
798 { "ReloadGameProc", ReloadGameProc },
799 { "LoadPositionProc", LoadPositionProc },
800 { "LoadNextPositionProc", LoadNextPositionProc },
801 { "LoadPrevPositionProc", LoadPrevPositionProc },
802 { "ReloadPositionProc", ReloadPositionProc },
804 { "CopyPositionProc", CopyPositionProc },
805 { "PastePositionProc", PastePositionProc },
806 { "CopyGameProc", CopyGameProc },
807 { "PasteGameProc", PasteGameProc },
808 // { "SaveGameProc", SaveGameProc },
809 // { "SavePositionProc", SavePositionProc },
810 { "MailMoveProc", MailMoveProc },
811 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
812 // { "MachineWhiteProc", MachineWhiteProc },
813 // { "MachineBlackProc", MachineBlackProc },
814 // { "AnalysisModeProc", AnalyzeModeProc },
815 // { "AnalyzeFileProc", AnalyzeFileProc },
816 // { "TwoMachinesProc", TwoMachinesProc },
817 // { "IcsClientProc", IcsClientProc },
818 // { "EditGameProc", EditGameProc },
819 // { "EditPositionProc", EditPositionProc },
820 // { "TrainingProc", EditPositionProc },
821 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
822 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
823 { "ShowGameListProc", ShowGameListProc },
824 // { "ShowMoveListProc", HistoryShowProc},
825 // { "EditTagsProc", EditCommentProc },
826 { "EditCommentProc", EditCommentProc },
827 // { "IcsAlarmProc", IcsAlarmProc },
828 { "IcsInputBoxProc", IcsInputBoxProc },
829 // { "AcceptProc", AcceptProc },
830 // { "DeclineProc", DeclineProc },
831 // { "RematchProc", RematchProc },
832 // { "CallFlagProc", CallFlagProc },
833 // { "DrawProc", DrawProc },
834 // { "AdjournProc", AdjournProc },
835 // { "AbortProc", AbortProc },
836 // { "ResignProc", ResignProc },
837 // { "AdjuWhiteProc", AdjuWhiteProc },
838 // { "AdjuBlackProc", AdjuBlackProc },
839 // { "AdjuDrawProc", AdjuDrawProc },
840 { "EnterKeyProc", EnterKeyProc },
841 // { "StopObservingProc", StopObservingProc },
842 // { "StopExaminingProc", StopExaminingProc },
843 // { "BackwardProc", BackwardProc },
844 // { "ForwardProc", ForwardProc },
845 // { "ToStartProc", ToStartProc },
846 // { "ToEndProc", ToEndProc },
847 // { "RevertProc", RevertProc },
848 // { "TruncateGameProc", TruncateGameProc },
849 // { "MoveNowProc", MoveNowProc },
850 // { "RetractMoveProc", RetractMoveProc },
851 // { "AlwaysQueenProc", AlwaysQueenProc },
852 // { "AnimateDraggingProc", AnimateDraggingProc },
853 // { "AnimateMovingProc", AnimateMovingProc },
854 // { "AutoflagProc", AutoflagProc },
855 // { "AutoflipProc", AutoflipProc },
856 // { "AutobsProc", AutobsProc },
857 // { "AutoraiseProc", AutoraiseProc },
858 // { "AutosaveProc", AutosaveProc },
859 // { "BlindfoldProc", BlindfoldProc },
860 // { "FlashMovesProc", FlashMovesProc },
861 // { "FlipViewProc", FlipViewProc },
862 // { "GetMoveListProc", GetMoveListProc },
864 // { "HighlightDraggingProc", HighlightDraggingProc },
866 // { "HighlightLastMoveProc", HighlightLastMoveProc },
867 // { "IcsAlarmProc", IcsAlarmProc },
868 // { "MoveSoundProc", MoveSoundProc },
869 // { "OldSaveStyleProc", OldSaveStyleProc },
870 // { "PeriodicUpdatesProc", PeriodicUpdatesProc },
871 // { "PonderNextMoveProc", PonderNextMoveProc },
872 // { "PopupExitMessageProc", PopupExitMessageProc },
873 // { "PopupMoveErrorsProc", PopupMoveErrorsProc },
874 // { "PremoveProc", PremoveProc },
875 // { "QuietPlayProc", QuietPlayProc },
876 // { "ShowThinkingProc", ShowThinkingProc },
877 // { "HideThinkingProc", HideThinkingProc },
878 // { "TestLegalityProc", TestLegalityProc },
879 { "SaveSettingsProc", SaveSettingsProc },
880 { "SaveOnExitProc", SaveOnExitProc },
881 // { "InfoProc", InfoProc },
882 // { "ManProc", ManProc },
883 // { "HintProc", HintProc },
884 // { "BookProc", BookProc },
885 { "AboutGameProc", AboutGameProc },
886 { "DebugProc", DebugProc },
887 { "NothingProc", NothingProc },
888 { "CommentPopDown", (XtActionProc) CommentPopDown },
889 { "EditCommentPopDown", (XtActionProc) EditCommentPopDown },
890 { "TagsPopDown", (XtActionProc) TagsPopDown },
891 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
892 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
893 // { "FileNamePopDown", (XtActionProc) FileNamePopDown },
894 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
895 { "GameListPopDown", (XtActionProc) GameListPopDown },
896 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
897 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
898 // { "HistoryPopDown", (XtActionProc) HistoryPopDown },
899 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
900 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
901 { "ShufflePopDown", (XtActionProc) ShufflePopDown },
902 { "EnginePopDown", (XtActionProc) EnginePopDown },
903 { "UciPopDown", (XtActionProc) UciPopDown },
904 { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
905 { "NewVariantPopDown", (XtActionProc) NewVariantPopDown },
906 { "SettingsPopDown", (XtActionProc) SettingsPopDown },
907 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
910 //char globalTranslations[] =
911 // ":<Key>R: ResignProc() \n \
912 // :<Key>r: ResetProc() \n \
913 // :<Key>g: LoadGameProc() \n \
914 // :<Key>N: LoadNextGameProc() \n \
915 // :<Key>P: LoadPrevGameProc() \n \
916 // :<Key>Q: QuitProc() \n \
917 // :<Key>F: ToEndProc() \n \
918 // :<Key>f: ForwardProc() \n \
919 // :<Key>B: ToStartProc() \n \
920 // :<Key>b: BackwardProc() \n \
921 // :<Key>p: PauseProc() \n \
922 // :<Key>d: DrawProc() \n \
923 // :<Key>t: CallFlagProc() \n \
924 // :<Key>i: Iconify() \n \
925 // :<Key>c: Iconify() \n \
926 // :<Key>v: FlipViewProc() \n \
927 // <KeyDown>Control_L: BackwardProc() \n \
928 // <KeyUp>Control_L: ForwardProc() \n \
929 // <KeyDown>Control_R: BackwardProc() \n \
930 // <KeyUp>Control_R: ForwardProc() \n \
931 // Shift<Key>1: AskQuestionProc(\"Direct command\",\
932 // \"Send to chess program:\",,1) \n \
933 // Shift<Key>2: AskQuestionProc(\"Direct command\",\
934 // \"Send to second chess program:\",,2) \n";
936 //char boardTranslations[] =
937 // "<Btn1Down>: HandleUserMove() \n \
938 // <Btn1Up>: HandleUserMove() \n \
939 // <Btn1Motion>: AnimateUserMove() \n \
940 // <Btn3Motion>: HandlePV() \n \
941 // <Btn3Up>: UnLoadPV() \n \
942 // Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
943 // PieceMenuPopup(menuB) \n \
944 // Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
945 // PieceMenuPopup(menuW) \n \
946 // Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
947 // PieceMenuPopup(menuW) \n \
948 // Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
949 // PieceMenuPopup(menuB) \n";
951 //char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
952 //char blackTranslations[] = "<BtnDown>: BlackClock()\n";
954 char ICSInputTranslations[] =
955 "<Key>Return: EnterKeyProc() \n";
957 String xboardResources[] = {
958 // "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
959 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
960 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
964 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
965 "magenta", "cyan", "white" };
969 TextColors textColors[(int)NColorClasses];
971 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
973 parse_color(str, which)
977 char *p, buf[100], *d;
980 if (strlen(str) > 99) /* watch bounds on buf */
985 for (i=0; i<which; ++i) {
992 /* Could be looking at something like:
994 .. in which case we want to stop on a comma also */
995 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
999 return -1; /* Use default for empty field */
1002 if (which == 2 || isdigit(*p))
1005 while (*p && isalpha(*p))
1010 for (i=0; i<8; ++i) {
1011 if (!StrCaseCmp(buf, cnames[i]))
1012 return which? (i+40) : (i+30);
1014 if (!StrCaseCmp(buf, "default")) return -1;
1016 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1021 parse_cpair(cc, str)
1025 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1026 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1031 /* bg and attr are optional */
1032 textColors[(int)cc].bg = parse_color(str, 1);
1033 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1034 textColors[(int)cc].attr = 0;
1043 /* this should raise the board to the top */
1044 gtk_window_present(GTK_WINDOW(GUI_Window));
1048 //---------------------------------------------------------------------------------------------------------
1049 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1052 #define CW_USEDEFAULT (1<<31)
1053 #define ICS_TEXT_MENU_SIZE 90
1054 #define DEBUG_FILE "xboard.debug"
1055 #define SetCurrentDirectory chdir
1056 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1060 // these two must some day move to frontend.h, when they are implemented
1061 Boolean GameListIsUp();
1063 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1066 // front-end part of option handling
1068 // [HGM] This platform-dependent table provides the location for storing the color info
1069 extern char *crWhite, * crBlack;
1073 &appData.whitePieceColor,
1074 &appData.blackPieceColor,
1075 &appData.lightSquareColor,
1076 &appData.darkSquareColor,
1077 &appData.highlightSquareColor,
1078 &appData.premoveHighlightColor,
1079 &appData.lowTimeWarningColor,
1090 // [HGM] font: keep a font for each square size, even non-stndard ones
1091 #define NUM_SIZES 18
1092 #define MAX_SIZE 130
1093 Boolean fontSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1094 char *fontTable[NUM_FONTS][MAX_SIZE];
1097 ParseFont(char *name, int number)
1098 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1100 if(sscanf(name, "size%d:", &size)) {
1101 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1102 // defer processing it until we know if it matches our board size
1103 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1104 fontTable[number][size] = strdup(strchr(name, ':')+1);
1105 fontValid[number][size] = True;
1110 case 0: // CLOCK_FONT
1111 appData.clockFont = strdup(name);
1113 case 1: // MESSAGE_FONT
1114 appData.font = strdup(name);
1116 case 2: // COORD_FONT
1117 appData.coordFont = strdup(name);
1122 fontSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1127 { // only 2 fonts currently
1128 appData.clockFont = CLOCK_FONT_NAME;
1129 appData.coordFont = COORD_FONT_NAME;
1130 appData.font = DEFAULT_FONT_NAME;
1135 { // no-op, until we identify the code for this already in XBoard and move it here
1139 ParseColor(int n, char *name)
1140 { // in XBoard, just copy the color-name string
1141 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1145 ParseTextAttribs(ColorClass cc, char *s)
1147 (&appData.colorShout)[cc] = strdup(s);
1151 ParseBoardSize(void *addr, char *name)
1153 appData.boardSize = strdup(name);
1158 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1162 SetCommPortDefaults()
1163 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1166 // [HGM] args: these three cases taken out to stay in front-end
1168 SaveFontArg(FILE *f, ArgDescriptor *ad)
1170 char *name, buf[MSG_SIZ];
1171 int i, n = (int)ad->argLoc;
1173 case 0: // CLOCK_FONT
1174 name = appData.clockFont;
1176 case 1: // MESSAGE_FONT
1177 name = appData.font;
1179 case 2: // COORD_FONT
1180 name = appData.coordFont;
1185 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1186 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1187 fontTable[n][squareSize] = strdup(name);
1188 fontValid[n][squareSize] = True;
1191 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1192 fprintf(f, OPTCHAR "%s" SEPCHAR "size%d:%s\n", ad->argName, i, fontTable[n][i]);
1197 { // nothing to do, as the sounds are at all times represented by their text-string names already
1201 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1202 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1203 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)ad->argLoc]);
1207 SaveColor(FILE *f, ArgDescriptor *ad)
1208 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1209 if(colorVariable[(int)ad->argLoc])
1210 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)ad->argLoc]);
1214 SaveBoardSize(FILE *f, char *name, void *addr)
1215 { // wrapper to shield back-end from BoardSize & sizeInfo
1216 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1220 ParseCommPortSettings(char *s)
1221 { // no such option in XBoard (yet)
1224 extern Widget engineOutputShell;
1225 extern Widget tagsShell, editTagsShell;
1228 GetActualPlacement(Widget wg, WindowPlacement *wp)
1238 XtSetArg(args[i], XtNx, &x); i++;
1239 XtSetArg(args[i], XtNy, &y); i++;
1240 XtSetArg(args[i], XtNwidth, &w); i++;
1241 XtSetArg(args[i], XtNheight, &h); i++;
1242 XtGetValues(wg, args, i);
1251 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1252 // In XBoard this will have to wait until awareness of window parameters is implemented
1254 // GetActualPlacement(shellWidget, &wpMain);
1255 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1256 // if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1257 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1258 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1259 if(commentShell) GetActualPlacement(commentShell, &wpComment);
1260 else GetActualPlacement(editShell, &wpComment);
1261 if(tagsShell) GetActualPlacement(tagsShell, &wpTags);
1262 else GetActualPlacement(editTagsShell, &wpTags);
1266 PrintCommPortSettings(FILE *f, char *name)
1267 { // This option does not exist in XBoard
1271 MySearchPath(char *installDir, char *name, char *fullname)
1272 { // just append installDir and name. Perhaps ExpandPath should be used here?
1273 name = ExpandPathName(name);
1274 if(name && name[0] == '/') strcpy(fullname, name); else {
1275 sprintf(fullname, "%s%c%s", installDir, '/', name);
1281 MyGetFullPathName(char *name, char *fullname)
1282 { // should use ExpandPath?
1283 name = ExpandPathName(name);
1284 strcpy(fullname, name);
1289 EnsureOnScreen(int *x, int *y, int minX, int minY)
1296 { // [HGM] args: allows testing if main window is realized from back-end
1297 return xBoardWindow != 0;
1301 PopUpStartupDialog()
1302 { // start menu not implemented in XBoard
1305 ConvertToLine(int argc, char **argv)
1307 static char line[128*1024], buf[1024];
1311 for(i=1; i<argc; i++) {
1312 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1313 && argv[i][0] != '{' )
1314 sprintf(buf, "{%s} ", argv[i]);
1315 else sprintf(buf, "%s ", argv[i]);
1318 line[strlen(line)-1] = NULLCHAR;
1322 //--------------------------------------------------------------------------------------------
1325 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1328 #define BoardSize int
1329 void InitDrawingSizes(BoardSize boardSize, int flags)
1330 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1331 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1333 XtGeometryResult gres;
1336 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1337 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1339 timerWidth = (boardWidth - sep) / 2;
1341 if (appData.titleInWindow)
1346 w = boardWidth - 2*bor;
1350 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1354 if(!formWidget) return;
1357 * Inhibit shell resizing.
1360 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1363 for(i=0; i<4; i++) {
1365 for(p=0; p<=(int)WhiteKing; p++)
1366 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1367 if(gameInfo.variant == VariantShogi) {
1368 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1369 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1370 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1371 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1372 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1375 if(gameInfo.variant == VariantGothic) {
1376 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1380 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1381 for(p=0; p<=(int)WhiteKing; p++)
1382 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1383 if(gameInfo.variant == VariantShogi) {
1384 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1385 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1386 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1387 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1388 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1391 if(gameInfo.variant == VariantGothic) {
1392 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1398 for(i=0; i<2; i++) {
1400 for(p=0; p<=(int)WhiteKing; p++)
1401 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1402 if(gameInfo.variant == VariantShogi) {
1403 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1404 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1405 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1406 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1407 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1410 if(gameInfo.variant == VariantGothic) {
1411 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1422 void EscapeExpand(char *p, char *q)
1423 { // [HGM] initstring: routine to shape up string arguments
1424 while(*p++ = *q++) if(p[-1] == '\\')
1426 case 'n': p[-1] = '\n'; break;
1427 case 'r': p[-1] = '\r'; break;
1428 case 't': p[-1] = '\t'; break;
1429 case '\\': p[-1] = '\\'; break;
1430 case 0: *p = 0; return;
1431 default: p[-1] = q[-1]; break;
1440 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1441 XSetWindowAttributes window_attributes;
1443 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1444 XrmValue vFrom, vTo;
1445 XtGeometryResult gres;
1448 int forceMono = False;
1450 srandom(time(0)); // [HGM] book: make random truly random
1452 setbuf(stdout, NULL);
1453 setbuf(stderr, NULL);
1456 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1457 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1461 programName = strrchr(argv[0], '/');
1462 if (programName == NULL)
1463 programName = argv[0];
1468 XtSetLanguageProc(NULL, NULL, NULL);
1469 bindtextdomain(PACKAGE, LOCALEDIR);
1470 textdomain(PACKAGE);
1474 gtk_init (&argc, &argv);
1476 /* parse glade file to build widgets */
1478 builder = gtk_builder_new ();
1479 gtk_builder_add_from_file (builder, "gtk-interface.xml", NULL);
1481 /* test if everything worked ok */
1483 GUI_Window = GTK_WIDGET (gtk_builder_get_object (builder, "MainWindow"));
1484 if(!GUI_Window) printf("Error: gtk_builder didn't work (MainWindow)!\n");
1486 GUI_Aspect = GTK_WIDGET (gtk_builder_get_object (builder, "Aspectframe"));
1487 if(!GUI_Aspect) printf("Error: gtk_builder didn't work (Aspectframe)!\n");
1489 GUI_Menubar = GTK_WIDGET (gtk_builder_get_object (builder, "MenuBar"));
1490 if(!GUI_Menubar) printf("Error: gtk_builder didn't work (MenuBar)!\n");
1491 GUI_Timer = GTK_WIDGET (gtk_builder_get_object (builder, "Timer"));
1492 if(!GUI_Timer) printf("Error: gtk_builder didn't work (Timer)!\n");
1493 GUI_Buttonbar = GTK_WIDGET (gtk_builder_get_object (builder, "ButtonBar"));
1494 if(!GUI_Buttonbar) printf("Error: gtk_builder didn't work (ButtonBar)!\n");
1495 GUI_Board = GTK_WIDGET (gtk_builder_get_object (builder, "Board"));
1496 if(!GUI_Board) printf("Error: gtk_builder didn't work (Board)!\n");
1498 GUI_Whiteclock = GTK_WIDGET (gtk_builder_get_object (builder, "WhiteClock"));
1499 if(!GUI_Whiteclock) printf("Error: gtk_builder didn't work (WhiteClock)!\n");
1501 GUI_Blackclock = GTK_WIDGET (gtk_builder_get_object (builder, "BlackClock"));
1502 if(!GUI_Blackclock) printf("Error: gtk_builder didn't work (BlackClock)!\n");
1504 /* GTK lists stores*/
1505 LIST_MoveHistory = GTK_LIST_STORE (gtk_builder_get_object (builder, "MoveHistoryStore"));
1506 if(!LIST_MoveHistory) printf("Error: gtk_builder didn't work (MoveHistoryStore)!\n");
1508 LIST_GameList = GTK_LIST_STORE (gtk_builder_get_object (builder, "GameListStore"));
1509 if(!LIST_GameList) printf("Error: gtk_builder didn't work (GameListStore)!\n");
1511 /* EditTags window */
1512 GUI_EditTags = GTK_WIDGET (gtk_builder_get_object (builder, "EditTags"));
1513 if(!GUI_EditTags) printf("Error: gtk_builder didn't work (EditTags)!\n");
1515 GUI_EditTagsTextArea = GTK_WIDGET (gtk_builder_get_object (builder, "EditTagsTextArea"));
1516 if(!GUI_EditTagsTextArea) printf("Error: gtk_builder didn't work(EditTagsTextArea)!\n");
1518 /* move history and game list windows */
1519 GUI_History = GTK_WIDGET (gtk_builder_get_object (builder, "MoveHistory"));
1520 if(!GUI_History) printf("Error: gtk_builder didn't work (MoveHistory)!\n");
1522 TREE_History = GTK_TREE_VIEW (gtk_builder_get_object (builder, "MoveHistoryView"));
1523 if(!TREE_History) printf("Error: gtk_builder didn't work (MoveHistoryView)!\n");
1525 GUI_GameList = GTK_WIDGET (gtk_builder_get_object (builder, "GameList"));
1526 if(!GUI_GameList) printf("Error: gtk_builder didn't work (GameList)!\n");
1528 TREE_Game = GTK_TREE_VIEW (gtk_builder_get_object (builder, "GameListView"));
1529 if(!TREE_Game) printf("Error: gtk_builder didn't work (GameListView)!\n");
1532 /* connect lists to views */
1533 gtk_tree_view_set_model(TREE_History, GTK_TREE_MODEL(LIST_MoveHistory));
1534 gtk_tree_view_set_model(TREE_Game, GTK_TREE_MODEL(LIST_GameList));
1536 gtk_builder_connect_signals (builder, NULL);
1538 // don't unref the builder, since we use it to get references to widgets
1539 // g_object_unref (G_OBJECT (builder));
1541 /* end parse glade file */
1543 appData.boardSize = "";
1544 InitAppData(ConvertToLine(argc, argv));
1547 if (p == NULL) p = "/tmp";
1548 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1549 gameCopyFilename = (char*) malloc(i);
1550 gamePasteFilename = (char*) malloc(i);
1551 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1552 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1554 // XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1555 // clientResources, XtNumber(clientResources),
1558 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1559 static char buf[MSG_SIZ];
1560 EscapeExpand(buf, appData.initString);
1561 appData.initString = strdup(buf);
1562 EscapeExpand(buf, appData.secondInitString);
1563 appData.secondInitString = strdup(buf);
1564 EscapeExpand(buf, appData.firstComputerString);
1565 appData.firstComputerString = strdup(buf);
1566 EscapeExpand(buf, appData.secondComputerString);
1567 appData.secondComputerString = strdup(buf);
1570 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1573 if (chdir(chessDir) != 0) {
1574 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1580 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1581 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1582 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1583 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1586 setbuf(debugFP, NULL);
1591 /* This feature does not work; animation needs a rewrite */
1592 appData.highlightDragging = FALSE;
1596 gameInfo.variant = StringToVariant(appData.variant);
1597 InitPosition(FALSE);
1602 clockFontPxlSize = 20;
1603 coordFontPxlSize = 20;
1609 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1610 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1613 * Determine what fonts to use.
1615 // appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1616 // clockFontID = XLoadFont(xDisplay, appData.clockFont);
1617 // clockFontStruct = XQueryFont(xDisplay, clockFontID);
1618 // appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1619 // coordFontID = XLoadFont(xDisplay, appData.coordFont);
1620 // coordFontStruct = XQueryFont(xDisplay, coordFontID);
1621 // appData.font = FindFont(appData.font, fontPxlSize);
1622 // countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
1623 // countFontStruct = XQueryFont(xDisplay, countFontID);
1624 // appData.font = FindFont(appData.font, fontPxlSize);
1626 // xdb = XtDatabase(xDisplay);
1627 // XrmPutStringResource(&xdb, "*font", appData.font);
1630 * Detect if there are not enough colors available and adapt.
1632 // if (DefaultDepth(xDisplay, xScreen) <= 2) {
1633 // appData.monoMode = True;
1636 if (!appData.monoMode) {
1637 vFrom.addr = (caddr_t) appData.lightSquareColor;
1638 vFrom.size = strlen(appData.lightSquareColor);
1639 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1640 if (vTo.addr == NULL) {
1641 appData.monoMode = True;
1644 lightSquareColor = *(Pixel *) vTo.addr;
1647 if (!appData.monoMode) {
1648 vFrom.addr = (caddr_t) appData.darkSquareColor;
1649 vFrom.size = strlen(appData.darkSquareColor);
1650 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1651 if (vTo.addr == NULL) {
1652 appData.monoMode = True;
1655 darkSquareColor = *(Pixel *) vTo.addr;
1658 if (!appData.monoMode) {
1659 vFrom.addr = (caddr_t) appData.whitePieceColor;
1660 vFrom.size = strlen(appData.whitePieceColor);
1661 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1662 if (vTo.addr == NULL) {
1663 appData.monoMode = True;
1666 whitePieceColor = *(Pixel *) vTo.addr;
1669 if (!appData.monoMode) {
1670 vFrom.addr = (caddr_t) appData.blackPieceColor;
1671 vFrom.size = strlen(appData.blackPieceColor);
1672 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1673 if (vTo.addr == NULL) {
1674 appData.monoMode = True;
1677 blackPieceColor = *(Pixel *) vTo.addr;
1681 if (!appData.monoMode) {
1682 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1683 vFrom.size = strlen(appData.highlightSquareColor);
1684 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1685 if (vTo.addr == NULL) {
1686 appData.monoMode = True;
1689 highlightSquareColor = *(Pixel *) vTo.addr;
1693 if (!appData.monoMode) {
1694 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1695 vFrom.size = strlen(appData.premoveHighlightColor);
1696 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1697 if (vTo.addr == NULL) {
1698 appData.monoMode = True;
1701 premoveHighlightColor = *(Pixel *) vTo.addr;
1706 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1709 if (appData.bitmapDirectory == NULL ||
1710 appData.bitmapDirectory[0] == NULLCHAR)
1711 appData.bitmapDirectory = DEF_BITMAP_DIR;
1714 if (appData.lowTimeWarning && !appData.monoMode) {
1715 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1716 vFrom.size = strlen(appData.lowTimeWarningColor);
1717 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1718 if (vTo.addr == NULL)
1719 appData.monoMode = True;
1721 lowTimeWarningColor = *(Pixel *) vTo.addr;
1724 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1725 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1726 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1727 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1728 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1729 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1730 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1731 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1732 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1733 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1735 if (appData.colorize) {
1737 _("%s: can't parse color names; disabling colorization\n"),
1740 appData.colorize = FALSE;
1742 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
1743 textColors[ColorNone].attr = 0;
1745 // XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1751 layoutName = "tinyLayout";
1752 } else if (smallLayout) {
1753 layoutName = "smallLayout";
1755 layoutName = "normalLayout";
1758 if (appData.titleInWindow) {
1759 /* todo check what this appdata does */
1762 if (appData.showButtonBar) {
1763 /* TODO hide button bar if requested */
1767 if (appData.titleInWindow)
1772 if (appData.showButtonBar)
1779 if (appData.showButtonBar)
1789 /* set some checkboxes in the menu according to appData */
1791 if (appData.alwaysPromoteToQueen)
1792 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Always Queen")),TRUE);
1794 if (appData.animateDragging)
1795 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Animate Dragging")),TRUE);
1797 if (appData.animate)
1798 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Animate Moving")),TRUE);
1800 if (appData.autoComment)
1801 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Comment")),TRUE);
1803 if (appData.autoCallFlag)
1804 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Flag")),TRUE);
1806 if (appData.autoFlipView)
1807 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Flip View")),TRUE);
1809 if (appData.autoObserve)
1810 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Observe")),TRUE);
1812 if (appData.autoRaiseBoard)
1813 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Raise Board")),TRUE);
1815 if (appData.autoSaveGames)
1816 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Save")),TRUE);
1818 if (appData.saveGameFile[0] != NULLCHAR)
1820 /* Can't turn this off from menu */
1821 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Save")),TRUE);
1822 gtk_action_set_sensitive(GTK_ACTION (gtk_builder_get_object (builder, "menuOptions.Auto Save")),FALSE);
1825 if (appData.blindfold)
1826 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Blindfold")),TRUE);
1828 if (appData.flashCount > 0)
1829 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Flash Moves")),TRUE);
1831 if (appData.getMoveList)
1832 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Get Move List")),TRUE);
1835 if (appData.highlightDragging)
1836 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Highlight Dragging")),TRUE);
1839 if (appData.highlightLastMove)
1840 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Highlight Last Move")),TRUE);
1842 if (appData.icsAlarm)
1843 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.ICS Alarm")),TRUE);
1845 if (appData.ringBellAfterMoves)
1846 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Move Sound")),TRUE);
1848 if (appData.oldSaveStyle)
1849 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Old Save Style")),TRUE);
1851 if (appData.periodicUpdates)
1852 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Periodic Updates")),TRUE);
1854 if (appData.ponderNextMove)
1855 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Ponder Next Move")),TRUE);
1857 if (appData.popupExitMessage)
1858 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Popup Exit Message")),TRUE);
1860 if (appData.popupMoveErrors)
1861 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Popup Move Errors")),TRUE);
1863 if (appData.premove)
1864 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Premove")),TRUE);
1866 if (appData.quietPlay)
1867 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Quit Play")),TRUE);
1869 if (appData.showCoords)
1870 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Show Coords")),TRUE);
1872 if (appData.showThinking)
1873 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Hide Thinking")),TRUE);
1875 if (appData.testLegality)
1876 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Test Legality")),TRUE);
1879 // if (saveSettingsOnExit) {
1880 // XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
1885 /* end setting check boxes */
1887 /* load square colors */
1888 SVGLightSquare = load_pixbuf("svg/LightSquare.svg",squareSize);
1889 SVGDarkSquare = load_pixbuf("svg/DarkSquare.svg",squareSize);
1890 SVGNeutralSquare = load_pixbuf("svg/NeutralSquare.svg",squareSize);
1892 /* use two icons to indicate if it is white's or black's turn */
1893 WhiteIcon = load_pixbuf("svg/icon_white.svg",0);
1894 BlackIcon = load_pixbuf("svg/icon_black.svg",0);
1895 WindowIcon = WhiteIcon;
1896 gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
1899 /* realize window */
1900 gtk_widget_show (GUI_Window);
1902 /* recalc boardsize */
1907 if (appData.animate || appData.animateDragging)
1910 /* [AS] Restore layout */
1911 if( wpMoveHistory.visible ) {
1915 if( wpEvalGraph.visible )
1920 if( wpEngineOutput.visible ) {
1921 EngineOutputPopUp();
1926 if (errorExitStatus == -1) {
1927 if (appData.icsActive) {
1928 /* We now wait until we see "login:" from the ICS before
1929 sending the logon script (problems with timestamp otherwise) */
1930 /*ICSInitScript();*/
1931 if (appData.icsInputBox) ICSInputBoxPopUp();
1935 signal(SIGWINCH, TermSizeSigHandler);
1937 signal(SIGINT, IntSigHandler);
1938 signal(SIGTERM, IntSigHandler);
1939 if (*appData.cmailGameName != NULLCHAR) {
1940 signal(SIGUSR1, CmailSigHandler);
1943 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1947 * Create a cursor for the board widget.
1948 * (This needs to be called after the window has been created to have access to board-window)
1951 BoardCursor = gdk_cursor_new(GDK_HAND2);
1952 gdk_window_set_cursor(GUI_Board->window, BoardCursor);
1953 gdk_cursor_destroy(BoardCursor);
1958 if (appData.debugMode) fclose(debugFP); // [DM] debug
1965 if (appData.icsActive && oldICSInteractionTitle != NULL) {
1966 DisplayIcsInteractionTitle(oldICSInteractionTitle);
1968 if (saveSettingsOnExit) SaveSettings(settingsFileName);
1969 unlink(gameCopyFilename);
1970 unlink(gamePasteFilename);
1973 RETSIGTYPE TermSizeSigHandler(int sig)
1986 CmailSigHandler(sig)
1992 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1994 /* Activate call-back function CmailSigHandlerCallBack() */
1995 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1997 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2001 CmailSigHandlerCallBack(isr, closure, message, count, error)
2009 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2011 /**** end signal code ****/
2021 f = fopen(appData.icsLogon, "r");
2027 strcat(buf, appData.icsLogon);
2028 f = fopen(buf, "r");
2032 ProcessICSInitScript(f);
2039 EditCommentPopDown();
2049 if (!menuBarWidget) return;
2050 w = XtNameToWidget(menuBarWidget, "menuStep.Revert");
2052 DisplayError("menuStep.Revert", 0);
2054 XtSetSensitive(w, !grey);
2059 SetMenuEnables(enab)
2064 if (!builder) return;
2065 while (enab->name != NULL) {
2066 o = gtk_builder_get_object(builder, enab->name);
2067 if(GTK_IS_WIDGET(o))
2068 gtk_widget_set_sensitive(GTK_WIDGET (o),enab->value);
2071 if(GTK_IS_ACTION(o))
2072 gtk_action_set_sensitive(GTK_ACTION (o),enab->value);
2074 DisplayError(enab->name, 0);
2082 SetMenuEnables(icsEnables);
2085 if (appData.zippyPlay && !appData.noChessProgram) /* [DM] icsEngineAnalyze */
2086 {}; // XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2093 SetMenuEnables(ncpEnables);
2099 SetMenuEnables(gnuEnables);
2105 SetMenuEnables(cmailEnables);
2111 SetMenuEnables(trainingOnEnables);
2112 if (appData.showButtonBar) {
2113 // XtSetSensitive(buttonBarWidget, False);
2119 SetTrainingModeOff()
2121 SetMenuEnables(trainingOffEnables);
2122 if (appData.showButtonBar) {
2123 // XtSetSensitive(buttonBarWidget, True);
2128 SetUserThinkingEnables()
2130 if (appData.noChessProgram) return;
2131 SetMenuEnables(userThinkingEnables);
2135 SetMachineThinkingEnables()
2137 if (appData.noChessProgram) return;
2138 SetMenuEnables(machineThinkingEnables);
2140 case MachinePlaysBlack:
2141 case MachinePlaysWhite:
2142 case TwoMachinesPlay:
2143 // XtSetSensitive(XtNameToWidget(menuBarWidget,
2144 // ModeToWidgetName(gameMode)), True);
2151 #define Abs(n) ((n)<0 ? -(n) : (n))
2154 * Find a font that matches "pattern" that is as close as
2155 * possible to the targetPxlSize. Prefer fonts that are k
2156 * pixels smaller to fonts that are k pixels larger. The
2157 * pattern must be in the X Consortium standard format,
2158 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2159 * The return value should be freed with XtFree when no
2162 char *FindFont(pattern, targetPxlSize)
2166 char **fonts, *p, *best, *scalable, *scalableTail;
2167 int i, j, nfonts, minerr, err, pxlSize;
2170 char **missing_list;
2172 char *def_string, *base_fnt_lst, strInt[3];
2174 XFontStruct **fnt_list;
2176 base_fnt_lst = calloc(1, strlen(pattern) + 3);
2177 sprintf(strInt, "%d", targetPxlSize);
2178 p = strstr(pattern, "--");
2179 strncpy(base_fnt_lst, pattern, p - pattern + 2);
2180 strcat(base_fnt_lst, strInt);
2181 strcat(base_fnt_lst, strchr(p + 2, '-'));
2183 if ((fntSet = XCreateFontSet(xDisplay,
2187 &def_string)) == NULL) {
2189 fprintf(stderr, _("Unable to create font set.\n"));
2193 nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
2195 // fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2196 // if (nfonts < 1) {
2197 // fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2198 // programName, pattern);
2206 for (i=0; i<nfonts; i++) {
2209 if (*p != '-') continue;
2211 if (*p == NULLCHAR) break;
2212 if (*p++ == '-') j++;
2214 if (j < 7) continue;
2217 scalable = fonts[i];
2220 err = pxlSize - targetPxlSize;
2221 if (Abs(err) < Abs(minerr) ||
2222 (minerr > 0 && err < 0 && -err == minerr)) {
2228 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2229 /* If the error is too big and there is a scalable font,
2230 use the scalable font. */
2231 int headlen = scalableTail - scalable;
2232 p = (char *) XtMalloc(strlen(scalable) + 10);
2233 while (isdigit(*scalableTail)) scalableTail++;
2234 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2236 p = (char *) XtMalloc(strlen(best) + 1);
2239 if (appData.debugMode) {
2240 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2241 pattern, targetPxlSize, p);
2244 if (missing_count > 0)
2245 XFreeStringList(missing_list);
2246 // XFreeFontSet(xDisplay, fntSet);
2248 XFreeFontNames(fonts);
2255 /* 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*/
2264 for(i=0;i<MAXPIECES;i++)
2268 g_free(SVGpieces[i]);
2275 SVGLightSquare = load_pixbuf("svg/LightSquare.svg",squareSize);
2276 SVGDarkSquare = load_pixbuf("svg/DarkSquare.svg",squareSize);
2277 SVGNeutralSquare = load_pixbuf("svg/NeutralSquare.svg",squareSize);
2280 /* get some defaults going */
2281 for(i=WhitePawn; i<DemotePiece+1; i++)
2282 SVGpieces[i] = load_pixbuf("svg/NeutralSquare.svg",squareSize);
2284 SVGpieces[WhitePawn] = load_pixbuf("svg/WhitePawn.svg",squareSize);
2285 SVGpieces[WhiteKnight] = load_pixbuf("svg/WhiteKnight.svg",squareSize);
2286 SVGpieces[WhiteBishop] = load_pixbuf("svg/WhiteBishop.svg",squareSize);
2287 SVGpieces[WhiteRook] = load_pixbuf("svg/WhiteRook.svg",squareSize);
2288 SVGpieces[WhiteQueen] = load_pixbuf("svg/WhiteQueen.svg",squareSize);
2289 SVGpieces[WhiteKing] = load_pixbuf("svg/WhiteKing.svg",squareSize);
2291 SVGpieces[BlackPawn] = load_pixbuf("svg/BlackPawn.svg",squareSize);
2292 SVGpieces[BlackKnight] = load_pixbuf("svg/BlackKnight.svg",squareSize);
2293 SVGpieces[BlackBishop] = load_pixbuf("svg/BlackBishop.svg",squareSize);
2294 SVGpieces[BlackRook] = load_pixbuf("svg/BlackRook.svg",squareSize);
2295 SVGpieces[BlackQueen] = load_pixbuf("svg/BlackQueen.svg",squareSize);
2296 SVGpieces[BlackKing] = load_pixbuf("svg/BlackKing.svg",squareSize);
2302 static void MenuBarSelect(w, addr, index)
2307 XtActionProc proc = (XtActionProc) addr;
2309 (proc)(NULL, NULL, NULL, NULL);
2312 void CreateMenuBarPopup(parent, name, mb)
2322 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2325 XtSetArg(args[j], XtNleftMargin, 20); j++;
2326 XtSetArg(args[j], XtNrightMargin, 20); j++;
2328 while (mi->string != NULL) {
2329 if (strcmp(mi->string, "----") == 0) {
2330 entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
2333 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
2334 entry = XtCreateManagedWidget(mi->string, smeBSBObjectClass,
2336 XtAddCallback(entry, XtNcallback,
2337 (XtCallbackProc) MenuBarSelect,
2338 (caddr_t) mi->proc);
2344 Widget CreateMenuBar(mb)
2348 Widget anchor, menuBar;
2350 char menuName[MSG_SIZ];
2353 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2354 XtSetArg(args[j], XtNvSpace, 0); j++;
2355 XtSetArg(args[j], XtNborderWidth, 0); j++;
2356 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
2357 formWidget, args, j);
2359 while (mb->name != NULL) {
2360 strcpy(menuName, "menu");
2361 strcat(menuName, mb->name);
2363 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
2366 shortName[0] = _(mb->name)[0];
2367 shortName[1] = NULLCHAR;
2368 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
2371 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
2374 XtSetArg(args[j], XtNborderWidth, 0); j++;
2375 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
2377 CreateMenuBarPopup(menuBar, menuName, mb);
2385 CreatePieceMenu(name, color)
2392 ChessSquare selection;
2394 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2395 boardWidget, args, 0);
2397 for (i = 0; i < PIECE_MENU_SIZE; i++) {
2398 String item = pieceMenuStrings[color][i];
2400 if (strcmp(item, "----") == 0) {
2401 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2404 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2405 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2407 selection = pieceMenuTranslation[color][i];
2408 XtAddCallback(entry, XtNcallback,
2409 (XtCallbackProc) PieceMenuSelect,
2410 (caddr_t) selection);
2411 if (selection == WhitePawn || selection == BlackPawn) {
2412 XtSetArg(args[0], XtNpopupOnEntry, entry);
2413 XtSetValues(menu, args, 1);
2426 ChessSquare selection;
2428 // whitePieceMenu = CreatePieceMenu("menuW", 0);
2429 // blackPieceMenu = CreatePieceMenu("menuB", 1);
2431 // XtRegisterGrabAction(PieceMenuPopup, True,
2432 // (unsigned)(ButtonPressMask|ButtonReleaseMask),
2433 // GrabModeAsync, GrabModeAsync);
2435 // XtSetArg(args[0], XtNlabel, _("Drop"));
2436 // dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
2437 // boardWidget, args, 1);
2438 // for (i = 0; i < DROP_MENU_SIZE; i++) {
2439 // String item = dropMenuStrings[i];
2441 // if (strcmp(item, "----") == 0) {
2442 // entry = XtCreateManagedWidget(item, smeLineObjectClass,
2443 // dropMenu, NULL, 0);
2445 // XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2446 // entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2447 // dropMenu, args, 1);
2448 // selection = dropMenuTranslation[i];
2449 // XtAddCallback(entry, XtNcallback,
2450 // (XtCallbackProc) DropMenuSelect,
2451 // (caddr_t) selection);
2456 void SetupDropMenu()
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);
2480 void PieceMenuPopup(w, event, params, num_params)
2484 Cardinal *num_params;
2486 String whichMenu; int menuNr;
2487 if (event->type == ButtonRelease)
2488 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
2489 else if (event->type == ButtonPress)
2490 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
2492 case 0: whichMenu = params[0]; break;
2493 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
2495 case -1: if (errorUp) ErrorPopDown();
2498 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
2501 static void PieceMenuSelect(w, piece, junk)
2506 if (pmFromX < 0 || pmFromY < 0) return;
2507 EditPositionMenuEvent(piece, pmFromX, pmFromY);
2510 static void DropMenuSelect(w, piece, junk)
2515 if (pmFromX < 0 || pmFromY < 0) return;
2516 DropMenuEvent(piece, pmFromX, pmFromY);
2520 * If the user selects on a border boundary, return -1; if off the board,
2521 * return -2. Otherwise map the event coordinate to the square.
2523 int EventToSquare(x, limit)
2531 if ((x % (squareSize + lineGap)) >= squareSize)
2533 x /= (squareSize + lineGap);
2539 static void do_flash_delay(msec)
2545 static void drawHighlight(file, rank, line_type)
2546 int file, rank, line_type;
2551 if (lineGap == 0 || appData.blindfold) return;
2555 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
2556 (squareSize + lineGap);
2557 y = lineGap/2 + rank * (squareSize + lineGap);
2561 x = lineGap/2 + file * (squareSize + lineGap);
2562 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
2563 (squareSize + lineGap);
2567 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2569 /* draw the highlight */
2570 cairo_move_to (cr, x, y);
2571 cairo_rel_line_to (cr, 0,squareSize+lineGap);
2572 cairo_rel_line_to (cr, squareSize+lineGap,0);
2573 cairo_rel_line_to (cr, 0,-squareSize-lineGap);
2574 cairo_close_path (cr);
2576 cairo_set_line_width (cr, lineGap);
2579 /* TODO: use appdata colors */
2580 case LINE_TYPE_HIGHLIGHT:
2581 cairo_set_source_rgba (cr, 1, 1, 0, 1.0);
2584 cairo_set_source_rgba (cr, 1, 0, 0, 1.0);
2586 case LINE_TYPE_NORMAL:
2588 cairo_set_source_rgba (cr, 0, 1, 0, 1.0);
2599 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
2600 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
2603 SetHighlights(fromX, fromY, toX, toY)
2604 int fromX, fromY, toX, toY;
2606 if (hi1X != fromX || hi1Y != fromY)
2608 if (hi1X >= 0 && hi1Y >= 0)
2610 drawHighlight(hi1X, hi1Y, LINE_TYPE_NORMAL);
2613 if (hi2X != toX || hi2Y != toY)
2615 if (hi2X >= 0 && hi2Y >= 0)
2617 drawHighlight(hi2X, hi2Y, LINE_TYPE_NORMAL);
2620 if (hi1X != fromX || hi1Y != fromY)
2622 if (fromX >= 0 && fromY >= 0)
2624 drawHighlight(fromX, fromY, LINE_TYPE_HIGHLIGHT);
2626 if (hi2X != toX || hi2Y != toY)
2628 if (toX >= 0 && toY >= 0)
2630 drawHighlight(toX, toY, LINE_TYPE_HIGHLIGHT);
2644 SetHighlights(-1, -1, -1, -1);
2649 SetPremoveHighlights(fromX, fromY, toX, toY)
2650 int fromX, fromY, toX, toY;
2652 if (pm1X != fromX || pm1Y != fromY)
2654 if (pm1X >= 0 && pm1Y >= 0)
2656 drawHighlight(pm1X, pm1Y, LINE_TYPE_NORMAL);
2658 if (fromX >= 0 && fromY >= 0)
2660 drawHighlight(fromX, fromY, LINE_TYPE_PRE);
2663 if (pm2X != toX || pm2Y != toY)
2665 if (pm2X >= 0 && pm2Y >= 0)
2667 drawHighlight(pm2X, pm2Y, LINE_TYPE_NORMAL);
2669 if (toX >= 0 && toY >= 0)
2671 drawHighlight(toX, toY, LINE_TYPE_PRE);
2684 ClearPremoveHighlights()
2686 SetPremoveHighlights(-1, -1, -1, -1);
2689 static void BlankSquare(x, y, color, piece, dest)
2702 pb = SVGLightSquare;
2704 case 2: /* neutral */
2706 pb = SVGNeutralSquare;
2709 gdk_draw_pixbuf(GDK_WINDOW(GUI_Board->window),NULL,pb,0,0,x,y,-1,-1, GDK_RGB_DITHER_NORMAL, 0, 0);
2713 static void DrawPiece(piece, square_color, x, y, dest)
2715 int square_color, x, y;
2718 /* redraw background, since piece might be transparent in some areas */
2719 BlankSquare(x,y,square_color,piece,dest);
2722 gdk_draw_pixbuf(GDK_WINDOW(GUI_Board->window),NULL,
2723 GDK_PIXBUF(SVGpieces[piece]),0,0,x,y,-1,-1,
2724 GDK_RGB_DITHER_NORMAL, 0, 0);
2728 /* [HR] determine square color depending on chess variant. */
2729 static int SquareColor(row, column)
2734 if (gameInfo.variant == VariantXiangqi) {
2735 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
2737 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
2739 } else if (row <= 4) {
2745 square_color = ((column + row) % 2) == 1;
2748 /* [hgm] holdings: next line makes all holdings squares light */
2749 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
2751 return square_color;
2754 void DrawSquare(row, column, piece, do_flash)
2755 int row, column, do_flash;
2758 int square_color, x, y;
2763 /* Calculate delay in milliseconds (2-delays per complete flash) */
2764 flash_delay = 500 / appData.flashRate;
2766 /* calculate x and y coordinates from row and column */
2769 x = lineGap + ((BOARD_WIDTH-1)-column) *
2770 (squareSize + lineGap);
2771 y = lineGap + row * (squareSize + lineGap);
2775 x = lineGap + column * (squareSize + lineGap);
2776 y = lineGap + ((BOARD_HEIGHT-1)-row) *
2777 (squareSize + lineGap);
2780 square_color = SquareColor(row, column);
2782 // [HGM] holdings: blank out area between board and holdings
2783 if ( column == BOARD_LEFT-1 || column == BOARD_RGHT
2784 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
2785 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) )
2787 BlankSquare(x, y, 2, EmptySquare, xBoardWindow);
2789 // [HGM] print piece counts next to holdings
2790 string[1] = NULLCHAR;
2793 cairo_text_extents_t extents;
2798 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2800 string[0] = '0' + piece;
2802 /* TODO this has to go into the font-selection */
2803 cairo_select_font_face (cr, "Sans",
2804 CAIRO_FONT_SLANT_NORMAL,
2805 CAIRO_FONT_WEIGHT_NORMAL);
2807 // switch (event->type) {
2809 // if (event->xexpose.count > 0) return; /* no clipping is done */
2810 // XDrawPosition(widget, True, NULL);
2812 // case MotionNotify:
2813 // if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
2820 cairo_set_font_size (cr, 12.0);
2821 cairo_text_extents (cr, string, &extents);
2823 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) )
2825 xpos= x + squareSize - extents.width - 2;
2826 ypos= y + extents.y_bearing + 1;
2828 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1)
2831 ypos = y + extents.y_bearing + 1;
2834 /* TODO mono mode? */
2835 cairo_move_to (cr, xpos, ypos);
2836 cairo_text_path (cr, string);
2837 cairo_set_source_rgb (cr, 1.0, 1.0, 1);
2838 cairo_fill_preserve (cr);
2839 cairo_set_source_rgb (cr, 0, 0, 0);
2840 cairo_set_line_width (cr, 0.1);
2849 /* square on the board */
2850 if (piece == EmptySquare || appData.blindfold)
2852 BlankSquare(x, y, square_color, piece, xBoardWindow);
2856 if (do_flash && appData.flashCount > 0)
2858 for (i=0; i<appData.flashCount; ++i)
2861 DrawPiece(piece, square_color, x, y, xBoardWindow);
2862 do_flash_delay(flash_delay);
2864 BlankSquare(x, y, square_color, piece, xBoardWindow);
2865 do_flash_delay(flash_delay);
2868 DrawPiece(piece, square_color, x, y, xBoardWindow);
2872 /* show coordinates if necessary */
2873 if(appData.showCoords)
2875 cairo_text_extents_t extents;
2879 /* TODO this has to go into the font-selection */
2880 cairo_select_font_face (cr, "Sans",
2881 CAIRO_FONT_SLANT_NORMAL,
2882 CAIRO_FONT_WEIGHT_NORMAL);
2883 cairo_set_font_size (cr, 12.0);
2885 string[1] = NULLCHAR;
2888 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2890 if (row == (flipView ? BOARD_HEIGHT-1 : 0) &&
2891 column >= BOARD_LEFT && column < BOARD_RGHT)
2893 string[0] = 'a' + column - BOARD_LEFT;
2894 cairo_text_extents (cr, string, &extents);
2896 xpos = x + squareSize - extents.width - 2;
2897 ypos = y + squareSize - extents.height - extents.y_bearing - 1;
2899 if (appData.monoMode)
2906 cairo_move_to (cr, xpos, ypos);
2907 cairo_text_path (cr, string);
2908 cairo_set_source_rgb (cr, 0.0, 0.0, 0);
2909 cairo_fill_preserve (cr);
2910 cairo_set_source_rgb (cr, 0, 1.0, 0);
2911 cairo_set_line_width (cr, 0.1);
2914 if ( column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT))
2917 string[0] = ONE + row;
2918 cairo_text_extents (cr, string, &extents);
2921 ypos = y + extents.height + 1;
2923 if (appData.monoMode)
2930 cairo_move_to (cr, xpos, ypos);
2931 cairo_text_path (cr, string);
2932 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
2933 cairo_fill_preserve (cr);
2934 cairo_set_source_rgb (cr, 0, 0, 1.0);
2935 cairo_set_line_width (cr, 0.1);
2947 /* Returns 1 if there are "too many" differences between b1 and b2
2948 (i.e. more than 1 move was made) */
2949 static int too_many_diffs(b1, b2)
2955 for (i=0; i<BOARD_HEIGHT; ++i) {
2956 for (j=0; j<BOARD_WIDTH; ++j) {
2957 if (b1[i][j] != b2[i][j]) {
2958 if (++c > 4) /* Castling causes 4 diffs */
2967 /* Matrix describing castling maneuvers */
2968 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
2969 static int castling_matrix[4][5] = {
2970 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
2971 { 0, 7, 4, 5, 6 }, /* 0-0, white */
2972 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
2973 { 7, 7, 4, 5, 6 } /* 0-0, black */
2976 /* Checks whether castling occurred. If it did, *rrow and *rcol
2977 are set to the destination (row,col) of the rook that moved.
2979 Returns 1 if castling occurred, 0 if not.
2981 Note: Only handles a max of 1 castling move, so be sure
2982 to call too_many_diffs() first.
2984 static int check_castle_draw(newb, oldb, rrow, rcol)
2991 /* For each type of castling... */
2992 for (i=0; i<4; ++i) {
2993 r = castling_matrix[i];
2995 /* Check the 4 squares involved in the castling move */
2997 for (j=1; j<=4; ++j) {
2998 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
3005 /* All 4 changed, so it must be a castling move */
3014 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
3015 void DrawSeekAxis( int x, int y, int xTo, int yTo )
3017 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
3020 void DrawSeekBackground( int left, int top, int right, int bottom )
3022 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
3025 void DrawSeekText(char *buf, int x, int y)
3027 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
3030 void DrawSeekDot(int x, int y, int colorNr)
3032 int square = colorNr & 0x80;
3035 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
3037 XFillRectangle(xDisplay, xBoardWindow, color,
3038 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3040 XFillArc(xDisplay, xBoardWindow, color,
3041 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
3044 static int damage[BOARD_RANKS][BOARD_FILES];
3047 * event handler for redrawing the board
3049 void DrawPosition( repaint, board)
3050 /*Boolean*/int repaint;
3054 static int lastFlipView = 0;
3055 static int lastBoardValid = 0;
3056 static Board lastBoard;
3059 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
3061 if (board == NULL) {
3062 if (!lastBoardValid) return;
3065 if (!lastBoardValid || lastFlipView != flipView) {
3066 // XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
3067 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flip View"),
3072 * It would be simpler to clear the window with XClearWindow()
3073 * but this causes a very distracting flicker.
3076 if (!repaint && lastBoardValid && lastFlipView == flipView)
3078 /* If too much changes (begin observing new game, etc.), don't
3080 do_flash = too_many_diffs(board, lastBoard) ? 0 : 1;
3082 /* Special check for castling so we don't flash both the king
3083 and the rook (just flash the king). */
3086 if (check_castle_draw(board, lastBoard, &rrow, &rcol))
3088 /* Draw rook with NO flashing. King will be drawn flashing later */
3089 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
3090 lastBoard[rrow][rcol] = board[rrow][rcol];
3094 /* First pass -- Draw (newly) empty squares and repair damage.
3095 This prevents you from having a piece show up twice while it
3096 is flashing on its new square */
3097 for (i = 0; i < BOARD_HEIGHT; i++)
3098 for (j = 0; j < BOARD_WIDTH; j++)
3099 if ((board[i][j] != lastBoard[i][j] && board[i][j] == EmptySquare)
3102 DrawSquare(i, j, board[i][j], 0);
3103 damage[i][j] = False;
3106 /* Second pass -- Draw piece(s) in new position and flash them */
3107 for (i = 0; i < BOARD_HEIGHT; i++)
3108 for (j = 0; j < BOARD_WIDTH; j++)
3109 if (board[i][j] != lastBoard[i][j])
3111 DrawSquare(i, j, board[i][j], do_flash);
3123 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
3125 cairo_set_line_width (cr, lineGap);
3127 /* TODO: use appdata colors */
3128 cairo_set_source_rgba (cr, 0, 1, 0, 1.0);
3132 for (i = 0; i < BOARD_HEIGHT + 1; i++)
3135 x2 = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3136 y1 = y2 = lineGap / 2 + (i * (squareSize + lineGap));
3138 cairo_move_to (cr, x1, y1);
3139 cairo_rel_line_to (cr, x2,0);
3143 for (j = 0; j < BOARD_WIDTH + 1; j++)
3146 y2 = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3147 x1 = x2 = lineGap / 2 + (j * (squareSize + lineGap));
3149 cairo_move_to (cr, x1, y1);
3150 cairo_rel_line_to (cr, 0, y2);
3159 for (i = 0; i < BOARD_HEIGHT; i++)
3160 for (j = 0; j < BOARD_WIDTH; j++)
3162 DrawSquare(i, j, board[i][j], 0);
3163 damage[i][j] = False;
3167 CopyBoard(lastBoard, board);
3169 lastFlipView = flipView;
3171 /* Draw highlights */
3172 if (pm1X >= 0 && pm1Y >= 0)
3174 drawHighlight(pm1X, pm1Y, LINE_TYPE_PRE);
3176 if (pm2X >= 0 && pm2Y >= 0)
3178 drawHighlight(pm2X, pm2Y, LINE_TYPE_PRE);
3180 if (hi1X >= 0 && hi1Y >= 0)
3182 drawHighlight(hi1X, hi1Y, LINE_TYPE_HIGHLIGHT);
3184 if (hi2X >= 0 && hi2Y >= 0)
3186 drawHighlight(hi2X, hi2Y, LINE_TYPE_HIGHLIGHT);
3189 /* If piece being dragged around board, must redraw that too */
3195 void AnimateUserMove (Widget w, XEvent * event,
3196 String * params, Cardinal * nParams)
3198 DragPieceMove(event->xmotion.x, event->xmotion.y);
3201 void HandlePV (Widget w, XEvent * event,
3202 String * params, Cardinal * nParams)
3203 { // [HGM] pv: walk PV
3204 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3207 Widget CommentCreate(name, text, mutable, callback, lines)
3209 int /*Boolean*/ mutable;
3210 XtCallbackProc callback;
3214 Widget shell, layout, form, edit, b_ok, b_cancel, b_clear, b_close, b_edit;
3219 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3220 XtGetValues(boardWidget, args, j);
3223 XtSetArg(args[j], XtNresizable, True); j++;
3226 // XtCreatePopupShell(name, topLevelShellWidgetClass,
3227 // shellWidget, args, j);
3230 // XtCreatePopupShell(name, transientShellWidgetClass,
3231 // shellWidget, args, j);
3234 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
3235 layoutArgs, XtNumber(layoutArgs));
3237 XtCreateManagedWidget("form", formWidgetClass, layout,
3238 formArgs, XtNumber(formArgs));
3242 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
3243 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
3245 XtSetArg(args[j], XtNstring, text); j++;
3246 XtSetArg(args[j], XtNtop, XtChainTop); j++;
3247 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3248 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3249 XtSetArg(args[j], XtNright, XtChainRight); j++;
3250 XtSetArg(args[j], XtNresizable, True); j++;
3251 XtSetArg(args[j], XtNwidth, bw_width); j++; /*force wider than buttons*/
3252 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
3253 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
3254 XtSetArg(args[j], XtNautoFill, True); j++;
3255 XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
3257 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
3261 XtSetArg(args[j], XtNfromVert, edit); j++;
3262 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3263 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3264 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3265 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3267 XtCreateManagedWidget(_("ok"), commandWidgetClass, form, args, j);
3268 XtAddCallback(b_ok, XtNcallback, callback, (XtPointer) 0);
3271 XtSetArg(args[j], XtNfromVert, edit); j++;
3272 XtSetArg(args[j], XtNfromHoriz, b_ok); j++;
3273 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3274 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3275 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3276 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3278 XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
3279 XtAddCallback(b_cancel, XtNcallback, callback, (XtPointer) 0);
3282 XtSetArg(args[j], XtNfromVert, edit); j++;
3283 XtSetArg(args[j], XtNfromHoriz, b_cancel); j++;
3284 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3285 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3286 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3287 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3289 XtCreateManagedWidget(_("clear"), commandWidgetClass, form, args, j);
3290 XtAddCallback(b_clear, XtNcallback, callback, (XtPointer) 0);
3293 XtSetArg(args[j], XtNfromVert, edit); j++;
3294 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3295 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3296 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3297 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3299 XtCreateManagedWidget(_("close"), commandWidgetClass, form, args, j);
3300 XtAddCallback(b_close, XtNcallback, callback, (XtPointer) 0);
3303 XtSetArg(args[j], XtNfromVert, edit); j++;
3304 XtSetArg(args[j], XtNfromHoriz, b_close); j++;
3305 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3306 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3307 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3308 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3310 XtCreateManagedWidget(_("edit"), commandWidgetClass, form, args, j);
3311 XtAddCallback(b_edit, XtNcallback, callback, (XtPointer) 0);
3314 XtRealizeWidget(shell);
3316 if (commentX == -1) {
3319 Dimension pw_height;
3320 Dimension ew_height;
3323 XtSetArg(args[j], XtNheight, &ew_height); j++;
3324 XtGetValues(edit, args, j);
3327 XtSetArg(args[j], XtNheight, &pw_height); j++;
3328 XtGetValues(shell, args, j);
3329 commentH = pw_height + (lines - 1) * ew_height;
3330 commentW = bw_width - 16;
3332 // XSync(xDisplay, False);
3334 /* This code seems to tickle an X bug if it is executed too soon
3335 after xboard starts up. The coordinates get transformed as if
3336 the main window was positioned at (0, 0).
3338 // XtTranslateCoords(shellWidget,
3339 // (bw_width - commentW) / 2, 0 - commentH / 2,
3340 // &commentX, &commentY);
3342 // XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
3343 // RootWindowOfScreen(XtScreen(shellWidget)),
3344 // (bw_width - commentW) / 2, 0 - commentH / 2,
3345 // &xx, &yy, &junk);
3349 if (commentY < 0) commentY = 0; /*avoid positioning top offscreen*/
3352 if(wpComment.width > 0) {
3353 commentX = wpComment.x;
3354 commentY = wpComment.y;
3355 commentW = wpComment.width;
3356 commentH = wpComment.height;
3360 XtSetArg(args[j], XtNheight, commentH); j++;
3361 XtSetArg(args[j], XtNwidth, commentW); j++;
3362 XtSetArg(args[j], XtNx, commentX); j++;
3363 XtSetArg(args[j], XtNy, commentY); j++;
3364 XtSetValues(shell, args, j);
3365 XtSetKeyboardFocus(shell, edit);
3370 /* Used for analysis window and ICS input window */
3371 Widget MiscCreate(name, text, mutable, callback, lines)
3373 int /*Boolean*/ mutable;
3374 XtCallbackProc callback;
3378 Widget shell, layout, form, edit;
3380 Dimension bw_width, pw_height, ew_height, w, h;
3386 XtSetArg(args[j], XtNresizable, True); j++;
3389 // XtCreatePopupShell(name, topLevelShellWidgetClass,
3390 // shellWidget, args, j);
3393 // XtCreatePopupShell(name, transientShellWidgetClass,
3394 // shellWidget, args, j);
3397 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
3398 layoutArgs, XtNumber(layoutArgs));
3400 XtCreateManagedWidget("form", formWidgetClass, layout,
3401 formArgs, XtNumber(formArgs));
3405 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
3406 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
3408 XtSetArg(args[j], XtNstring, text); j++;
3409 XtSetArg(args[j], XtNtop, XtChainTop); j++;
3410 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3411 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3412 XtSetArg(args[j], XtNright, XtChainRight); j++;
3413 XtSetArg(args[j], XtNresizable, True); j++;
3414 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
3415 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
3416 XtSetArg(args[j], XtNautoFill, True); j++;
3417 XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
3419 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
3421 XtRealizeWidget(shell);
3424 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3425 XtGetValues(boardWidget, args, j);
3428 XtSetArg(args[j], XtNheight, &ew_height); j++;
3429 XtGetValues(edit, args, j);
3432 XtSetArg(args[j], XtNheight, &pw_height); j++;
3433 XtGetValues(shell, args, j);
3434 h = pw_height + (lines - 1) * ew_height;
3437 // XSync(xDisplay, False);
3439 /* This code seems to tickle an X bug if it is executed too soon
3440 after xboard starts up. The coordinates get transformed as if
3441 the main window was positioned at (0, 0).
3443 // XtTranslateCoords(shellWidget, (bw_width - w) / 2, 0 - h / 2, &x, &y);
3445 // XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
3446 // RootWindowOfScreen(XtScreen(shellWidget)),
3447 // (bw_width - w) / 2, 0 - h / 2, &xx, &yy, &junk);
3451 if (y < 0) y = 0; /*avoid positioning top offscreen*/
3454 XtSetArg(args[j], XtNheight, h); j++;
3455 XtSetArg(args[j], XtNwidth, w); j++;
3456 XtSetArg(args[j], XtNx, x); j++;
3457 XtSetArg(args[j], XtNy, y); j++;
3458 XtSetValues(shell, args, j);
3464 static int savedIndex; /* gross that this is global */
3466 void EditCommentPopUp(index, title, text)
3475 if (text == NULL) text = "";
3477 if (editShell == NULL) {
3479 CommentCreate(title, text, True, EditCommentCallback, 4);
3480 XtRealizeWidget(editShell);
3481 // CatchDeleteWindow(editShell, "EditCommentPopDown");
3483 edit = XtNameToWidget(editShell, "*form.text");
3485 XtSetArg(args[j], XtNstring, text); j++;
3486 XtSetValues(edit, args, j);
3488 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
3489 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
3490 XtSetValues(editShell, args, j);
3493 XtPopup(editShell, XtGrabNone);
3497 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
3498 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
3502 void EditCommentCallback(w, client_data, call_data)
3504 XtPointer client_data, call_data;
3512 XtSetArg(args[j], XtNlabel, &name); j++;
3513 XtGetValues(w, args, j);
3515 if (strcmp(name, _("ok")) == 0) {
3516 edit = XtNameToWidget(editShell, "*form.text");
3518 XtSetArg(args[j], XtNstring, &val); j++;
3519 XtGetValues(edit, args, j);
3520 ReplaceComment(savedIndex, val);
3521 EditCommentPopDown();
3522 } else if (strcmp(name, _("cancel")) == 0) {
3523 EditCommentPopDown();
3524 } else if (strcmp(name, _("clear")) == 0) {
3525 edit = XtNameToWidget(editShell, "*form.text");
3526 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
3527 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
3531 void EditCommentPopDown()
3536 if (!editUp) return;
3538 XtSetArg(args[j], XtNx, &commentX); j++;
3539 XtSetArg(args[j], XtNy, &commentY); j++;
3540 XtSetArg(args[j], XtNheight, &commentH); j++;
3541 XtSetArg(args[j], XtNwidth, &commentW); j++;
3542 XtGetValues(editShell, args, j);
3543 XtPopdown(editShell);
3546 XtSetArg(args[j], XtNleftBitmap, None); j++;
3547 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
3551 void ICSInputBoxPopUp()
3556 char *title = _("ICS Input");
3559 if (ICSInputShell == NULL) {
3560 ICSInputShell = MiscCreate(title, "", True, NULL, 1);
3561 tr = XtParseTranslationTable(ICSInputTranslations);
3562 edit = XtNameToWidget(ICSInputShell, "*form.text");
3563 XtOverrideTranslations(edit, tr);
3564 XtRealizeWidget(ICSInputShell);
3565 // CatchDeleteWindow(ICSInputShell, "ICSInputBoxPopDown");
3568 edit = XtNameToWidget(ICSInputShell, "*form.text");
3570 XtSetArg(args[j], XtNstring, ""); j++;
3571 XtSetValues(edit, args, j);
3573 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
3574 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
3575 XtSetValues(ICSInputShell, args, j);
3578 XtPopup(ICSInputShell, XtGrabNone);
3579 XtSetKeyboardFocus(ICSInputShell, edit);
3581 ICSInputBoxUp = True;
3583 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
3584 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
3588 void ICSInputSendText()
3595 edit = XtNameToWidget(ICSInputShell, "*form.text");
3597 XtSetArg(args[j], XtNstring, &val); j++;
3598 XtGetValues(edit, args, j);
3599 SendMultiLineToICS(val);
3600 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
3601 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
3604 void ICSInputBoxPopDown()
3609 if (!ICSInputBoxUp) return;
3611 XtPopdown(ICSInputShell);
3612 ICSInputBoxUp = False;
3614 XtSetArg(args[j], XtNleftBitmap, None); j++;
3615 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
3619 void CommentPopUp(title, text)
3626 if (commentShell == NULL) {
3628 CommentCreate(title, text, False, CommentCallback, 4);
3629 XtRealizeWidget(commentShell);
3630 // CatchDeleteWindow(commentShell, "CommentPopDown");
3632 edit = XtNameToWidget(commentShell, "*form.text");
3634 XtSetArg(args[j], XtNstring, text); j++;
3635 XtSetValues(edit, args, j);
3637 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
3638 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
3639 XtSetValues(commentShell, args, j);
3642 XtPopup(commentShell, XtGrabNone);
3643 // XSync(xDisplay, False);
3648 void CommentCallback(w, client_data, call_data)
3650 XtPointer client_data, call_data;
3657 XtSetArg(args[j], XtNlabel, &name); j++;
3658 XtGetValues(w, args, j);
3660 if (strcmp(name, _("close")) == 0) {
3662 } else if (strcmp(name, _("edit")) == 0) {
3669 void CommentPopDown()
3674 if (!commentUp) return;
3676 XtSetArg(args[j], XtNx, &commentX); j++;
3677 XtSetArg(args[j], XtNy, &commentY); j++;
3678 XtSetArg(args[j], XtNwidth, &commentW); j++;
3679 XtSetArg(args[j], XtNheight, &commentH); j++;
3680 XtGetValues(commentShell, args, j);
3681 XtPopdown(commentShell);
3682 // XSync(xDisplay, False);
3686 void PromotionPopUp()
3689 Widget dialog, layout;
3691 Dimension bw_width, pw_width;
3695 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3696 XtGetValues(boardWidget, args, j);
3699 XtSetArg(args[j], XtNresizable, True); j++;
3700 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
3702 // XtCreatePopupShell("Promotion", transientShellWidgetClass,
3703 // shellWidget, args, j);
3705 // XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
3706 // layoutArgs, XtNumber(layoutArgs));
3709 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
3710 XtSetArg(args[j], XtNborderWidth, 0); j++;
3711 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
3714 if(gameInfo.variant != VariantShogi) {
3715 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
3716 (XtPointer) dialog);
3717 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
3718 (XtPointer) dialog);
3719 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
3720 (XtPointer) dialog);
3721 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
3722 (XtPointer) dialog);
3723 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
3724 gameInfo.variant == VariantGiveaway) {
3725 XawDialogAddButton(dialog, _("King"), PromotionCallback,
3726 (XtPointer) dialog);
3728 if(gameInfo.variant == VariantCapablanca ||
3729 gameInfo.variant == VariantGothic ||
3730 gameInfo.variant == VariantCapaRandom) {
3731 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
3732 (XtPointer) dialog);
3733 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
3734 (XtPointer) dialog);
3736 } else // [HGM] shogi
3738 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
3739 (XtPointer) dialog);
3740 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
3741 (XtPointer) dialog);
3743 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
3744 (XtPointer) dialog);
3746 XtRealizeWidget(promotionShell);
3747 // CatchDeleteWindow(promotionShell, "PromotionPopDown");
3750 XtSetArg(args[j], XtNwidth, &pw_width); j++;
3751 XtGetValues(promotionShell, args, j);
3753 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3754 lineGap + squareSize/3 +
3755 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
3756 0 : 6*(squareSize + lineGap)), &x, &y);
3759 XtSetArg(args[j], XtNx, x); j++;
3760 XtSetArg(args[j], XtNy, y); j++;
3761 XtSetValues(promotionShell, args, j);
3763 XtPopup(promotionShell, XtGrabNone);
3768 void PromotionPopDown()
3770 if (!promotionUp) return;
3771 XtPopdown(promotionShell);
3772 XtDestroyWidget(promotionShell);
3773 promotionUp = False;
3776 void PromotionCallback(w, client_data, call_data)
3778 XtPointer client_data, call_data;
3784 XtSetArg(args[0], XtNlabel, &name);
3785 XtGetValues(w, args, 1);
3789 if (fromX == -1) return;
3791 if (strcmp(name, _("cancel")) == 0) {
3795 } else if (strcmp(name, _("Knight")) == 0) {
3797 } else if (strcmp(name, _("Promote")) == 0) {
3799 } else if (strcmp(name, _("Defer")) == 0) {
3802 promoChar = ToLower(name[0]);
3805 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
3807 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
3808 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3813 void ErrorCallback(w, client_data, call_data)
3815 XtPointer client_data, call_data;
3818 XtPopdown(w = XtParent(XtParent(XtParent(w))));
3820 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3826 if (!errorUp) return;
3830 gtk_widget_destroy(GTK_WIDGET(GUI_Error));
3832 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3837 void ErrorPopUp(title, label, modal)
3838 char *title, *label;
3841 GUI_Error = gtk_message_dialog_new(GTK_WINDOW(GUI_Window),
3842 GTK_DIALOG_DESTROY_WITH_PARENT,
3847 gtk_window_set_title(GTK_WINDOW(GUI_Error),(gchar *) title);
3850 gtk_dialog_run(GTK_DIALOG(GUI_Error));
3851 gtk_widget_destroy(GTK_WIDGET(GUI_Error));
3855 g_signal_connect_swapped (GUI_Error, "response",
3856 G_CALLBACK (ErrorPopDownProc),
3859 gtk_widget_show(GTK_WIDGET(GUI_Error));
3865 /* Disable all user input other than deleting the window */
3866 static int frozen = 0;
3870 /* Grab by a widget that doesn't accept input */
3871 // XtAddGrab(messageWidget, TRUE, FALSE);
3875 /* Undo a FreezeUI */
3878 if (!frozen) return;
3879 // XtRemoveGrab(messageWidget);
3883 char *ModeToWidgetName(mode)
3887 case BeginningOfGame:
3888 if (appData.icsActive)
3889 return "menuMode.ICS Client";
3890 else if (appData.noChessProgram ||
3891 *appData.cmailGameName != NULLCHAR)
3892 return "menuMode.Edit Game";
3894 return "menuMode.Machine Black";
3895 case MachinePlaysBlack:
3896 return "menuMode.Machine Black";
3897 case MachinePlaysWhite:
3898 return "menuMode.Machine White";
3900 return "menuMode.Analysis Mode";
3902 return "menuMode.Analyze File";
3903 case TwoMachinesPlay:
3904 return "menuMode.Two Machines";
3906 return "menuMode.Edit Game";
3907 case PlayFromGameFile:
3908 return "menuFile.Load Game";
3910 return "menuMode.Edit Position";
3912 return "menuMode.Training";
3913 case IcsPlayingWhite:
3914 case IcsPlayingBlack:
3918 return "menuMode.ICS Client";
3925 void ModeHighlight()
3927 static int oldPausing = FALSE;
3928 static GameMode oldmode = (GameMode) -1;
3931 // todo this toggling of the pause button doesn't seem to work?
3932 // e.g. select pause from buttonbar doesn't activate menumode.pause
3934 // if (!boardWidget || !XtIsRealized(boardWidget)) return;
3936 if (pausing != oldPausing) {
3937 oldPausing = pausing;
3938 gtk_button_set_relief(GTK_BUTTON (gtk_builder_get_object (builder, "menuMode.Pause")),pausing?GTK_RELIEF_NORMAL:GTK_RELIEF_NONE);
3939 /* toggle background color in showbuttonbar */
3940 if (appData.showButtonBar) {
3942 gtk_button_pressed(GTK_BUTTON (gtk_builder_get_object (builder, "buttonbar.Pause")));
3944 gtk_button_released(GTK_BUTTON (gtk_builder_get_object (builder, "buttonbar.Pause")));
3949 // probably not needed anymore
3950 // wname = ModeToWidgetName(oldmode);
3952 // gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, wname)),True);
3956 /* Maybe all the enables should be handled here, not just this one */
3957 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuMode.Training")),
3958 gameMode == Training || gameMode == PlayFromGameFile);
3963 * Button/menu procedures
3966 int LoadGamePopUp(f, gameNumber, title)
3971 cmailMsgLoaded = FALSE;
3973 if (gameNumber == 0)
3975 int error = GameListBuild(f);
3979 DisplayError(_("Cannot build game list"), error);
3981 else if (!ListEmpty(&gameList)
3982 && ((ListGame *) gameList.tailPred)->number > 1)
3984 /* we need an answer which game to load, so let's make it modal for a while*/
3985 gtk_window_set_modal(GTK_WINDOW(GUI_GameList) , TRUE);
3986 GameListPopUp(f, title);
3987 gtk_window_set_modal(GTK_WINDOW(GUI_GameList) , FALSE);
3996 return LoadGame(f, gameNumber, title, FALSE);
3999 void ReloadCmailMsgProc(w, event, prms, nprms)
4005 ReloadCmailMsgEvent(FALSE);
4008 void MailMoveProc(w, event, prms, nprms)
4017 /* this variable is shared between CopyPositionProc and SendPositionSelection */
4018 char *selected_fen_position=NULL;
4021 SendPositionSelection(Widget w, Atom *selection, Atom *target,
4022 Atom *type_return, XtPointer *value_return,
4023 unsigned long *length_return, int *format_return)
4025 char *selection_tmp;
4027 if (!selected_fen_position) return False; /* should never happen */
4028 // if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4029 // /* note: since no XtSelectionDoneProc was registered, Xt will
4030 // * automatically call XtFree on the value returned. So have to
4031 // * make a copy of it allocated with XtMalloc */
4032 // selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
4033 // strcpy(selection_tmp, selected_fen_position);
4035 // *value_return=selection_tmp;
4036 // *length_return=strlen(selection_tmp);
4037 // *type_return=*target;
4038 // *format_return = 8; /* bits per byte */
4040 // } else if (*target == XA_TARGETS(xDisplay)) {
4041 // Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4042 // targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4043 // targets_tmp[1] = XA_STRING;
4044 // *value_return = targets_tmp;
4045 // *type_return = XA_ATOM;
4046 // *length_return = 2;
4047 // *format_return = 8 * sizeof(Atom);
4048 // if (*format_return > 32) {
4049 // *length_return *= *format_return / 32;
4050 // *format_return = 32;
4058 /* note: when called from menu all parameters are NULL, so no clue what the
4059 * Widget which was clicked on was, or what the click event was
4061 void CopyPositionProc(w, event, prms, nprms)
4068 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4069 * have a notion of a position that is selected but not copied.
4070 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4072 if(gameMode == EditPosition) EditPositionDone(TRUE);
4073 if (selected_fen_position) free(selected_fen_position);
4074 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
4075 if (!selected_fen_position) return;
4076 // XtOwnSelection(menuBarWidget, XA_PRIMARY,
4078 // SendPositionSelection,
4079 // NULL/* lose_ownership_proc */ ,
4080 // NULL/* transfer_done_proc */);
4081 // XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4083 // SendPositionSelection,
4084 // NULL/* lose_ownership_proc */ ,
4085 // NULL/* transfer_done_proc */);
4088 /* function called when the data to Paste is ready */
4090 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
4091 Atom *type, XtPointer value, unsigned long *len, int *format)
4094 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
4095 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
4096 EditPositionPasteFEN(fenstr);
4100 /* called when Paste Position button is pressed,
4101 * all parameters will be NULL */
4102 void PastePositionProc(w, event, prms, nprms)
4108 // XtGetSelectionValue(menuBarWidget,
4109 // appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4110 // /* (XtSelectionCallbackProc) */ PastePositionCB,
4111 // NULL, /* client_data passed to PastePositionCB */
4113 // /* better to use the time field from the event that triggered the
4114 // * call to this function, but that isn't trivial to get
4122 SendGameSelection(Widget w, Atom *selection, Atom *target,
4123 Atom *type_return, XtPointer *value_return,
4124 unsigned long *length_return, int *format_return)
4126 char *selection_tmp;
4128 // if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4129 // FILE* f = fopen(gameCopyFilename, "r");
4132 // if (f == NULL) return False;
4136 // selection_tmp = XtMalloc(len + 1);
4137 // count = fread(selection_tmp, 1, len, f);
4138 // if (len != count) {
4139 // XtFree(selection_tmp);
4142 // selection_tmp[len] = NULLCHAR;
4143 // *value_return = selection_tmp;
4144 // *length_return = len;
4145 // *type_return = *target;
4146 // *format_return = 8; /* bits per byte */
4148 // } else if (*target == XA_TARGETS(xDisplay)) {
4149 // Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4150 // targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4151 // targets_tmp[1] = XA_STRING;
4152 // *value_return = targets_tmp;
4153 // *type_return = XA_ATOM;
4154 // *length_return = 2;
4155 // *format_return = 8 * sizeof(Atom);
4156 // if (*format_return > 32) {
4157 // *length_return *= *format_return / 32;
4158 // *format_return = 32;
4166 /* note: when called from menu all parameters are NULL, so no clue what the
4167 * Widget which was clicked on was, or what the click event was
4169 void CopyGameProc(w, event, prms, nprms)
4177 ret = SaveGameToFile(gameCopyFilename, FALSE);
4181 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4182 * have a notion of a game that is selected but not copied.
4183 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4185 // XtOwnSelection(menuBarWidget, XA_PRIMARY,
4187 // SendGameSelection,
4188 // NULL/* lose_ownership_proc */ ,
4189 // NULL/* transfer_done_proc */);
4190 // XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4192 // SendGameSelection,
4193 // NULL/* lose_ownership_proc */ ,
4194 // NULL/* transfer_done_proc */);
4197 /* function called when the data to Paste is ready */
4199 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
4200 Atom *type, XtPointer value, unsigned long *len, int *format)
4203 if (value == NULL || *len == 0) {
4204 return; /* nothing had been selected to copy */
4206 f = fopen(gamePasteFilename, "w");
4208 DisplayError(_("Can't open temp file"), errno);
4211 fwrite(value, 1, *len, f);
4214 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
4217 /* called when Paste Game button is pressed,
4218 * all parameters will be NULL */
4219 void PasteGameProc(w, event, prms, nprms)
4225 // XtGetSelectionValue(menuBarWidget,
4226 // appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4227 // /* (XtSelectionCallbackProc) */ PasteGameCB,
4228 // NULL, /* client_data passed to PasteGameCB */
4230 // /* better to use the time field from the event that triggered the
4231 // * call to this function, but that isn't trivial to get
4238 void SaveOnExitProc(w, event, prms, nprms)
4246 saveSettingsOnExit = !saveSettingsOnExit;
4248 if (saveSettingsOnExit) {
4249 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
4251 XtSetArg(args[0], XtNleftBitmap, None);
4253 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
4257 void SaveSettingsProc(w, event, prms, nprms)
4263 SaveSettings(settingsFileName);
4269 SaveGameProc(NULL, NULL);
4274 void EditCommentProc(w, event, prms, nprms)
4281 EditCommentPopDown();
4287 void IcsInputBoxProc(w, event, prms, nprms)
4293 if (ICSInputBoxUp) {
4294 ICSInputBoxPopDown();
4301 void EnterKeyProc(w, event, prms, nprms)
4307 if (ICSInputBoxUp == True)
4312 void DebugProc(w, event, prms, nprms)
4318 appData.debugMode = !appData.debugMode;
4321 void AboutGameProc(w, event, prms, nprms)
4330 void NothingProc(w, event, prms, nprms)
4339 void Iconify(w, event, prms, nprms)
4347 // fromX = fromY = -1;
4348 // XtSetArg(args[0], XtNiconic, True);
4349 // XtSetValues(shellWidget, args, 1);
4352 void DisplayMessage(message, extMessage)
4353 gchar *message, *extMessage;
4360 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
4363 message = extMessage;
4366 gtk_label_set_text( GTK_LABEL(gtk_builder_get_object (builder, "Messages")),message);
4371 void DisplayTitle(text)
4374 gchar title[MSG_SIZ];
4376 if (text == NULL) text = "";
4378 if (appData.titleInWindow)
4383 if (*text != NULLCHAR)
4385 strcpy(title, text);
4387 else if (appData.icsActive)
4389 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
4391 else if (appData.cmailGameName[0] != NULLCHAR)
4393 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
4395 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
4397 else if (gameInfo.variant == VariantGothic)
4399 strcpy(title, GOTHIC);
4403 else if (gameInfo.variant == VariantFalcon)
4405 strcpy(title, FALCON);
4408 else if (appData.noChessProgram)
4410 strcpy(title, programName);
4414 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
4416 gtk_window_set_title(GTK_WINDOW(GUI_Window),title);
4422 void DisplayError(message, error)
4429 if (appData.debugMode || appData.matchMode) {
4430 fprintf(stderr, "%s: %s\n", programName, message);
4433 if (appData.debugMode || appData.matchMode) {
4434 fprintf(stderr, "%s: %s: %s\n",
4435 programName, message, strerror(error));
4437 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
4440 ErrorPopUp(_("Error"), message, FALSE);
4444 void DisplayMoveError(message)
4449 DrawPosition(FALSE, NULL);
4450 if (appData.debugMode || appData.matchMode) {
4451 fprintf(stderr, "%s: %s\n", programName, message);
4453 if (appData.popupMoveErrors) {
4454 ErrorPopUp(_("Error"), message, FALSE);
4456 DisplayMessage(message, "");
4461 void DisplayFatalError(message, error, status)
4467 errorExitStatus = status;
4469 fprintf(stderr, "%s: %s\n", programName, message);
4471 fprintf(stderr, "%s: %s: %s\n",
4472 programName, message, strerror(error));
4473 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
4476 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
4477 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
4483 void DisplayInformation(message)
4487 ErrorPopUp(_("Information"), message, TRUE);
4490 void DisplayNote(message)
4494 ErrorPopUp(_("Note"), message, FALSE);
4498 NullXErrorCheck(dpy, error_event)
4500 XErrorEvent *error_event;
4505 void DisplayIcsInteractionTitle(message)
4508 if (oldICSInteractionTitle == NULL) {
4509 /* Magic to find the old window title, adapted from vim */
4510 char *wina = getenv("WINDOWID");
4512 Window win = (Window) atoi(wina);
4513 Window root, parent, *children;
4514 unsigned int nchildren;
4515 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4517 // if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4518 // if (!XQueryTree(xDisplay, win, &root, &parent,
4519 // &children, &nchildren)) break;
4520 // if (children) XFree((void *)children);
4521 // if (parent == root || parent == 0) break;
4524 XSetErrorHandler(oldHandler);
4526 if (oldICSInteractionTitle == NULL) {
4527 oldICSInteractionTitle = "xterm";
4530 printf("\033]0;%s\007", message);
4534 char pendingReplyPrefix[MSG_SIZ];
4535 ProcRef pendingReplyPR;
4537 void AskQuestionProc(w, event, prms, nprms)
4544 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
4548 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
4551 void AskQuestionPopDown()
4553 if (!askQuestionUp) return;
4554 XtPopdown(askQuestionShell);
4555 XtDestroyWidget(askQuestionShell);
4556 askQuestionUp = False;
4559 void AskQuestionReplyAction(w, event, prms, nprms)
4569 reply = XawDialogGetValueString(w = XtParent(w));
4570 strcpy(buf, pendingReplyPrefix);
4571 if (*buf) strcat(buf, " ");
4574 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
4575 AskQuestionPopDown();
4577 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
4580 void AskQuestionCallback(w, client_data, call_data)
4582 XtPointer client_data, call_data;
4587 XtSetArg(args[0], XtNlabel, &name);
4588 XtGetValues(w, args, 1);
4590 if (strcmp(name, _("cancel")) == 0) {
4591 AskQuestionPopDown();
4593 AskQuestionReplyAction(w, NULL, NULL, NULL);
4597 void AskQuestion(title, question, replyPrefix, pr)
4598 char *title, *question, *replyPrefix;
4602 Widget popup, layout, dialog, edit;
4608 strcpy(pendingReplyPrefix, replyPrefix);
4609 pendingReplyPR = pr;
4612 XtSetArg(args[i], XtNresizable, True); i++;
4613 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
4614 // askQuestionShell = popup =
4615 // XtCreatePopupShell(title, transientShellWidgetClass,
4616 // shellWidget, args, i);
4619 // XtCreateManagedWidget(layoutName, formWidgetClass, popup,
4620 // layoutArgs, XtNumber(layoutArgs));
4623 XtSetArg(args[i], XtNlabel, question); i++;
4624 XtSetArg(args[i], XtNvalue, ""); i++;
4625 XtSetArg(args[i], XtNborderWidth, 0); i++;
4626 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
4629 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
4630 (XtPointer) dialog);
4631 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
4632 (XtPointer) dialog);
4634 XtRealizeWidget(popup);
4635 // CatchDeleteWindow(popup, "AskQuestionPopDown");
4637 // XQueryPointer(xDisplay, xBoardWindow, &root, &child,
4638 // &x, &y, &win_x, &win_y, &mask);
4640 // XtSetArg(args[0], XtNx, x - 10);
4641 // XtSetArg(args[1], XtNy, y - 30);
4642 // XtSetValues(popup, args, 2);
4644 // XtPopup(popup, XtGrabExclusive);
4645 // askQuestionUp = True;
4647 // edit = XtNameToWidget(dialog, "*value");
4648 // XtSetKeyboardFocus(popup, edit);
4656 if (*name == NULLCHAR) {
4658 } else if (strcmp(name, "$") == 0) {
4659 putc(BELLCHAR, stderr);
4662 snprintf(buf, sizeof(buf), "%s '%s' &", appData.soundProgram, name);
4670 PlaySound(appData.soundMove);
4676 PlaySound(appData.soundIcsWin);
4682 PlaySound(appData.soundIcsLoss);
4688 PlaySound(appData.soundIcsDraw);
4692 PlayIcsUnfinishedSound()
4694 PlaySound(appData.soundIcsUnfinished);
4700 PlaySound(appData.soundIcsAlarm);
4706 system("stty echo");
4712 system("stty -echo");
4716 Colorize(cc, continuation)
4721 int count, outCount, error;
4723 if (textColors[(int)cc].bg > 0) {
4724 if (textColors[(int)cc].fg > 0) {
4725 sprintf(buf, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
4726 textColors[(int)cc].fg, textColors[(int)cc].bg);
4728 sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
4729 textColors[(int)cc].bg);
4732 if (textColors[(int)cc].fg > 0) {
4733 sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
4734 textColors[(int)cc].fg);
4736 sprintf(buf, "\033[0;%dm", textColors[(int)cc].attr);
4739 count = strlen(buf);
4740 outCount = OutputToProcess(NoProc, buf, count, &error);
4741 if (outCount < count) {
4742 DisplayFatalError(_("Error writing to display"), error, 1);
4745 if (continuation) return;
4748 PlaySound(appData.soundShout);
4751 PlaySound(appData.soundSShout);
4754 PlaySound(appData.soundChannel1);
4757 PlaySound(appData.soundChannel);
4760 PlaySound(appData.soundKibitz);
4763 PlaySound(appData.soundTell);
4765 case ColorChallenge:
4766 PlaySound(appData.soundChallenge);
4769 PlaySound(appData.soundRequest);
4772 PlaySound(appData.soundSeek);
4783 return getpwuid(getuid())->pw_name;
4786 static char *ExpandPathName(path)
4789 static char static_buf[2000];
4790 char *d, *s, buf[2000];
4796 while (*s && isspace(*s))
4805 if (*(s+1) == '/') {
4806 strcpy(d, getpwuid(getuid())->pw_dir);
4811 *strchr(buf, '/') = 0;
4812 pwd = getpwnam(buf);
4815 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
4819 strcpy(d, pwd->pw_dir);
4820 strcat(d, strchr(s+1, '/'));
4831 static char host_name[MSG_SIZ];
4833 #if HAVE_GETHOSTNAME
4834 gethostname(host_name, MSG_SIZ);
4836 #else /* not HAVE_GETHOSTNAME */
4837 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
4838 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
4840 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4842 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4843 #endif /* not HAVE_GETHOSTNAME */
4846 guint delayedEventTimerTag = 0;
4847 DelayedEventCallback delayedEventCallback = 0;
4850 FireDelayedEvent(data)
4854 g_source_remove(delayedEventTimerTag);
4855 delayedEventTimerTag = 0;
4858 delayedEventCallback();
4864 ScheduleDelayedEvent(cb, millisec)
4865 DelayedEventCallback cb; guint millisec;
4867 if(delayedEventTimerTag && delayedEventCallback == cb)
4868 // [HGM] alive: replace, rather than add or flush identical event
4869 g_source_remove(delayedEventTimerTag);
4870 delayedEventCallback = cb;
4871 delayedEventTimerTag = g_timeout_add(millisec,(GSourceFunc) FireDelayedEvent, NULL);
4875 DelayedEventCallback
4878 if (delayedEventTimerTag)
4880 return delayedEventCallback;
4889 CancelDelayedEvent()
4891 if (delayedEventTimerTag)
4893 g_source_remove(delayedEventTimerTag);
4894 delayedEventTimerTag = 0;
4900 guint loadGameTimerTag = 0;
4902 int LoadGameTimerRunning()
4904 return loadGameTimerTag != 0;
4907 int StopLoadGameTimer()
4909 if (loadGameTimerTag != 0) {
4910 g_source_remove(loadGameTimerTag);
4911 loadGameTimerTag = 0;
4919 LoadGameTimerCallback(data)
4923 g_source_remove(loadGameTimerTag);
4924 loadGameTimerTag = 0;
4931 StartLoadGameTimer(millisec)
4935 g_timeout_add( millisec, (GSourceFunc) LoadGameTimerCallback, NULL);
4939 guint analysisClockTag = 0;
4942 AnalysisClockCallback(data)
4945 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
4946 || appData.icsEngineAnalyze)
4948 AnalysisPeriodicEvent(0);
4949 return 1; /* keep on going */
4951 return 0; /* stop timer */
4955 StartAnalysisClock()
4958 g_timeout_add( 2000,(GSourceFunc) AnalysisClockCallback, NULL);
4962 guint clockTimerTag = 0;
4964 int ClockTimerRunning()
4966 return clockTimerTag != 0;
4969 int StopClockTimer()
4971 if (clockTimerTag != 0)
4973 g_source_remove(clockTimerTag);
4984 ClockTimerCallback(data)
4988 g_source_remove(clockTimerTag);
4996 StartClockTimer(millisec)
4999 clockTimerTag = g_timeout_add(millisec,(GSourceFunc) ClockTimerCallback,NULL);
5004 DisplayTimerLabel(w, color, timer, highlight)
5013 if (appData.clockMode) {
5014 sprintf(buf, "%s: %s", color, TimeString(timer));
5016 sprintf(buf, "%s ", color);
5018 gtk_label_set_text(GTK_LABEL(w),buf);
5020 /* check for low time warning */
5021 // Pixel foregroundOrWarningColor = timerForegroundPixel;
5024 // appData.lowTimeWarning &&
5025 // (timer / 1000) < appData.icsAlarmTime)
5026 // foregroundOrWarningColor = lowTimeWarningColor;
5028 // if (appData.clockMode) {
5029 // sprintf(buf, "%s: %s", color, TimeString(timer));
5030 // XtSetArg(args[0], XtNlabel, buf);
5032 // sprintf(buf, "%s ", color);
5033 // XtSetArg(args[0], XtNlabel, buf);
5038 // XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
5039 // XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
5041 // XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
5042 // XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
5045 // XtSetValues(w, args, 3);
5050 DisplayWhiteClock(timeRemaining, highlight)
5054 if(appData.noGUI) return;
5056 DisplayTimerLabel(GUI_Whiteclock, _("White"), timeRemaining, highlight);
5057 if (highlight && WindowIcon == BlackIcon)
5059 WindowIcon = WhiteIcon;
5060 gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
5065 DisplayBlackClock(timeRemaining, highlight)
5069 if(appData.noGUI) return;
5071 DisplayTimerLabel(GUI_Blackclock, _("Black"), timeRemaining, highlight);
5072 if (highlight && WindowIcon == WhiteIcon)
5074 WindowIcon = BlackIcon;
5075 gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
5093 int StartChildProcess(cmdLine, dir, pr)
5100 int to_prog[2], from_prog[2];
5104 if (appData.debugMode) {
5105 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
5108 /* We do NOT feed the cmdLine to the shell; we just
5109 parse it into blank-separated arguments in the
5110 most simple-minded way possible.
5113 strcpy(buf, cmdLine);
5116 while(*p == ' ') p++;
5118 if(*p == '"' || *p == '\'')
5119 p = strchr(++argv[i-1], *p);
5120 else p = strchr(p, ' ');
5121 if (p == NULL) break;
5126 SetUpChildIO(to_prog, from_prog);
5128 if ((pid = fork()) == 0) {
5130 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
5131 close(to_prog[1]); // first close the unused pipe ends
5132 close(from_prog[0]);
5133 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
5134 dup2(from_prog[1], 1);
5135 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
5136 close(from_prog[1]); // and closing again loses one of the pipes!
5137 if(fileno(stderr) >= 2) // better safe than sorry...
5138 dup2(1, fileno(stderr)); /* force stderr to the pipe */
5140 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
5145 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
5147 execvp(argv[0], argv);
5149 /* If we get here, exec failed */
5154 /* Parent process */
5156 close(from_prog[1]);
5158 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5161 cp->fdFrom = from_prog[0];
5162 cp->fdTo = to_prog[1];
5167 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
5168 static RETSIGTYPE AlarmCallBack(int n)
5174 DestroyChildProcess(pr, signalType)
5178 ChildProc *cp = (ChildProc *) pr;
5180 if (cp->kind != CPReal) return;
5182 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
5183 signal(SIGALRM, AlarmCallBack);
5185 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
5186 kill(cp->pid, SIGKILL); // kill it forcefully
5187 wait((int *) 0); // and wait again
5191 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
5193 /* Process is exiting either because of the kill or because of
5194 a quit command sent by the backend; either way, wait for it to die.
5203 InterruptChildProcess(pr)
5206 ChildProc *cp = (ChildProc *) pr;
5208 if (cp->kind != CPReal) return;
5209 (void) kill(cp->pid, SIGINT); /* stop it thinking */
5212 int OpenTelnet(host, port, pr)
5217 char cmdLine[MSG_SIZ];
5219 if (port[0] == NULLCHAR) {
5220 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
5222 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
5224 return StartChildProcess(cmdLine, "", pr);
5227 int OpenTCP(host, port, pr)
5233 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
5234 #else /* !OMIT_SOCKETS */
5236 struct sockaddr_in sa;
5238 unsigned short uport;
5241 if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
5245 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
5246 sa.sin_family = AF_INET;
5247 sa.sin_addr.s_addr = INADDR_ANY;
5248 uport = (unsigned short) 0;
5249 sa.sin_port = htons(uport);
5250 if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
5254 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
5255 if (!(hp = gethostbyname(host))) {
5257 if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
5258 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
5259 hp->h_addrtype = AF_INET;
5261 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
5262 hp->h_addr_list[0] = (char *) malloc(4);
5263 hp->h_addr_list[0][0] = b0;
5264 hp->h_addr_list[0][1] = b1;
5265 hp->h_addr_list[0][2] = b2;
5266 hp->h_addr_list[0][3] = b3;
5271 sa.sin_family = hp->h_addrtype;
5272 uport = (unsigned short) atoi(port);
5273 sa.sin_port = htons(uport);
5274 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
5276 if (connect(s, (struct sockaddr *) &sa,
5277 sizeof(struct sockaddr_in)) < 0) {
5281 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5288 #endif /* !OMIT_SOCKETS */
5293 int OpenCommPort(name, pr)
5300 fd = open(name, 2, 0);
5301 if (fd < 0) return errno;
5303 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5313 int OpenLoopback(pr)
5319 SetUpChildIO(to, from);
5321 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5324 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
5331 int OpenRcmd(host, user, cmd, pr)
5332 char *host, *user, *cmd;
5335 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
5339 #define INPUT_SOURCE_BUF_SIZE 8192
5348 char buf[INPUT_SOURCE_BUF_SIZE];
5353 DoInputCallback(io,cond,data)
5358 /* read input from one of the input source (for example a chess program, ICS, etc).
5359 * and call a function that will handle the input
5362 int count; /* how many bytes did we read */
5366 /* All information (callback function, file descriptor, etc) is
5367 * saved in an InputSource structure
5369 InputSource *is = (InputSource *) data;
5373 count = read(is->fd, is->unused,
5374 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
5378 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
5381 is->unused += count;
5383 /* break input into lines and call the callback function on each
5386 while (p < is->unused)
5388 q = memchr(p, '\n', is->unused - p);
5389 if (q == NULL) break;
5391 (is->func)(is, is->closure, p, q - p, 0);
5394 /* remember not yet used part of the buffer */
5396 while (p < is->unused)
5404 /* read maximum length of input buffer and send the whole buffer
5405 * to the callback function
5407 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
5412 (is->func)(is, is->closure, is->buf, count, error);
5418 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
5425 GIOChannel *channel;
5426 ChildProc *cp = (ChildProc *) pr;
5428 is = (InputSource *) calloc(1, sizeof(InputSource));
5429 is->lineByLine = lineByLine;
5433 is->fd = fileno(stdin);
5435 is->kind = cp->kind;
5436 is->fd = cp->fdFrom;
5439 is->unused = is->buf;
5443 // is->xid = XtAppAddInput(appContext, is->fd,
5444 // (XtPointer) (XtInputReadMask),
5445 // (XtInputCallbackProc) DoInputCallback,
5449 /* TODO: will this work on windows?*/
5451 channel = g_io_channel_unix_new(is->fd);
5452 g_io_channel_set_close_on_unref (channel, TRUE);
5453 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
5454 is->closure = closure;
5455 return (InputSourceRef) is;
5459 RemoveInputSource(isr)
5462 InputSource *is = (InputSource *) isr;
5464 if (is->sid == 0) return;
5465 g_source_remove(is->sid);
5470 int OutputToProcess(pr, message, count, outError)
5476 static int line = 0;
5477 ChildProc *cp = (ChildProc *) pr;
5482 if (appData.noJoin || !appData.useInternalWrap)
5483 outCount = fwrite(message, 1, count, stdout);
5486 int width = get_term_width();
5487 int len = wrap(NULL, message, count, width, &line);
5488 char *msg = malloc(len);
5492 outCount = fwrite(message, 1, count, stdout);
5495 dbgchk = wrap(msg, message, count, width, &line);
5496 if (dbgchk != len && appData.debugMode)
5497 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
5498 outCount = fwrite(msg, 1, dbgchk, stdout);
5504 outCount = write(cp->fdTo, message, count);
5514 /* Output message to process, with "ms" milliseconds of delay
5515 between each character. This is needed when sending the logon
5516 script to ICC, which for some reason doesn't like the
5517 instantaneous send. */
5518 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
5525 ChildProc *cp = (ChildProc *) pr;
5530 r = write(cp->fdTo, message++, 1);
5543 /**** Animation code by Hugh Fisher, DCS, ANU.
5545 Known problem: if a window overlapping the board is
5546 moved away while a piece is being animated underneath,
5547 the newly exposed area won't be updated properly.
5548 I can live with this.
5550 Known problem: if you look carefully at the animation
5551 of pieces in mono mode, they are being drawn as solid
5552 shapes without interior detail while moving. Fixing
5553 this would be a major complication for minimal return.
5556 /* Masks for XPM pieces. Black and white pieces can have
5557 different shapes, but in the interest of retaining my
5558 sanity pieces must have the same outline on both light
5559 and dark squares, and all pieces must use the same
5560 background square colors/images. */
5562 static int xpmDone = 0;
5565 CreateAnimMasks (pieceDepth)
5572 unsigned long plane;
5575 /* just return for gtk at the moment */
5578 /* Need a bitmap just to get a GC with right depth */
5579 // buf = XCreatePixmap(xDisplay, xBoardWindow,
5581 values.foreground = 1;
5582 values.background = 0;
5583 /* Don't use XtGetGC, not read only */
5584 // maskGC = XCreateGC(xDisplay, buf,
5585 // GCForeground | GCBackground, &values);
5586 // XFreePixmap(xDisplay, buf);
5588 // buf = XCreatePixmap(xDisplay, xBoardWindow,
5589 // squareSize, squareSize, pieceDepth);
5590 // values.foreground = XBlackPixel(xDisplay, xScreen);
5591 // values.background = XWhitePixel(xDisplay, xScreen);
5592 // bufGC = XCreateGC(xDisplay, buf,
5593 // GCForeground | GCBackground, &values);
5595 for (piece = WhitePawn; piece <= BlackKing; piece++) {
5596 /* Begin with empty mask */
5597 // if(!xpmDone) // [HGM] pieces: keep using existing
5598 // xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
5599 // squareSize, squareSize, 1);
5600 // XSetFunction(xDisplay, maskGC, GXclear);
5601 // XFillRectangle(xDisplay, xpmMask[piece], maskGC,
5602 // 0, 0, squareSize, squareSize);
5604 /* Take a copy of the piece */
5609 // XSetFunction(xDisplay, bufGC, GXcopy);
5610 // XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
5612 // 0, 0, squareSize, squareSize, 0, 0);
5614 /* XOR the background (light) over the piece */
5615 // XSetFunction(xDisplay, bufGC, GXxor);
5617 // XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
5618 // 0, 0, squareSize, squareSize, 0, 0);
5620 // XSetForeground(xDisplay, bufGC, lightSquareColor);
5621 // XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
5624 /* We now have an inverted piece image with the background
5625 erased. Construct mask by just selecting all the non-zero
5626 pixels - no need to reconstruct the original image. */
5627 // XSetFunction(xDisplay, maskGC, GXor);
5629 /* Might be quicker to download an XImage and create bitmap
5630 data from it rather than this N copies per piece, but it
5631 only takes a fraction of a second and there is a much
5632 longer delay for loading the pieces. */
5633 // for (n = 0; n < pieceDepth; n ++) {
5634 // XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
5635 // 0, 0, squareSize, squareSize,
5637 // plane = plane << 1;
5641 // XFreePixmap(xDisplay, buf);
5642 // XFreeGC(xDisplay, bufGC);
5643 // XFreeGC(xDisplay, maskGC);
5647 InitAnimState (anim, info)
5649 XWindowAttributes * info;
5654 /* Each buffer is square size, same depth as window */
5655 // anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
5656 // squareSize, squareSize, info->depth);
5657 // anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
5658 // squareSize, squareSize, info->depth);
5660 // /* Create a plain GC for blitting */
5661 // mask = GCForeground | GCBackground | GCFunction |
5662 // GCPlaneMask | GCGraphicsExposures;
5663 // values.foreground = XBlackPixel(xDisplay, xScreen);
5664 // values.background = XWhitePixel(xDisplay, xScreen);
5665 // values.function = GXcopy;
5666 // values.plane_mask = AllPlanes;
5667 // values.graphics_exposures = False;
5668 // anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
5670 // /* Piece will be copied from an existing context at
5671 // the start of each new animation/drag. */
5672 // anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
5674 // /* Outline will be a read-only copy of an existing */
5675 // anim->outlineGC = None;
5681 static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
5682 XWindowAttributes info;
5684 /* for gtk at the moment just ... */
5687 if (xpmDone && gameInfo.variant == old) return;
5688 if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet
5689 // XGetWindowAttributes(xDisplay, xBoardWindow, &info);
5691 // InitAnimState(&game, &info);
5692 // InitAnimState(&player, &info);
5694 /* For XPM pieces, we need bitmaps to use as masks. */
5696 // CreateAnimMasks(info.depth);
5702 static Boolean frameWaiting;
5704 static RETSIGTYPE FrameAlarm (sig)
5707 frameWaiting = False;
5708 /* In case System-V style signals. Needed?? */
5709 signal(SIGALRM, FrameAlarm);
5716 struct itimerval delay;
5718 XSync(xDisplay, False);
5721 frameWaiting = True;
5722 signal(SIGALRM, FrameAlarm);
5723 delay.it_interval.tv_sec =
5724 delay.it_value.tv_sec = time / 1000;
5725 delay.it_interval.tv_usec =
5726 delay.it_value.tv_usec = (time % 1000) * 1000;
5727 setitimer(ITIMER_REAL, &delay, NULL);
5728 while (frameWaiting) pause();
5729 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
5730 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
5731 setitimer(ITIMER_REAL, &delay, NULL);
5741 // XSync(xDisplay, False);
5743 usleep(time * 1000);
5748 /* Convert board position to corner of screen rect and color */
5751 ScreenSquare(column, row, pt, color)
5752 int column; int row; XPoint * pt; int * color;
5755 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
5756 pt->y = lineGap + row * (squareSize + lineGap);
5758 pt->x = lineGap + column * (squareSize + lineGap);
5759 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
5761 *color = SquareColor(row, column);
5764 /* Convert window coords to square */
5767 BoardSquare(x, y, column, row)
5768 int x; int y; int * column; int * row;
5770 *column = EventToSquare(x, BOARD_WIDTH);
5771 if (flipView && *column >= 0)
5772 *column = BOARD_WIDTH - 1 - *column;
5773 *row = EventToSquare(y, BOARD_HEIGHT);
5774 if (!flipView && *row >= 0)
5775 *row = BOARD_HEIGHT - 1 - *row;
5780 #undef Max /* just in case */
5782 #define Max(a, b) ((a) > (b) ? (a) : (b))
5783 #define Min(a, b) ((a) < (b) ? (a) : (b))
5786 SetRect(rect, x, y, width, height)
5787 XRectangle * rect; int x; int y; int width; int height;
5791 rect->width = width;
5792 rect->height = height;
5795 /* Test if two frames overlap. If they do, return
5796 intersection rect within old and location of
5797 that rect within new. */
5800 Intersect(old, new, size, area, pt)
5801 XPoint * old; XPoint * new;
5802 int size; XRectangle * area; XPoint * pt;
5804 if (old->x > new->x + size || new->x > old->x + size ||
5805 old->y > new->y + size || new->y > old->y + size) {
5808 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
5809 size - abs(old->x - new->x), size - abs(old->y - new->y));
5810 pt->x = Max(old->x - new->x, 0);
5811 pt->y = Max(old->y - new->y, 0);
5816 /* For two overlapping frames, return the rect(s)
5817 in the old that do not intersect with the new. */
5820 CalcUpdateRects(old, new, size, update, nUpdates)
5821 XPoint * old; XPoint * new; int size;
5822 XRectangle update[]; int * nUpdates;
5826 /* If old = new (shouldn't happen) then nothing to draw */
5827 if (old->x == new->x && old->y == new->y) {
5831 /* Work out what bits overlap. Since we know the rects
5832 are the same size we don't need a full intersect calc. */
5834 /* Top or bottom edge? */
5835 if (new->y > old->y) {
5836 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
5838 } else if (old->y > new->y) {
5839 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
5840 size, old->y - new->y);
5843 /* Left or right edge - don't overlap any update calculated above. */
5844 if (new->x > old->x) {
5845 SetRect(&(update[count]), old->x, Max(new->y, old->y),
5846 new->x - old->x, size - abs(new->y - old->y));
5848 } else if (old->x > new->x) {
5849 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
5850 old->x - new->x, size - abs(new->y - old->y));
5857 /* Generate a series of frame coords from start->mid->finish.
5858 The movement rate doubles until the half way point is
5859 reached, then halves back down to the final destination,
5860 which gives a nice slow in/out effect. The algorithmn
5861 may seem to generate too many intermediates for short
5862 moves, but remember that the purpose is to attract the
5863 viewers attention to the piece about to be moved and
5864 then to where it ends up. Too few frames would be less
5868 Tween(start, mid, finish, factor, frames, nFrames)
5869 XPoint * start; XPoint * mid;
5870 XPoint * finish; int factor;
5871 XPoint frames[]; int * nFrames;
5873 int fraction, n, count;
5877 /* Slow in, stepping 1/16th, then 1/8th, ... */
5879 for (n = 0; n < factor; n++)
5881 for (n = 0; n < factor; n++) {
5882 frames[count].x = start->x + (mid->x - start->x) / fraction;
5883 frames[count].y = start->y + (mid->y - start->y) / fraction;
5885 fraction = fraction / 2;
5889 frames[count] = *mid;
5892 /* Slow out, stepping 1/2, then 1/4, ... */
5894 for (n = 0; n < factor; n++) {
5895 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
5896 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
5898 fraction = fraction * 2;
5903 /* Draw a piece on the screen without disturbing what's there */
5906 SelectGCMask(piece, clip, outline, mask)
5907 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
5911 /* Bitmap for piece being moved. */
5912 if (appData.monoMode) {
5913 *mask = *pieceToSolid(piece);
5914 } else if (useImages) {
5916 *mask = xpmMask[piece];
5918 *mask = ximMaskPm[piece];
5921 *mask = *pieceToSolid(piece);
5924 /* GC for piece being moved. Square color doesn't matter, but
5925 since it gets modified we make a copy of the original. */
5927 if (appData.monoMode)
5932 if (appData.monoMode)
5937 // XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
5939 /* Outline only used in mono mode and is not modified */
5941 *outline = bwPieceGC;
5943 *outline = wbPieceGC;
5947 OverlayPiece(piece, clip, outline, dest)
5948 ChessSquare piece; GC clip; GC outline; Drawable dest;
5953 /* Draw solid rectangle which will be clipped to shape of piece */
5954 // XFillRectangle(xDisplay, dest, clip,
5955 // 0, 0, squareSize, squareSize)
5957 if (appData.monoMode)
5958 /* Also draw outline in contrasting color for black
5959 on black / white on white cases */
5960 // XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
5961 // 0, 0, squareSize, squareSize, 0, 0, 1)
5964 /* Copy the piece */
5969 // XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
5971 // 0, 0, squareSize, squareSize,
5976 /* Animate the movement of a single piece */
5979 BeginAnimation(anim, piece, startColor, start)
5987 /* The old buffer is initialised with the start square (empty) */
5988 BlankSquare(0, 0, startColor, EmptySquare, anim->saveBuf);
5989 anim->prevFrame = *start;
5991 /* The piece will be drawn using its own bitmap as a matte */
5992 // SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
5993 // XSetClipMask(xDisplay, anim->pieceGC, mask);
5997 AnimationFrame(anim, frame, piece)
6002 XRectangle updates[4];
6007 /* Save what we are about to draw into the new buffer */
6008 // XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
6009 // frame->x, frame->y, squareSize, squareSize,
6012 /* Erase bits of the previous frame */
6013 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
6014 /* Where the new frame overlapped the previous,
6015 the contents in newBuf are wrong. */
6016 // XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
6017 // overlap.x, overlap.y,
6018 // overlap.width, overlap.height,
6020 /* Repaint the areas in the old that don't overlap new */
6021 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
6022 for (i = 0; i < count; i++)
6023 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6024 // updates[i].x - anim->prevFrame.x,
6025 // updates[i].y - anim->prevFrame.y,
6026 // updates[i].width, updates[i].height,
6027 // updates[i].x, updates[i].y)
6030 /* Easy when no overlap */
6031 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6032 // 0, 0, squareSize, squareSize,
6033 // anim->prevFrame.x, anim->prevFrame.y);
6036 /* Save this frame for next time round */
6037 // XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
6038 // 0, 0, squareSize, squareSize,
6040 anim->prevFrame = *frame;
6042 /* Draw piece over original screen contents, not current,
6043 and copy entire rect. Wipes out overlapping piece images. */
6044 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
6045 // XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
6046 // 0, 0, squareSize, squareSize,
6047 // frame->x, frame->y);
6051 EndAnimation (anim, finish)
6055 XRectangle updates[4];
6060 /* The main code will redraw the final square, so we
6061 only need to erase the bits that don't overlap. */
6062 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
6063 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
6064 for (i = 0; i < count; i++)
6065 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6066 // updates[i].x - anim->prevFrame.x,
6067 // updates[i].y - anim->prevFrame.y,
6068 // updates[i].width, updates[i].height,
6069 // updates[i].x, updates[i].y)
6072 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6073 // 0, 0, squareSize, squareSize,
6074 // anim->prevFrame.x, anim->prevFrame.y);
6079 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
6081 ChessSquare piece; int startColor;
6082 XPoint * start; XPoint * finish;
6083 XPoint frames[]; int nFrames;
6087 BeginAnimation(anim, piece, startColor, start);
6088 for (n = 0; n < nFrames; n++) {
6089 AnimationFrame(anim, &(frames[n]), piece);
6090 FrameDelay(appData.animSpeed);
6092 EndAnimation(anim, finish);
6095 /* Main control logic for deciding what to animate and how */
6098 AnimateMove(board, fromX, fromY, toX, toY)
6107 XPoint start, finish, mid;
6108 XPoint frames[kFactor * 2 + 1];
6109 int nFrames, startColor, endColor;
6111 /* Are we animating? */
6112 if (!appData.animate || appData.blindfold)
6115 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
6116 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
6117 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
6119 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
6120 piece = board[fromY][fromX];
6121 if (piece >= EmptySquare) return;
6126 hop = (piece == WhiteKnight || piece == BlackKnight);
6129 if (appData.debugMode) {
6130 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
6131 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
6132 piece, fromX, fromY, toX, toY); }
6134 ScreenSquare(fromX, fromY, &start, &startColor);
6135 ScreenSquare(toX, toY, &finish, &endColor);
6138 /* Knight: make diagonal movement then straight */
6139 if (abs(toY - fromY) < abs(toX - fromX)) {
6140 mid.x = start.x + (finish.x - start.x) / 2;
6144 mid.y = start.y + (finish.y - start.y) / 2;
6147 mid.x = start.x + (finish.x - start.x) / 2;
6148 mid.y = start.y + (finish.y - start.y) / 2;
6151 /* Don't use as many frames for very short moves */
6152 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
6153 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
6155 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
6156 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
6158 /* Be sure end square is redrawn */
6159 damage[toY][toX] = True;
6163 DragPieceBegin(x, y)
6166 int boardX, boardY, color;
6169 /* Are we animating? */
6170 if (!appData.animateDragging || appData.blindfold)
6173 /* Figure out which square we start in and the
6174 mouse position relative to top left corner. */
6175 BoardSquare(x, y, &boardX, &boardY);
6176 player.startBoardX = boardX;
6177 player.startBoardY = boardY;
6178 ScreenSquare(boardX, boardY, &corner, &color);
6179 player.startSquare = corner;
6180 player.startColor = color;
6181 /* As soon as we start dragging, the piece will jump slightly to
6182 be centered over the mouse pointer. */
6183 player.mouseDelta.x = squareSize/2;
6184 player.mouseDelta.y = squareSize/2;
6185 /* Initialise animation */
6186 player.dragPiece = PieceForSquare(boardX, boardY);
6188 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
6189 player.dragActive = True;
6190 BeginAnimation(&player, player.dragPiece, color, &corner);
6191 /* Mark this square as needing to be redrawn. Note that
6192 we don't remove the piece though, since logically (ie
6193 as seen by opponent) the move hasn't been made yet. */
6194 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
6195 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
6196 // XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
6197 // corner.x, corner.y, squareSize, squareSize,
6198 // 0, 0); // [HGM] zh: unstack in stead of grab
6199 damage[boardY][boardX] = True;
6201 player.dragActive = False;
6211 /* Are we animating? */
6212 if (!appData.animateDragging || appData.blindfold)
6216 if (! player.dragActive)
6218 /* Move piece, maintaining same relative position
6219 of mouse within square */
6220 corner.x = x - player.mouseDelta.x;
6221 corner.y = y - player.mouseDelta.y;
6222 AnimationFrame(&player, &corner, player.dragPiece);
6224 if (appData.highlightDragging) {
6226 BoardSquare(x, y, &boardX, &boardY);
6227 SetHighlights(fromX, fromY, boardX, boardY);
6236 int boardX, boardY, color;
6239 /* Are we animating? */
6240 if (!appData.animateDragging || appData.blindfold)
6244 if (! player.dragActive)
6246 /* Last frame in sequence is square piece is
6247 placed on, which may not match mouse exactly. */
6248 BoardSquare(x, y, &boardX, &boardY);
6249 ScreenSquare(boardX, boardY, &corner, &color);
6250 EndAnimation(&player, &corner);
6252 /* Be sure end square is redrawn */
6253 damage[boardY][boardX] = True;
6255 /* This prevents weird things happening with fast successive
6256 clicks which on my Sun at least can cause motion events
6257 without corresponding press/release. */
6258 player.dragActive = False;
6261 /* Handle expose event while piece being dragged */
6266 if (!player.dragActive || appData.blindfold)
6269 /* What we're doing: logically, the move hasn't been made yet,
6270 so the piece is still in it's original square. But visually
6271 it's being dragged around the board. So we erase the square
6272 that the piece is on and draw it at the last known drag point. */
6273 BlankSquare(player.startSquare.x, player.startSquare.y,
6274 player.startColor, EmptySquare, xBoardWindow);
6275 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
6276 damage[player.startBoardY][player.startBoardX] = TRUE;
6279 #include <sys/ioctl.h>
6280 int get_term_width()
6282 int fd, default_width;
6285 default_width = 79; // this is FICS default anyway...
6287 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
6289 if (!ioctl(fd, TIOCGSIZE, &win))
6290 default_width = win.ts_cols;
6291 #elif defined(TIOCGWINSZ)
6293 if (!ioctl(fd, TIOCGWINSZ, &win))
6294 default_width = win.ws_col;
6296 return default_width;
6299 void update_ics_width()
6301 static int old_width = 0;
6302 int new_width = get_term_width();
6304 if (old_width != new_width)
6305 ics_printf("set width %d\n", new_width);
6306 old_width = new_width;
6309 void NotifyFrontendLogin()