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 ShufflePopDown P(());
326 void EnginePopDown P(());
327 void UciPopDown P(());
328 void TimeControlPopDown P(());
329 void NewVariantPopDown P(());
330 void SettingsPopDown P(());
331 void SetMenuEnables P((Enables *enab));
332 void update_ics_width P(());
333 int get_term_width P(());
334 int CopyMemoProc P(());
336 * XBoard depends on Xt R4 or higher
338 int xtVersion = XtSpecificationRelease;
343 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
344 jailSquareColor, highlightSquareColor, premoveHighlightColor;
345 Pixel lowTimeWarningColor;
347 #define LINE_TYPE_NORMAL 0
348 #define LINE_TYPE_HIGHLIGHT 1
349 #define LINE_TYPE_PRE 2
352 GC lightSquareGC, darkSquareGC, jailSquareGC, wdPieceGC, wlPieceGC,
353 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC,
354 wjPieceGC, bjPieceGC;
355 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
356 Widget layoutWidget, formWidget, boardWidget, messageWidget,
357 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
358 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
359 menuBarWidget, editShell, errorShell, analysisShell,
360 ICSInputShell, fileNameShell, askQuestionShell;
362 Widget evalGraphShell, gameListShell;
363 //XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
364 //XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
366 Font clockFontID, coordFontID, countFontID;
367 XFontStruct *clockFontStruct, *coordFontStruct, *countFontStruct;
368 XtAppContext appContext;
370 char *oldICSInteractionTitle;
374 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
376 Position commentX = -1, commentY = -1;
377 Dimension commentW, commentH;
378 typedef unsigned int BoardSize;
380 Boolean chessProgram;
382 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
383 int squareSize, smallLayout = 0, tinyLayout = 0,
384 marginW, marginH, // [HGM] for run-time resizing
385 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
386 ICSInputBoxUp = False, askQuestionUp = False,
387 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
388 editUp = False, errorUp = False, errorExitStatus = -1, lineGap;
389 Pixel timerForegroundPixel, timerBackgroundPixel;
390 Pixel buttonForegroundPixel, buttonBackgroundPixel;
391 char *chessDir, *programName, *programVersion,
392 *gameCopyFilename, *gamePasteFilename;
393 Boolean alwaysOnTop = False;
394 Boolean saveSettingsOnExit;
395 char *settingsFileName;
396 char *icsTextMenuString;
398 char *firstChessProgramNames;
399 char *secondChessProgramNames;
401 WindowPlacement wpMain;
402 WindowPlacement wpConsole;
403 WindowPlacement wpComment;
404 WindowPlacement wpMoveHistory;
405 WindowPlacement wpEvalGraph;
406 WindowPlacement wpEngineOutput;
407 WindowPlacement wpGameList;
408 WindowPlacement wpTags;
412 Pixmap pieceBitmap[2][(int)BlackPawn];
413 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
414 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
415 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
416 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
417 int useImages=0, useImageSqs;
418 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
419 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
420 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
421 XImage *ximLightSquare, *ximDarkSquare;
424 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
425 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
427 #define White(piece) ((int)(piece) < (int)BlackPawn)
429 /* Variables for doing smooth animation. This whole thing
430 would be much easier if the board was double-buffered,
431 but that would require a fairly major rewrite. */
436 GC blitGC, pieceGC, outlineGC;
437 XPoint startSquare, prevFrame, mouseDelta;
441 int startBoardX, startBoardY;
444 /* There can be two pieces being animated at once: a player
445 can begin dragging a piece before the remote opponent has moved. */
447 static AnimState game, player;
449 /* Bitmaps for use as masks when drawing XPM pieces.
450 Need one for each black and white piece. */
451 static Pixmap xpmMask[BlackKing + 1];
453 /* This magic number is the number of intermediate frames used
454 in each half of the animation. For short moves it's reduced
455 by 1. The total number of frames will be factor * 2 + 1. */
458 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
460 Enables icsEnables[] = {
461 { "menuFile.Mail Move", False },
462 { "menuFile.Reload CMail Message", False },
463 { "menuMode.Machine Black", False },
464 { "menuMode.Machine White", False },
465 { "menuMode.Analysis Mode", False },
466 { "menuMode.Analyze File", False },
467 { "menuMode.Two Machines", False },
469 { "menuHelp.Hint", False },
470 { "menuHelp.Book", False },
471 { "menuStep.Move Now", False },
472 { "menuOptions.Periodic Updates", False },
473 { "menuOptions.Hide Thinking", False },
474 { "menuOptions.Ponder Next Move", False },
479 Enables ncpEnables[] = {
480 { "menuFile.Mail Move", False },
481 { "menuFile.Reload CMail Message", False },
482 { "menuMode.Machine White", False },
483 { "menuMode.Machine Black", False },
484 { "menuMode.Analysis Mode", False },
485 { "menuMode.Analyze File", False },
486 { "menuMode.Two Machines", False },
487 { "menuMode.ICS Client", False },
488 { "menuMode.ICS Input Box", False },
490 { "menuStep.Revert", False },
491 { "menuStep.Move Now", False },
492 { "menuStep.Retract Move", False },
493 { "menuOptions.Auto Comment", False },
494 { "menuOptions.Auto Flag", False },
495 { "menuOptions.Auto Flip View", False },
496 { "menuOptions.Auto Observe", False },
497 { "menuOptions.Auto Raise Board", False },
498 { "menuOptions.Get Move List", False },
499 { "menuOptions.ICS Alarm", False },
500 { "menuOptions.Move Sound", False },
501 { "menuOptions.Quiet Play", False },
502 { "menuOptions.Hide Thinking", False },
503 { "menuOptions.Periodic Updates", False },
504 { "menuOptions.Ponder Next Move", False },
505 { "menuHelp.Hint", False },
506 { "menuHelp.Book", False },
510 Enables gnuEnables[] = {
511 { "menuMode.ICS Client", False },
512 { "menuMode.ICS Input Box", False },
513 { "menuAction.Accept", False },
514 { "menuAction.Decline", False },
515 { "menuAction.Rematch", False },
516 { "menuAction.Adjourn", False },
517 { "menuAction.Stop Examining", False },
518 { "menuAction.Stop Observing", False },
519 { "menuStep.Revert", False },
520 { "menuOptions.Auto Comment", False },
521 { "menuOptions.Auto Observe", False },
522 { "menuOptions.Auto Raise Board", False },
523 { "menuOptions.Get Move List", False },
524 { "menuOptions.Premove", False },
525 { "menuOptions.Quiet Play", False },
527 /* The next two options rely on SetCmailMode being called *after* */
528 /* SetGNUMode so that when GNU is being used to give hints these */
529 /* menu options are still available */
531 { "menuFile.Mail Move", False },
532 { "menuFile.Reload CMail Message", False },
536 Enables cmailEnables[] = {
538 { "menuAction.Call Flag", False },
539 { "menuAction.Draw", True },
540 { "menuAction.Adjourn", False },
541 { "menuAction.Abort", False },
542 { "menuAction.Stop Observing", False },
543 { "menuAction.Stop Examining", False },
544 { "menuFile.Mail Move", True },
545 { "menuFile.Reload CMail Message", True },
549 Enables trainingOnEnables[] = {
550 { "menuMode.Edit Comment", False },
551 { "menuMode.Pause", False },
552 { "menuStep.Forward", False },
553 { "menuStep.Backward", False },
554 { "menuStep.Forward to End", False },
555 { "menuStep.Back to Start", False },
556 { "menuStep.Move Now", False },
557 { "menuStep.Truncate Game", False },
561 Enables trainingOffEnables[] = {
562 { "menuMode.Edit Comment", True },
563 { "menuMode.Pause", True },
564 { "menuStep.Forward", True },
565 { "menuStep.Backward", True },
566 { "menuStep.Forward to End", True },
567 { "menuStep.Back to Start", True },
568 { "menuStep.Move Now", True },
569 { "menuStep.Truncate Game", True },
573 Enables machineThinkingEnables[] = {
574 { "menuFile.Load Game", False },
575 { "menuFile.Load Next Game", False },
576 { "menuFile.Load Previous Game", False },
577 { "menuFile.Reload Same Game", False },
578 { "menuFile.Paste Game", False },
579 { "menuFile.Load Position", False },
580 { "menuFile.Load Next Position", False },
581 { "menuFile.Load Previous Position", False },
582 { "menuFile.Reload Same Position", False },
583 { "menuFile.Paste Position", False },
584 { "menuMode.Machine White", False },
585 { "menuMode.Machine Black", False },
586 { "menuMode.Two Machines", False },
587 { "menuStep.Retract Move", False },
591 Enables userThinkingEnables[] = {
592 { "menuFile.Load Game", True },
593 { "menuFile.Load Next Game", True },
594 { "menuFile.Load Previous Game", True },
595 { "menuFile.Reload Same Game", True },
596 { "menuFile.Paste Game", True },
597 { "menuFile.Load Position", True },
598 { "menuFile.Load Next Position", True },
599 { "menuFile.Load Previous Position", True },
600 { "menuFile.Reload Same Position", True },
601 { "menuFile.Paste Position", True },
602 { "menuMode.Machine White", True },
603 { "menuMode.Machine Black", True },
604 { "menuMode.Two Machines", True },
605 { "menuStep.Retract Move", True },
611 MenuItem fileMenu[] = {
612 {N_("New Shuffle Game ..."), ShuffleMenuProc},
613 {N_("New Variant ..."), NewVariantProc}, // [HGM] variant: not functional yet
614 // {"----", NothingProc},
615 // {N_("Save Game"), SaveGameProc},
616 // {"----", NothingProc},
617 {N_("Copy Game"), CopyGameProc},
618 {N_("Paste Game"), PasteGameProc},
619 // {"----", NothingProc},
620 // {N_("Load Position"), LoadPositionProc},
621 // {N_("Load Next Position"), LoadNextPositionProc},
622 // {N_("Load Previous Position"), LoadPrevPositionProc},
623 // {N_("Reload Same Position"), ReloadPositionProc},
624 // {N_("Save Position"), SavePositionProc},
625 // {"----", NothingProc},
626 {N_("Copy Position"), CopyPositionProc},
627 {N_("Paste Position"), PastePositionProc},
628 // {"----", NothingProc},
629 {N_("Mail Move"), MailMoveProc},
630 {N_("Reload CMail Message"), ReloadCmailMsgProc},
631 // {"----", NothingProc},
635 MenuItem modeMenu[] = {
636 // {N_("Machine White"), MachineWhiteProc},
637 // {N_("Machine Black"), MachineBlackProc},
638 // {N_("Two Machines"), TwoMachinesProc},
639 // {N_("Analysis Mode"), AnalyzeModeProc},
640 // {N_("Analyze File"), AnalyzeFileProc },
641 // {N_("ICS Client"), IcsClientProc},
642 // {N_("Edit Game"), EditGameProc},
643 // {N_("Edit Position"), EditPositionProc},
644 // {N_("Training"), TrainingProc},
645 // {"----", NothingProc},
646 {N_("Show Engine Output"), EngineOutputProc},
647 {N_("Show Evaluation Graph"), EvalGraphProc},
648 {N_("Show Game List"), ShowGameListProc},
649 // {"Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
650 // {"----", NothingProc},
651 // {N_("Edit Tags"), EditTagsProc},
652 {N_("Edit Comment"), EditCommentProc},
653 {N_("ICS Input Box"), IcsInputBoxProc},
657 MenuItem optionsMenu[] = {
658 // {N_("Flip View"), FlipViewProc},
659 // {"----", NothingProc},
660 {N_("Adjudications ..."), EngineMenuProc},
661 {N_("General Settings ..."), UciMenuProc},
662 {N_("Engine #1 Settings ..."), FirstSettingsProc},
663 {N_("Engine #2 Settings ..."), SecondSettingsProc},
664 {N_("Time Control ..."), TimeControlProc},
665 {"----", NothingProc},
666 // {N_("Always Queen"), AlwaysQueenProc},
667 // {N_("Animate Dragging"), AnimateDraggingProc},
668 // {N_("Animate Moving"), AnimateMovingProc},
669 // {N_("Auto Comment"), AutocommProc},
670 // {N_("Auto Flag"), AutoflagProc},
671 // {N_("Auto Flip View"), AutoflipProc},
672 // {N_("Auto Observe"), AutobsProc},
673 // {N_("Auto Raise Board"), AutoraiseProc},
674 // {N_("Auto Save"), AutosaveProc},
675 // {N_("Blindfold"), BlindfoldProc},
676 // {N_("Flash Moves"), FlashMovesProc},
677 // {N_("Get Move List"), GetMoveListProc},
679 // {N_("Highlight Dragging"), HighlightDraggingProc},
681 // {N_("Highlight Last Move"), HighlightLastMoveProc},
682 // {N_("Move Sound"), MoveSoundProc},
683 // {N_("ICS Alarm"), IcsAlarmProc},
684 // {N_("Old Save Style"), OldSaveStyleProc},
685 // {N_("Periodic Updates"), PeriodicUpdatesProc},
686 // {N_("Ponder Next Move"), PonderNextMoveProc},
687 // {N_("Popup Exit Message"), PopupExitMessageProc},
688 // {N_("Popup Move Errors"), PopupMoveErrorsProc},
689 // {N_("Premove"), PremoveProc},
690 // {N_("Quiet Play"), QuietPlayProc},
691 // {N_("Hide Thinking"), HideThinkingProc},
692 // {N_("Test Legality"), TestLegalityProc},
693 // {N_("Show Coords"), ShowCoordsProc},
694 {"----", NothingProc},
695 {N_("Save Settings Now"), SaveSettingsProc},
696 {N_("Save Settings on Exit"), SaveOnExitProc},
701 {N_("File"), fileMenu},
702 {N_("Mode"), modeMenu},
703 {N_("Options"), optionsMenu},
707 #define PIECE_MENU_SIZE 18
708 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
709 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
710 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
711 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
712 N_("Empty square"), N_("Clear board") },
713 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
714 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
715 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
716 N_("Empty square"), N_("Clear board") }
718 /* must be in same order as PieceMenuStrings! */
719 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
720 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
721 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
722 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
723 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
724 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
725 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
726 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
727 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
730 #define DROP_MENU_SIZE 6
731 String dropMenuStrings[DROP_MENU_SIZE] = {
732 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
734 /* must be in same order as PieceMenuStrings! */
735 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
736 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
737 WhiteRook, WhiteQueen
745 DropMenuEnables dmEnables[] = {
754 { XtNborderWidth, 0 },
755 { XtNdefaultDistance, 0 },
759 { XtNborderWidth, 0 },
760 { XtNresizable, (XtArgVal) True },
764 { XtNborderWidth, 0 },
769 XtResource clientResources[] = {
770 { "flashCount", "flashCount", XtRInt, sizeof(int),
771 XtOffset(AppDataPtr, flashCount), XtRImmediate,
772 (XtPointer) FLASH_COUNT },
775 XtActionsRec boardActions[] = {
776 // { "HandleUserMove", HandleUserMove },
777 { "AnimateUserMove", AnimateUserMove },
778 // { "FileNameAction", FileNameAction },
779 { "HandlePV", HandlePV },
780 { "UnLoadPV", UnLoadPV },
781 { "AskQuestionProc", AskQuestionProc },
782 { "AskQuestionReplyAction", AskQuestionReplyAction },
783 { "PieceMenuPopup", PieceMenuPopup },
784 // { "WhiteClock", WhiteClock },
785 // { "BlackClock", BlackClock },
786 { "Iconify", Iconify },
787 { "LoadSelectedProc", LoadSelectedProc },
788 // { "LoadPositionProc", LoadPositionProc },
789 // { "LoadNextPositionProc", LoadNextPositionProc },
790 // { "LoadPrevPositionProc", LoadPrevPositionProc },
791 // { "ReloadPositionProc", ReloadPositionProc },
792 { "CopyPositionProc", CopyPositionProc },
793 { "PastePositionProc", PastePositionProc },
794 { "CopyGameProc", CopyGameProc },
795 { "PasteGameProc", PasteGameProc },
796 // { "SaveGameProc", SaveGameProc },
797 // { "SavePositionProc", SavePositionProc },
798 { "MailMoveProc", MailMoveProc },
799 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
800 // { "MachineWhiteProc", MachineWhiteProc },
801 // { "MachineBlackProc", MachineBlackProc },
802 // { "AnalysisModeProc", AnalyzeModeProc },
803 // { "AnalyzeFileProc", AnalyzeFileProc },
804 // { "TwoMachinesProc", TwoMachinesProc },
805 // { "IcsClientProc", IcsClientProc },
806 // { "EditGameProc", EditGameProc },
807 // { "EditPositionProc", EditPositionProc },
808 // { "TrainingProc", EditPositionProc },
809 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
810 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
811 { "ShowGameListProc", ShowGameListProc },
812 // { "ShowMoveListProc", HistoryShowProc},
813 // { "EditTagsProc", EditCommentProc },
814 { "EditCommentProc", EditCommentProc },
815 // { "IcsAlarmProc", IcsAlarmProc },
816 { "IcsInputBoxProc", IcsInputBoxProc },
817 // { "AcceptProc", AcceptProc },
818 // { "DeclineProc", DeclineProc },
819 // { "RematchProc", RematchProc },
820 // { "CallFlagProc", CallFlagProc },
821 // { "DrawProc", DrawProc },
822 // { "AdjournProc", AdjournProc },
823 // { "AbortProc", AbortProc },
824 // { "ResignProc", ResignProc },
825 // { "AdjuWhiteProc", AdjuWhiteProc },
826 // { "AdjuBlackProc", AdjuBlackProc },
827 // { "AdjuDrawProc", AdjuDrawProc },
828 { "EnterKeyProc", EnterKeyProc },
829 // { "StopObservingProc", StopObservingProc },
830 // { "StopExaminingProc", StopExaminingProc },
831 // { "BackwardProc", BackwardProc },
832 // { "ForwardProc", ForwardProc },
833 // { "ToStartProc", ToStartProc },
834 // { "ToEndProc", ToEndProc },
835 // { "RevertProc", RevertProc },
836 // { "TruncateGameProc", TruncateGameProc },
837 // { "MoveNowProc", MoveNowProc },
838 // { "RetractMoveProc", RetractMoveProc },
839 // { "AlwaysQueenProc", AlwaysQueenProc },
840 // { "AnimateDraggingProc", AnimateDraggingProc },
841 // { "AnimateMovingProc", AnimateMovingProc },
842 // { "AutoflagProc", AutoflagProc },
843 // { "AutoflipProc", AutoflipProc },
844 // { "AutobsProc", AutobsProc },
845 // { "AutoraiseProc", AutoraiseProc },
846 // { "AutosaveProc", AutosaveProc },
847 // { "BlindfoldProc", BlindfoldProc },
848 // { "FlashMovesProc", FlashMovesProc },
849 // { "FlipViewProc", FlipViewProc },
850 // { "GetMoveListProc", GetMoveListProc },
852 // { "HighlightDraggingProc", HighlightDraggingProc },
854 // { "HighlightLastMoveProc", HighlightLastMoveProc },
855 // { "IcsAlarmProc", IcsAlarmProc },
856 // { "MoveSoundProc", MoveSoundProc },
857 // { "OldSaveStyleProc", OldSaveStyleProc },
858 // { "PeriodicUpdatesProc", PeriodicUpdatesProc },
859 // { "PonderNextMoveProc", PonderNextMoveProc },
860 // { "PopupExitMessageProc", PopupExitMessageProc },
861 // { "PopupMoveErrorsProc", PopupMoveErrorsProc },
862 // { "PremoveProc", PremoveProc },
863 // { "QuietPlayProc", QuietPlayProc },
864 // { "ShowThinkingProc", ShowThinkingProc },
865 // { "HideThinkingProc", HideThinkingProc },
866 // { "TestLegalityProc", TestLegalityProc },
867 { "SaveSettingsProc", SaveSettingsProc },
868 { "SaveOnExitProc", SaveOnExitProc },
869 // { "InfoProc", InfoProc },
870 // { "ManProc", ManProc },
871 // { "HintProc", HintProc },
872 // { "BookProc", BookProc },
873 { "AboutGameProc", AboutGameProc },
874 { "DebugProc", DebugProc },
875 { "NothingProc", NothingProc },
876 { "CommentPopDown", (XtActionProc) CommentPopDown },
877 { "EditCommentPopDown", (XtActionProc) EditCommentPopDown },
878 { "TagsPopDown", (XtActionProc) TagsPopDown },
879 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
880 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
881 // { "FileNamePopDown", (XtActionProc) FileNamePopDown },
882 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
883 { "GameListPopDown", (XtActionProc) GameListPopDown },
884 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
885 // { "HistoryPopDown", (XtActionProc) HistoryPopDown },
886 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
887 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
888 { "ShufflePopDown", (XtActionProc) ShufflePopDown },
889 { "EnginePopDown", (XtActionProc) EnginePopDown },
890 { "UciPopDown", (XtActionProc) UciPopDown },
891 { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
892 { "NewVariantPopDown", (XtActionProc) NewVariantPopDown },
893 { "SettingsPopDown", (XtActionProc) SettingsPopDown },
894 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
897 //char globalTranslations[] =
898 // ":<Key>R: ResignProc() \n \
899 // :<Key>r: ResetProc() \n \
900 // :<Key>g: LoadGameProc() \n \
901 // :<Key>N: LoadNextGameProc() \n \
902 // :<Key>P: LoadPrevGameProc() \n \
903 // :<Key>Q: QuitProc() \n \
904 // :<Key>F: ToEndProc() \n \
905 // :<Key>f: ForwardProc() \n \
906 // :<Key>B: ToStartProc() \n \
907 // :<Key>b: BackwardProc() \n \
908 // :<Key>p: PauseProc() \n \
909 // :<Key>d: DrawProc() \n \
910 // :<Key>t: CallFlagProc() \n \
911 // :<Key>i: Iconify() \n \
912 // :<Key>c: Iconify() \n \
913 // :<Key>v: FlipViewProc() \n \
914 // <KeyDown>Control_L: BackwardProc() \n \
915 // <KeyUp>Control_L: ForwardProc() \n \
916 // <KeyDown>Control_R: BackwardProc() \n \
917 // <KeyUp>Control_R: ForwardProc() \n \
918 // Shift<Key>1: AskQuestionProc(\"Direct command\",\
919 // \"Send to chess program:\",,1) \n \
920 // Shift<Key>2: AskQuestionProc(\"Direct command\",\
921 // \"Send to second chess program:\",,2) \n";
923 //char boardTranslations[] =
924 // "<Btn1Down>: HandleUserMove() \n \
925 // <Btn1Up>: HandleUserMove() \n \
926 // <Btn1Motion>: AnimateUserMove() \n \
927 // <Btn3Motion>: HandlePV() \n \
928 // <Btn3Up>: UnLoadPV() \n \
929 // Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
930 // PieceMenuPopup(menuB) \n \
931 // Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
932 // PieceMenuPopup(menuW) \n \
933 // Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
934 // PieceMenuPopup(menuW) \n \
935 // Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
936 // PieceMenuPopup(menuB) \n";
938 //char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
939 //char blackTranslations[] = "<BtnDown>: BlackClock()\n";
941 char ICSInputTranslations[] =
942 "<Key>Return: EnterKeyProc() \n";
944 String xboardResources[] = {
945 // "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
946 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
947 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
951 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
952 "magenta", "cyan", "white" };
956 TextColors textColors[(int)NColorClasses];
958 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
960 parse_color(str, which)
964 char *p, buf[100], *d;
967 if (strlen(str) > 99) /* watch bounds on buf */
972 for (i=0; i<which; ++i) {
979 /* Could be looking at something like:
981 .. in which case we want to stop on a comma also */
982 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
986 return -1; /* Use default for empty field */
989 if (which == 2 || isdigit(*p))
992 while (*p && isalpha(*p))
997 for (i=0; i<8; ++i) {
998 if (!StrCaseCmp(buf, cnames[i]))
999 return which? (i+40) : (i+30);
1001 if (!StrCaseCmp(buf, "default")) return -1;
1003 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1008 parse_cpair(cc, str)
1012 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1013 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1018 /* bg and attr are optional */
1019 textColors[(int)cc].bg = parse_color(str, 1);
1020 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1021 textColors[(int)cc].attr = 0;
1030 /* this should raise the board to the top */
1031 gtk_window_present(GTK_WINDOW(GUI_Window));
1035 //---------------------------------------------------------------------------------------------------------
1036 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1039 #define CW_USEDEFAULT (1<<31)
1040 #define ICS_TEXT_MENU_SIZE 90
1041 #define DEBUG_FILE "xboard.debug"
1042 #define SetCurrentDirectory chdir
1043 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1047 // these two must some day move to frontend.h, when they are implemented
1048 Boolean GameListIsUp();
1050 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1053 // front-end part of option handling
1055 // [HGM] This platform-dependent table provides the location for storing the color info
1056 extern char *crWhite, * crBlack;
1060 &appData.whitePieceColor,
1061 &appData.blackPieceColor,
1062 &appData.lightSquareColor,
1063 &appData.darkSquareColor,
1064 &appData.highlightSquareColor,
1065 &appData.premoveHighlightColor,
1066 &appData.lowTimeWarningColor,
1078 ParseFont(char *name, int number)
1079 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1081 case 0: // CLOCK_FONT
1082 appData.clockFont = strdup(name);
1084 case 1: // MESSAGE_FONT
1085 appData.font = strdup(name);
1087 case 2: // COORD_FONT
1088 appData.coordFont = strdup(name);
1097 { // only 2 fonts currently
1098 appData.clockFont = CLOCK_FONT_NAME;
1099 appData.coordFont = COORD_FONT_NAME;
1100 appData.font = DEFAULT_FONT_NAME;
1105 { // no-op, until we identify the code for this already in XBoard and move it here
1109 ParseColor(int n, char *name)
1110 { // in XBoard, just copy the color-name string
1111 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1115 ParseTextAttribs(ColorClass cc, char *s)
1117 (&appData.colorShout)[cc] = strdup(s);
1121 ParseBoardSize(void *addr, char *name)
1123 appData.boardSize = strdup(name);
1128 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1132 SetCommPortDefaults()
1133 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1136 // [HGM] args: these three cases taken out to stay in front-end
1138 SaveFontArg(FILE *f, ArgDescriptor *ad)
1141 switch((int)ad->argLoc) {
1142 case 0: // CLOCK_FONT
1143 name = appData.clockFont;
1145 case 1: // MESSAGE_FONT
1146 name = appData.font;
1148 case 2: // COORD_FONT
1149 name = appData.coordFont;
1154 // Do not save fonts for now, as the saved font would be board-size specific
1155 // and not suitable for a re-start at another board size
1156 // fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, name);
1161 { // nothing to do, as the sounds are at all times represented by their text-string names already
1165 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1166 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1167 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)ad->argLoc]);
1171 SaveColor(FILE *f, ArgDescriptor *ad)
1172 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1173 if(colorVariable[(int)ad->argLoc])
1174 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)ad->argLoc]);
1178 SaveBoardSize(FILE *f, char *name, void *addr)
1179 { // wrapper to shield back-end from BoardSize & sizeInfo
1180 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1184 ParseCommPortSettings(char *s)
1185 { // no such option in XBoard (yet)
1188 extern Widget engineOutputShell;
1189 extern Widget tagsShell, editTagsShell;
1192 GetActualPlacement(Widget wg, WindowPlacement *wp)
1202 XtSetArg(args[i], XtNx, &x); i++;
1203 XtSetArg(args[i], XtNy, &y); i++;
1204 XtSetArg(args[i], XtNwidth, &w); i++;
1205 XtSetArg(args[i], XtNheight, &h); i++;
1206 XtGetValues(wg, args, i);
1215 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1216 // In XBoard this will have to wait until awareness of window parameters is implemented
1218 // GetActualPlacement(shellWidget, &wpMain);
1219 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1220 // if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1221 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1222 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1223 if(commentShell) GetActualPlacement(commentShell, &wpComment);
1224 else GetActualPlacement(editShell, &wpComment);
1225 if(tagsShell) GetActualPlacement(tagsShell, &wpTags);
1226 else GetActualPlacement(editTagsShell, &wpTags);
1230 PrintCommPortSettings(FILE *f, char *name)
1231 { // This option does not exist in XBoard
1235 MySearchPath(char *installDir, char *name, char *fullname)
1236 { // just append installDir and name. Perhaps ExpandPath should be used here?
1237 name = ExpandPathName(name);
1238 if(name && name[0] == '/') strcpy(fullname, name); else {
1239 sprintf(fullname, "%s%c%s", installDir, '/', name);
1245 MyGetFullPathName(char *name, char *fullname)
1246 { // should use ExpandPath?
1247 name = ExpandPathName(name);
1248 strcpy(fullname, name);
1253 EnsureOnScreen(int *x, int *y, int minX, int minY)
1260 { // [HGM] args: allows testing if main window is realized from back-end
1261 return xBoardWindow != 0;
1265 PopUpStartupDialog()
1266 { // start menu not implemented in XBoard
1269 ConvertToLine(int argc, char **argv)
1271 static char line[128*1024], buf[1024];
1275 for(i=1; i<argc; i++) {
1276 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1277 && argv[i][0] != '{' )
1278 sprintf(buf, "{%s} ", argv[i]);
1279 else sprintf(buf, "%s ", argv[i]);
1282 line[strlen(line)-1] = NULLCHAR;
1286 //--------------------------------------------------------------------------------------------
1289 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1292 #define BoardSize int
1293 void InitDrawingSizes(BoardSize boardSize, int flags)
1294 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1295 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1297 XtGeometryResult gres;
1300 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1301 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1303 timerWidth = (boardWidth - sep) / 2;
1305 if (appData.titleInWindow)
1310 w = boardWidth - 2*bor;
1314 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1318 if(!formWidget) return;
1321 * Inhibit shell resizing.
1324 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1327 for(i=0; i<4; i++) {
1329 for(p=0; p<=(int)WhiteKing; p++)
1330 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1331 if(gameInfo.variant == VariantShogi) {
1332 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1333 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1334 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1335 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1336 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1339 if(gameInfo.variant == VariantGothic) {
1340 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1344 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1345 for(p=0; p<=(int)WhiteKing; p++)
1346 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1347 if(gameInfo.variant == VariantShogi) {
1348 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1349 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1350 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1351 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1352 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1355 if(gameInfo.variant == VariantGothic) {
1356 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1362 for(i=0; i<2; i++) {
1364 for(p=0; p<=(int)WhiteKing; p++)
1365 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1366 if(gameInfo.variant == VariantShogi) {
1367 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1368 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1369 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1370 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1371 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1374 if(gameInfo.variant == VariantGothic) {
1375 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1386 void EscapeExpand(char *p, char *q)
1387 { // [HGM] initstring: routine to shape up string arguments
1388 while(*p++ = *q++) if(p[-1] == '\\')
1390 case 'n': p[-1] = '\n'; break;
1391 case 'r': p[-1] = '\r'; break;
1392 case 't': p[-1] = '\t'; break;
1393 case '\\': p[-1] = '\\'; break;
1394 case 0: *p = 0; return;
1395 default: p[-1] = q[-1]; break;
1404 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1405 XSetWindowAttributes window_attributes;
1407 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1408 XrmValue vFrom, vTo;
1409 XtGeometryResult gres;
1412 int forceMono = False;
1414 srandom(time(0)); // [HGM] book: make random truly random
1416 setbuf(stdout, NULL);
1417 setbuf(stderr, NULL);
1420 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1421 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1425 programName = strrchr(argv[0], '/');
1426 if (programName == NULL)
1427 programName = argv[0];
1432 XtSetLanguageProc(NULL, NULL, NULL);
1433 bindtextdomain(PACKAGE, LOCALEDIR);
1434 textdomain(PACKAGE);
1438 gtk_init (&argc, &argv);
1440 /* parse glade file to build widgets */
1442 builder = gtk_builder_new ();
1443 gtk_builder_add_from_file (builder, "gtk-interface.xml", NULL);
1445 /* test if everything worked ok */
1447 GUI_Window = GTK_WIDGET (gtk_builder_get_object (builder, "MainWindow"));
1448 if(!GUI_Window) printf("Error: gtk_builder didn't work!\n");
1450 GUI_Aspect = GTK_WIDGET (gtk_builder_get_object (builder, "Aspectframe"));
1451 if(!GUI_Aspect) printf("Error: gtk_builder didn't work!\n");
1453 GUI_History = GTK_WIDGET (gtk_builder_get_object (builder, "MoveHistory"));
1454 if(!GUI_History) printf("Error: gtk_builder didn't work!\n");
1456 GUI_Menubar = GTK_WIDGET (gtk_builder_get_object (builder, "MenuBar"));
1457 if(!GUI_Menubar) printf("Error: gtk_builder didn't work!\n");
1458 GUI_Timer = GTK_WIDGET (gtk_builder_get_object (builder, "Timer"));
1459 if(!GUI_Timer) printf("Error: gtk_builder didn't work!\n");
1460 GUI_Buttonbar = GTK_WIDGET (gtk_builder_get_object (builder, "ButtonBar"));
1461 if(!GUI_Buttonbar) printf("Error: gtk_builder didn't work!\n");
1462 GUI_Board = GTK_WIDGET (gtk_builder_get_object (builder, "Board"));
1463 if(!GUI_Board) printf("Error: gtk_builder didn't work!\n");
1465 GUI_Whiteclock = GTK_WIDGET (gtk_builder_get_object (builder, "WhiteClock"));
1466 if(!GUI_Whiteclock) printf("Error: gtk_builder didn't work!\n");
1468 GUI_Blackclock = GTK_WIDGET (gtk_builder_get_object (builder, "BlackClock"));
1469 if(!GUI_Blackclock) printf("Error: gtk_builder didn't work!\n");
1471 LIST_MoveHistory = GTK_LIST_STORE (gtk_builder_get_object (builder, "MoveHistoryStore"));
1472 if(!LIST_MoveHistory) printf("Error: gtk_builder didn't work!\n");
1474 /* EditTags window */
1475 GUI_EditTags = GTK_WIDGET (gtk_builder_get_object (builder, "EditTags"));
1476 if(!GUI_EditTags) printf("Error: gtk_builder didn't work!\n");
1478 GUI_EditTagsTextArea = GTK_WIDGET (gtk_builder_get_object (builder, "EditTagsTextArea"));
1479 if(!GUI_EditTagsTextArea) printf("Error: gtk_builder didn't work!\n");
1482 gtk_builder_connect_signals (builder, NULL);
1484 // don't unref the builder, since we use it to get references to widgets
1485 // g_object_unref (G_OBJECT (builder));
1487 /* end parse glade file */
1489 appData.boardSize = "";
1490 InitAppData(ConvertToLine(argc, argv));
1493 if (p == NULL) p = "/tmp";
1494 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1495 gameCopyFilename = (char*) malloc(i);
1496 gamePasteFilename = (char*) malloc(i);
1497 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1498 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1500 // XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1501 // clientResources, XtNumber(clientResources),
1504 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1505 static char buf[MSG_SIZ];
1506 EscapeExpand(buf, appData.initString);
1507 appData.initString = strdup(buf);
1508 EscapeExpand(buf, appData.secondInitString);
1509 appData.secondInitString = strdup(buf);
1510 EscapeExpand(buf, appData.firstComputerString);
1511 appData.firstComputerString = strdup(buf);
1512 EscapeExpand(buf, appData.secondComputerString);
1513 appData.secondComputerString = strdup(buf);
1516 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1519 if (chdir(chessDir) != 0) {
1520 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1526 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1527 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1528 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1529 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1532 setbuf(debugFP, NULL);
1537 /* This feature does not work; animation needs a rewrite */
1538 appData.highlightDragging = FALSE;
1542 gameInfo.variant = StringToVariant(appData.variant);
1543 InitPosition(FALSE);
1548 clockFontPxlSize = 20;
1549 coordFontPxlSize = 20;
1555 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1556 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1559 * Determine what fonts to use.
1561 // appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1562 // clockFontID = XLoadFont(xDisplay, appData.clockFont);
1563 // clockFontStruct = XQueryFont(xDisplay, clockFontID);
1564 // appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1565 // coordFontID = XLoadFont(xDisplay, appData.coordFont);
1566 // coordFontStruct = XQueryFont(xDisplay, coordFontID);
1567 // appData.font = FindFont(appData.font, fontPxlSize);
1568 // countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
1569 // countFontStruct = XQueryFont(xDisplay, countFontID);
1570 // appData.font = FindFont(appData.font, fontPxlSize);
1572 // xdb = XtDatabase(xDisplay);
1573 // XrmPutStringResource(&xdb, "*font", appData.font);
1576 * Detect if there are not enough colors available and adapt.
1578 // if (DefaultDepth(xDisplay, xScreen) <= 2) {
1579 // appData.monoMode = True;
1582 if (!appData.monoMode) {
1583 vFrom.addr = (caddr_t) appData.lightSquareColor;
1584 vFrom.size = strlen(appData.lightSquareColor);
1585 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1586 if (vTo.addr == NULL) {
1587 appData.monoMode = True;
1590 lightSquareColor = *(Pixel *) vTo.addr;
1593 if (!appData.monoMode) {
1594 vFrom.addr = (caddr_t) appData.darkSquareColor;
1595 vFrom.size = strlen(appData.darkSquareColor);
1596 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1597 if (vTo.addr == NULL) {
1598 appData.monoMode = True;
1601 darkSquareColor = *(Pixel *) vTo.addr;
1604 if (!appData.monoMode) {
1605 vFrom.addr = (caddr_t) appData.whitePieceColor;
1606 vFrom.size = strlen(appData.whitePieceColor);
1607 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1608 if (vTo.addr == NULL) {
1609 appData.monoMode = True;
1612 whitePieceColor = *(Pixel *) vTo.addr;
1615 if (!appData.monoMode) {
1616 vFrom.addr = (caddr_t) appData.blackPieceColor;
1617 vFrom.size = strlen(appData.blackPieceColor);
1618 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1619 if (vTo.addr == NULL) {
1620 appData.monoMode = True;
1623 blackPieceColor = *(Pixel *) vTo.addr;
1627 if (!appData.monoMode) {
1628 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1629 vFrom.size = strlen(appData.highlightSquareColor);
1630 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1631 if (vTo.addr == NULL) {
1632 appData.monoMode = True;
1635 highlightSquareColor = *(Pixel *) vTo.addr;
1639 if (!appData.monoMode) {
1640 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1641 vFrom.size = strlen(appData.premoveHighlightColor);
1642 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1643 if (vTo.addr == NULL) {
1644 appData.monoMode = True;
1647 premoveHighlightColor = *(Pixel *) vTo.addr;
1652 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1655 if (appData.bitmapDirectory == NULL ||
1656 appData.bitmapDirectory[0] == NULLCHAR)
1657 appData.bitmapDirectory = DEF_BITMAP_DIR;
1660 if (appData.lowTimeWarning && !appData.monoMode) {
1661 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1662 vFrom.size = strlen(appData.lowTimeWarningColor);
1663 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1664 if (vTo.addr == NULL)
1665 appData.monoMode = True;
1667 lowTimeWarningColor = *(Pixel *) vTo.addr;
1670 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1671 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1672 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1673 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1674 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1675 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1676 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1677 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1678 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1679 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1681 if (appData.colorize) {
1683 _("%s: can't parse color names; disabling colorization\n"),
1686 appData.colorize = FALSE;
1688 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
1689 textColors[ColorNone].attr = 0;
1691 // XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1697 layoutName = "tinyLayout";
1698 } else if (smallLayout) {
1699 layoutName = "smallLayout";
1701 layoutName = "normalLayout";
1704 if (appData.titleInWindow) {
1705 /* todo check what this appdata does */
1708 if (appData.showButtonBar) {
1709 /* TODO hide button bar if requested */
1713 if (appData.titleInWindow)
1718 if (appData.showButtonBar)
1725 if (appData.showButtonBar)
1735 /* set some checkboxes in the menu according to appData */
1737 if (appData.alwaysPromoteToQueen)
1738 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Always Queen")),TRUE);
1740 if (appData.animateDragging)
1741 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Animate Dragging")),TRUE);
1743 if (appData.animate)
1744 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Animate Moving")),TRUE);
1746 if (appData.autoComment)
1747 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Comment")),TRUE);
1749 if (appData.autoCallFlag)
1750 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Flag")),TRUE);
1752 if (appData.autoFlipView)
1753 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Flip View")),TRUE);
1755 if (appData.autoObserve)
1756 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Observe")),TRUE);
1758 if (appData.autoRaiseBoard)
1759 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Raise Board")),TRUE);
1761 if (appData.autoSaveGames)
1762 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Save")),TRUE);
1764 if (appData.saveGameFile[0] != NULLCHAR)
1766 /* Can't turn this off from menu */
1767 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Save")),TRUE);
1768 gtk_action_set_sensitive(GTK_ACTION (gtk_builder_get_object (builder, "menuOptions.Auto Save")),FALSE);
1771 if (appData.blindfold)
1772 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Blindfold")),TRUE);
1774 if (appData.flashCount > 0)
1775 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Flash Moves")),TRUE);
1777 if (appData.getMoveList)
1778 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Get Move List")),TRUE);
1781 if (appData.highlightDragging)
1782 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Highlight Dragging")),TRUE);
1785 if (appData.highlightLastMove)
1786 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Highlight Last Move")),TRUE);
1788 if (appData.icsAlarm)
1789 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.ICS Alarm")),TRUE);
1791 if (appData.ringBellAfterMoves)
1792 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Move Sound")),TRUE);
1794 if (appData.oldSaveStyle)
1795 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Old Save Style")),TRUE);
1797 if (appData.periodicUpdates)
1798 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Periodic Updates")),TRUE);
1800 if (appData.ponderNextMove)
1801 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Ponder Next Move")),TRUE);
1803 if (appData.popupExitMessage)
1804 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Popup Exit Message")),TRUE);
1806 if (appData.popupMoveErrors)
1807 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Popup Move Errors")),TRUE);
1809 if (appData.premove)
1810 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Premove")),TRUE);
1812 if (appData.quietPlay)
1813 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Quit Play")),TRUE);
1815 if (appData.showCoords)
1816 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Show Coords")),TRUE);
1818 if (appData.showThinking)
1819 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Hide Thinking")),TRUE);
1821 if (appData.testLegality)
1822 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Test Legality")),TRUE);
1825 // if (saveSettingsOnExit) {
1826 // XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
1831 /* end setting check boxes */
1833 /* load square colors */
1834 SVGLightSquare = load_pixbuf("svg/LightSquare.svg",squareSize);
1835 SVGDarkSquare = load_pixbuf("svg/DarkSquare.svg",squareSize);
1836 SVGNeutralSquare = load_pixbuf("svg/NeutralSquare.svg",squareSize);
1838 /* use two icons to indicate if it is white's or black's turn */
1839 WhiteIcon = load_pixbuf("svg/icon_white.svg",0);
1840 BlackIcon = load_pixbuf("svg/icon_black.svg",0);
1841 WindowIcon = WhiteIcon;
1842 gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
1845 /* realize window */
1846 gtk_widget_show (GUI_Window);
1848 /* recalc boardsize */
1853 if (appData.animate || appData.animateDragging)
1858 if (errorExitStatus == -1) {
1859 if (appData.icsActive) {
1860 /* We now wait until we see "login:" from the ICS before
1861 sending the logon script (problems with timestamp otherwise) */
1862 /*ICSInitScript();*/
1863 if (appData.icsInputBox) ICSInputBoxPopUp();
1867 signal(SIGWINCH, TermSizeSigHandler);
1869 signal(SIGINT, IntSigHandler);
1870 signal(SIGTERM, IntSigHandler);
1871 if (*appData.cmailGameName != NULLCHAR) {
1872 signal(SIGUSR1, CmailSigHandler);
1875 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1879 * Create a cursor for the board widget.
1880 * (This needs to be called after the window has been created to have access to board-window)
1883 BoardCursor = gdk_cursor_new(GDK_HAND2);
1884 gdk_window_set_cursor(GUI_Board->window, BoardCursor);
1885 gdk_cursor_destroy(BoardCursor);
1890 if (appData.debugMode) fclose(debugFP); // [DM] debug
1897 if (appData.icsActive && oldICSInteractionTitle != NULL) {
1898 DisplayIcsInteractionTitle(oldICSInteractionTitle);
1900 if (saveSettingsOnExit) SaveSettings(settingsFileName);
1901 unlink(gameCopyFilename);
1902 unlink(gamePasteFilename);
1905 RETSIGTYPE TermSizeSigHandler(int sig)
1918 CmailSigHandler(sig)
1924 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1926 /* Activate call-back function CmailSigHandlerCallBack() */
1927 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1929 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1933 CmailSigHandlerCallBack(isr, closure, message, count, error)
1941 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1943 /**** end signal code ****/
1953 f = fopen(appData.icsLogon, "r");
1959 strcat(buf, appData.icsLogon);
1960 f = fopen(buf, "r");
1964 ProcessICSInitScript(f);
1971 EditCommentPopDown();
1981 if (!menuBarWidget) return;
1982 w = XtNameToWidget(menuBarWidget, "menuStep.Revert");
1984 DisplayError("menuStep.Revert", 0);
1986 XtSetSensitive(w, !grey);
1991 SetMenuEnables(enab)
1996 if (!builder) return;
1997 while (enab->name != NULL) {
1998 o = gtk_builder_get_object(builder, enab->name);
1999 if(GTK_IS_WIDGET(o))
2000 gtk_widget_set_sensitive(GTK_WIDGET (o),enab->value);
2003 if(GTK_IS_ACTION(o))
2004 gtk_action_set_sensitive(GTK_ACTION (o),enab->value);
2006 DisplayError(enab->name, 0);
2014 SetMenuEnables(icsEnables);
2017 if (appData.zippyPlay && !appData.noChessProgram) /* [DM] icsEngineAnalyze */
2018 {}; // XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2025 SetMenuEnables(ncpEnables);
2031 SetMenuEnables(gnuEnables);
2037 SetMenuEnables(cmailEnables);
2043 SetMenuEnables(trainingOnEnables);
2044 if (appData.showButtonBar) {
2045 // XtSetSensitive(buttonBarWidget, False);
2051 SetTrainingModeOff()
2053 SetMenuEnables(trainingOffEnables);
2054 if (appData.showButtonBar) {
2055 // XtSetSensitive(buttonBarWidget, True);
2060 SetUserThinkingEnables()
2062 if (appData.noChessProgram) return;
2063 SetMenuEnables(userThinkingEnables);
2067 SetMachineThinkingEnables()
2069 if (appData.noChessProgram) return;
2070 SetMenuEnables(machineThinkingEnables);
2072 case MachinePlaysBlack:
2073 case MachinePlaysWhite:
2074 case TwoMachinesPlay:
2075 // XtSetSensitive(XtNameToWidget(menuBarWidget,
2076 // ModeToWidgetName(gameMode)), True);
2083 #define Abs(n) ((n)<0 ? -(n) : (n))
2086 * Find a font that matches "pattern" that is as close as
2087 * possible to the targetPxlSize. Prefer fonts that are k
2088 * pixels smaller to fonts that are k pixels larger. The
2089 * pattern must be in the X Consortium standard format,
2090 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2091 * The return value should be freed with XtFree when no
2094 char *FindFont(pattern, targetPxlSize)
2098 char **fonts, *p, *best, *scalable, *scalableTail;
2099 int i, j, nfonts, minerr, err, pxlSize;
2102 char **missing_list;
2104 char *def_string, *base_fnt_lst, strInt[3];
2106 XFontStruct **fnt_list;
2108 base_fnt_lst = calloc(1, strlen(pattern) + 3);
2109 sprintf(strInt, "%d", targetPxlSize);
2110 p = strstr(pattern, "--");
2111 strncpy(base_fnt_lst, pattern, p - pattern + 2);
2112 strcat(base_fnt_lst, strInt);
2113 strcat(base_fnt_lst, strchr(p + 2, '-'));
2115 if ((fntSet = XCreateFontSet(xDisplay,
2119 &def_string)) == NULL) {
2121 fprintf(stderr, _("Unable to create font set.\n"));
2125 nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
2127 // fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2128 // if (nfonts < 1) {
2129 // fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2130 // programName, pattern);
2138 for (i=0; i<nfonts; i++) {
2141 if (*p != '-') continue;
2143 if (*p == NULLCHAR) break;
2144 if (*p++ == '-') j++;
2146 if (j < 7) continue;
2149 scalable = fonts[i];
2152 err = pxlSize - targetPxlSize;
2153 if (Abs(err) < Abs(minerr) ||
2154 (minerr > 0 && err < 0 && -err == minerr)) {
2160 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2161 /* If the error is too big and there is a scalable font,
2162 use the scalable font. */
2163 int headlen = scalableTail - scalable;
2164 p = (char *) XtMalloc(strlen(scalable) + 10);
2165 while (isdigit(*scalableTail)) scalableTail++;
2166 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2168 p = (char *) XtMalloc(strlen(best) + 1);
2171 if (appData.debugMode) {
2172 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2173 pattern, targetPxlSize, p);
2176 if (missing_count > 0)
2177 XFreeStringList(missing_list);
2178 // XFreeFontSet(xDisplay, fntSet);
2180 XFreeFontNames(fonts);
2187 /* 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*/
2196 for(i=0;i<MAXPIECES;i++)
2200 g_free(SVGpieces[i]);
2207 SVGLightSquare = load_pixbuf("svg/LightSquare.svg",squareSize);
2208 SVGDarkSquare = load_pixbuf("svg/DarkSquare.svg",squareSize);
2209 SVGNeutralSquare = load_pixbuf("svg/NeutralSquare.svg",squareSize);
2212 /* get some defaults going */
2213 for(i=WhitePawn; i<DemotePiece+1; i++)
2214 SVGpieces[i] = load_pixbuf("svg/NeutralSquare.svg",squareSize);
2216 SVGpieces[WhitePawn] = load_pixbuf("svg/WhitePawn.svg",squareSize);
2217 SVGpieces[WhiteKnight] = load_pixbuf("svg/WhiteKnight.svg",squareSize);
2218 SVGpieces[WhiteBishop] = load_pixbuf("svg/WhiteBishop.svg",squareSize);
2219 SVGpieces[WhiteRook] = load_pixbuf("svg/WhiteRook.svg",squareSize);
2220 SVGpieces[WhiteQueen] = load_pixbuf("svg/WhiteQueen.svg",squareSize);
2221 SVGpieces[WhiteKing] = load_pixbuf("svg/WhiteKing.svg",squareSize);
2223 SVGpieces[BlackPawn] = load_pixbuf("svg/BlackPawn.svg",squareSize);
2224 SVGpieces[BlackKnight] = load_pixbuf("svg/BlackKnight.svg",squareSize);
2225 SVGpieces[BlackBishop] = load_pixbuf("svg/BlackBishop.svg",squareSize);
2226 SVGpieces[BlackRook] = load_pixbuf("svg/BlackRook.svg",squareSize);
2227 SVGpieces[BlackQueen] = load_pixbuf("svg/BlackQueen.svg",squareSize);
2228 SVGpieces[BlackKing] = load_pixbuf("svg/BlackKing.svg",squareSize);
2234 static void MenuBarSelect(w, addr, index)
2239 XtActionProc proc = (XtActionProc) addr;
2241 (proc)(NULL, NULL, NULL, NULL);
2244 void CreateMenuBarPopup(parent, name, mb)
2254 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2257 XtSetArg(args[j], XtNleftMargin, 20); j++;
2258 XtSetArg(args[j], XtNrightMargin, 20); j++;
2260 while (mi->string != NULL) {
2261 if (strcmp(mi->string, "----") == 0) {
2262 entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
2265 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
2266 entry = XtCreateManagedWidget(mi->string, smeBSBObjectClass,
2268 XtAddCallback(entry, XtNcallback,
2269 (XtCallbackProc) MenuBarSelect,
2270 (caddr_t) mi->proc);
2276 Widget CreateMenuBar(mb)
2280 Widget anchor, menuBar;
2282 char menuName[MSG_SIZ];
2285 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2286 XtSetArg(args[j], XtNvSpace, 0); j++;
2287 XtSetArg(args[j], XtNborderWidth, 0); j++;
2288 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
2289 formWidget, args, j);
2291 while (mb->name != NULL) {
2292 strcpy(menuName, "menu");
2293 strcat(menuName, mb->name);
2295 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
2298 shortName[0] = _(mb->name)[0];
2299 shortName[1] = NULLCHAR;
2300 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
2303 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
2306 XtSetArg(args[j], XtNborderWidth, 0); j++;
2307 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
2309 CreateMenuBarPopup(menuBar, menuName, mb);
2317 CreatePieceMenu(name, color)
2324 ChessSquare selection;
2326 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2327 boardWidget, args, 0);
2329 for (i = 0; i < PIECE_MENU_SIZE; i++) {
2330 String item = pieceMenuStrings[color][i];
2332 if (strcmp(item, "----") == 0) {
2333 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2336 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2337 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2339 selection = pieceMenuTranslation[color][i];
2340 XtAddCallback(entry, XtNcallback,
2341 (XtCallbackProc) PieceMenuSelect,
2342 (caddr_t) selection);
2343 if (selection == WhitePawn || selection == BlackPawn) {
2344 XtSetArg(args[0], XtNpopupOnEntry, entry);
2345 XtSetValues(menu, args, 1);
2358 ChessSquare selection;
2360 // whitePieceMenu = CreatePieceMenu("menuW", 0);
2361 // blackPieceMenu = CreatePieceMenu("menuB", 1);
2363 // XtRegisterGrabAction(PieceMenuPopup, True,
2364 // (unsigned)(ButtonPressMask|ButtonReleaseMask),
2365 // GrabModeAsync, GrabModeAsync);
2367 // XtSetArg(args[0], XtNlabel, _("Drop"));
2368 // dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
2369 // boardWidget, args, 1);
2370 // for (i = 0; i < DROP_MENU_SIZE; i++) {
2371 // String item = dropMenuStrings[i];
2373 // if (strcmp(item, "----") == 0) {
2374 // entry = XtCreateManagedWidget(item, smeLineObjectClass,
2375 // dropMenu, NULL, 0);
2377 // XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2378 // entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2379 // dropMenu, args, 1);
2380 // selection = dropMenuTranslation[i];
2381 // XtAddCallback(entry, XtNcallback,
2382 // (XtCallbackProc) DropMenuSelect,
2383 // (caddr_t) selection);
2388 void SetupDropMenu()
2396 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2397 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2398 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2399 dmEnables[i].piece);
2400 XtSetSensitive(entry, p != NULL || !appData.testLegality
2401 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2402 && !appData.icsActive));
2404 while (p && *p++ == dmEnables[i].piece) count++;
2405 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2407 XtSetArg(args[j], XtNlabel, label); j++;
2408 XtSetValues(entry, args, j);
2412 void PieceMenuPopup(w, event, params, num_params)
2416 Cardinal *num_params;
2420 if (event->type != ButtonRelease) UnLoadPV(); // [HGM] pv
2421 if (event->type != ButtonPress) return;
2422 if (errorUp) ErrorPopDown();
2426 whichMenu = params[0];
2429 if(!appData.icsEngineAnalyze) return;
2430 case IcsPlayingWhite:
2431 case IcsPlayingBlack:
2432 if(!appData.zippyPlay) goto noZip;
2435 case MachinePlaysWhite:
2436 case MachinePlaysBlack:
2437 case TwoMachinesPlay: // [HGM] pv: use for showing PV
2438 if (!appData.dropMenu) {
2439 LoadPV(event->xbutton.x, event->xbutton.y);
2442 if(gameMode == TwoMachinesPlay || gameMode == AnalyzeMode ||
2443 gameMode == AnalyzeFile || gameMode == IcsObserving) return;
2446 if (!appData.dropMenu || appData.testLegality &&
2447 gameInfo.variant != VariantBughouse &&
2448 gameInfo.variant != VariantCrazyhouse) return;
2450 whichMenu = "menuD";
2456 if (((pmFromX = EventToSquare(event->xbutton.x, BOARD_WIDTH)) < 0) ||
2457 ((pmFromY = EventToSquare(event->xbutton.y, BOARD_HEIGHT)) < 0)) {
2458 pmFromX = pmFromY = -1;
2462 pmFromX = BOARD_WIDTH - 1 - pmFromX;
2464 pmFromY = BOARD_HEIGHT - 1 - pmFromY;
2466 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
2469 static void PieceMenuSelect(w, piece, junk)
2474 if (pmFromX < 0 || pmFromY < 0) return;
2475 EditPositionMenuEvent(piece, pmFromX, pmFromY);
2478 static void DropMenuSelect(w, piece, junk)
2483 if (pmFromX < 0 || pmFromY < 0) return;
2484 DropMenuEvent(piece, pmFromX, pmFromY);
2488 * If the user selects on a border boundary, return -1; if off the board,
2489 * return -2. Otherwise map the event coordinate to the square.
2491 int EventToSquare(x, limit)
2499 if ((x % (squareSize + lineGap)) >= squareSize)
2501 x /= (squareSize + lineGap);
2507 static void do_flash_delay(msec)
2513 static void drawHighlight(file, rank, line_type)
2514 int file, rank, line_type;
2519 if (lineGap == 0 || appData.blindfold) return;
2523 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
2524 (squareSize + lineGap);
2525 y = lineGap/2 + rank * (squareSize + lineGap);
2529 x = lineGap/2 + file * (squareSize + lineGap);
2530 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
2531 (squareSize + lineGap);
2535 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2537 /* draw the highlight */
2538 cairo_move_to (cr, x, y);
2539 cairo_rel_line_to (cr, 0,squareSize+lineGap);
2540 cairo_rel_line_to (cr, squareSize+lineGap,0);
2541 cairo_rel_line_to (cr, 0,-squareSize-lineGap);
2542 cairo_close_path (cr);
2544 cairo_set_line_width (cr, lineGap);
2547 /* TODO: use appdata colors */
2548 case LINE_TYPE_HIGHLIGHT:
2549 cairo_set_source_rgba (cr, 1, 1, 0, 1.0);
2552 cairo_set_source_rgba (cr, 1, 0, 0, 1.0);
2554 case LINE_TYPE_NORMAL:
2556 cairo_set_source_rgba (cr, 0, 1, 0, 1.0);
2567 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
2568 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
2571 SetHighlights(fromX, fromY, toX, toY)
2572 int fromX, fromY, toX, toY;
2574 if (hi1X != fromX || hi1Y != fromY)
2576 if (hi1X >= 0 && hi1Y >= 0)
2578 drawHighlight(hi1X, hi1Y, LINE_TYPE_NORMAL);
2580 if (fromX >= 0 && fromY >= 0)
2582 drawHighlight(fromX, fromY, LINE_TYPE_HIGHLIGHT);
2585 if (hi2X != toX || hi2Y != toY)
2587 if (hi2X >= 0 && hi2Y >= 0)
2589 drawHighlight(hi2X, hi2Y, LINE_TYPE_NORMAL);
2591 if (toX >= 0 && toY >= 0)
2593 drawHighlight(toX, toY, LINE_TYPE_HIGHLIGHT);
2607 SetHighlights(-1, -1, -1, -1);
2612 SetPremoveHighlights(fromX, fromY, toX, toY)
2613 int fromX, fromY, toX, toY;
2615 if (pm1X != fromX || pm1Y != fromY)
2617 if (pm1X >= 0 && pm1Y >= 0)
2619 drawHighlight(pm1X, pm1Y, LINE_TYPE_NORMAL);
2621 if (fromX >= 0 && fromY >= 0)
2623 drawHighlight(fromX, fromY, LINE_TYPE_PRE);
2626 if (pm2X != toX || pm2Y != toY)
2628 if (pm2X >= 0 && pm2Y >= 0)
2630 drawHighlight(pm2X, pm2Y, LINE_TYPE_NORMAL);
2632 if (toX >= 0 && toY >= 0)
2634 drawHighlight(toX, toY, LINE_TYPE_PRE);
2647 ClearPremoveHighlights()
2649 SetPremoveHighlights(-1, -1, -1, -1);
2652 static void BlankSquare(x, y, color, piece, dest)
2665 pb = SVGLightSquare;
2667 case 2: /* neutral */
2669 pb = SVGNeutralSquare;
2672 gdk_draw_pixbuf(GDK_WINDOW(GUI_Board->window),NULL,pb,0,0,x,y,-1,-1, GDK_RGB_DITHER_NORMAL, 0, 0);
2676 static void DrawPiece(piece, square_color, x, y, dest)
2678 int square_color, x, y;
2681 /* redraw background, since piece might be transparent in some areas */
2682 BlankSquare(x,y,square_color,piece,dest);
2685 gdk_draw_pixbuf(GDK_WINDOW(GUI_Board->window),NULL,
2686 GDK_PIXBUF(SVGpieces[piece]),0,0,x,y,-1,-1,
2687 GDK_RGB_DITHER_NORMAL, 0, 0);
2691 /* [HR] determine square color depending on chess variant. */
2692 static int SquareColor(row, column)
2697 if (gameInfo.variant == VariantXiangqi) {
2698 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
2700 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
2702 } else if (row <= 4) {
2708 square_color = ((column + row) % 2) == 1;
2711 /* [hgm] holdings: next line makes all holdings squares light */
2712 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
2714 return square_color;
2717 void DrawSquare(row, column, piece, do_flash)
2718 int row, column, do_flash;
2721 int square_color, x, y;
2726 /* Calculate delay in milliseconds (2-delays per complete flash) */
2727 flash_delay = 500 / appData.flashRate;
2729 /* calculate x and y coordinates from row and column */
2732 x = lineGap + ((BOARD_WIDTH-1)-column) *
2733 (squareSize + lineGap);
2734 y = lineGap + row * (squareSize + lineGap);
2738 x = lineGap + column * (squareSize + lineGap);
2739 y = lineGap + ((BOARD_HEIGHT-1)-row) *
2740 (squareSize + lineGap);
2743 square_color = SquareColor(row, column);
2745 // [HGM] holdings: blank out area between board and holdings
2746 if ( column == BOARD_LEFT-1 || column == BOARD_RGHT
2747 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
2748 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) )
2750 BlankSquare(x, y, 2, EmptySquare, xBoardWindow);
2752 // [HGM] print piece counts next to holdings
2753 string[1] = NULLCHAR;
2756 cairo_text_extents_t extents;
2761 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2763 string[0] = '0' + piece;
2765 /* TODO this has to go into the font-selection */
2766 cairo_select_font_face (cr, "Sans",
2767 CAIRO_FONT_SLANT_NORMAL,
2768 CAIRO_FONT_WEIGHT_NORMAL);
2770 cairo_set_font_size (cr, 12.0);
2771 cairo_text_extents (cr, string, &extents);
2773 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) )
2775 xpos= x + squareSize - extents.width - 2;
2776 ypos= y + extents.y_bearing + 1;
2778 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1)
2781 ypos = y + extents.y_bearing + 1;
2784 /* TODO mono mode? */
2785 cairo_move_to (cr, xpos, ypos);
2786 cairo_text_path (cr, string);
2787 cairo_set_source_rgb (cr, 1.0, 1.0, 1);
2788 cairo_fill_preserve (cr);
2789 cairo_set_source_rgb (cr, 0, 0, 0);
2790 cairo_set_line_width (cr, 0.1);
2799 /* square on the board */
2800 if (piece == EmptySquare || appData.blindfold)
2802 BlankSquare(x, y, square_color, piece, xBoardWindow);
2806 if (do_flash && appData.flashCount > 0)
2808 for (i=0; i<appData.flashCount; ++i)
2811 DrawPiece(piece, square_color, x, y, xBoardWindow);
2812 do_flash_delay(flash_delay);
2814 BlankSquare(x, y, square_color, piece, xBoardWindow);
2815 do_flash_delay(flash_delay);
2818 DrawPiece(piece, square_color, x, y, xBoardWindow);
2822 /* show coordinates if necessary */
2823 if(appData.showCoords)
2825 cairo_text_extents_t extents;
2829 /* TODO this has to go into the font-selection */
2830 cairo_select_font_face (cr, "Sans",
2831 CAIRO_FONT_SLANT_NORMAL,
2832 CAIRO_FONT_WEIGHT_NORMAL);
2833 cairo_set_font_size (cr, 12.0);
2835 string[1] = NULLCHAR;
2838 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2840 if (row == (flipView ? BOARD_HEIGHT-1 : 0) &&
2841 column >= BOARD_LEFT && column < BOARD_RGHT)
2843 string[0] = 'a' + column - BOARD_LEFT;
2844 cairo_text_extents (cr, string, &extents);
2846 xpos = x + squareSize - extents.width - 2;
2847 ypos = y + squareSize - extents.height - extents.y_bearing - 1;
2849 if (appData.monoMode)
2856 cairo_move_to (cr, xpos, ypos);
2857 cairo_text_path (cr, string);
2858 cairo_set_source_rgb (cr, 0.0, 0.0, 0);
2859 cairo_fill_preserve (cr);
2860 cairo_set_source_rgb (cr, 0, 1.0, 0);
2861 cairo_set_line_width (cr, 0.1);
2864 if ( column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT))
2867 string[0] = ONE + row;
2868 cairo_text_extents (cr, string, &extents);
2871 ypos = y + extents.height + 1;
2873 if (appData.monoMode)
2880 cairo_move_to (cr, xpos, ypos);
2881 cairo_text_path (cr, string);
2882 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
2883 cairo_fill_preserve (cr);
2884 cairo_set_source_rgb (cr, 0, 0, 1.0);
2885 cairo_set_line_width (cr, 0.1);
2897 /* Returns 1 if there are "too many" differences between b1 and b2
2898 (i.e. more than 1 move was made) */
2899 static int too_many_diffs(b1, b2)
2905 for (i=0; i<BOARD_HEIGHT; ++i) {
2906 for (j=0; j<BOARD_WIDTH; ++j) {
2907 if (b1[i][j] != b2[i][j]) {
2908 if (++c > 4) /* Castling causes 4 diffs */
2917 /* Matrix describing castling maneuvers */
2918 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
2919 static int castling_matrix[4][5] = {
2920 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
2921 { 0, 7, 4, 5, 6 }, /* 0-0, white */
2922 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
2923 { 7, 7, 4, 5, 6 } /* 0-0, black */
2926 /* Checks whether castling occurred. If it did, *rrow and *rcol
2927 are set to the destination (row,col) of the rook that moved.
2929 Returns 1 if castling occurred, 0 if not.
2931 Note: Only handles a max of 1 castling move, so be sure
2932 to call too_many_diffs() first.
2934 static int check_castle_draw(newb, oldb, rrow, rcol)
2941 /* For each type of castling... */
2942 for (i=0; i<4; ++i) {
2943 r = castling_matrix[i];
2945 /* Check the 4 squares involved in the castling move */
2947 for (j=1; j<=4; ++j) {
2948 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
2955 /* All 4 changed, so it must be a castling move */
2964 static int damage[BOARD_RANKS][BOARD_FILES];
2967 * event handler for redrawing the board
2969 void DrawPosition( repaint, board)
2970 /*Boolean*/int repaint;
2974 static int lastFlipView = 0;
2975 static int lastBoardValid = 0;
2976 static Board lastBoard;
2979 if (board == NULL) {
2980 if (!lastBoardValid) return;
2983 if (!lastBoardValid || lastFlipView != flipView) {
2984 // XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
2985 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flip View"),
2990 * It would be simpler to clear the window with XClearWindow()
2991 * but this causes a very distracting flicker.
2994 if (!repaint && lastBoardValid && lastFlipView == flipView)
2996 /* If too much changes (begin observing new game, etc.), don't
2998 do_flash = too_many_diffs(board, lastBoard) ? 0 : 1;
3000 /* Special check for castling so we don't flash both the king
3001 and the rook (just flash the king). */
3004 if (check_castle_draw(board, lastBoard, &rrow, &rcol))
3006 /* Draw rook with NO flashing. King will be drawn flashing later */
3007 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
3008 lastBoard[rrow][rcol] = board[rrow][rcol];
3012 /* First pass -- Draw (newly) empty squares and repair damage.
3013 This prevents you from having a piece show up twice while it
3014 is flashing on its new square */
3015 for (i = 0; i < BOARD_HEIGHT; i++)
3016 for (j = 0; j < BOARD_WIDTH; j++)
3017 if ((board[i][j] != lastBoard[i][j] && board[i][j] == EmptySquare)
3020 DrawSquare(i, j, board[i][j], 0);
3021 damage[i][j] = False;
3024 /* Second pass -- Draw piece(s) in new position and flash them */
3025 for (i = 0; i < BOARD_HEIGHT; i++)
3026 for (j = 0; j < BOARD_WIDTH; j++)
3027 if (board[i][j] != lastBoard[i][j])
3029 DrawSquare(i, j, board[i][j], do_flash);
3041 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
3043 cairo_set_line_width (cr, lineGap);
3045 /* TODO: use appdata colors */
3046 cairo_set_source_rgba (cr, 0, 1, 0, 1.0);
3050 for (i = 0; i < BOARD_HEIGHT + 1; i++)
3053 x2 = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3054 y1 = y2 = lineGap / 2 + (i * (squareSize + lineGap));
3056 cairo_move_to (cr, x1, y1);
3057 cairo_rel_line_to (cr, x2,0);
3061 for (j = 0; j < BOARD_WIDTH + 1; j++)
3064 y2 = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3065 x1 = x2 = lineGap / 2 + (j * (squareSize + lineGap));
3067 cairo_move_to (cr, x1, y1);
3068 cairo_rel_line_to (cr, 0, y2);
3077 for (i = 0; i < BOARD_HEIGHT; i++)
3078 for (j = 0; j < BOARD_WIDTH; j++)
3080 DrawSquare(i, j, board[i][j], 0);
3081 damage[i][j] = False;
3085 CopyBoard(lastBoard, board);
3087 lastFlipView = flipView;
3089 /* Draw highlights */
3090 if (pm1X >= 0 && pm1Y >= 0)
3092 drawHighlight(pm1X, pm1Y, LINE_TYPE_PRE);
3094 if (pm2X >= 0 && pm2Y >= 0)
3096 drawHighlight(pm2X, pm2Y, LINE_TYPE_PRE);
3098 if (hi1X >= 0 && hi1Y >= 0)
3100 drawHighlight(hi1X, hi1Y, LINE_TYPE_HIGHLIGHT);
3102 if (hi2X >= 0 && hi2Y >= 0)
3104 drawHighlight(hi2X, hi2Y, LINE_TYPE_HIGHLIGHT);
3107 /* If piece being dragged around board, must redraw that too */
3113 void AnimateUserMove (Widget w, XEvent * event,
3114 String * params, Cardinal * nParams)
3116 DragPieceMove(event->xmotion.x, event->xmotion.y);
3119 void HandlePV (Widget w, XEvent * event,
3120 String * params, Cardinal * nParams)
3121 { // [HGM] pv: walk PV
3122 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3125 Widget CommentCreate(name, text, mutable, callback, lines)
3127 int /*Boolean*/ mutable;
3128 XtCallbackProc callback;
3132 Widget shell, layout, form, edit, b_ok, b_cancel, b_clear, b_close, b_edit;
3137 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3138 XtGetValues(boardWidget, args, j);
3141 XtSetArg(args[j], XtNresizable, True); j++;
3144 // XtCreatePopupShell(name, topLevelShellWidgetClass,
3145 // shellWidget, args, j);
3148 // XtCreatePopupShell(name, transientShellWidgetClass,
3149 // shellWidget, args, j);
3152 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
3153 layoutArgs, XtNumber(layoutArgs));
3155 XtCreateManagedWidget("form", formWidgetClass, layout,
3156 formArgs, XtNumber(formArgs));
3160 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
3161 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
3163 XtSetArg(args[j], XtNstring, text); j++;
3164 XtSetArg(args[j], XtNtop, XtChainTop); j++;
3165 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3166 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3167 XtSetArg(args[j], XtNright, XtChainRight); j++;
3168 XtSetArg(args[j], XtNresizable, True); j++;
3169 XtSetArg(args[j], XtNwidth, bw_width); j++; /*force wider than buttons*/
3170 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
3171 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
3172 XtSetArg(args[j], XtNautoFill, True); j++;
3173 XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
3175 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
3179 XtSetArg(args[j], XtNfromVert, edit); j++;
3180 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3181 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3182 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3183 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3185 XtCreateManagedWidget(_("ok"), commandWidgetClass, form, args, j);
3186 XtAddCallback(b_ok, XtNcallback, callback, (XtPointer) 0);
3189 XtSetArg(args[j], XtNfromVert, edit); j++;
3190 XtSetArg(args[j], XtNfromHoriz, b_ok); j++;
3191 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3192 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3193 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3194 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3196 XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
3197 XtAddCallback(b_cancel, XtNcallback, callback, (XtPointer) 0);
3200 XtSetArg(args[j], XtNfromVert, edit); j++;
3201 XtSetArg(args[j], XtNfromHoriz, b_cancel); j++;
3202 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3203 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3204 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3205 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3207 XtCreateManagedWidget(_("clear"), commandWidgetClass, form, args, j);
3208 XtAddCallback(b_clear, XtNcallback, callback, (XtPointer) 0);
3211 XtSetArg(args[j], XtNfromVert, edit); j++;
3212 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3213 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3214 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3215 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3217 XtCreateManagedWidget(_("close"), commandWidgetClass, form, args, j);
3218 XtAddCallback(b_close, XtNcallback, callback, (XtPointer) 0);
3221 XtSetArg(args[j], XtNfromVert, edit); j++;
3222 XtSetArg(args[j], XtNfromHoriz, b_close); j++;
3223 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3224 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3225 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3226 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3228 XtCreateManagedWidget(_("edit"), commandWidgetClass, form, args, j);
3229 XtAddCallback(b_edit, XtNcallback, callback, (XtPointer) 0);
3232 XtRealizeWidget(shell);
3234 if (commentX == -1) {
3237 Dimension pw_height;
3238 Dimension ew_height;
3241 XtSetArg(args[j], XtNheight, &ew_height); j++;
3242 XtGetValues(edit, args, j);
3245 XtSetArg(args[j], XtNheight, &pw_height); j++;
3246 XtGetValues(shell, args, j);
3247 commentH = pw_height + (lines - 1) * ew_height;
3248 commentW = bw_width - 16;
3250 // XSync(xDisplay, False);
3252 /* This code seems to tickle an X bug if it is executed too soon
3253 after xboard starts up. The coordinates get transformed as if
3254 the main window was positioned at (0, 0).
3256 // XtTranslateCoords(shellWidget,
3257 // (bw_width - commentW) / 2, 0 - commentH / 2,
3258 // &commentX, &commentY);
3260 // XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
3261 // RootWindowOfScreen(XtScreen(shellWidget)),
3262 // (bw_width - commentW) / 2, 0 - commentH / 2,
3263 // &xx, &yy, &junk);
3267 if (commentY < 0) commentY = 0; /*avoid positioning top offscreen*/
3270 if(wpComment.width > 0) {
3271 commentX = wpComment.x;
3272 commentY = wpComment.y;
3273 commentW = wpComment.width;
3274 commentH = wpComment.height;
3278 XtSetArg(args[j], XtNheight, commentH); j++;
3279 XtSetArg(args[j], XtNwidth, commentW); j++;
3280 XtSetArg(args[j], XtNx, commentX); j++;
3281 XtSetArg(args[j], XtNy, commentY); j++;
3282 XtSetValues(shell, args, j);
3283 XtSetKeyboardFocus(shell, edit);
3288 /* Used for analysis window and ICS input window */
3289 Widget MiscCreate(name, text, mutable, callback, lines)
3291 int /*Boolean*/ mutable;
3292 XtCallbackProc callback;
3296 Widget shell, layout, form, edit;
3298 Dimension bw_width, pw_height, ew_height, w, h;
3304 XtSetArg(args[j], XtNresizable, True); j++;
3307 // XtCreatePopupShell(name, topLevelShellWidgetClass,
3308 // shellWidget, args, j);
3311 // XtCreatePopupShell(name, transientShellWidgetClass,
3312 // shellWidget, args, j);
3315 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
3316 layoutArgs, XtNumber(layoutArgs));
3318 XtCreateManagedWidget("form", formWidgetClass, layout,
3319 formArgs, XtNumber(formArgs));
3323 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
3324 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
3326 XtSetArg(args[j], XtNstring, text); j++;
3327 XtSetArg(args[j], XtNtop, XtChainTop); j++;
3328 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3329 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3330 XtSetArg(args[j], XtNright, XtChainRight); j++;
3331 XtSetArg(args[j], XtNresizable, True); j++;
3332 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
3333 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
3334 XtSetArg(args[j], XtNautoFill, True); j++;
3335 XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
3337 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
3339 XtRealizeWidget(shell);
3342 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3343 XtGetValues(boardWidget, args, j);
3346 XtSetArg(args[j], XtNheight, &ew_height); j++;
3347 XtGetValues(edit, args, j);
3350 XtSetArg(args[j], XtNheight, &pw_height); j++;
3351 XtGetValues(shell, args, j);
3352 h = pw_height + (lines - 1) * ew_height;
3355 // XSync(xDisplay, False);
3357 /* This code seems to tickle an X bug if it is executed too soon
3358 after xboard starts up. The coordinates get transformed as if
3359 the main window was positioned at (0, 0).
3361 // XtTranslateCoords(shellWidget, (bw_width - w) / 2, 0 - h / 2, &x, &y);
3363 // XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
3364 // RootWindowOfScreen(XtScreen(shellWidget)),
3365 // (bw_width - w) / 2, 0 - h / 2, &xx, &yy, &junk);
3369 if (y < 0) y = 0; /*avoid positioning top offscreen*/
3372 XtSetArg(args[j], XtNheight, h); j++;
3373 XtSetArg(args[j], XtNwidth, w); j++;
3374 XtSetArg(args[j], XtNx, x); j++;
3375 XtSetArg(args[j], XtNy, y); j++;
3376 XtSetValues(shell, args, j);
3382 static int savedIndex; /* gross that this is global */
3384 void EditCommentPopUp(index, title, text)
3393 if (text == NULL) text = "";
3395 if (editShell == NULL) {
3397 CommentCreate(title, text, True, EditCommentCallback, 4);
3398 XtRealizeWidget(editShell);
3399 // CatchDeleteWindow(editShell, "EditCommentPopDown");
3401 edit = XtNameToWidget(editShell, "*form.text");
3403 XtSetArg(args[j], XtNstring, text); j++;
3404 XtSetValues(edit, args, j);
3406 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
3407 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
3408 XtSetValues(editShell, args, j);
3411 XtPopup(editShell, XtGrabNone);
3415 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
3416 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
3420 void EditCommentCallback(w, client_data, call_data)
3422 XtPointer client_data, call_data;
3430 XtSetArg(args[j], XtNlabel, &name); j++;
3431 XtGetValues(w, args, j);
3433 if (strcmp(name, _("ok")) == 0) {
3434 edit = XtNameToWidget(editShell, "*form.text");
3436 XtSetArg(args[j], XtNstring, &val); j++;
3437 XtGetValues(edit, args, j);
3438 ReplaceComment(savedIndex, val);
3439 EditCommentPopDown();
3440 } else if (strcmp(name, _("cancel")) == 0) {
3441 EditCommentPopDown();
3442 } else if (strcmp(name, _("clear")) == 0) {
3443 edit = XtNameToWidget(editShell, "*form.text");
3444 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
3445 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
3449 void EditCommentPopDown()
3454 if (!editUp) return;
3456 XtSetArg(args[j], XtNx, &commentX); j++;
3457 XtSetArg(args[j], XtNy, &commentY); j++;
3458 XtSetArg(args[j], XtNheight, &commentH); j++;
3459 XtSetArg(args[j], XtNwidth, &commentW); j++;
3460 XtGetValues(editShell, args, j);
3461 XtPopdown(editShell);
3464 XtSetArg(args[j], XtNleftBitmap, None); j++;
3465 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
3469 void ICSInputBoxPopUp()
3474 char *title = _("ICS Input");
3477 if (ICSInputShell == NULL) {
3478 ICSInputShell = MiscCreate(title, "", True, NULL, 1);
3479 tr = XtParseTranslationTable(ICSInputTranslations);
3480 edit = XtNameToWidget(ICSInputShell, "*form.text");
3481 XtOverrideTranslations(edit, tr);
3482 XtRealizeWidget(ICSInputShell);
3483 // CatchDeleteWindow(ICSInputShell, "ICSInputBoxPopDown");
3486 edit = XtNameToWidget(ICSInputShell, "*form.text");
3488 XtSetArg(args[j], XtNstring, ""); j++;
3489 XtSetValues(edit, args, j);
3491 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
3492 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
3493 XtSetValues(ICSInputShell, args, j);
3496 XtPopup(ICSInputShell, XtGrabNone);
3497 XtSetKeyboardFocus(ICSInputShell, edit);
3499 ICSInputBoxUp = True;
3501 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
3502 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
3506 void ICSInputSendText()
3513 edit = XtNameToWidget(ICSInputShell, "*form.text");
3515 XtSetArg(args[j], XtNstring, &val); j++;
3516 XtGetValues(edit, args, j);
3517 SendMultiLineToICS(val);
3518 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
3519 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
3522 void ICSInputBoxPopDown()
3527 if (!ICSInputBoxUp) return;
3529 XtPopdown(ICSInputShell);
3530 ICSInputBoxUp = False;
3532 XtSetArg(args[j], XtNleftBitmap, None); j++;
3533 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
3537 void CommentPopUp(title, text)
3544 if (commentShell == NULL) {
3546 CommentCreate(title, text, False, CommentCallback, 4);
3547 XtRealizeWidget(commentShell);
3548 // CatchDeleteWindow(commentShell, "CommentPopDown");
3550 edit = XtNameToWidget(commentShell, "*form.text");
3552 XtSetArg(args[j], XtNstring, text); j++;
3553 XtSetValues(edit, args, j);
3555 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
3556 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
3557 XtSetValues(commentShell, args, j);
3560 XtPopup(commentShell, XtGrabNone);
3561 // XSync(xDisplay, False);
3566 void CommentCallback(w, client_data, call_data)
3568 XtPointer client_data, call_data;
3575 XtSetArg(args[j], XtNlabel, &name); j++;
3576 XtGetValues(w, args, j);
3578 if (strcmp(name, _("close")) == 0) {
3580 } else if (strcmp(name, _("edit")) == 0) {
3587 void CommentPopDown()
3592 if (!commentUp) return;
3594 XtSetArg(args[j], XtNx, &commentX); j++;
3595 XtSetArg(args[j], XtNy, &commentY); j++;
3596 XtSetArg(args[j], XtNwidth, &commentW); j++;
3597 XtSetArg(args[j], XtNheight, &commentH); j++;
3598 XtGetValues(commentShell, args, j);
3599 XtPopdown(commentShell);
3600 // XSync(xDisplay, False);
3604 void PromotionPopUp()
3607 Widget dialog, layout;
3609 Dimension bw_width, pw_width;
3613 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3614 XtGetValues(boardWidget, args, j);
3617 XtSetArg(args[j], XtNresizable, True); j++;
3618 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
3620 // XtCreatePopupShell("Promotion", transientShellWidgetClass,
3621 // shellWidget, args, j);
3623 // XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
3624 // layoutArgs, XtNumber(layoutArgs));
3627 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
3628 XtSetArg(args[j], XtNborderWidth, 0); j++;
3629 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
3632 if(gameInfo.variant != VariantShogi) {
3633 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
3634 (XtPointer) dialog);
3635 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
3636 (XtPointer) dialog);
3637 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
3638 (XtPointer) dialog);
3639 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
3640 (XtPointer) dialog);
3641 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
3642 gameInfo.variant == VariantGiveaway) {
3643 XawDialogAddButton(dialog, _("King"), PromotionCallback,
3644 (XtPointer) dialog);
3646 if(gameInfo.variant == VariantCapablanca ||
3647 gameInfo.variant == VariantGothic ||
3648 gameInfo.variant == VariantCapaRandom) {
3649 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
3650 (XtPointer) dialog);
3651 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
3652 (XtPointer) dialog);
3654 } else // [HGM] shogi
3656 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
3657 (XtPointer) dialog);
3658 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
3659 (XtPointer) dialog);
3661 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
3662 (XtPointer) dialog);
3664 XtRealizeWidget(promotionShell);
3665 // CatchDeleteWindow(promotionShell, "PromotionPopDown");
3668 XtSetArg(args[j], XtNwidth, &pw_width); j++;
3669 XtGetValues(promotionShell, args, j);
3671 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3672 lineGap + squareSize/3 +
3673 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
3674 0 : 6*(squareSize + lineGap)), &x, &y);
3677 XtSetArg(args[j], XtNx, x); j++;
3678 XtSetArg(args[j], XtNy, y); j++;
3679 XtSetValues(promotionShell, args, j);
3681 XtPopup(promotionShell, XtGrabNone);
3686 void PromotionPopDown()
3688 if (!promotionUp) return;
3689 XtPopdown(promotionShell);
3690 XtDestroyWidget(promotionShell);
3691 promotionUp = False;
3694 void PromotionCallback(w, client_data, call_data)
3696 XtPointer client_data, call_data;
3702 XtSetArg(args[0], XtNlabel, &name);
3703 XtGetValues(w, args, 1);
3707 if (fromX == -1) return;
3709 if (strcmp(name, _("cancel")) == 0) {
3713 } else if (strcmp(name, _("Knight")) == 0) {
3715 } else if (strcmp(name, _("Promote")) == 0) {
3717 } else if (strcmp(name, _("Defer")) == 0) {
3720 promoChar = ToLower(name[0]);
3723 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
3725 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
3726 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3731 void ErrorCallback(w, client_data, call_data)
3733 XtPointer client_data, call_data;
3736 XtPopdown(w = XtParent(XtParent(XtParent(w))));
3738 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3744 if (!errorUp) return;
3748 gtk_widget_destroy(GTK_WIDGET(GUI_Error));
3750 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3755 void ErrorPopUp(title, label, modal)
3756 char *title, *label;
3759 GUI_Error = gtk_message_dialog_new(GTK_WINDOW(GUI_Window),
3760 GTK_DIALOG_DESTROY_WITH_PARENT,
3765 gtk_window_set_title(GTK_WINDOW(GUI_Error),(gchar *) title);
3768 gtk_dialog_run(GTK_DIALOG(GUI_Error));
3769 gtk_widget_destroy(GTK_WIDGET(GUI_Error));
3773 g_signal_connect_swapped (GUI_Error, "response",
3774 G_CALLBACK (ErrorPopDownProc),
3777 gtk_widget_show(GTK_WIDGET(GUI_Error));
3783 /* Disable all user input other than deleting the window */
3784 static int frozen = 0;
3788 /* Grab by a widget that doesn't accept input */
3789 // XtAddGrab(messageWidget, TRUE, FALSE);
3793 /* Undo a FreezeUI */
3796 if (!frozen) return;
3797 // XtRemoveGrab(messageWidget);
3801 char *ModeToWidgetName(mode)
3805 case BeginningOfGame:
3806 if (appData.icsActive)
3807 return "menuMode.ICS Client";
3808 else if (appData.noChessProgram ||
3809 *appData.cmailGameName != NULLCHAR)
3810 return "menuMode.Edit Game";
3812 return "menuMode.Machine Black";
3813 case MachinePlaysBlack:
3814 return "menuMode.Machine Black";
3815 case MachinePlaysWhite:
3816 return "menuMode.Machine White";
3818 return "menuMode.Analysis Mode";
3820 return "menuMode.Analyze File";
3821 case TwoMachinesPlay:
3822 return "menuMode.Two Machines";
3824 return "menuMode.Edit Game";
3825 case PlayFromGameFile:
3826 return "menuFile.Load Game";
3828 return "menuMode.Edit Position";
3830 return "menuMode.Training";
3831 case IcsPlayingWhite:
3832 case IcsPlayingBlack:
3836 return "menuMode.ICS Client";
3843 void ModeHighlight()
3845 static int oldPausing = FALSE;
3846 static GameMode oldmode = (GameMode) -1;
3849 // todo this toggling of the pause button doesn't seem to work?
3850 // e.g. select pause from buttonbar doesn't activate menumode.pause
3852 // if (!boardWidget || !XtIsRealized(boardWidget)) return;
3854 if (pausing != oldPausing) {
3855 oldPausing = pausing;
3856 gtk_button_set_relief(GTK_BUTTON (gtk_builder_get_object (builder, "menuMode.Pause")),pausing?GTK_RELIEF_NORMAL:GTK_RELIEF_NONE);
3857 /* toggle background color in showbuttonbar */
3858 if (appData.showButtonBar) {
3860 gtk_button_pressed(GTK_BUTTON (gtk_builder_get_object (builder, "buttonbar.Pause")));
3862 gtk_button_released(GTK_BUTTON (gtk_builder_get_object (builder, "buttonbar.Pause")));
3867 // probably not needed anymore
3868 // wname = ModeToWidgetName(oldmode);
3870 // gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, wname)),True);
3874 /* Maybe all the enables should be handled here, not just this one */
3875 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuMode.Training")),
3876 gameMode == Training || gameMode == PlayFromGameFile);
3881 * Button/menu procedures
3884 int LoadGamePopUp(f, gameNumber, title)
3889 cmailMsgLoaded = FALSE;
3891 if (gameNumber == 0)
3893 int error = GameListBuild(f);
3897 DisplayError(_("Cannot build game list"), error);
3899 else if (!ListEmpty(&gameList)
3900 && ((ListGame *) gameList.tailPred)->number > 1)
3902 // TODO convert to GTK
3903 // GameListPopUp(f, title);
3911 return LoadGame(f, gameNumber, title, FALSE);
3914 void ReloadCmailMsgProc(w, event, prms, nprms)
3920 ReloadCmailMsgEvent(FALSE);
3923 void MailMoveProc(w, event, prms, nprms)
3932 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3933 char *selected_fen_position=NULL;
3936 SendPositionSelection(Widget w, Atom *selection, Atom *target,
3937 Atom *type_return, XtPointer *value_return,
3938 unsigned long *length_return, int *format_return)
3940 char *selection_tmp;
3942 if (!selected_fen_position) return False; /* should never happen */
3943 // if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3944 // /* note: since no XtSelectionDoneProc was registered, Xt will
3945 // * automatically call XtFree on the value returned. So have to
3946 // * make a copy of it allocated with XtMalloc */
3947 // selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3948 // strcpy(selection_tmp, selected_fen_position);
3950 // *value_return=selection_tmp;
3951 // *length_return=strlen(selection_tmp);
3952 // *type_return=*target;
3953 // *format_return = 8; /* bits per byte */
3955 // } else if (*target == XA_TARGETS(xDisplay)) {
3956 // Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3957 // targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3958 // targets_tmp[1] = XA_STRING;
3959 // *value_return = targets_tmp;
3960 // *type_return = XA_ATOM;
3961 // *length_return = 2;
3962 // *format_return = 8 * sizeof(Atom);
3963 // if (*format_return > 32) {
3964 // *length_return *= *format_return / 32;
3965 // *format_return = 32;
3973 /* note: when called from menu all parameters are NULL, so no clue what the
3974 * Widget which was clicked on was, or what the click event was
3976 void CopyPositionProc(w, event, prms, nprms)
3983 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3984 * have a notion of a position that is selected but not copied.
3985 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3987 if(gameMode == EditPosition) EditPositionDone(TRUE);
3988 if (selected_fen_position) free(selected_fen_position);
3989 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
3990 if (!selected_fen_position) return;
3991 // XtOwnSelection(menuBarWidget, XA_PRIMARY,
3993 // SendPositionSelection,
3994 // NULL/* lose_ownership_proc */ ,
3995 // NULL/* transfer_done_proc */);
3996 // XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3998 // SendPositionSelection,
3999 // NULL/* lose_ownership_proc */ ,
4000 // NULL/* transfer_done_proc */);
4003 /* function called when the data to Paste is ready */
4005 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
4006 Atom *type, XtPointer value, unsigned long *len, int *format)
4009 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
4010 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
4011 EditPositionPasteFEN(fenstr);
4015 /* called when Paste Position button is pressed,
4016 * all parameters will be NULL */
4017 void PastePositionProc(w, event, prms, nprms)
4023 // XtGetSelectionValue(menuBarWidget,
4024 // appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4025 // /* (XtSelectionCallbackProc) */ PastePositionCB,
4026 // NULL, /* client_data passed to PastePositionCB */
4028 // /* better to use the time field from the event that triggered the
4029 // * call to this function, but that isn't trivial to get
4037 SendGameSelection(Widget w, Atom *selection, Atom *target,
4038 Atom *type_return, XtPointer *value_return,
4039 unsigned long *length_return, int *format_return)
4041 char *selection_tmp;
4043 // if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4044 // FILE* f = fopen(gameCopyFilename, "r");
4047 // if (f == NULL) return False;
4051 // selection_tmp = XtMalloc(len + 1);
4052 // count = fread(selection_tmp, 1, len, f);
4053 // if (len != count) {
4054 // XtFree(selection_tmp);
4057 // selection_tmp[len] = NULLCHAR;
4058 // *value_return = selection_tmp;
4059 // *length_return = len;
4060 // *type_return = *target;
4061 // *format_return = 8; /* bits per byte */
4063 // } else if (*target == XA_TARGETS(xDisplay)) {
4064 // Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4065 // targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4066 // targets_tmp[1] = XA_STRING;
4067 // *value_return = targets_tmp;
4068 // *type_return = XA_ATOM;
4069 // *length_return = 2;
4070 // *format_return = 8 * sizeof(Atom);
4071 // if (*format_return > 32) {
4072 // *length_return *= *format_return / 32;
4073 // *format_return = 32;
4081 /* note: when called from menu all parameters are NULL, so no clue what the
4082 * Widget which was clicked on was, or what the click event was
4084 void CopyGameProc(w, event, prms, nprms)
4092 ret = SaveGameToFile(gameCopyFilename, FALSE);
4096 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4097 * have a notion of a game that is selected but not copied.
4098 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4100 // XtOwnSelection(menuBarWidget, XA_PRIMARY,
4102 // SendGameSelection,
4103 // NULL/* lose_ownership_proc */ ,
4104 // NULL/* transfer_done_proc */);
4105 // XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4107 // SendGameSelection,
4108 // NULL/* lose_ownership_proc */ ,
4109 // NULL/* transfer_done_proc */);
4112 /* function called when the data to Paste is ready */
4114 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
4115 Atom *type, XtPointer value, unsigned long *len, int *format)
4118 if (value == NULL || *len == 0) {
4119 return; /* nothing had been selected to copy */
4121 f = fopen(gamePasteFilename, "w");
4123 DisplayError(_("Can't open temp file"), errno);
4126 fwrite(value, 1, *len, f);
4129 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
4132 /* called when Paste Game button is pressed,
4133 * all parameters will be NULL */
4134 void PasteGameProc(w, event, prms, nprms)
4140 // XtGetSelectionValue(menuBarWidget,
4141 // appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4142 // /* (XtSelectionCallbackProc) */ PasteGameCB,
4143 // NULL, /* client_data passed to PasteGameCB */
4145 // /* better to use the time field from the event that triggered the
4146 // * call to this function, but that isn't trivial to get
4153 void SaveOnExitProc(w, event, prms, nprms)
4161 saveSettingsOnExit = !saveSettingsOnExit;
4163 if (saveSettingsOnExit) {
4164 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
4166 XtSetArg(args[0], XtNleftBitmap, None);
4168 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
4172 void SaveSettingsProc(w, event, prms, nprms)
4178 SaveSettings(settingsFileName);
4184 SaveGameProc(NULL, NULL);
4189 void EditCommentProc(w, event, prms, nprms)
4196 EditCommentPopDown();
4202 void IcsInputBoxProc(w, event, prms, nprms)
4208 if (ICSInputBoxUp) {
4209 ICSInputBoxPopDown();
4216 void EnterKeyProc(w, event, prms, nprms)
4222 if (ICSInputBoxUp == True)
4227 void DebugProc(w, event, prms, nprms)
4233 appData.debugMode = !appData.debugMode;
4236 void AboutGameProc(w, event, prms, nprms)
4245 void NothingProc(w, event, prms, nprms)
4254 void Iconify(w, event, prms, nprms)
4262 // fromX = fromY = -1;
4263 // XtSetArg(args[0], XtNiconic, True);
4264 // XtSetValues(shellWidget, args, 1);
4267 void DisplayMessage(message, extMessage)
4268 gchar *message, *extMessage;
4275 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
4278 message = extMessage;
4281 gtk_label_set_text( GTK_LABEL(gtk_builder_get_object (builder, "Messages")),message);
4286 void DisplayTitle(text)
4289 gchar title[MSG_SIZ];
4291 if (text == NULL) text = "";
4293 if (appData.titleInWindow)
4298 if (*text != NULLCHAR)
4300 strcpy(title, text);
4302 else if (appData.icsActive)
4304 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
4306 else if (appData.cmailGameName[0] != NULLCHAR)
4308 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
4310 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
4312 else if (gameInfo.variant == VariantGothic)
4314 strcpy(title, GOTHIC);
4318 else if (gameInfo.variant == VariantFalcon)
4320 strcpy(title, FALCON);
4323 else if (appData.noChessProgram)
4325 strcpy(title, programName);
4329 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
4331 gtk_window_set_title(GTK_WINDOW(GUI_Window),title);
4337 void DisplayError(message, error)
4344 if (appData.debugMode || appData.matchMode) {
4345 fprintf(stderr, "%s: %s\n", programName, message);
4348 if (appData.debugMode || appData.matchMode) {
4349 fprintf(stderr, "%s: %s: %s\n",
4350 programName, message, strerror(error));
4352 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
4355 ErrorPopUp(_("Error"), message, FALSE);
4359 void DisplayMoveError(message)
4364 DrawPosition(FALSE, NULL);
4365 if (appData.debugMode || appData.matchMode) {
4366 fprintf(stderr, "%s: %s\n", programName, message);
4368 if (appData.popupMoveErrors) {
4369 ErrorPopUp(_("Error"), message, FALSE);
4371 DisplayMessage(message, "");
4376 void DisplayFatalError(message, error, status)
4382 errorExitStatus = status;
4384 fprintf(stderr, "%s: %s\n", programName, message);
4386 fprintf(stderr, "%s: %s: %s\n",
4387 programName, message, strerror(error));
4388 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
4391 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
4392 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
4398 void DisplayInformation(message)
4402 ErrorPopUp(_("Information"), message, TRUE);
4405 void DisplayNote(message)
4409 ErrorPopUp(_("Note"), message, FALSE);
4413 NullXErrorCheck(dpy, error_event)
4415 XErrorEvent *error_event;
4420 void DisplayIcsInteractionTitle(message)
4423 if (oldICSInteractionTitle == NULL) {
4424 /* Magic to find the old window title, adapted from vim */
4425 char *wina = getenv("WINDOWID");
4427 Window win = (Window) atoi(wina);
4428 Window root, parent, *children;
4429 unsigned int nchildren;
4430 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4432 // if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4433 // if (!XQueryTree(xDisplay, win, &root, &parent,
4434 // &children, &nchildren)) break;
4435 // if (children) XFree((void *)children);
4436 // if (parent == root || parent == 0) break;
4439 XSetErrorHandler(oldHandler);
4441 if (oldICSInteractionTitle == NULL) {
4442 oldICSInteractionTitle = "xterm";
4445 printf("\033]0;%s\007", message);
4449 char pendingReplyPrefix[MSG_SIZ];
4450 ProcRef pendingReplyPR;
4452 void AskQuestionProc(w, event, prms, nprms)
4459 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
4463 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
4466 void AskQuestionPopDown()
4468 if (!askQuestionUp) return;
4469 XtPopdown(askQuestionShell);
4470 XtDestroyWidget(askQuestionShell);
4471 askQuestionUp = False;
4474 void AskQuestionReplyAction(w, event, prms, nprms)
4484 reply = XawDialogGetValueString(w = XtParent(w));
4485 strcpy(buf, pendingReplyPrefix);
4486 if (*buf) strcat(buf, " ");
4489 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
4490 AskQuestionPopDown();
4492 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
4495 void AskQuestionCallback(w, client_data, call_data)
4497 XtPointer client_data, call_data;
4502 XtSetArg(args[0], XtNlabel, &name);
4503 XtGetValues(w, args, 1);
4505 if (strcmp(name, _("cancel")) == 0) {
4506 AskQuestionPopDown();
4508 AskQuestionReplyAction(w, NULL, NULL, NULL);
4512 void AskQuestion(title, question, replyPrefix, pr)
4513 char *title, *question, *replyPrefix;
4517 Widget popup, layout, dialog, edit;
4523 strcpy(pendingReplyPrefix, replyPrefix);
4524 pendingReplyPR = pr;
4527 XtSetArg(args[i], XtNresizable, True); i++;
4528 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
4529 // askQuestionShell = popup =
4530 // XtCreatePopupShell(title, transientShellWidgetClass,
4531 // shellWidget, args, i);
4534 // XtCreateManagedWidget(layoutName, formWidgetClass, popup,
4535 // layoutArgs, XtNumber(layoutArgs));
4538 XtSetArg(args[i], XtNlabel, question); i++;
4539 XtSetArg(args[i], XtNvalue, ""); i++;
4540 XtSetArg(args[i], XtNborderWidth, 0); i++;
4541 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
4544 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
4545 (XtPointer) dialog);
4546 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
4547 (XtPointer) dialog);
4549 XtRealizeWidget(popup);
4550 // CatchDeleteWindow(popup, "AskQuestionPopDown");
4552 // XQueryPointer(xDisplay, xBoardWindow, &root, &child,
4553 // &x, &y, &win_x, &win_y, &mask);
4555 // XtSetArg(args[0], XtNx, x - 10);
4556 // XtSetArg(args[1], XtNy, y - 30);
4557 // XtSetValues(popup, args, 2);
4559 // XtPopup(popup, XtGrabExclusive);
4560 // askQuestionUp = True;
4562 // edit = XtNameToWidget(dialog, "*value");
4563 // XtSetKeyboardFocus(popup, edit);
4571 if (*name == NULLCHAR) {
4573 } else if (strcmp(name, "$") == 0) {
4574 putc(BELLCHAR, stderr);
4577 snprintf(buf, sizeof(buf), "%s '%s' &", appData.soundProgram, name);
4585 PlaySound(appData.soundMove);
4591 PlaySound(appData.soundIcsWin);
4597 PlaySound(appData.soundIcsLoss);
4603 PlaySound(appData.soundIcsDraw);
4607 PlayIcsUnfinishedSound()
4609 PlaySound(appData.soundIcsUnfinished);
4615 PlaySound(appData.soundIcsAlarm);
4621 system("stty echo");
4627 system("stty -echo");
4631 Colorize(cc, continuation)
4636 int count, outCount, error;
4638 if (textColors[(int)cc].bg > 0) {
4639 if (textColors[(int)cc].fg > 0) {
4640 sprintf(buf, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
4641 textColors[(int)cc].fg, textColors[(int)cc].bg);
4643 sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
4644 textColors[(int)cc].bg);
4647 if (textColors[(int)cc].fg > 0) {
4648 sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
4649 textColors[(int)cc].fg);
4651 sprintf(buf, "\033[0;%dm", textColors[(int)cc].attr);
4654 count = strlen(buf);
4655 outCount = OutputToProcess(NoProc, buf, count, &error);
4656 if (outCount < count) {
4657 DisplayFatalError(_("Error writing to display"), error, 1);
4660 if (continuation) return;
4663 PlaySound(appData.soundShout);
4666 PlaySound(appData.soundSShout);
4669 PlaySound(appData.soundChannel1);
4672 PlaySound(appData.soundChannel);
4675 PlaySound(appData.soundKibitz);
4678 PlaySound(appData.soundTell);
4680 case ColorChallenge:
4681 PlaySound(appData.soundChallenge);
4684 PlaySound(appData.soundRequest);
4687 PlaySound(appData.soundSeek);
4698 return getpwuid(getuid())->pw_name;
4701 static char *ExpandPathName(path)
4704 static char static_buf[2000];
4705 char *d, *s, buf[2000];
4711 while (*s && isspace(*s))
4720 if (*(s+1) == '/') {
4721 strcpy(d, getpwuid(getuid())->pw_dir);
4726 *strchr(buf, '/') = 0;
4727 pwd = getpwnam(buf);
4730 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
4734 strcpy(d, pwd->pw_dir);
4735 strcat(d, strchr(s+1, '/'));
4746 static char host_name[MSG_SIZ];
4748 #if HAVE_GETHOSTNAME
4749 gethostname(host_name, MSG_SIZ);
4751 #else /* not HAVE_GETHOSTNAME */
4752 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
4753 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
4755 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4757 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4758 #endif /* not HAVE_GETHOSTNAME */
4761 guint delayedEventTimerTag = 0;
4762 DelayedEventCallback delayedEventCallback = 0;
4765 FireDelayedEvent(data)
4769 g_source_remove(delayedEventTimerTag);
4770 delayedEventTimerTag = 0;
4773 delayedEventCallback();
4779 ScheduleDelayedEvent(cb, millisec)
4780 DelayedEventCallback cb; guint millisec;
4782 if(delayedEventTimerTag && delayedEventCallback == cb)
4783 // [HGM] alive: replace, rather than add or flush identical event
4784 g_source_remove(delayedEventTimerTag);
4785 delayedEventCallback = cb;
4786 delayedEventTimerTag = g_timeout_add(millisec,(GSourceFunc) FireDelayedEvent, NULL);
4790 DelayedEventCallback
4793 if (delayedEventTimerTag)
4795 return delayedEventCallback;
4804 CancelDelayedEvent()
4806 if (delayedEventTimerTag)
4808 g_source_remove(delayedEventTimerTag);
4809 delayedEventTimerTag = 0;
4815 guint loadGameTimerTag = 0;
4817 int LoadGameTimerRunning()
4819 return loadGameTimerTag != 0;
4822 int StopLoadGameTimer()
4824 if (loadGameTimerTag != 0) {
4825 g_source_remove(loadGameTimerTag);
4826 loadGameTimerTag = 0;
4834 LoadGameTimerCallback(data)
4838 g_source_remove(loadGameTimerTag);
4839 loadGameTimerTag = 0;
4846 StartLoadGameTimer(millisec)
4850 g_timeout_add( millisec, (GSourceFunc) LoadGameTimerCallback, NULL);
4854 guint analysisClockTag = 0;
4857 AnalysisClockCallback(data)
4860 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
4861 || appData.icsEngineAnalyze)
4863 AnalysisPeriodicEvent(0);
4864 return 1; /* keep on going */
4866 return 0; /* stop timer */
4870 StartAnalysisClock()
4873 g_timeout_add( 2000,(GSourceFunc) AnalysisClockCallback, NULL);
4877 guint clockTimerTag = 0;
4879 int ClockTimerRunning()
4881 return clockTimerTag != 0;
4884 int StopClockTimer()
4886 if (clockTimerTag != 0)
4888 g_source_remove(clockTimerTag);
4899 ClockTimerCallback(data)
4903 g_source_remove(clockTimerTag);
4911 StartClockTimer(millisec)
4914 clockTimerTag = g_timeout_add(millisec,(GSourceFunc) ClockTimerCallback,NULL);
4919 DisplayTimerLabel(w, color, timer, highlight)
4928 if (appData.clockMode) {
4929 sprintf(buf, "%s: %s", color, TimeString(timer));
4931 sprintf(buf, "%s ", color);
4933 gtk_label_set_text(GTK_LABEL(w),buf);
4935 /* check for low time warning */
4936 // Pixel foregroundOrWarningColor = timerForegroundPixel;
4939 // appData.lowTimeWarning &&
4940 // (timer / 1000) < appData.icsAlarmTime)
4941 // foregroundOrWarningColor = lowTimeWarningColor;
4943 // if (appData.clockMode) {
4944 // sprintf(buf, "%s: %s", color, TimeString(timer));
4945 // XtSetArg(args[0], XtNlabel, buf);
4947 // sprintf(buf, "%s ", color);
4948 // XtSetArg(args[0], XtNlabel, buf);
4953 // XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
4954 // XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
4956 // XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
4957 // XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
4960 // XtSetValues(w, args, 3);
4965 DisplayWhiteClock(timeRemaining, highlight)
4969 if(appData.noGUI) return;
4971 DisplayTimerLabel(GUI_Whiteclock, _("White"), timeRemaining, highlight);
4972 if (highlight && WindowIcon == BlackIcon)
4974 WindowIcon = WhiteIcon;
4975 gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
4980 DisplayBlackClock(timeRemaining, highlight)
4984 if(appData.noGUI) return;
4986 DisplayTimerLabel(GUI_Blackclock, _("Black"), timeRemaining, highlight);
4987 if (highlight && WindowIcon == WhiteIcon)
4989 WindowIcon = BlackIcon;
4990 gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
5008 int StartChildProcess(cmdLine, dir, pr)
5015 int to_prog[2], from_prog[2];
5019 if (appData.debugMode) {
5020 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
5023 /* We do NOT feed the cmdLine to the shell; we just
5024 parse it into blank-separated arguments in the
5025 most simple-minded way possible.
5028 strcpy(buf, cmdLine);
5031 while(*p == ' ') p++;
5033 if(*p == '"' || *p == '\'')
5034 p = strchr(++argv[i-1], *p);
5035 else p = strchr(p, ' ');
5036 if (p == NULL) break;
5041 SetUpChildIO(to_prog, from_prog);
5043 if ((pid = fork()) == 0) {
5045 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
5046 close(to_prog[1]); // first close the unused pipe ends
5047 close(from_prog[0]);
5048 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
5049 dup2(from_prog[1], 1);
5050 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
5051 close(from_prog[1]); // and closing again loses one of the pipes!
5052 if(fileno(stderr) >= 2) // better safe than sorry...
5053 dup2(1, fileno(stderr)); /* force stderr to the pipe */
5055 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
5060 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
5062 execvp(argv[0], argv);
5064 /* If we get here, exec failed */
5069 /* Parent process */
5071 close(from_prog[1]);
5073 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5076 cp->fdFrom = from_prog[0];
5077 cp->fdTo = to_prog[1];
5082 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
5083 static RETSIGTYPE AlarmCallBack(int n)
5089 DestroyChildProcess(pr, signalType)
5093 ChildProc *cp = (ChildProc *) pr;
5095 if (cp->kind != CPReal) return;
5097 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
5098 signal(SIGALRM, AlarmCallBack);
5100 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
5101 kill(cp->pid, SIGKILL); // kill it forcefully
5102 wait((int *) 0); // and wait again
5106 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
5108 /* Process is exiting either because of the kill or because of
5109 a quit command sent by the backend; either way, wait for it to die.
5118 InterruptChildProcess(pr)
5121 ChildProc *cp = (ChildProc *) pr;
5123 if (cp->kind != CPReal) return;
5124 (void) kill(cp->pid, SIGINT); /* stop it thinking */
5127 int OpenTelnet(host, port, pr)
5132 char cmdLine[MSG_SIZ];
5134 if (port[0] == NULLCHAR) {
5135 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
5137 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
5139 return StartChildProcess(cmdLine, "", pr);
5142 int OpenTCP(host, port, pr)
5148 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
5149 #else /* !OMIT_SOCKETS */
5151 struct sockaddr_in sa;
5153 unsigned short uport;
5156 if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
5160 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
5161 sa.sin_family = AF_INET;
5162 sa.sin_addr.s_addr = INADDR_ANY;
5163 uport = (unsigned short) 0;
5164 sa.sin_port = htons(uport);
5165 if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
5169 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
5170 if (!(hp = gethostbyname(host))) {
5172 if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
5173 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
5174 hp->h_addrtype = AF_INET;
5176 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
5177 hp->h_addr_list[0] = (char *) malloc(4);
5178 hp->h_addr_list[0][0] = b0;
5179 hp->h_addr_list[0][1] = b1;
5180 hp->h_addr_list[0][2] = b2;
5181 hp->h_addr_list[0][3] = b3;
5186 sa.sin_family = hp->h_addrtype;
5187 uport = (unsigned short) atoi(port);
5188 sa.sin_port = htons(uport);
5189 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
5191 if (connect(s, (struct sockaddr *) &sa,
5192 sizeof(struct sockaddr_in)) < 0) {
5196 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5203 #endif /* !OMIT_SOCKETS */
5208 int OpenCommPort(name, pr)
5215 fd = open(name, 2, 0);
5216 if (fd < 0) return errno;
5218 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5228 int OpenLoopback(pr)
5234 SetUpChildIO(to, from);
5236 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5239 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
5246 int OpenRcmd(host, user, cmd, pr)
5247 char *host, *user, *cmd;
5250 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
5254 #define INPUT_SOURCE_BUF_SIZE 8192
5263 char buf[INPUT_SOURCE_BUF_SIZE];
5268 DoInputCallback(io,cond,data)
5273 /* read input from one of the input source (for example a chess program, ICS, etc).
5274 * and call a function that will handle the input
5277 int count; /* how many bytes did we read */
5281 /* All information (callback function, file descriptor, etc) is
5282 * saved in an InputSource structure
5284 InputSource *is = (InputSource *) data;
5288 count = read(is->fd, is->unused,
5289 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
5293 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
5296 is->unused += count;
5298 /* break input into lines and call the callback function on each
5301 while (p < is->unused)
5303 q = memchr(p, '\n', is->unused - p);
5304 if (q == NULL) break;
5306 (is->func)(is, is->closure, p, q - p, 0);
5309 /* remember not yet used part of the buffer */
5311 while (p < is->unused)
5319 /* read maximum length of input buffer and send the whole buffer
5320 * to the callback function
5322 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
5327 (is->func)(is, is->closure, is->buf, count, error);
5333 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
5340 GIOChannel *channel;
5341 ChildProc *cp = (ChildProc *) pr;
5343 is = (InputSource *) calloc(1, sizeof(InputSource));
5344 is->lineByLine = lineByLine;
5348 is->fd = fileno(stdin);
5350 is->kind = cp->kind;
5351 is->fd = cp->fdFrom;
5354 is->unused = is->buf;
5358 // is->xid = XtAppAddInput(appContext, is->fd,
5359 // (XtPointer) (XtInputReadMask),
5360 // (XtInputCallbackProc) DoInputCallback,
5364 /* TODO: will this work on windows?*/
5366 channel = g_io_channel_unix_new(is->fd);
5367 g_io_channel_set_close_on_unref (channel, TRUE);
5368 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
5369 is->closure = closure;
5370 return (InputSourceRef) is;
5374 RemoveInputSource(isr)
5377 InputSource *is = (InputSource *) isr;
5379 if (is->sid == 0) return;
5380 g_source_remove(is->sid);
5385 int OutputToProcess(pr, message, count, outError)
5391 static int line = 0;
5392 ChildProc *cp = (ChildProc *) pr;
5397 if (appData.noJoin || !appData.useInternalWrap)
5398 outCount = fwrite(message, 1, count, stdout);
5401 int width = get_term_width();
5402 int len = wrap(NULL, message, count, width, &line);
5403 char *msg = malloc(len);
5407 outCount = fwrite(message, 1, count, stdout);
5410 dbgchk = wrap(msg, message, count, width, &line);
5411 if (dbgchk != len && appData.debugMode)
5412 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
5413 outCount = fwrite(msg, 1, dbgchk, stdout);
5419 outCount = write(cp->fdTo, message, count);
5429 /* Output message to process, with "ms" milliseconds of delay
5430 between each character. This is needed when sending the logon
5431 script to ICC, which for some reason doesn't like the
5432 instantaneous send. */
5433 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
5440 ChildProc *cp = (ChildProc *) pr;
5445 r = write(cp->fdTo, message++, 1);
5458 /**** Animation code by Hugh Fisher, DCS, ANU.
5460 Known problem: if a window overlapping the board is
5461 moved away while a piece is being animated underneath,
5462 the newly exposed area won't be updated properly.
5463 I can live with this.
5465 Known problem: if you look carefully at the animation
5466 of pieces in mono mode, they are being drawn as solid
5467 shapes without interior detail while moving. Fixing
5468 this would be a major complication for minimal return.
5471 /* Masks for XPM pieces. Black and white pieces can have
5472 different shapes, but in the interest of retaining my
5473 sanity pieces must have the same outline on both light
5474 and dark squares, and all pieces must use the same
5475 background square colors/images. */
5477 static int xpmDone = 0;
5480 CreateAnimMasks (pieceDepth)
5487 unsigned long plane;
5490 /* just return for gtk at the moment */
5493 /* Need a bitmap just to get a GC with right depth */
5494 // buf = XCreatePixmap(xDisplay, xBoardWindow,
5496 values.foreground = 1;
5497 values.background = 0;
5498 /* Don't use XtGetGC, not read only */
5499 // maskGC = XCreateGC(xDisplay, buf,
5500 // GCForeground | GCBackground, &values);
5501 // XFreePixmap(xDisplay, buf);
5503 // buf = XCreatePixmap(xDisplay, xBoardWindow,
5504 // squareSize, squareSize, pieceDepth);
5505 // values.foreground = XBlackPixel(xDisplay, xScreen);
5506 // values.background = XWhitePixel(xDisplay, xScreen);
5507 // bufGC = XCreateGC(xDisplay, buf,
5508 // GCForeground | GCBackground, &values);
5510 for (piece = WhitePawn; piece <= BlackKing; piece++) {
5511 /* Begin with empty mask */
5512 // if(!xpmDone) // [HGM] pieces: keep using existing
5513 // xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
5514 // squareSize, squareSize, 1);
5515 // XSetFunction(xDisplay, maskGC, GXclear);
5516 // XFillRectangle(xDisplay, xpmMask[piece], maskGC,
5517 // 0, 0, squareSize, squareSize);
5519 /* Take a copy of the piece */
5524 // XSetFunction(xDisplay, bufGC, GXcopy);
5525 // XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
5527 // 0, 0, squareSize, squareSize, 0, 0);
5529 /* XOR the background (light) over the piece */
5530 // XSetFunction(xDisplay, bufGC, GXxor);
5532 // XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
5533 // 0, 0, squareSize, squareSize, 0, 0);
5535 // XSetForeground(xDisplay, bufGC, lightSquareColor);
5536 // XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
5539 /* We now have an inverted piece image with the background
5540 erased. Construct mask by just selecting all the non-zero
5541 pixels - no need to reconstruct the original image. */
5542 // XSetFunction(xDisplay, maskGC, GXor);
5544 /* Might be quicker to download an XImage and create bitmap
5545 data from it rather than this N copies per piece, but it
5546 only takes a fraction of a second and there is a much
5547 longer delay for loading the pieces. */
5548 // for (n = 0; n < pieceDepth; n ++) {
5549 // XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
5550 // 0, 0, squareSize, squareSize,
5552 // plane = plane << 1;
5556 // XFreePixmap(xDisplay, buf);
5557 // XFreeGC(xDisplay, bufGC);
5558 // XFreeGC(xDisplay, maskGC);
5562 InitAnimState (anim, info)
5564 XWindowAttributes * info;
5569 /* Each buffer is square size, same depth as window */
5570 // anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
5571 // squareSize, squareSize, info->depth);
5572 // anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
5573 // squareSize, squareSize, info->depth);
5575 // /* Create a plain GC for blitting */
5576 // mask = GCForeground | GCBackground | GCFunction |
5577 // GCPlaneMask | GCGraphicsExposures;
5578 // values.foreground = XBlackPixel(xDisplay, xScreen);
5579 // values.background = XWhitePixel(xDisplay, xScreen);
5580 // values.function = GXcopy;
5581 // values.plane_mask = AllPlanes;
5582 // values.graphics_exposures = False;
5583 // anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
5585 // /* Piece will be copied from an existing context at
5586 // the start of each new animation/drag. */
5587 // anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
5589 // /* Outline will be a read-only copy of an existing */
5590 // anim->outlineGC = None;
5596 static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
5597 XWindowAttributes info;
5599 /* for gtk at the moment just ... */
5602 if (xpmDone && gameInfo.variant == old) return;
5603 if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet
5604 // XGetWindowAttributes(xDisplay, xBoardWindow, &info);
5606 // InitAnimState(&game, &info);
5607 // InitAnimState(&player, &info);
5609 /* For XPM pieces, we need bitmaps to use as masks. */
5611 // CreateAnimMasks(info.depth);
5617 static Boolean frameWaiting;
5619 static RETSIGTYPE FrameAlarm (sig)
5622 frameWaiting = False;
5623 /* In case System-V style signals. Needed?? */
5624 signal(SIGALRM, FrameAlarm);
5631 struct itimerval delay;
5633 XSync(xDisplay, False);
5636 frameWaiting = True;
5637 signal(SIGALRM, FrameAlarm);
5638 delay.it_interval.tv_sec =
5639 delay.it_value.tv_sec = time / 1000;
5640 delay.it_interval.tv_usec =
5641 delay.it_value.tv_usec = (time % 1000) * 1000;
5642 setitimer(ITIMER_REAL, &delay, NULL);
5643 while (frameWaiting) pause();
5644 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
5645 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
5646 setitimer(ITIMER_REAL, &delay, NULL);
5656 // XSync(xDisplay, False);
5658 usleep(time * 1000);
5663 /* Convert board position to corner of screen rect and color */
5666 ScreenSquare(column, row, pt, color)
5667 int column; int row; XPoint * pt; int * color;
5670 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
5671 pt->y = lineGap + row * (squareSize + lineGap);
5673 pt->x = lineGap + column * (squareSize + lineGap);
5674 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
5676 *color = SquareColor(row, column);
5679 /* Convert window coords to square */
5682 BoardSquare(x, y, column, row)
5683 int x; int y; int * column; int * row;
5685 *column = EventToSquare(x, BOARD_WIDTH);
5686 if (flipView && *column >= 0)
5687 *column = BOARD_WIDTH - 1 - *column;
5688 *row = EventToSquare(y, BOARD_HEIGHT);
5689 if (!flipView && *row >= 0)
5690 *row = BOARD_HEIGHT - 1 - *row;
5695 #undef Max /* just in case */
5697 #define Max(a, b) ((a) > (b) ? (a) : (b))
5698 #define Min(a, b) ((a) < (b) ? (a) : (b))
5701 SetRect(rect, x, y, width, height)
5702 XRectangle * rect; int x; int y; int width; int height;
5706 rect->width = width;
5707 rect->height = height;
5710 /* Test if two frames overlap. If they do, return
5711 intersection rect within old and location of
5712 that rect within new. */
5715 Intersect(old, new, size, area, pt)
5716 XPoint * old; XPoint * new;
5717 int size; XRectangle * area; XPoint * pt;
5719 if (old->x > new->x + size || new->x > old->x + size ||
5720 old->y > new->y + size || new->y > old->y + size) {
5723 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
5724 size - abs(old->x - new->x), size - abs(old->y - new->y));
5725 pt->x = Max(old->x - new->x, 0);
5726 pt->y = Max(old->y - new->y, 0);
5731 /* For two overlapping frames, return the rect(s)
5732 in the old that do not intersect with the new. */
5735 CalcUpdateRects(old, new, size, update, nUpdates)
5736 XPoint * old; XPoint * new; int size;
5737 XRectangle update[]; int * nUpdates;
5741 /* If old = new (shouldn't happen) then nothing to draw */
5742 if (old->x == new->x && old->y == new->y) {
5746 /* Work out what bits overlap. Since we know the rects
5747 are the same size we don't need a full intersect calc. */
5749 /* Top or bottom edge? */
5750 if (new->y > old->y) {
5751 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
5753 } else if (old->y > new->y) {
5754 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
5755 size, old->y - new->y);
5758 /* Left or right edge - don't overlap any update calculated above. */
5759 if (new->x > old->x) {
5760 SetRect(&(update[count]), old->x, Max(new->y, old->y),
5761 new->x - old->x, size - abs(new->y - old->y));
5763 } else if (old->x > new->x) {
5764 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
5765 old->x - new->x, size - abs(new->y - old->y));
5772 /* Generate a series of frame coords from start->mid->finish.
5773 The movement rate doubles until the half way point is
5774 reached, then halves back down to the final destination,
5775 which gives a nice slow in/out effect. The algorithmn
5776 may seem to generate too many intermediates for short
5777 moves, but remember that the purpose is to attract the
5778 viewers attention to the piece about to be moved and
5779 then to where it ends up. Too few frames would be less
5783 Tween(start, mid, finish, factor, frames, nFrames)
5784 XPoint * start; XPoint * mid;
5785 XPoint * finish; int factor;
5786 XPoint frames[]; int * nFrames;
5788 int fraction, n, count;
5792 /* Slow in, stepping 1/16th, then 1/8th, ... */
5794 for (n = 0; n < factor; n++)
5796 for (n = 0; n < factor; n++) {
5797 frames[count].x = start->x + (mid->x - start->x) / fraction;
5798 frames[count].y = start->y + (mid->y - start->y) / fraction;
5800 fraction = fraction / 2;
5804 frames[count] = *mid;
5807 /* Slow out, stepping 1/2, then 1/4, ... */
5809 for (n = 0; n < factor; n++) {
5810 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
5811 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
5813 fraction = fraction * 2;
5818 /* Draw a piece on the screen without disturbing what's there */
5821 SelectGCMask(piece, clip, outline, mask)
5822 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
5826 /* Bitmap for piece being moved. */
5827 if (appData.monoMode) {
5828 *mask = *pieceToSolid(piece);
5829 } else if (useImages) {
5831 *mask = xpmMask[piece];
5833 *mask = ximMaskPm[piece];
5836 *mask = *pieceToSolid(piece);
5839 /* GC for piece being moved. Square color doesn't matter, but
5840 since it gets modified we make a copy of the original. */
5842 if (appData.monoMode)
5847 if (appData.monoMode)
5852 // XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
5854 /* Outline only used in mono mode and is not modified */
5856 *outline = bwPieceGC;
5858 *outline = wbPieceGC;
5862 OverlayPiece(piece, clip, outline, dest)
5863 ChessSquare piece; GC clip; GC outline; Drawable dest;
5868 /* Draw solid rectangle which will be clipped to shape of piece */
5869 // XFillRectangle(xDisplay, dest, clip,
5870 // 0, 0, squareSize, squareSize)
5872 if (appData.monoMode)
5873 /* Also draw outline in contrasting color for black
5874 on black / white on white cases */
5875 // XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
5876 // 0, 0, squareSize, squareSize, 0, 0, 1)
5879 /* Copy the piece */
5884 // XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
5886 // 0, 0, squareSize, squareSize,
5891 /* Animate the movement of a single piece */
5894 BeginAnimation(anim, piece, startColor, start)
5902 /* The old buffer is initialised with the start square (empty) */
5903 BlankSquare(0, 0, startColor, EmptySquare, anim->saveBuf);
5904 anim->prevFrame = *start;
5906 /* The piece will be drawn using its own bitmap as a matte */
5907 // SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
5908 // XSetClipMask(xDisplay, anim->pieceGC, mask);
5912 AnimationFrame(anim, frame, piece)
5917 XRectangle updates[4];
5922 /* Save what we are about to draw into the new buffer */
5923 // XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
5924 // frame->x, frame->y, squareSize, squareSize,
5927 /* Erase bits of the previous frame */
5928 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
5929 /* Where the new frame overlapped the previous,
5930 the contents in newBuf are wrong. */
5931 // XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
5932 // overlap.x, overlap.y,
5933 // overlap.width, overlap.height,
5935 /* Repaint the areas in the old that don't overlap new */
5936 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
5937 for (i = 0; i < count; i++)
5938 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
5939 // updates[i].x - anim->prevFrame.x,
5940 // updates[i].y - anim->prevFrame.y,
5941 // updates[i].width, updates[i].height,
5942 // updates[i].x, updates[i].y)
5945 /* Easy when no overlap */
5946 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
5947 // 0, 0, squareSize, squareSize,
5948 // anim->prevFrame.x, anim->prevFrame.y);
5951 /* Save this frame for next time round */
5952 // XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
5953 // 0, 0, squareSize, squareSize,
5955 anim->prevFrame = *frame;
5957 /* Draw piece over original screen contents, not current,
5958 and copy entire rect. Wipes out overlapping piece images. */
5959 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
5960 // XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
5961 // 0, 0, squareSize, squareSize,
5962 // frame->x, frame->y);
5966 EndAnimation (anim, finish)
5970 XRectangle updates[4];
5975 /* The main code will redraw the final square, so we
5976 only need to erase the bits that don't overlap. */
5977 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
5978 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
5979 for (i = 0; i < count; i++)
5980 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
5981 // updates[i].x - anim->prevFrame.x,
5982 // updates[i].y - anim->prevFrame.y,
5983 // updates[i].width, updates[i].height,
5984 // updates[i].x, updates[i].y)
5987 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
5988 // 0, 0, squareSize, squareSize,
5989 // anim->prevFrame.x, anim->prevFrame.y);
5994 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
5996 ChessSquare piece; int startColor;
5997 XPoint * start; XPoint * finish;
5998 XPoint frames[]; int nFrames;
6002 BeginAnimation(anim, piece, startColor, start);
6003 for (n = 0; n < nFrames; n++) {
6004 AnimationFrame(anim, &(frames[n]), piece);
6005 FrameDelay(appData.animSpeed);
6007 EndAnimation(anim, finish);
6010 /* Main control logic for deciding what to animate and how */
6013 AnimateMove(board, fromX, fromY, toX, toY)
6022 XPoint start, finish, mid;
6023 XPoint frames[kFactor * 2 + 1];
6024 int nFrames, startColor, endColor;
6026 /* Are we animating? */
6027 if (!appData.animate || appData.blindfold)
6030 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
6031 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
6032 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
6034 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
6035 piece = board[fromY][fromX];
6036 if (piece >= EmptySquare) return;
6041 hop = (piece == WhiteKnight || piece == BlackKnight);
6044 if (appData.debugMode) {
6045 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
6046 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
6047 piece, fromX, fromY, toX, toY); }
6049 ScreenSquare(fromX, fromY, &start, &startColor);
6050 ScreenSquare(toX, toY, &finish, &endColor);
6053 /* Knight: make diagonal movement then straight */
6054 if (abs(toY - fromY) < abs(toX - fromX)) {
6055 mid.x = start.x + (finish.x - start.x) / 2;
6059 mid.y = start.y + (finish.y - start.y) / 2;
6062 mid.x = start.x + (finish.x - start.x) / 2;
6063 mid.y = start.y + (finish.y - start.y) / 2;
6066 /* Don't use as many frames for very short moves */
6067 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
6068 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
6070 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
6071 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
6073 /* Be sure end square is redrawn */
6074 damage[toY][toX] = True;
6078 DragPieceBegin(x, y)
6081 int boardX, boardY, color;
6084 /* Are we animating? */
6085 if (!appData.animateDragging || appData.blindfold)
6088 /* Figure out which square we start in and the
6089 mouse position relative to top left corner. */
6090 BoardSquare(x, y, &boardX, &boardY);
6091 player.startBoardX = boardX;
6092 player.startBoardY = boardY;
6093 ScreenSquare(boardX, boardY, &corner, &color);
6094 player.startSquare = corner;
6095 player.startColor = color;
6096 /* As soon as we start dragging, the piece will jump slightly to
6097 be centered over the mouse pointer. */
6098 player.mouseDelta.x = squareSize/2;
6099 player.mouseDelta.y = squareSize/2;
6100 /* Initialise animation */
6101 player.dragPiece = PieceForSquare(boardX, boardY);
6103 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
6104 player.dragActive = True;
6105 BeginAnimation(&player, player.dragPiece, color, &corner);
6106 /* Mark this square as needing to be redrawn. Note that
6107 we don't remove the piece though, since logically (ie
6108 as seen by opponent) the move hasn't been made yet. */
6109 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
6110 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
6111 // XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
6112 // corner.x, corner.y, squareSize, squareSize,
6113 // 0, 0); // [HGM] zh: unstack in stead of grab
6114 damage[boardY][boardX] = True;
6116 player.dragActive = False;
6126 /* Are we animating? */
6127 if (!appData.animateDragging || appData.blindfold)
6131 if (! player.dragActive)
6133 /* Move piece, maintaining same relative position
6134 of mouse within square */
6135 corner.x = x - player.mouseDelta.x;
6136 corner.y = y - player.mouseDelta.y;
6137 AnimationFrame(&player, &corner, player.dragPiece);
6139 if (appData.highlightDragging) {
6141 BoardSquare(x, y, &boardX, &boardY);
6142 SetHighlights(fromX, fromY, boardX, boardY);
6151 int boardX, boardY, color;
6154 /* Are we animating? */
6155 if (!appData.animateDragging || appData.blindfold)
6159 if (! player.dragActive)
6161 /* Last frame in sequence is square piece is
6162 placed on, which may not match mouse exactly. */
6163 BoardSquare(x, y, &boardX, &boardY);
6164 ScreenSquare(boardX, boardY, &corner, &color);
6165 EndAnimation(&player, &corner);
6167 /* Be sure end square is redrawn */
6168 damage[boardY][boardX] = True;
6170 /* This prevents weird things happening with fast successive
6171 clicks which on my Sun at least can cause motion events
6172 without corresponding press/release. */
6173 player.dragActive = False;
6176 /* Handle expose event while piece being dragged */
6181 if (!player.dragActive || appData.blindfold)
6184 /* What we're doing: logically, the move hasn't been made yet,
6185 so the piece is still in it's original square. But visually
6186 it's being dragged around the board. So we erase the square
6187 that the piece is on and draw it at the last known drag point. */
6188 BlankSquare(player.startSquare.x, player.startSquare.y,
6189 player.startColor, EmptySquare, xBoardWindow);
6190 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
6191 damage[player.startBoardY][player.startBoardX] = TRUE;
6194 #include <sys/ioctl.h>
6195 int get_term_width()
6197 int fd, default_width;
6200 default_width = 79; // this is FICS default anyway...
6202 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
6204 if (!ioctl(fd, TIOCGSIZE, &win))
6205 default_width = win.ts_cols;
6206 #elif defined(TIOCGWINSZ)
6208 if (!ioctl(fd, TIOCGWINSZ, &win))
6209 default_width = win.ws_col;
6211 return default_width;
6214 void update_ics_width()
6216 static int old_width = 0;
6217 int new_width = get_term_width();
6219 if (old_width != new_width)
6220 ics_printf("set width %d\n", new_width);
6221 old_width = new_width;
6224 void NotifyFrontendLogin()