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 historyShell, 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;
1191 GetActualPlacement(Widget wg, WindowPlacement *wp)
1201 XtSetArg(args[i], XtNx, &x); i++;
1202 XtSetArg(args[i], XtNy, &y); i++;
1203 XtSetArg(args[i], XtNwidth, &w); i++;
1204 XtSetArg(args[i], XtNheight, &h); i++;
1205 XtGetValues(wg, args, i);
1214 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1215 // In XBoard this will have to wait until awareness of window parameters is implemented
1217 // GetActualPlacement(shellWidget, &wpMain);
1218 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1219 if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1220 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1221 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1222 if(commentShell) GetActualPlacement(commentShell, &wpComment);
1223 else GetActualPlacement(editShell, &wpComment);
1224 if(tagsShell) GetActualPlacement(tagsShell, &wpTags);
1225 else GetActualPlacement(editTagsShell, &wpTags);
1229 PrintCommPortSettings(FILE *f, char *name)
1230 { // This option does not exist in XBoard
1234 MySearchPath(char *installDir, char *name, char *fullname)
1235 { // just append installDir and name. Perhaps ExpandPath should be used here?
1236 name = ExpandPathName(name);
1237 if(name && name[0] == '/') strcpy(fullname, name); else {
1238 sprintf(fullname, "%s%c%s", installDir, '/', name);
1244 MyGetFullPathName(char *name, char *fullname)
1245 { // should use ExpandPath?
1246 name = ExpandPathName(name);
1247 strcpy(fullname, name);
1252 EnsureOnScreen(int *x, int *y, int minX, int minY)
1259 { // [HGM] args: allows testing if main window is realized from back-end
1260 return xBoardWindow != 0;
1264 PopUpStartupDialog()
1265 { // start menu not implemented in XBoard
1268 ConvertToLine(int argc, char **argv)
1270 static char line[128*1024], buf[1024];
1274 for(i=1; i<argc; i++) {
1275 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1276 && argv[i][0] != '{' )
1277 sprintf(buf, "{%s} ", argv[i]);
1278 else sprintf(buf, "%s ", argv[i]);
1281 line[strlen(line)-1] = NULLCHAR;
1285 //--------------------------------------------------------------------------------------------
1288 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1291 #define BoardSize int
1292 void InitDrawingSizes(BoardSize boardSize, int flags)
1293 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1294 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1296 XtGeometryResult gres;
1299 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1300 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1302 timerWidth = (boardWidth - sep) / 2;
1304 if (appData.titleInWindow)
1309 w = boardWidth - 2*bor;
1313 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1317 if(!formWidget) return;
1320 * Inhibit shell resizing.
1323 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1326 for(i=0; i<4; i++) {
1328 for(p=0; p<=(int)WhiteKing; p++)
1329 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1330 if(gameInfo.variant == VariantShogi) {
1331 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1332 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1333 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1334 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1335 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1338 if(gameInfo.variant == VariantGothic) {
1339 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1343 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1344 for(p=0; p<=(int)WhiteKing; p++)
1345 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1346 if(gameInfo.variant == VariantShogi) {
1347 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1348 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1349 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1350 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1351 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1354 if(gameInfo.variant == VariantGothic) {
1355 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1361 for(i=0; i<2; i++) {
1363 for(p=0; p<=(int)WhiteKing; p++)
1364 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1365 if(gameInfo.variant == VariantShogi) {
1366 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1367 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1368 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1369 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1370 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1373 if(gameInfo.variant == VariantGothic) {
1374 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1385 void EscapeExpand(char *p, char *q)
1386 { // [HGM] initstring: routine to shape up string arguments
1387 while(*p++ = *q++) if(p[-1] == '\\')
1389 case 'n': p[-1] = '\n'; break;
1390 case 'r': p[-1] = '\r'; break;
1391 case 't': p[-1] = '\t'; break;
1392 case '\\': p[-1] = '\\'; break;
1393 case 0: *p = 0; return;
1394 default: p[-1] = q[-1]; break;
1403 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1404 XSetWindowAttributes window_attributes;
1406 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1407 XrmValue vFrom, vTo;
1408 XtGeometryResult gres;
1411 int forceMono = False;
1413 srandom(time(0)); // [HGM] book: make random truly random
1415 setbuf(stdout, NULL);
1416 setbuf(stderr, NULL);
1419 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1420 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1424 programName = strrchr(argv[0], '/');
1425 if (programName == NULL)
1426 programName = argv[0];
1431 XtSetLanguageProc(NULL, NULL, NULL);
1432 bindtextdomain(PACKAGE, LOCALEDIR);
1433 textdomain(PACKAGE);
1437 gtk_init (&argc, &argv);
1439 /* parse glade file to build widgets */
1441 builder = gtk_builder_new ();
1442 gtk_builder_add_from_file (builder, "gtk-interface.xml", NULL);
1444 /* test if everything worked ok */
1446 GUI_Window = GTK_WIDGET (gtk_builder_get_object (builder, "MainWindow"));
1447 if(!GUI_Window) printf("Error: gtk_builder didn't work!\n");
1449 GUI_Aspect = GTK_WIDGET (gtk_builder_get_object (builder, "Aspectframe"));
1450 if(!GUI_Aspect) printf("Error: gtk_builder didn't work!\n");
1452 GUI_History = GTK_WIDGET (gtk_builder_get_object (builder, "MoveHistory"));
1453 if(!GUI_History) printf("Error: gtk_builder didn't work!\n");
1455 GUI_Menubar = GTK_WIDGET (gtk_builder_get_object (builder, "MenuBar"));
1456 if(!GUI_Menubar) printf("Error: gtk_builder didn't work!\n");
1457 GUI_Timer = GTK_WIDGET (gtk_builder_get_object (builder, "Timer"));
1458 if(!GUI_Timer) printf("Error: gtk_builder didn't work!\n");
1459 GUI_Buttonbar = GTK_WIDGET (gtk_builder_get_object (builder, "ButtonBar"));
1460 if(!GUI_Buttonbar) printf("Error: gtk_builder didn't work!\n");
1461 GUI_Board = GTK_WIDGET (gtk_builder_get_object (builder, "Board"));
1462 if(!GUI_Board) printf("Error: gtk_builder didn't work!\n");
1464 GUI_Whiteclock = GTK_WIDGET (gtk_builder_get_object (builder, "WhiteClock"));
1465 if(!GUI_Whiteclock) printf("Error: gtk_builder didn't work!\n");
1467 GUI_Blackclock = GTK_WIDGET (gtk_builder_get_object (builder, "BlackClock"));
1468 if(!GUI_Blackclock) printf("Error: gtk_builder didn't work!\n");
1470 LIST_MoveHistory = GTK_LIST_STORE (gtk_builder_get_object (builder, "MoveHistoryStore"));
1471 if(!LIST_MoveHistory) printf("Error: gtk_builder didn't work!\n");
1473 /* EditTags window */
1474 GUI_EditTags = GTK_WIDGET (gtk_builder_get_object (builder, "EditTags"));
1475 if(!GUI_EditTags) printf("Error: gtk_builder didn't work!\n");
1477 GUI_EditTagsTextArea = GTK_WIDGET (gtk_builder_get_object (builder, "EditTagsTextArea"));
1478 if(!GUI_EditTagsTextArea) printf("Error: gtk_builder didn't work!\n");
1481 gtk_builder_connect_signals (builder, NULL);
1483 // don't unref the builder, since we use it to get references to widgets
1484 // g_object_unref (G_OBJECT (builder));
1486 /* end parse glade file */
1488 appData.boardSize = "";
1489 InitAppData(ConvertToLine(argc, argv));
1492 if (p == NULL) p = "/tmp";
1493 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1494 gameCopyFilename = (char*) malloc(i);
1495 gamePasteFilename = (char*) malloc(i);
1496 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1497 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1499 // XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1500 // clientResources, XtNumber(clientResources),
1503 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1504 static char buf[MSG_SIZ];
1505 EscapeExpand(buf, appData.initString);
1506 appData.initString = strdup(buf);
1507 EscapeExpand(buf, appData.secondInitString);
1508 appData.secondInitString = strdup(buf);
1509 EscapeExpand(buf, appData.firstComputerString);
1510 appData.firstComputerString = strdup(buf);
1511 EscapeExpand(buf, appData.secondComputerString);
1512 appData.secondComputerString = strdup(buf);
1515 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1518 if (chdir(chessDir) != 0) {
1519 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1525 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1526 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1527 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1528 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1531 setbuf(debugFP, NULL);
1536 /* This feature does not work; animation needs a rewrite */
1537 appData.highlightDragging = FALSE;
1541 gameInfo.variant = StringToVariant(appData.variant);
1542 InitPosition(FALSE);
1547 clockFontPxlSize = 20;
1548 coordFontPxlSize = 20;
1554 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1555 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1558 * Determine what fonts to use.
1560 // appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1561 // clockFontID = XLoadFont(xDisplay, appData.clockFont);
1562 // clockFontStruct = XQueryFont(xDisplay, clockFontID);
1563 // appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1564 // coordFontID = XLoadFont(xDisplay, appData.coordFont);
1565 // coordFontStruct = XQueryFont(xDisplay, coordFontID);
1566 // appData.font = FindFont(appData.font, fontPxlSize);
1567 // countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
1568 // countFontStruct = XQueryFont(xDisplay, countFontID);
1569 // appData.font = FindFont(appData.font, fontPxlSize);
1571 // xdb = XtDatabase(xDisplay);
1572 // XrmPutStringResource(&xdb, "*font", appData.font);
1575 * Detect if there are not enough colors available and adapt.
1577 // if (DefaultDepth(xDisplay, xScreen) <= 2) {
1578 // appData.monoMode = True;
1581 if (!appData.monoMode) {
1582 vFrom.addr = (caddr_t) appData.lightSquareColor;
1583 vFrom.size = strlen(appData.lightSquareColor);
1584 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1585 if (vTo.addr == NULL) {
1586 appData.monoMode = True;
1589 lightSquareColor = *(Pixel *) vTo.addr;
1592 if (!appData.monoMode) {
1593 vFrom.addr = (caddr_t) appData.darkSquareColor;
1594 vFrom.size = strlen(appData.darkSquareColor);
1595 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1596 if (vTo.addr == NULL) {
1597 appData.monoMode = True;
1600 darkSquareColor = *(Pixel *) vTo.addr;
1603 if (!appData.monoMode) {
1604 vFrom.addr = (caddr_t) appData.whitePieceColor;
1605 vFrom.size = strlen(appData.whitePieceColor);
1606 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1607 if (vTo.addr == NULL) {
1608 appData.monoMode = True;
1611 whitePieceColor = *(Pixel *) vTo.addr;
1614 if (!appData.monoMode) {
1615 vFrom.addr = (caddr_t) appData.blackPieceColor;
1616 vFrom.size = strlen(appData.blackPieceColor);
1617 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1618 if (vTo.addr == NULL) {
1619 appData.monoMode = True;
1622 blackPieceColor = *(Pixel *) vTo.addr;
1626 if (!appData.monoMode) {
1627 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1628 vFrom.size = strlen(appData.highlightSquareColor);
1629 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1630 if (vTo.addr == NULL) {
1631 appData.monoMode = True;
1634 highlightSquareColor = *(Pixel *) vTo.addr;
1638 if (!appData.monoMode) {
1639 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1640 vFrom.size = strlen(appData.premoveHighlightColor);
1641 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1642 if (vTo.addr == NULL) {
1643 appData.monoMode = True;
1646 premoveHighlightColor = *(Pixel *) vTo.addr;
1651 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1654 if (appData.bitmapDirectory == NULL ||
1655 appData.bitmapDirectory[0] == NULLCHAR)
1656 appData.bitmapDirectory = DEF_BITMAP_DIR;
1659 if (appData.lowTimeWarning && !appData.monoMode) {
1660 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1661 vFrom.size = strlen(appData.lowTimeWarningColor);
1662 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1663 if (vTo.addr == NULL)
1664 appData.monoMode = True;
1666 lowTimeWarningColor = *(Pixel *) vTo.addr;
1669 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1670 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1671 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1672 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1673 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1674 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1675 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1676 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1677 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1678 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1680 if (appData.colorize) {
1682 _("%s: can't parse color names; disabling colorization\n"),
1685 appData.colorize = FALSE;
1687 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
1688 textColors[ColorNone].attr = 0;
1690 // XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1696 layoutName = "tinyLayout";
1697 } else if (smallLayout) {
1698 layoutName = "smallLayout";
1700 layoutName = "normalLayout";
1703 if (appData.titleInWindow) {
1704 /* todo check what this appdata does */
1707 if (appData.showButtonBar) {
1708 /* TODO hide button bar if requested */
1712 if (appData.titleInWindow)
1717 if (appData.showButtonBar)
1724 if (appData.showButtonBar)
1734 /* set some checkboxes in the menu according to appData */
1736 if (appData.alwaysPromoteToQueen)
1737 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Always Queen")),TRUE);
1739 if (appData.animateDragging)
1740 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Animate Dragging")),TRUE);
1742 if (appData.animate)
1743 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Animate Moving")),TRUE);
1745 if (appData.autoComment)
1746 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Comment")),TRUE);
1748 if (appData.autoCallFlag)
1749 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Flag")),TRUE);
1751 if (appData.autoFlipView)
1752 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Flip View")),TRUE);
1754 if (appData.autoObserve)
1755 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Observe")),TRUE);
1757 if (appData.autoRaiseBoard)
1758 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Raise Board")),TRUE);
1760 if (appData.autoSaveGames)
1761 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Save")),TRUE);
1763 if (appData.saveGameFile[0] != NULLCHAR)
1765 /* Can't turn this off from menu */
1766 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Save")),TRUE);
1767 gtk_action_set_sensitive(GTK_ACTION (gtk_builder_get_object (builder, "menuOptions.Auto Save")),FALSE);
1770 if (appData.blindfold)
1771 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Blindfold")),TRUE);
1773 if (appData.flashCount > 0)
1774 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Flash Moves")),TRUE);
1776 if (appData.getMoveList)
1777 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Get Move List")),TRUE);
1780 if (appData.highlightDragging)
1781 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Highlight Dragging")),TRUE);
1784 if (appData.highlightLastMove)
1785 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Highlight Last Move")),TRUE);
1787 if (appData.icsAlarm)
1788 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.ICS Alarm")),TRUE);
1790 if (appData.ringBellAfterMoves)
1791 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Move Sound")),TRUE);
1793 if (appData.oldSaveStyle)
1794 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Old Save Style")),TRUE);
1796 if (appData.periodicUpdates)
1797 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Periodic Updates")),TRUE);
1799 if (appData.ponderNextMove)
1800 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Ponder Next Move")),TRUE);
1802 if (appData.popupExitMessage)
1803 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Popup Exit Message")),TRUE);
1805 if (appData.popupMoveErrors)
1806 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Popup Move Errors")),TRUE);
1808 if (appData.premove)
1809 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Premove")),TRUE);
1811 if (appData.quietPlay)
1812 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Quit Play")),TRUE);
1814 if (appData.showCoords)
1815 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Show Coords")),TRUE);
1817 if (appData.showThinking)
1818 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Hide Thinking")),TRUE);
1820 if (appData.testLegality)
1821 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Test Legality")),TRUE);
1824 // if (saveSettingsOnExit) {
1825 // XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
1830 /* end setting check boxes */
1832 /* load square colors */
1833 SVGLightSquare = load_pixbuf("svg/LightSquare.svg",squareSize);
1834 SVGDarkSquare = load_pixbuf("svg/DarkSquare.svg",squareSize);
1835 SVGNeutralSquare = load_pixbuf("svg/NeutralSquare.svg",squareSize);
1837 /* use two icons to indicate if it is white's or black's turn */
1838 WhiteIcon = load_pixbuf("svg/icon_white.svg",0);
1839 BlackIcon = load_pixbuf("svg/icon_black.svg",0);
1840 WindowIcon = WhiteIcon;
1841 gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
1844 /* realize window */
1845 gtk_widget_show (GUI_Window);
1847 /* recalc boardsize */
1852 if (appData.animate || appData.animateDragging)
1857 if (errorExitStatus == -1) {
1858 if (appData.icsActive) {
1859 /* We now wait until we see "login:" from the ICS before
1860 sending the logon script (problems with timestamp otherwise) */
1861 /*ICSInitScript();*/
1862 if (appData.icsInputBox) ICSInputBoxPopUp();
1866 signal(SIGWINCH, TermSizeSigHandler);
1868 signal(SIGINT, IntSigHandler);
1869 signal(SIGTERM, IntSigHandler);
1870 if (*appData.cmailGameName != NULLCHAR) {
1871 signal(SIGUSR1, CmailSigHandler);
1874 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1878 * Create a cursor for the board widget.
1879 * (This needs to be called after the window has been created to have access to board-window)
1882 BoardCursor = gdk_cursor_new(GDK_HAND2);
1883 gdk_window_set_cursor(GUI_Board->window, BoardCursor);
1884 gdk_cursor_destroy(BoardCursor);
1889 if (appData.debugMode) fclose(debugFP); // [DM] debug
1896 if (appData.icsActive && oldICSInteractionTitle != NULL) {
1897 DisplayIcsInteractionTitle(oldICSInteractionTitle);
1899 if (saveSettingsOnExit) SaveSettings(settingsFileName);
1900 unlink(gameCopyFilename);
1901 unlink(gamePasteFilename);
1904 RETSIGTYPE TermSizeSigHandler(int sig)
1917 CmailSigHandler(sig)
1923 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1925 /* Activate call-back function CmailSigHandlerCallBack() */
1926 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1928 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1932 CmailSigHandlerCallBack(isr, closure, message, count, error)
1940 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1942 /**** end signal code ****/
1952 f = fopen(appData.icsLogon, "r");
1958 strcat(buf, appData.icsLogon);
1959 f = fopen(buf, "r");
1963 ProcessICSInitScript(f);
1970 EditCommentPopDown();
1980 if (!menuBarWidget) return;
1981 w = XtNameToWidget(menuBarWidget, "menuStep.Revert");
1983 DisplayError("menuStep.Revert", 0);
1985 XtSetSensitive(w, !grey);
1990 SetMenuEnables(enab)
1995 if (!builder) return;
1996 while (enab->name != NULL) {
1997 o = gtk_builder_get_object(builder, enab->name);
1998 if(GTK_IS_WIDGET(o))
1999 gtk_widget_set_sensitive(GTK_WIDGET (o),enab->value);
2002 if(GTK_IS_ACTION(o))
2003 gtk_action_set_sensitive(GTK_ACTION (o),enab->value);
2005 DisplayError(enab->name, 0);
2013 SetMenuEnables(icsEnables);
2016 if (appData.zippyPlay && !appData.noChessProgram) /* [DM] icsEngineAnalyze */
2017 {}; // XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2024 SetMenuEnables(ncpEnables);
2030 SetMenuEnables(gnuEnables);
2036 SetMenuEnables(cmailEnables);
2042 SetMenuEnables(trainingOnEnables);
2043 if (appData.showButtonBar) {
2044 // XtSetSensitive(buttonBarWidget, False);
2050 SetTrainingModeOff()
2052 SetMenuEnables(trainingOffEnables);
2053 if (appData.showButtonBar) {
2054 // XtSetSensitive(buttonBarWidget, True);
2059 SetUserThinkingEnables()
2061 if (appData.noChessProgram) return;
2062 SetMenuEnables(userThinkingEnables);
2066 SetMachineThinkingEnables()
2068 if (appData.noChessProgram) return;
2069 SetMenuEnables(machineThinkingEnables);
2071 case MachinePlaysBlack:
2072 case MachinePlaysWhite:
2073 case TwoMachinesPlay:
2074 // XtSetSensitive(XtNameToWidget(menuBarWidget,
2075 // ModeToWidgetName(gameMode)), True);
2082 #define Abs(n) ((n)<0 ? -(n) : (n))
2085 * Find a font that matches "pattern" that is as close as
2086 * possible to the targetPxlSize. Prefer fonts that are k
2087 * pixels smaller to fonts that are k pixels larger. The
2088 * pattern must be in the X Consortium standard format,
2089 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2090 * The return value should be freed with XtFree when no
2093 char *FindFont(pattern, targetPxlSize)
2097 char **fonts, *p, *best, *scalable, *scalableTail;
2098 int i, j, nfonts, minerr, err, pxlSize;
2101 char **missing_list;
2103 char *def_string, *base_fnt_lst, strInt[3];
2105 XFontStruct **fnt_list;
2107 base_fnt_lst = calloc(1, strlen(pattern) + 3);
2108 sprintf(strInt, "%d", targetPxlSize);
2109 p = strstr(pattern, "--");
2110 strncpy(base_fnt_lst, pattern, p - pattern + 2);
2111 strcat(base_fnt_lst, strInt);
2112 strcat(base_fnt_lst, strchr(p + 2, '-'));
2114 if ((fntSet = XCreateFontSet(xDisplay,
2118 &def_string)) == NULL) {
2120 fprintf(stderr, _("Unable to create font set.\n"));
2124 nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
2126 // fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2127 // if (nfonts < 1) {
2128 // fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2129 // programName, pattern);
2137 for (i=0; i<nfonts; i++) {
2140 if (*p != '-') continue;
2142 if (*p == NULLCHAR) break;
2143 if (*p++ == '-') j++;
2145 if (j < 7) continue;
2148 scalable = fonts[i];
2151 err = pxlSize - targetPxlSize;
2152 if (Abs(err) < Abs(minerr) ||
2153 (minerr > 0 && err < 0 && -err == minerr)) {
2159 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2160 /* If the error is too big and there is a scalable font,
2161 use the scalable font. */
2162 int headlen = scalableTail - scalable;
2163 p = (char *) XtMalloc(strlen(scalable) + 10);
2164 while (isdigit(*scalableTail)) scalableTail++;
2165 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2167 p = (char *) XtMalloc(strlen(best) + 1);
2170 if (appData.debugMode) {
2171 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2172 pattern, targetPxlSize, p);
2175 if (missing_count > 0)
2176 XFreeStringList(missing_list);
2177 // XFreeFontSet(xDisplay, fntSet);
2179 XFreeFontNames(fonts);
2186 /* 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*/
2195 for(i=0;i<MAXPIECES;i++)
2199 g_free(SVGpieces[i]);
2206 SVGLightSquare = load_pixbuf("svg/LightSquare.svg",squareSize);
2207 SVGDarkSquare = load_pixbuf("svg/DarkSquare.svg",squareSize);
2208 SVGNeutralSquare = load_pixbuf("svg/NeutralSquare.svg",squareSize);
2211 /* get some defaults going */
2212 for(i=WhitePawn; i<DemotePiece+1; i++)
2213 SVGpieces[i] = load_pixbuf("svg/NeutralSquare.svg",squareSize);
2215 SVGpieces[WhitePawn] = load_pixbuf("svg/WhitePawn.svg",squareSize);
2216 SVGpieces[WhiteKnight] = load_pixbuf("svg/WhiteKnight.svg",squareSize);
2217 SVGpieces[WhiteBishop] = load_pixbuf("svg/WhiteBishop.svg",squareSize);
2218 SVGpieces[WhiteRook] = load_pixbuf("svg/WhiteRook.svg",squareSize);
2219 SVGpieces[WhiteQueen] = load_pixbuf("svg/WhiteQueen.svg",squareSize);
2220 SVGpieces[WhiteKing] = load_pixbuf("svg/WhiteKing.svg",squareSize);
2222 SVGpieces[BlackPawn] = load_pixbuf("svg/BlackPawn.svg",squareSize);
2223 SVGpieces[BlackKnight] = load_pixbuf("svg/BlackKnight.svg",squareSize);
2224 SVGpieces[BlackBishop] = load_pixbuf("svg/BlackBishop.svg",squareSize);
2225 SVGpieces[BlackRook] = load_pixbuf("svg/BlackRook.svg",squareSize);
2226 SVGpieces[BlackQueen] = load_pixbuf("svg/BlackQueen.svg",squareSize);
2227 SVGpieces[BlackKing] = load_pixbuf("svg/BlackKing.svg",squareSize);
2233 static void MenuBarSelect(w, addr, index)
2238 XtActionProc proc = (XtActionProc) addr;
2240 (proc)(NULL, NULL, NULL, NULL);
2243 void CreateMenuBarPopup(parent, name, mb)
2253 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2256 XtSetArg(args[j], XtNleftMargin, 20); j++;
2257 XtSetArg(args[j], XtNrightMargin, 20); j++;
2259 while (mi->string != NULL) {
2260 if (strcmp(mi->string, "----") == 0) {
2261 entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
2264 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
2265 entry = XtCreateManagedWidget(mi->string, smeBSBObjectClass,
2267 XtAddCallback(entry, XtNcallback,
2268 (XtCallbackProc) MenuBarSelect,
2269 (caddr_t) mi->proc);
2275 Widget CreateMenuBar(mb)
2279 Widget anchor, menuBar;
2281 char menuName[MSG_SIZ];
2284 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2285 XtSetArg(args[j], XtNvSpace, 0); j++;
2286 XtSetArg(args[j], XtNborderWidth, 0); j++;
2287 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
2288 formWidget, args, j);
2290 while (mb->name != NULL) {
2291 strcpy(menuName, "menu");
2292 strcat(menuName, mb->name);
2294 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
2297 shortName[0] = _(mb->name)[0];
2298 shortName[1] = NULLCHAR;
2299 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
2302 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
2305 XtSetArg(args[j], XtNborderWidth, 0); j++;
2306 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
2308 CreateMenuBarPopup(menuBar, menuName, mb);
2316 CreatePieceMenu(name, color)
2323 ChessSquare selection;
2325 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2326 boardWidget, args, 0);
2328 for (i = 0; i < PIECE_MENU_SIZE; i++) {
2329 String item = pieceMenuStrings[color][i];
2331 if (strcmp(item, "----") == 0) {
2332 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2335 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2336 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2338 selection = pieceMenuTranslation[color][i];
2339 XtAddCallback(entry, XtNcallback,
2340 (XtCallbackProc) PieceMenuSelect,
2341 (caddr_t) selection);
2342 if (selection == WhitePawn || selection == BlackPawn) {
2343 XtSetArg(args[0], XtNpopupOnEntry, entry);
2344 XtSetValues(menu, args, 1);
2357 ChessSquare selection;
2359 // whitePieceMenu = CreatePieceMenu("menuW", 0);
2360 // blackPieceMenu = CreatePieceMenu("menuB", 1);
2362 // XtRegisterGrabAction(PieceMenuPopup, True,
2363 // (unsigned)(ButtonPressMask|ButtonReleaseMask),
2364 // GrabModeAsync, GrabModeAsync);
2366 // XtSetArg(args[0], XtNlabel, _("Drop"));
2367 // dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
2368 // boardWidget, args, 1);
2369 // for (i = 0; i < DROP_MENU_SIZE; i++) {
2370 // String item = dropMenuStrings[i];
2372 // if (strcmp(item, "----") == 0) {
2373 // entry = XtCreateManagedWidget(item, smeLineObjectClass,
2374 // dropMenu, NULL, 0);
2376 // XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2377 // entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2378 // dropMenu, args, 1);
2379 // selection = dropMenuTranslation[i];
2380 // XtAddCallback(entry, XtNcallback,
2381 // (XtCallbackProc) DropMenuSelect,
2382 // (caddr_t) selection);
2387 void SetupDropMenu()
2395 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2396 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2397 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2398 dmEnables[i].piece);
2399 XtSetSensitive(entry, p != NULL || !appData.testLegality
2400 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2401 && !appData.icsActive));
2403 while (p && *p++ == dmEnables[i].piece) count++;
2404 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2406 XtSetArg(args[j], XtNlabel, label); j++;
2407 XtSetValues(entry, args, j);
2411 void PieceMenuPopup(w, event, params, num_params)
2415 Cardinal *num_params;
2419 if (event->type != ButtonRelease) UnLoadPV(); // [HGM] pv
2420 if (event->type != ButtonPress) return;
2421 if (errorUp) ErrorPopDown();
2425 whichMenu = params[0];
2428 if(!appData.icsEngineAnalyze) return;
2429 case IcsPlayingWhite:
2430 case IcsPlayingBlack:
2431 if(!appData.zippyPlay) goto noZip;
2434 case MachinePlaysWhite:
2435 case MachinePlaysBlack:
2436 case TwoMachinesPlay: // [HGM] pv: use for showing PV
2437 if (!appData.dropMenu) {
2438 LoadPV(event->xbutton.x, event->xbutton.y);
2441 if(gameMode == TwoMachinesPlay || gameMode == AnalyzeMode ||
2442 gameMode == AnalyzeFile || gameMode == IcsObserving) return;
2445 if (!appData.dropMenu || appData.testLegality &&
2446 gameInfo.variant != VariantBughouse &&
2447 gameInfo.variant != VariantCrazyhouse) return;
2449 whichMenu = "menuD";
2455 if (((pmFromX = EventToSquare(event->xbutton.x, BOARD_WIDTH)) < 0) ||
2456 ((pmFromY = EventToSquare(event->xbutton.y, BOARD_HEIGHT)) < 0)) {
2457 pmFromX = pmFromY = -1;
2461 pmFromX = BOARD_WIDTH - 1 - pmFromX;
2463 pmFromY = BOARD_HEIGHT - 1 - pmFromY;
2465 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
2468 static void PieceMenuSelect(w, piece, junk)
2473 if (pmFromX < 0 || pmFromY < 0) return;
2474 EditPositionMenuEvent(piece, pmFromX, pmFromY);
2477 static void DropMenuSelect(w, piece, junk)
2482 if (pmFromX < 0 || pmFromY < 0) return;
2483 DropMenuEvent(piece, pmFromX, pmFromY);
2487 * If the user selects on a border boundary, return -1; if off the board,
2488 * return -2. Otherwise map the event coordinate to the square.
2490 int EventToSquare(x, limit)
2498 if ((x % (squareSize + lineGap)) >= squareSize)
2500 x /= (squareSize + lineGap);
2506 static void do_flash_delay(msec)
2512 static void drawHighlight(file, rank, line_type)
2513 int file, rank, line_type;
2518 if (lineGap == 0 || appData.blindfold) return;
2522 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
2523 (squareSize + lineGap);
2524 y = lineGap/2 + rank * (squareSize + lineGap);
2528 x = lineGap/2 + file * (squareSize + lineGap);
2529 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
2530 (squareSize + lineGap);
2534 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2536 /* draw the highlight */
2537 cairo_move_to (cr, x, y);
2538 cairo_rel_line_to (cr, 0,squareSize+lineGap);
2539 cairo_rel_line_to (cr, squareSize+lineGap,0);
2540 cairo_rel_line_to (cr, 0,-squareSize-lineGap);
2541 cairo_close_path (cr);
2543 cairo_set_line_width (cr, lineGap);
2546 /* TODO: use appdata colors */
2547 case LINE_TYPE_HIGHLIGHT:
2548 cairo_set_source_rgba (cr, 1, 1, 0, 1.0);
2551 cairo_set_source_rgba (cr, 1, 0, 0, 1.0);
2553 case LINE_TYPE_NORMAL:
2555 cairo_set_source_rgba (cr, 0, 1, 0, 1.0);
2566 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
2567 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
2570 SetHighlights(fromX, fromY, toX, toY)
2571 int fromX, fromY, toX, toY;
2573 if (hi1X != fromX || hi1Y != fromY)
2575 if (hi1X >= 0 && hi1Y >= 0)
2577 drawHighlight(hi1X, hi1Y, LINE_TYPE_NORMAL);
2579 if (fromX >= 0 && fromY >= 0)
2581 drawHighlight(fromX, fromY, LINE_TYPE_HIGHLIGHT);
2584 if (hi2X != toX || hi2Y != toY)
2586 if (hi2X >= 0 && hi2Y >= 0)
2588 drawHighlight(hi2X, hi2Y, LINE_TYPE_NORMAL);
2590 if (toX >= 0 && toY >= 0)
2592 drawHighlight(toX, toY, LINE_TYPE_HIGHLIGHT);
2606 SetHighlights(-1, -1, -1, -1);
2611 SetPremoveHighlights(fromX, fromY, toX, toY)
2612 int fromX, fromY, toX, toY;
2614 if (pm1X != fromX || pm1Y != fromY)
2616 if (pm1X >= 0 && pm1Y >= 0)
2618 drawHighlight(pm1X, pm1Y, LINE_TYPE_NORMAL);
2620 if (fromX >= 0 && fromY >= 0)
2622 drawHighlight(fromX, fromY, LINE_TYPE_PRE);
2625 if (pm2X != toX || pm2Y != toY)
2627 if (pm2X >= 0 && pm2Y >= 0)
2629 drawHighlight(pm2X, pm2Y, LINE_TYPE_NORMAL);
2631 if (toX >= 0 && toY >= 0)
2633 drawHighlight(toX, toY, LINE_TYPE_PRE);
2646 ClearPremoveHighlights()
2648 SetPremoveHighlights(-1, -1, -1, -1);
2651 static void BlankSquare(x, y, color, piece, dest)
2664 pb = SVGLightSquare;
2666 case 2: /* neutral */
2668 pb = SVGNeutralSquare;
2671 gdk_draw_pixbuf(GDK_WINDOW(GUI_Board->window),NULL,pb,0,0,x,y,-1,-1, GDK_RGB_DITHER_NORMAL, 0, 0);
2675 static void DrawPiece(piece, square_color, x, y, dest)
2677 int square_color, x, y;
2680 /* redraw background, since piece might be transparent in some areas */
2681 BlankSquare(x,y,square_color,piece,dest);
2684 gdk_draw_pixbuf(GDK_WINDOW(GUI_Board->window),NULL,
2685 GDK_PIXBUF(SVGpieces[piece]),0,0,x,y,-1,-1,
2686 GDK_RGB_DITHER_NORMAL, 0, 0);
2690 /* [HR] determine square color depending on chess variant. */
2691 static int SquareColor(row, column)
2696 if (gameInfo.variant == VariantXiangqi) {
2697 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
2699 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
2701 } else if (row <= 4) {
2707 square_color = ((column + row) % 2) == 1;
2710 /* [hgm] holdings: next line makes all holdings squares light */
2711 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
2713 return square_color;
2716 void DrawSquare(row, column, piece, do_flash)
2717 int row, column, do_flash;
2720 int square_color, x, y;
2725 /* Calculate delay in milliseconds (2-delays per complete flash) */
2726 flash_delay = 500 / appData.flashRate;
2728 /* calculate x and y coordinates from row and column */
2731 x = lineGap + ((BOARD_WIDTH-1)-column) *
2732 (squareSize + lineGap);
2733 y = lineGap + row * (squareSize + lineGap);
2737 x = lineGap + column * (squareSize + lineGap);
2738 y = lineGap + ((BOARD_HEIGHT-1)-row) *
2739 (squareSize + lineGap);
2742 square_color = SquareColor(row, column);
2744 // [HGM] holdings: blank out area between board and holdings
2745 if ( column == BOARD_LEFT-1 || column == BOARD_RGHT
2746 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
2747 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) )
2749 BlankSquare(x, y, 2, EmptySquare, xBoardWindow);
2751 // [HGM] print piece counts next to holdings
2752 string[1] = NULLCHAR;
2755 cairo_text_extents_t extents;
2760 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2762 string[0] = '0' + piece;
2764 /* TODO this has to go into the font-selection */
2765 cairo_select_font_face (cr, "Sans",
2766 CAIRO_FONT_SLANT_NORMAL,
2767 CAIRO_FONT_WEIGHT_NORMAL);
2769 cairo_set_font_size (cr, 12.0);
2770 cairo_text_extents (cr, string, &extents);
2772 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) )
2774 xpos= x + squareSize - extents.width - 2;
2775 ypos= y + extents.y_bearing + 1;
2777 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1)
2780 ypos = y + extents.y_bearing + 1;
2783 /* TODO mono mode? */
2784 cairo_move_to (cr, xpos, ypos);
2785 cairo_text_path (cr, string);
2786 cairo_set_source_rgb (cr, 1.0, 1.0, 1);
2787 cairo_fill_preserve (cr);
2788 cairo_set_source_rgb (cr, 0, 0, 0);
2789 cairo_set_line_width (cr, 0.1);
2798 /* square on the board */
2799 if (piece == EmptySquare || appData.blindfold)
2801 BlankSquare(x, y, square_color, piece, xBoardWindow);
2805 if (do_flash && appData.flashCount > 0)
2807 for (i=0; i<appData.flashCount; ++i)
2810 DrawPiece(piece, square_color, x, y, xBoardWindow);
2811 do_flash_delay(flash_delay);
2813 BlankSquare(x, y, square_color, piece, xBoardWindow);
2814 do_flash_delay(flash_delay);
2817 DrawPiece(piece, square_color, x, y, xBoardWindow);
2821 /* show coordinates if necessary */
2822 if(appData.showCoords)
2824 cairo_text_extents_t extents;
2828 /* TODO this has to go into the font-selection */
2829 cairo_select_font_face (cr, "Sans",
2830 CAIRO_FONT_SLANT_NORMAL,
2831 CAIRO_FONT_WEIGHT_NORMAL);
2832 cairo_set_font_size (cr, 12.0);
2834 string[1] = NULLCHAR;
2837 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2839 if (row == (flipView ? BOARD_HEIGHT-1 : 0) &&
2840 column >= BOARD_LEFT && column < BOARD_RGHT)
2842 string[0] = 'a' + column - BOARD_LEFT;
2843 cairo_text_extents (cr, string, &extents);
2845 xpos = x + squareSize - extents.width - 2;
2846 ypos = y + squareSize - extents.height - extents.y_bearing - 1;
2848 if (appData.monoMode)
2855 cairo_move_to (cr, xpos, ypos);
2856 cairo_text_path (cr, string);
2857 cairo_set_source_rgb (cr, 0.0, 0.0, 0);
2858 cairo_fill_preserve (cr);
2859 cairo_set_source_rgb (cr, 0, 1.0, 0);
2860 cairo_set_line_width (cr, 0.1);
2863 if ( column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT))
2866 string[0] = ONE + row;
2867 cairo_text_extents (cr, string, &extents);
2870 ypos = y + extents.height + 1;
2872 if (appData.monoMode)
2879 cairo_move_to (cr, xpos, ypos);
2880 cairo_text_path (cr, string);
2881 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
2882 cairo_fill_preserve (cr);
2883 cairo_set_source_rgb (cr, 0, 0, 1.0);
2884 cairo_set_line_width (cr, 0.1);
2896 /* Returns 1 if there are "too many" differences between b1 and b2
2897 (i.e. more than 1 move was made) */
2898 static int too_many_diffs(b1, b2)
2904 for (i=0; i<BOARD_HEIGHT; ++i) {
2905 for (j=0; j<BOARD_WIDTH; ++j) {
2906 if (b1[i][j] != b2[i][j]) {
2907 if (++c > 4) /* Castling causes 4 diffs */
2916 /* Matrix describing castling maneuvers */
2917 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
2918 static int castling_matrix[4][5] = {
2919 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
2920 { 0, 7, 4, 5, 6 }, /* 0-0, white */
2921 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
2922 { 7, 7, 4, 5, 6 } /* 0-0, black */
2925 /* Checks whether castling occurred. If it did, *rrow and *rcol
2926 are set to the destination (row,col) of the rook that moved.
2928 Returns 1 if castling occurred, 0 if not.
2930 Note: Only handles a max of 1 castling move, so be sure
2931 to call too_many_diffs() first.
2933 static int check_castle_draw(newb, oldb, rrow, rcol)
2940 /* For each type of castling... */
2941 for (i=0; i<4; ++i) {
2942 r = castling_matrix[i];
2944 /* Check the 4 squares involved in the castling move */
2946 for (j=1; j<=4; ++j) {
2947 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
2954 /* All 4 changed, so it must be a castling move */
2963 static int damage[BOARD_RANKS][BOARD_FILES];
2966 * event handler for redrawing the board
2968 void DrawPosition( repaint, board)
2969 /*Boolean*/int repaint;
2973 static int lastFlipView = 0;
2974 static int lastBoardValid = 0;
2975 static Board lastBoard;
2978 if (board == NULL) {
2979 if (!lastBoardValid) return;
2982 if (!lastBoardValid || lastFlipView != flipView) {
2983 // XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
2984 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flip View"),
2989 * It would be simpler to clear the window with XClearWindow()
2990 * but this causes a very distracting flicker.
2993 if (!repaint && lastBoardValid && lastFlipView == flipView)
2995 /* If too much changes (begin observing new game, etc.), don't
2997 do_flash = too_many_diffs(board, lastBoard) ? 0 : 1;
2999 /* Special check for castling so we don't flash both the king
3000 and the rook (just flash the king). */
3003 if (check_castle_draw(board, lastBoard, &rrow, &rcol))
3005 /* Draw rook with NO flashing. King will be drawn flashing later */
3006 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
3007 lastBoard[rrow][rcol] = board[rrow][rcol];
3011 /* First pass -- Draw (newly) empty squares and repair damage.
3012 This prevents you from having a piece show up twice while it
3013 is flashing on its new square */
3014 for (i = 0; i < BOARD_HEIGHT; i++)
3015 for (j = 0; j < BOARD_WIDTH; j++)
3016 if ((board[i][j] != lastBoard[i][j] && board[i][j] == EmptySquare)
3019 DrawSquare(i, j, board[i][j], 0);
3020 damage[i][j] = False;
3023 /* Second pass -- Draw piece(s) in new position and flash them */
3024 for (i = 0; i < BOARD_HEIGHT; i++)
3025 for (j = 0; j < BOARD_WIDTH; j++)
3026 if (board[i][j] != lastBoard[i][j])
3028 DrawSquare(i, j, board[i][j], do_flash);
3040 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
3042 cairo_set_line_width (cr, lineGap);
3044 /* TODO: use appdata colors */
3045 cairo_set_source_rgba (cr, 0, 1, 0, 1.0);
3049 for (i = 0; i < BOARD_HEIGHT + 1; i++)
3052 x2 = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3053 y1 = y2 = lineGap / 2 + (i * (squareSize + lineGap));
3055 cairo_move_to (cr, x1, y1);
3056 cairo_rel_line_to (cr, x2,0);
3060 for (j = 0; j < BOARD_WIDTH + 1; j++)
3063 y2 = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3064 x1 = x2 = lineGap / 2 + (j * (squareSize + lineGap));
3066 cairo_move_to (cr, x1, y1);
3067 cairo_rel_line_to (cr, 0, y2);
3076 for (i = 0; i < BOARD_HEIGHT; i++)
3077 for (j = 0; j < BOARD_WIDTH; j++)
3079 DrawSquare(i, j, board[i][j], 0);
3080 damage[i][j] = False;
3084 CopyBoard(lastBoard, board);
3086 lastFlipView = flipView;
3088 /* Draw highlights */
3089 if (pm1X >= 0 && pm1Y >= 0)
3091 drawHighlight(pm1X, pm1Y, LINE_TYPE_PRE);
3093 if (pm2X >= 0 && pm2Y >= 0)
3095 drawHighlight(pm2X, pm2Y, LINE_TYPE_PRE);
3097 if (hi1X >= 0 && hi1Y >= 0)
3099 drawHighlight(hi1X, hi1Y, LINE_TYPE_HIGHLIGHT);
3101 if (hi2X >= 0 && hi2Y >= 0)
3103 drawHighlight(hi2X, hi2Y, LINE_TYPE_HIGHLIGHT);
3106 /* If piece being dragged around board, must redraw that too */
3112 void AnimateUserMove (Widget w, XEvent * event,
3113 String * params, Cardinal * nParams)
3115 DragPieceMove(event->xmotion.x, event->xmotion.y);
3118 void HandlePV (Widget w, XEvent * event,
3119 String * params, Cardinal * nParams)
3120 { // [HGM] pv: walk PV
3121 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3124 Widget CommentCreate(name, text, mutable, callback, lines)
3126 int /*Boolean*/ mutable;
3127 XtCallbackProc callback;
3131 Widget shell, layout, form, edit, b_ok, b_cancel, b_clear, b_close, b_edit;
3136 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3137 XtGetValues(boardWidget, args, j);
3140 XtSetArg(args[j], XtNresizable, True); j++;
3143 // XtCreatePopupShell(name, topLevelShellWidgetClass,
3144 // shellWidget, args, j);
3147 // XtCreatePopupShell(name, transientShellWidgetClass,
3148 // shellWidget, args, j);
3151 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
3152 layoutArgs, XtNumber(layoutArgs));
3154 XtCreateManagedWidget("form", formWidgetClass, layout,
3155 formArgs, XtNumber(formArgs));
3159 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
3160 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
3162 XtSetArg(args[j], XtNstring, text); j++;
3163 XtSetArg(args[j], XtNtop, XtChainTop); j++;
3164 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3165 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3166 XtSetArg(args[j], XtNright, XtChainRight); j++;
3167 XtSetArg(args[j], XtNresizable, True); j++;
3168 XtSetArg(args[j], XtNwidth, bw_width); j++; /*force wider than buttons*/
3169 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
3170 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
3171 XtSetArg(args[j], XtNautoFill, True); j++;
3172 XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
3174 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
3178 XtSetArg(args[j], XtNfromVert, edit); j++;
3179 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3180 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3181 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3182 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3184 XtCreateManagedWidget(_("ok"), commandWidgetClass, form, args, j);
3185 XtAddCallback(b_ok, XtNcallback, callback, (XtPointer) 0);
3188 XtSetArg(args[j], XtNfromVert, edit); j++;
3189 XtSetArg(args[j], XtNfromHoriz, b_ok); j++;
3190 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3191 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3192 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3193 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3195 XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
3196 XtAddCallback(b_cancel, XtNcallback, callback, (XtPointer) 0);
3199 XtSetArg(args[j], XtNfromVert, edit); j++;
3200 XtSetArg(args[j], XtNfromHoriz, b_cancel); j++;
3201 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3202 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3203 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3204 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3206 XtCreateManagedWidget(_("clear"), commandWidgetClass, form, args, j);
3207 XtAddCallback(b_clear, XtNcallback, callback, (XtPointer) 0);
3210 XtSetArg(args[j], XtNfromVert, edit); j++;
3211 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3212 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3213 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3214 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3216 XtCreateManagedWidget(_("close"), commandWidgetClass, form, args, j);
3217 XtAddCallback(b_close, XtNcallback, callback, (XtPointer) 0);
3220 XtSetArg(args[j], XtNfromVert, edit); j++;
3221 XtSetArg(args[j], XtNfromHoriz, b_close); j++;
3222 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3223 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3224 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3225 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3227 XtCreateManagedWidget(_("edit"), commandWidgetClass, form, args, j);
3228 XtAddCallback(b_edit, XtNcallback, callback, (XtPointer) 0);
3231 XtRealizeWidget(shell);
3233 if (commentX == -1) {
3236 Dimension pw_height;
3237 Dimension ew_height;
3240 XtSetArg(args[j], XtNheight, &ew_height); j++;
3241 XtGetValues(edit, args, j);
3244 XtSetArg(args[j], XtNheight, &pw_height); j++;
3245 XtGetValues(shell, args, j);
3246 commentH = pw_height + (lines - 1) * ew_height;
3247 commentW = bw_width - 16;
3249 // XSync(xDisplay, False);
3251 /* This code seems to tickle an X bug if it is executed too soon
3252 after xboard starts up. The coordinates get transformed as if
3253 the main window was positioned at (0, 0).
3255 // XtTranslateCoords(shellWidget,
3256 // (bw_width - commentW) / 2, 0 - commentH / 2,
3257 // &commentX, &commentY);
3259 // XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
3260 // RootWindowOfScreen(XtScreen(shellWidget)),
3261 // (bw_width - commentW) / 2, 0 - commentH / 2,
3262 // &xx, &yy, &junk);
3266 if (commentY < 0) commentY = 0; /*avoid positioning top offscreen*/
3269 if(wpComment.width > 0) {
3270 commentX = wpComment.x;
3271 commentY = wpComment.y;
3272 commentW = wpComment.width;
3273 commentH = wpComment.height;
3277 XtSetArg(args[j], XtNheight, commentH); j++;
3278 XtSetArg(args[j], XtNwidth, commentW); j++;
3279 XtSetArg(args[j], XtNx, commentX); j++;
3280 XtSetArg(args[j], XtNy, commentY); j++;
3281 XtSetValues(shell, args, j);
3282 XtSetKeyboardFocus(shell, edit);
3287 /* Used for analysis window and ICS input window */
3288 Widget MiscCreate(name, text, mutable, callback, lines)
3290 int /*Boolean*/ mutable;
3291 XtCallbackProc callback;
3295 Widget shell, layout, form, edit;
3297 Dimension bw_width, pw_height, ew_height, w, h;
3303 XtSetArg(args[j], XtNresizable, True); j++;
3306 // XtCreatePopupShell(name, topLevelShellWidgetClass,
3307 // shellWidget, args, j);
3310 // XtCreatePopupShell(name, transientShellWidgetClass,
3311 // shellWidget, args, j);
3314 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
3315 layoutArgs, XtNumber(layoutArgs));
3317 XtCreateManagedWidget("form", formWidgetClass, layout,
3318 formArgs, XtNumber(formArgs));
3322 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
3323 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
3325 XtSetArg(args[j], XtNstring, text); j++;
3326 XtSetArg(args[j], XtNtop, XtChainTop); j++;
3327 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3328 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3329 XtSetArg(args[j], XtNright, XtChainRight); j++;
3330 XtSetArg(args[j], XtNresizable, True); j++;
3331 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
3332 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
3333 XtSetArg(args[j], XtNautoFill, True); j++;
3334 XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
3336 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
3338 XtRealizeWidget(shell);
3341 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3342 XtGetValues(boardWidget, args, j);
3345 XtSetArg(args[j], XtNheight, &ew_height); j++;
3346 XtGetValues(edit, args, j);
3349 XtSetArg(args[j], XtNheight, &pw_height); j++;
3350 XtGetValues(shell, args, j);
3351 h = pw_height + (lines - 1) * ew_height;
3354 // XSync(xDisplay, False);
3356 /* This code seems to tickle an X bug if it is executed too soon
3357 after xboard starts up. The coordinates get transformed as if
3358 the main window was positioned at (0, 0).
3360 // XtTranslateCoords(shellWidget, (bw_width - w) / 2, 0 - h / 2, &x, &y);
3362 // XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
3363 // RootWindowOfScreen(XtScreen(shellWidget)),
3364 // (bw_width - w) / 2, 0 - h / 2, &xx, &yy, &junk);
3368 if (y < 0) y = 0; /*avoid positioning top offscreen*/
3371 XtSetArg(args[j], XtNheight, h); j++;
3372 XtSetArg(args[j], XtNwidth, w); j++;
3373 XtSetArg(args[j], XtNx, x); j++;
3374 XtSetArg(args[j], XtNy, y); j++;
3375 XtSetValues(shell, args, j);
3381 static int savedIndex; /* gross that this is global */
3383 void EditCommentPopUp(index, title, text)
3392 if (text == NULL) text = "";
3394 if (editShell == NULL) {
3396 CommentCreate(title, text, True, EditCommentCallback, 4);
3397 XtRealizeWidget(editShell);
3398 // CatchDeleteWindow(editShell, "EditCommentPopDown");
3400 edit = XtNameToWidget(editShell, "*form.text");
3402 XtSetArg(args[j], XtNstring, text); j++;
3403 XtSetValues(edit, args, j);
3405 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
3406 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
3407 XtSetValues(editShell, args, j);
3410 XtPopup(editShell, XtGrabNone);
3414 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
3415 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
3419 void EditCommentCallback(w, client_data, call_data)
3421 XtPointer client_data, call_data;
3429 XtSetArg(args[j], XtNlabel, &name); j++;
3430 XtGetValues(w, args, j);
3432 if (strcmp(name, _("ok")) == 0) {
3433 edit = XtNameToWidget(editShell, "*form.text");
3435 XtSetArg(args[j], XtNstring, &val); j++;
3436 XtGetValues(edit, args, j);
3437 ReplaceComment(savedIndex, val);
3438 EditCommentPopDown();
3439 } else if (strcmp(name, _("cancel")) == 0) {
3440 EditCommentPopDown();
3441 } else if (strcmp(name, _("clear")) == 0) {
3442 edit = XtNameToWidget(editShell, "*form.text");
3443 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
3444 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
3448 void EditCommentPopDown()
3453 if (!editUp) return;
3455 XtSetArg(args[j], XtNx, &commentX); j++;
3456 XtSetArg(args[j], XtNy, &commentY); j++;
3457 XtSetArg(args[j], XtNheight, &commentH); j++;
3458 XtSetArg(args[j], XtNwidth, &commentW); j++;
3459 XtGetValues(editShell, args, j);
3460 XtPopdown(editShell);
3463 XtSetArg(args[j], XtNleftBitmap, None); j++;
3464 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
3468 void ICSInputBoxPopUp()
3473 char *title = _("ICS Input");
3476 if (ICSInputShell == NULL) {
3477 ICSInputShell = MiscCreate(title, "", True, NULL, 1);
3478 tr = XtParseTranslationTable(ICSInputTranslations);
3479 edit = XtNameToWidget(ICSInputShell, "*form.text");
3480 XtOverrideTranslations(edit, tr);
3481 XtRealizeWidget(ICSInputShell);
3482 // CatchDeleteWindow(ICSInputShell, "ICSInputBoxPopDown");
3485 edit = XtNameToWidget(ICSInputShell, "*form.text");
3487 XtSetArg(args[j], XtNstring, ""); j++;
3488 XtSetValues(edit, args, j);
3490 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
3491 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
3492 XtSetValues(ICSInputShell, args, j);
3495 XtPopup(ICSInputShell, XtGrabNone);
3496 XtSetKeyboardFocus(ICSInputShell, edit);
3498 ICSInputBoxUp = True;
3500 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
3501 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
3505 void ICSInputSendText()
3512 edit = XtNameToWidget(ICSInputShell, "*form.text");
3514 XtSetArg(args[j], XtNstring, &val); j++;
3515 XtGetValues(edit, args, j);
3516 SendMultiLineToICS(val);
3517 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
3518 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
3521 void ICSInputBoxPopDown()
3526 if (!ICSInputBoxUp) return;
3528 XtPopdown(ICSInputShell);
3529 ICSInputBoxUp = False;
3531 XtSetArg(args[j], XtNleftBitmap, None); j++;
3532 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
3536 void CommentPopUp(title, text)
3543 if (commentShell == NULL) {
3545 CommentCreate(title, text, False, CommentCallback, 4);
3546 XtRealizeWidget(commentShell);
3547 // CatchDeleteWindow(commentShell, "CommentPopDown");
3549 edit = XtNameToWidget(commentShell, "*form.text");
3551 XtSetArg(args[j], XtNstring, text); j++;
3552 XtSetValues(edit, args, j);
3554 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
3555 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
3556 XtSetValues(commentShell, args, j);
3559 XtPopup(commentShell, XtGrabNone);
3560 // XSync(xDisplay, False);
3565 void CommentCallback(w, client_data, call_data)
3567 XtPointer client_data, call_data;
3574 XtSetArg(args[j], XtNlabel, &name); j++;
3575 XtGetValues(w, args, j);
3577 if (strcmp(name, _("close")) == 0) {
3579 } else if (strcmp(name, _("edit")) == 0) {
3586 void CommentPopDown()
3591 if (!commentUp) return;
3593 XtSetArg(args[j], XtNx, &commentX); j++;
3594 XtSetArg(args[j], XtNy, &commentY); j++;
3595 XtSetArg(args[j], XtNwidth, &commentW); j++;
3596 XtSetArg(args[j], XtNheight, &commentH); j++;
3597 XtGetValues(commentShell, args, j);
3598 XtPopdown(commentShell);
3599 // XSync(xDisplay, False);
3603 void PromotionPopUp()
3606 Widget dialog, layout;
3608 Dimension bw_width, pw_width;
3612 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3613 XtGetValues(boardWidget, args, j);
3616 XtSetArg(args[j], XtNresizable, True); j++;
3617 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
3619 // XtCreatePopupShell("Promotion", transientShellWidgetClass,
3620 // shellWidget, args, j);
3622 // XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
3623 // layoutArgs, XtNumber(layoutArgs));
3626 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
3627 XtSetArg(args[j], XtNborderWidth, 0); j++;
3628 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
3631 if(gameInfo.variant != VariantShogi) {
3632 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
3633 (XtPointer) dialog);
3634 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
3635 (XtPointer) dialog);
3636 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
3637 (XtPointer) dialog);
3638 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
3639 (XtPointer) dialog);
3640 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
3641 gameInfo.variant == VariantGiveaway) {
3642 XawDialogAddButton(dialog, _("King"), PromotionCallback,
3643 (XtPointer) dialog);
3645 if(gameInfo.variant == VariantCapablanca ||
3646 gameInfo.variant == VariantGothic ||
3647 gameInfo.variant == VariantCapaRandom) {
3648 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
3649 (XtPointer) dialog);
3650 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
3651 (XtPointer) dialog);
3653 } else // [HGM] shogi
3655 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
3656 (XtPointer) dialog);
3657 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
3658 (XtPointer) dialog);
3660 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
3661 (XtPointer) dialog);
3663 XtRealizeWidget(promotionShell);
3664 // CatchDeleteWindow(promotionShell, "PromotionPopDown");
3667 XtSetArg(args[j], XtNwidth, &pw_width); j++;
3668 XtGetValues(promotionShell, args, j);
3670 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3671 lineGap + squareSize/3 +
3672 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
3673 0 : 6*(squareSize + lineGap)), &x, &y);
3676 XtSetArg(args[j], XtNx, x); j++;
3677 XtSetArg(args[j], XtNy, y); j++;
3678 XtSetValues(promotionShell, args, j);
3680 XtPopup(promotionShell, XtGrabNone);
3685 void PromotionPopDown()
3687 if (!promotionUp) return;
3688 XtPopdown(promotionShell);
3689 XtDestroyWidget(promotionShell);
3690 promotionUp = False;
3693 void PromotionCallback(w, client_data, call_data)
3695 XtPointer client_data, call_data;
3701 XtSetArg(args[0], XtNlabel, &name);
3702 XtGetValues(w, args, 1);
3706 if (fromX == -1) return;
3708 if (strcmp(name, _("cancel")) == 0) {
3712 } else if (strcmp(name, _("Knight")) == 0) {
3714 } else if (strcmp(name, _("Promote")) == 0) {
3716 } else if (strcmp(name, _("Defer")) == 0) {
3719 promoChar = ToLower(name[0]);
3722 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
3724 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
3725 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3730 void ErrorCallback(w, client_data, call_data)
3732 XtPointer client_data, call_data;
3735 XtPopdown(w = XtParent(XtParent(XtParent(w))));
3737 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3743 if (!errorUp) return;
3747 gtk_widget_destroy(GTK_WIDGET(GUI_Error));
3749 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3754 void ErrorPopUp(title, label, modal)
3755 char *title, *label;
3758 GUI_Error = gtk_message_dialog_new(GTK_WINDOW(GUI_Window),
3759 GTK_DIALOG_DESTROY_WITH_PARENT,
3764 gtk_window_set_title(GTK_WINDOW(GUI_Error),(gchar *) title);
3767 gtk_dialog_run(GTK_DIALOG(GUI_Error));
3768 gtk_widget_destroy(GTK_WIDGET(GUI_Error));
3772 g_signal_connect_swapped (GUI_Error, "response",
3773 G_CALLBACK (ErrorPopDownProc),
3776 gtk_widget_show(GTK_WIDGET(GUI_Error));
3782 /* Disable all user input other than deleting the window */
3783 static int frozen = 0;
3787 /* Grab by a widget that doesn't accept input */
3788 // XtAddGrab(messageWidget, TRUE, FALSE);
3792 /* Undo a FreezeUI */
3795 if (!frozen) return;
3796 // XtRemoveGrab(messageWidget);
3800 char *ModeToWidgetName(mode)
3804 case BeginningOfGame:
3805 if (appData.icsActive)
3806 return "menuMode.ICS Client";
3807 else if (appData.noChessProgram ||
3808 *appData.cmailGameName != NULLCHAR)
3809 return "menuMode.Edit Game";
3811 return "menuMode.Machine Black";
3812 case MachinePlaysBlack:
3813 return "menuMode.Machine Black";
3814 case MachinePlaysWhite:
3815 return "menuMode.Machine White";
3817 return "menuMode.Analysis Mode";
3819 return "menuMode.Analyze File";
3820 case TwoMachinesPlay:
3821 return "menuMode.Two Machines";
3823 return "menuMode.Edit Game";
3824 case PlayFromGameFile:
3825 return "menuFile.Load Game";
3827 return "menuMode.Edit Position";
3829 return "menuMode.Training";
3830 case IcsPlayingWhite:
3831 case IcsPlayingBlack:
3835 return "menuMode.ICS Client";
3842 void ModeHighlight()
3844 static int oldPausing = FALSE;
3845 static GameMode oldmode = (GameMode) -1;
3848 // todo this toggling of the pause button doesn't seem to work?
3849 // e.g. select pause from buttonbar doesn't activate menumode.pause
3851 // if (!boardWidget || !XtIsRealized(boardWidget)) return;
3853 if (pausing != oldPausing) {
3854 oldPausing = pausing;
3855 gtk_button_set_relief(GTK_BUTTON (gtk_builder_get_object (builder, "menuMode.Pause")),pausing?GTK_RELIEF_NORMAL:GTK_RELIEF_NONE);
3856 /* toggle background color in showbuttonbar */
3857 if (appData.showButtonBar) {
3859 gtk_button_pressed(GTK_BUTTON (gtk_builder_get_object (builder, "buttonbar.Pause")));
3861 gtk_button_released(GTK_BUTTON (gtk_builder_get_object (builder, "buttonbar.Pause")));
3866 // probably not needed anymore
3867 // wname = ModeToWidgetName(oldmode);
3869 // gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, wname)),True);
3873 /* Maybe all the enables should be handled here, not just this one */
3874 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuMode.Training")),
3875 gameMode == Training || gameMode == PlayFromGameFile);
3880 * Button/menu procedures
3883 int LoadGamePopUp(f, gameNumber, title)
3888 cmailMsgLoaded = FALSE;
3890 if (gameNumber == 0)
3892 int error = GameListBuild(f);
3896 DisplayError(_("Cannot build game list"), error);
3898 else if (!ListEmpty(&gameList)
3899 && ((ListGame *) gameList.tailPred)->number > 1)
3901 // TODO convert to GTK
3902 // GameListPopUp(f, title);
3910 return LoadGame(f, gameNumber, title, FALSE);
3913 void ReloadCmailMsgProc(w, event, prms, nprms)
3919 ReloadCmailMsgEvent(FALSE);
3922 void MailMoveProc(w, event, prms, nprms)
3931 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3932 char *selected_fen_position=NULL;
3935 SendPositionSelection(Widget w, Atom *selection, Atom *target,
3936 Atom *type_return, XtPointer *value_return,
3937 unsigned long *length_return, int *format_return)
3939 char *selection_tmp;
3941 if (!selected_fen_position) return False; /* should never happen */
3942 // if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3943 // /* note: since no XtSelectionDoneProc was registered, Xt will
3944 // * automatically call XtFree on the value returned. So have to
3945 // * make a copy of it allocated with XtMalloc */
3946 // selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3947 // strcpy(selection_tmp, selected_fen_position);
3949 // *value_return=selection_tmp;
3950 // *length_return=strlen(selection_tmp);
3951 // *type_return=*target;
3952 // *format_return = 8; /* bits per byte */
3954 // } else if (*target == XA_TARGETS(xDisplay)) {
3955 // Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3956 // targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3957 // targets_tmp[1] = XA_STRING;
3958 // *value_return = targets_tmp;
3959 // *type_return = XA_ATOM;
3960 // *length_return = 2;
3961 // *format_return = 8 * sizeof(Atom);
3962 // if (*format_return > 32) {
3963 // *length_return *= *format_return / 32;
3964 // *format_return = 32;
3972 /* note: when called from menu all parameters are NULL, so no clue what the
3973 * Widget which was clicked on was, or what the click event was
3975 void CopyPositionProc(w, event, prms, nprms)
3982 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3983 * have a notion of a position that is selected but not copied.
3984 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3986 if(gameMode == EditPosition) EditPositionDone(TRUE);
3987 if (selected_fen_position) free(selected_fen_position);
3988 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
3989 if (!selected_fen_position) return;
3990 // XtOwnSelection(menuBarWidget, XA_PRIMARY,
3992 // SendPositionSelection,
3993 // NULL/* lose_ownership_proc */ ,
3994 // NULL/* transfer_done_proc */);
3995 // XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3997 // SendPositionSelection,
3998 // NULL/* lose_ownership_proc */ ,
3999 // NULL/* transfer_done_proc */);
4002 /* function called when the data to Paste is ready */
4004 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
4005 Atom *type, XtPointer value, unsigned long *len, int *format)
4008 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
4009 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
4010 EditPositionPasteFEN(fenstr);
4014 /* called when Paste Position button is pressed,
4015 * all parameters will be NULL */
4016 void PastePositionProc(w, event, prms, nprms)
4022 // XtGetSelectionValue(menuBarWidget,
4023 // appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4024 // /* (XtSelectionCallbackProc) */ PastePositionCB,
4025 // NULL, /* client_data passed to PastePositionCB */
4027 // /* better to use the time field from the event that triggered the
4028 // * call to this function, but that isn't trivial to get
4036 SendGameSelection(Widget w, Atom *selection, Atom *target,
4037 Atom *type_return, XtPointer *value_return,
4038 unsigned long *length_return, int *format_return)
4040 char *selection_tmp;
4042 // if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4043 // FILE* f = fopen(gameCopyFilename, "r");
4046 // if (f == NULL) return False;
4050 // selection_tmp = XtMalloc(len + 1);
4051 // count = fread(selection_tmp, 1, len, f);
4052 // if (len != count) {
4053 // XtFree(selection_tmp);
4056 // selection_tmp[len] = NULLCHAR;
4057 // *value_return = selection_tmp;
4058 // *length_return = len;
4059 // *type_return = *target;
4060 // *format_return = 8; /* bits per byte */
4062 // } else if (*target == XA_TARGETS(xDisplay)) {
4063 // Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4064 // targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4065 // targets_tmp[1] = XA_STRING;
4066 // *value_return = targets_tmp;
4067 // *type_return = XA_ATOM;
4068 // *length_return = 2;
4069 // *format_return = 8 * sizeof(Atom);
4070 // if (*format_return > 32) {
4071 // *length_return *= *format_return / 32;
4072 // *format_return = 32;
4080 /* note: when called from menu all parameters are NULL, so no clue what the
4081 * Widget which was clicked on was, or what the click event was
4083 void CopyGameProc(w, event, prms, nprms)
4091 ret = SaveGameToFile(gameCopyFilename, FALSE);
4095 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4096 * have a notion of a game that is selected but not copied.
4097 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4099 // XtOwnSelection(menuBarWidget, XA_PRIMARY,
4101 // SendGameSelection,
4102 // NULL/* lose_ownership_proc */ ,
4103 // NULL/* transfer_done_proc */);
4104 // XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4106 // SendGameSelection,
4107 // NULL/* lose_ownership_proc */ ,
4108 // NULL/* transfer_done_proc */);
4111 /* function called when the data to Paste is ready */
4113 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
4114 Atom *type, XtPointer value, unsigned long *len, int *format)
4117 if (value == NULL || *len == 0) {
4118 return; /* nothing had been selected to copy */
4120 f = fopen(gamePasteFilename, "w");
4122 DisplayError(_("Can't open temp file"), errno);
4125 fwrite(value, 1, *len, f);
4128 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
4131 /* called when Paste Game button is pressed,
4132 * all parameters will be NULL */
4133 void PasteGameProc(w, event, prms, nprms)
4139 // XtGetSelectionValue(menuBarWidget,
4140 // appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4141 // /* (XtSelectionCallbackProc) */ PasteGameCB,
4142 // NULL, /* client_data passed to PasteGameCB */
4144 // /* better to use the time field from the event that triggered the
4145 // * call to this function, but that isn't trivial to get
4152 void SaveOnExitProc(w, event, prms, nprms)
4160 saveSettingsOnExit = !saveSettingsOnExit;
4162 if (saveSettingsOnExit) {
4163 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
4165 XtSetArg(args[0], XtNleftBitmap, None);
4167 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
4171 void SaveSettingsProc(w, event, prms, nprms)
4177 SaveSettings(settingsFileName);
4183 SaveGameProc(NULL, NULL);
4188 void EditCommentProc(w, event, prms, nprms)
4195 EditCommentPopDown();
4201 void IcsInputBoxProc(w, event, prms, nprms)
4207 if (ICSInputBoxUp) {
4208 ICSInputBoxPopDown();
4215 void EnterKeyProc(w, event, prms, nprms)
4221 if (ICSInputBoxUp == True)
4226 void DebugProc(w, event, prms, nprms)
4232 appData.debugMode = !appData.debugMode;
4235 void AboutGameProc(w, event, prms, nprms)
4244 void NothingProc(w, event, prms, nprms)
4253 void Iconify(w, event, prms, nprms)
4261 // fromX = fromY = -1;
4262 // XtSetArg(args[0], XtNiconic, True);
4263 // XtSetValues(shellWidget, args, 1);
4266 void DisplayMessage(message, extMessage)
4267 gchar *message, *extMessage;
4274 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
4277 message = extMessage;
4280 gtk_label_set_text( GTK_LABEL(gtk_builder_get_object (builder, "Messages")),message);
4285 void DisplayTitle(text)
4288 gchar title[MSG_SIZ];
4290 if (text == NULL) text = "";
4292 if (appData.titleInWindow)
4297 if (*text != NULLCHAR)
4299 strcpy(title, text);
4301 else if (appData.icsActive)
4303 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
4305 else if (appData.cmailGameName[0] != NULLCHAR)
4307 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
4309 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
4311 else if (gameInfo.variant == VariantGothic)
4313 strcpy(title, GOTHIC);
4317 else if (gameInfo.variant == VariantFalcon)
4319 strcpy(title, FALCON);
4322 else if (appData.noChessProgram)
4324 strcpy(title, programName);
4328 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
4330 gtk_window_set_title(GTK_WINDOW(GUI_Window),title);
4336 void DisplayError(message, error)
4343 if (appData.debugMode || appData.matchMode) {
4344 fprintf(stderr, "%s: %s\n", programName, message);
4347 if (appData.debugMode || appData.matchMode) {
4348 fprintf(stderr, "%s: %s: %s\n",
4349 programName, message, strerror(error));
4351 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
4354 ErrorPopUp(_("Error"), message, FALSE);
4358 void DisplayMoveError(message)
4363 DrawPosition(FALSE, NULL);
4364 if (appData.debugMode || appData.matchMode) {
4365 fprintf(stderr, "%s: %s\n", programName, message);
4367 if (appData.popupMoveErrors) {
4368 ErrorPopUp(_("Error"), message, FALSE);
4370 DisplayMessage(message, "");
4375 void DisplayFatalError(message, error, status)
4381 errorExitStatus = status;
4383 fprintf(stderr, "%s: %s\n", programName, message);
4385 fprintf(stderr, "%s: %s: %s\n",
4386 programName, message, strerror(error));
4387 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
4390 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
4391 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
4397 void DisplayInformation(message)
4401 ErrorPopUp(_("Information"), message, TRUE);
4404 void DisplayNote(message)
4408 ErrorPopUp(_("Note"), message, FALSE);
4412 NullXErrorCheck(dpy, error_event)
4414 XErrorEvent *error_event;
4419 void DisplayIcsInteractionTitle(message)
4422 if (oldICSInteractionTitle == NULL) {
4423 /* Magic to find the old window title, adapted from vim */
4424 char *wina = getenv("WINDOWID");
4426 Window win = (Window) atoi(wina);
4427 Window root, parent, *children;
4428 unsigned int nchildren;
4429 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4431 // if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4432 // if (!XQueryTree(xDisplay, win, &root, &parent,
4433 // &children, &nchildren)) break;
4434 // if (children) XFree((void *)children);
4435 // if (parent == root || parent == 0) break;
4438 XSetErrorHandler(oldHandler);
4440 if (oldICSInteractionTitle == NULL) {
4441 oldICSInteractionTitle = "xterm";
4444 printf("\033]0;%s\007", message);
4448 char pendingReplyPrefix[MSG_SIZ];
4449 ProcRef pendingReplyPR;
4451 void AskQuestionProc(w, event, prms, nprms)
4458 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
4462 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
4465 void AskQuestionPopDown()
4467 if (!askQuestionUp) return;
4468 XtPopdown(askQuestionShell);
4469 XtDestroyWidget(askQuestionShell);
4470 askQuestionUp = False;
4473 void AskQuestionReplyAction(w, event, prms, nprms)
4483 reply = XawDialogGetValueString(w = XtParent(w));
4484 strcpy(buf, pendingReplyPrefix);
4485 if (*buf) strcat(buf, " ");
4488 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
4489 AskQuestionPopDown();
4491 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
4494 void AskQuestionCallback(w, client_data, call_data)
4496 XtPointer client_data, call_data;
4501 XtSetArg(args[0], XtNlabel, &name);
4502 XtGetValues(w, args, 1);
4504 if (strcmp(name, _("cancel")) == 0) {
4505 AskQuestionPopDown();
4507 AskQuestionReplyAction(w, NULL, NULL, NULL);
4511 void AskQuestion(title, question, replyPrefix, pr)
4512 char *title, *question, *replyPrefix;
4516 Widget popup, layout, dialog, edit;
4522 strcpy(pendingReplyPrefix, replyPrefix);
4523 pendingReplyPR = pr;
4526 XtSetArg(args[i], XtNresizable, True); i++;
4527 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
4528 // askQuestionShell = popup =
4529 // XtCreatePopupShell(title, transientShellWidgetClass,
4530 // shellWidget, args, i);
4533 // XtCreateManagedWidget(layoutName, formWidgetClass, popup,
4534 // layoutArgs, XtNumber(layoutArgs));
4537 XtSetArg(args[i], XtNlabel, question); i++;
4538 XtSetArg(args[i], XtNvalue, ""); i++;
4539 XtSetArg(args[i], XtNborderWidth, 0); i++;
4540 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
4543 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
4544 (XtPointer) dialog);
4545 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
4546 (XtPointer) dialog);
4548 XtRealizeWidget(popup);
4549 // CatchDeleteWindow(popup, "AskQuestionPopDown");
4551 // XQueryPointer(xDisplay, xBoardWindow, &root, &child,
4552 // &x, &y, &win_x, &win_y, &mask);
4554 // XtSetArg(args[0], XtNx, x - 10);
4555 // XtSetArg(args[1], XtNy, y - 30);
4556 // XtSetValues(popup, args, 2);
4558 // XtPopup(popup, XtGrabExclusive);
4559 // askQuestionUp = True;
4561 // edit = XtNameToWidget(dialog, "*value");
4562 // XtSetKeyboardFocus(popup, edit);
4570 if (*name == NULLCHAR) {
4572 } else if (strcmp(name, "$") == 0) {
4573 putc(BELLCHAR, stderr);
4576 snprintf(buf, sizeof(buf), "%s '%s' &", appData.soundProgram, name);
4584 PlaySound(appData.soundMove);
4590 PlaySound(appData.soundIcsWin);
4596 PlaySound(appData.soundIcsLoss);
4602 PlaySound(appData.soundIcsDraw);
4606 PlayIcsUnfinishedSound()
4608 PlaySound(appData.soundIcsUnfinished);
4614 PlaySound(appData.soundIcsAlarm);
4620 system("stty echo");
4626 system("stty -echo");
4630 Colorize(cc, continuation)
4635 int count, outCount, error;
4637 if (textColors[(int)cc].bg > 0) {
4638 if (textColors[(int)cc].fg > 0) {
4639 sprintf(buf, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
4640 textColors[(int)cc].fg, textColors[(int)cc].bg);
4642 sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
4643 textColors[(int)cc].bg);
4646 if (textColors[(int)cc].fg > 0) {
4647 sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
4648 textColors[(int)cc].fg);
4650 sprintf(buf, "\033[0;%dm", textColors[(int)cc].attr);
4653 count = strlen(buf);
4654 outCount = OutputToProcess(NoProc, buf, count, &error);
4655 if (outCount < count) {
4656 DisplayFatalError(_("Error writing to display"), error, 1);
4659 if (continuation) return;
4662 PlaySound(appData.soundShout);
4665 PlaySound(appData.soundSShout);
4668 PlaySound(appData.soundChannel1);
4671 PlaySound(appData.soundChannel);
4674 PlaySound(appData.soundKibitz);
4677 PlaySound(appData.soundTell);
4679 case ColorChallenge:
4680 PlaySound(appData.soundChallenge);
4683 PlaySound(appData.soundRequest);
4686 PlaySound(appData.soundSeek);
4697 return getpwuid(getuid())->pw_name;
4700 static char *ExpandPathName(path)
4703 static char static_buf[2000];
4704 char *d, *s, buf[2000];
4710 while (*s && isspace(*s))
4719 if (*(s+1) == '/') {
4720 strcpy(d, getpwuid(getuid())->pw_dir);
4725 *strchr(buf, '/') = 0;
4726 pwd = getpwnam(buf);
4729 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
4733 strcpy(d, pwd->pw_dir);
4734 strcat(d, strchr(s+1, '/'));
4745 static char host_name[MSG_SIZ];
4747 #if HAVE_GETHOSTNAME
4748 gethostname(host_name, MSG_SIZ);
4750 #else /* not HAVE_GETHOSTNAME */
4751 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
4752 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
4754 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4756 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4757 #endif /* not HAVE_GETHOSTNAME */
4760 guint delayedEventTimerTag = 0;
4761 DelayedEventCallback delayedEventCallback = 0;
4764 FireDelayedEvent(data)
4768 g_source_remove(delayedEventTimerTag);
4769 delayedEventTimerTag = 0;
4772 delayedEventCallback();
4778 ScheduleDelayedEvent(cb, millisec)
4779 DelayedEventCallback cb; guint millisec;
4781 if(delayedEventTimerTag && delayedEventCallback == cb)
4782 // [HGM] alive: replace, rather than add or flush identical event
4783 g_source_remove(delayedEventTimerTag);
4784 delayedEventCallback = cb;
4785 delayedEventTimerTag = g_timeout_add(millisec,(GSourceFunc) FireDelayedEvent, NULL);
4789 DelayedEventCallback
4792 if (delayedEventTimerTag)
4794 return delayedEventCallback;
4803 CancelDelayedEvent()
4805 if (delayedEventTimerTag)
4807 g_source_remove(delayedEventTimerTag);
4808 delayedEventTimerTag = 0;
4814 guint loadGameTimerTag = 0;
4816 int LoadGameTimerRunning()
4818 return loadGameTimerTag != 0;
4821 int StopLoadGameTimer()
4823 if (loadGameTimerTag != 0) {
4824 g_source_remove(loadGameTimerTag);
4825 loadGameTimerTag = 0;
4833 LoadGameTimerCallback(data)
4837 g_source_remove(loadGameTimerTag);
4838 loadGameTimerTag = 0;
4845 StartLoadGameTimer(millisec)
4849 g_timeout_add( millisec, (GSourceFunc) LoadGameTimerCallback, NULL);
4853 guint analysisClockTag = 0;
4856 AnalysisClockCallback(data)
4859 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
4860 || appData.icsEngineAnalyze)
4862 AnalysisPeriodicEvent(0);
4863 return 1; /* keep on going */
4865 return 0; /* stop timer */
4869 StartAnalysisClock()
4872 g_timeout_add( 2000,(GSourceFunc) AnalysisClockCallback, NULL);
4876 guint clockTimerTag = 0;
4878 int ClockTimerRunning()
4880 return clockTimerTag != 0;
4883 int StopClockTimer()
4885 if (clockTimerTag != 0)
4887 g_source_remove(clockTimerTag);
4898 ClockTimerCallback(data)
4902 g_source_remove(clockTimerTag);
4910 StartClockTimer(millisec)
4913 clockTimerTag = g_timeout_add(millisec,(GSourceFunc) ClockTimerCallback,NULL);
4918 DisplayTimerLabel(w, color, timer, highlight)
4927 if (appData.clockMode) {
4928 sprintf(buf, "%s: %s", color, TimeString(timer));
4930 sprintf(buf, "%s ", color);
4932 gtk_label_set_text(GTK_LABEL(w),buf);
4934 /* check for low time warning */
4935 // Pixel foregroundOrWarningColor = timerForegroundPixel;
4938 // appData.lowTimeWarning &&
4939 // (timer / 1000) < appData.icsAlarmTime)
4940 // foregroundOrWarningColor = lowTimeWarningColor;
4942 // if (appData.clockMode) {
4943 // sprintf(buf, "%s: %s", color, TimeString(timer));
4944 // XtSetArg(args[0], XtNlabel, buf);
4946 // sprintf(buf, "%s ", color);
4947 // XtSetArg(args[0], XtNlabel, buf);
4952 // XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
4953 // XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
4955 // XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
4956 // XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
4959 // XtSetValues(w, args, 3);
4964 DisplayWhiteClock(timeRemaining, highlight)
4968 if(appData.noGUI) return;
4970 DisplayTimerLabel(GUI_Whiteclock, _("White"), timeRemaining, highlight);
4971 if (highlight && WindowIcon == BlackIcon)
4973 WindowIcon = WhiteIcon;
4974 gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
4979 DisplayBlackClock(timeRemaining, highlight)
4983 if(appData.noGUI) return;
4985 DisplayTimerLabel(GUI_Blackclock, _("Black"), timeRemaining, highlight);
4986 if (highlight && WindowIcon == WhiteIcon)
4988 WindowIcon = BlackIcon;
4989 gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
5007 int StartChildProcess(cmdLine, dir, pr)
5014 int to_prog[2], from_prog[2];
5018 if (appData.debugMode) {
5019 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
5022 /* We do NOT feed the cmdLine to the shell; we just
5023 parse it into blank-separated arguments in the
5024 most simple-minded way possible.
5027 strcpy(buf, cmdLine);
5030 while(*p == ' ') p++;
5032 if(*p == '"' || *p == '\'')
5033 p = strchr(++argv[i-1], *p);
5034 else p = strchr(p, ' ');
5035 if (p == NULL) break;
5040 SetUpChildIO(to_prog, from_prog);
5042 if ((pid = fork()) == 0) {
5044 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
5045 close(to_prog[1]); // first close the unused pipe ends
5046 close(from_prog[0]);
5047 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
5048 dup2(from_prog[1], 1);
5049 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
5050 close(from_prog[1]); // and closing again loses one of the pipes!
5051 if(fileno(stderr) >= 2) // better safe than sorry...
5052 dup2(1, fileno(stderr)); /* force stderr to the pipe */
5054 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
5059 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
5061 execvp(argv[0], argv);
5063 /* If we get here, exec failed */
5068 /* Parent process */
5070 close(from_prog[1]);
5072 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5075 cp->fdFrom = from_prog[0];
5076 cp->fdTo = to_prog[1];
5081 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
5082 static RETSIGTYPE AlarmCallBack(int n)
5088 DestroyChildProcess(pr, signalType)
5092 ChildProc *cp = (ChildProc *) pr;
5094 if (cp->kind != CPReal) return;
5096 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
5097 signal(SIGALRM, AlarmCallBack);
5099 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
5100 kill(cp->pid, SIGKILL); // kill it forcefully
5101 wait((int *) 0); // and wait again
5105 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
5107 /* Process is exiting either because of the kill or because of
5108 a quit command sent by the backend; either way, wait for it to die.
5117 InterruptChildProcess(pr)
5120 ChildProc *cp = (ChildProc *) pr;
5122 if (cp->kind != CPReal) return;
5123 (void) kill(cp->pid, SIGINT); /* stop it thinking */
5126 int OpenTelnet(host, port, pr)
5131 char cmdLine[MSG_SIZ];
5133 if (port[0] == NULLCHAR) {
5134 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
5136 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
5138 return StartChildProcess(cmdLine, "", pr);
5141 int OpenTCP(host, port, pr)
5147 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
5148 #else /* !OMIT_SOCKETS */
5150 struct sockaddr_in sa;
5152 unsigned short uport;
5155 if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
5159 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
5160 sa.sin_family = AF_INET;
5161 sa.sin_addr.s_addr = INADDR_ANY;
5162 uport = (unsigned short) 0;
5163 sa.sin_port = htons(uport);
5164 if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
5168 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
5169 if (!(hp = gethostbyname(host))) {
5171 if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
5172 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
5173 hp->h_addrtype = AF_INET;
5175 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
5176 hp->h_addr_list[0] = (char *) malloc(4);
5177 hp->h_addr_list[0][0] = b0;
5178 hp->h_addr_list[0][1] = b1;
5179 hp->h_addr_list[0][2] = b2;
5180 hp->h_addr_list[0][3] = b3;
5185 sa.sin_family = hp->h_addrtype;
5186 uport = (unsigned short) atoi(port);
5187 sa.sin_port = htons(uport);
5188 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
5190 if (connect(s, (struct sockaddr *) &sa,
5191 sizeof(struct sockaddr_in)) < 0) {
5195 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5202 #endif /* !OMIT_SOCKETS */
5207 int OpenCommPort(name, pr)
5214 fd = open(name, 2, 0);
5215 if (fd < 0) return errno;
5217 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5227 int OpenLoopback(pr)
5233 SetUpChildIO(to, from);
5235 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5238 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
5245 int OpenRcmd(host, user, cmd, pr)
5246 char *host, *user, *cmd;
5249 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
5253 #define INPUT_SOURCE_BUF_SIZE 8192
5262 char buf[INPUT_SOURCE_BUF_SIZE];
5267 DoInputCallback(io,cond,data)
5272 /* read input from one of the input source (for example a chess program, ICS, etc).
5273 * and call a function that will handle the input
5276 int count; /* how many bytes did we read */
5280 /* All information (callback function, file descriptor, etc) is
5281 * saved in an InputSource structure
5283 InputSource *is = (InputSource *) data;
5287 count = read(is->fd, is->unused,
5288 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
5292 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
5295 is->unused += count;
5297 /* break input into lines and call the callback function on each
5300 while (p < is->unused)
5302 q = memchr(p, '\n', is->unused - p);
5303 if (q == NULL) break;
5305 (is->func)(is, is->closure, p, q - p, 0);
5308 /* remember not yet used part of the buffer */
5310 while (p < is->unused)
5318 /* read maximum length of input buffer and send the whole buffer
5319 * to the callback function
5321 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
5326 (is->func)(is, is->closure, is->buf, count, error);
5332 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
5339 GIOChannel *channel;
5340 ChildProc *cp = (ChildProc *) pr;
5342 is = (InputSource *) calloc(1, sizeof(InputSource));
5343 is->lineByLine = lineByLine;
5347 is->fd = fileno(stdin);
5349 is->kind = cp->kind;
5350 is->fd = cp->fdFrom;
5353 is->unused = is->buf;
5357 // is->xid = XtAppAddInput(appContext, is->fd,
5358 // (XtPointer) (XtInputReadMask),
5359 // (XtInputCallbackProc) DoInputCallback,
5363 /* TODO: will this work on windows?*/
5365 channel = g_io_channel_unix_new(is->fd);
5366 g_io_channel_set_close_on_unref (channel, TRUE);
5367 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
5368 is->closure = closure;
5369 return (InputSourceRef) is;
5373 RemoveInputSource(isr)
5376 InputSource *is = (InputSource *) isr;
5378 if (is->sid == 0) return;
5379 g_source_remove(is->sid);
5384 int OutputToProcess(pr, message, count, outError)
5390 static int line = 0;
5391 ChildProc *cp = (ChildProc *) pr;
5396 if (appData.noJoin || !appData.useInternalWrap)
5397 outCount = fwrite(message, 1, count, stdout);
5400 int width = get_term_width();
5401 int len = wrap(NULL, message, count, width, &line);
5402 char *msg = malloc(len);
5406 outCount = fwrite(message, 1, count, stdout);
5409 dbgchk = wrap(msg, message, count, width, &line);
5410 if (dbgchk != len && appData.debugMode)
5411 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
5412 outCount = fwrite(msg, 1, dbgchk, stdout);
5418 outCount = write(cp->fdTo, message, count);
5428 /* Output message to process, with "ms" milliseconds of delay
5429 between each character. This is needed when sending the logon
5430 script to ICC, which for some reason doesn't like the
5431 instantaneous send. */
5432 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
5439 ChildProc *cp = (ChildProc *) pr;
5444 r = write(cp->fdTo, message++, 1);
5457 /**** Animation code by Hugh Fisher, DCS, ANU.
5459 Known problem: if a window overlapping the board is
5460 moved away while a piece is being animated underneath,
5461 the newly exposed area won't be updated properly.
5462 I can live with this.
5464 Known problem: if you look carefully at the animation
5465 of pieces in mono mode, they are being drawn as solid
5466 shapes without interior detail while moving. Fixing
5467 this would be a major complication for minimal return.
5470 /* Masks for XPM pieces. Black and white pieces can have
5471 different shapes, but in the interest of retaining my
5472 sanity pieces must have the same outline on both light
5473 and dark squares, and all pieces must use the same
5474 background square colors/images. */
5476 static int xpmDone = 0;
5479 CreateAnimMasks (pieceDepth)
5486 unsigned long plane;
5489 /* just return for gtk at the moment */
5492 /* Need a bitmap just to get a GC with right depth */
5493 // buf = XCreatePixmap(xDisplay, xBoardWindow,
5495 values.foreground = 1;
5496 values.background = 0;
5497 /* Don't use XtGetGC, not read only */
5498 // maskGC = XCreateGC(xDisplay, buf,
5499 // GCForeground | GCBackground, &values);
5500 // XFreePixmap(xDisplay, buf);
5502 // buf = XCreatePixmap(xDisplay, xBoardWindow,
5503 // squareSize, squareSize, pieceDepth);
5504 // values.foreground = XBlackPixel(xDisplay, xScreen);
5505 // values.background = XWhitePixel(xDisplay, xScreen);
5506 // bufGC = XCreateGC(xDisplay, buf,
5507 // GCForeground | GCBackground, &values);
5509 for (piece = WhitePawn; piece <= BlackKing; piece++) {
5510 /* Begin with empty mask */
5511 // if(!xpmDone) // [HGM] pieces: keep using existing
5512 // xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
5513 // squareSize, squareSize, 1);
5514 // XSetFunction(xDisplay, maskGC, GXclear);
5515 // XFillRectangle(xDisplay, xpmMask[piece], maskGC,
5516 // 0, 0, squareSize, squareSize);
5518 /* Take a copy of the piece */
5523 // XSetFunction(xDisplay, bufGC, GXcopy);
5524 // XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
5526 // 0, 0, squareSize, squareSize, 0, 0);
5528 /* XOR the background (light) over the piece */
5529 // XSetFunction(xDisplay, bufGC, GXxor);
5531 // XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
5532 // 0, 0, squareSize, squareSize, 0, 0);
5534 // XSetForeground(xDisplay, bufGC, lightSquareColor);
5535 // XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
5538 /* We now have an inverted piece image with the background
5539 erased. Construct mask by just selecting all the non-zero
5540 pixels - no need to reconstruct the original image. */
5541 // XSetFunction(xDisplay, maskGC, GXor);
5543 /* Might be quicker to download an XImage and create bitmap
5544 data from it rather than this N copies per piece, but it
5545 only takes a fraction of a second and there is a much
5546 longer delay for loading the pieces. */
5547 // for (n = 0; n < pieceDepth; n ++) {
5548 // XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
5549 // 0, 0, squareSize, squareSize,
5551 // plane = plane << 1;
5555 // XFreePixmap(xDisplay, buf);
5556 // XFreeGC(xDisplay, bufGC);
5557 // XFreeGC(xDisplay, maskGC);
5561 InitAnimState (anim, info)
5563 XWindowAttributes * info;
5568 /* Each buffer is square size, same depth as window */
5569 // anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
5570 // squareSize, squareSize, info->depth);
5571 // anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
5572 // squareSize, squareSize, info->depth);
5574 // /* Create a plain GC for blitting */
5575 // mask = GCForeground | GCBackground | GCFunction |
5576 // GCPlaneMask | GCGraphicsExposures;
5577 // values.foreground = XBlackPixel(xDisplay, xScreen);
5578 // values.background = XWhitePixel(xDisplay, xScreen);
5579 // values.function = GXcopy;
5580 // values.plane_mask = AllPlanes;
5581 // values.graphics_exposures = False;
5582 // anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
5584 // /* Piece will be copied from an existing context at
5585 // the start of each new animation/drag. */
5586 // anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
5588 // /* Outline will be a read-only copy of an existing */
5589 // anim->outlineGC = None;
5595 static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
5596 XWindowAttributes info;
5598 /* for gtk at the moment just ... */
5601 if (xpmDone && gameInfo.variant == old) return;
5602 if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet
5603 // XGetWindowAttributes(xDisplay, xBoardWindow, &info);
5605 // InitAnimState(&game, &info);
5606 // InitAnimState(&player, &info);
5608 /* For XPM pieces, we need bitmaps to use as masks. */
5610 // CreateAnimMasks(info.depth);
5616 static Boolean frameWaiting;
5618 static RETSIGTYPE FrameAlarm (sig)
5621 frameWaiting = False;
5622 /* In case System-V style signals. Needed?? */
5623 signal(SIGALRM, FrameAlarm);
5630 struct itimerval delay;
5632 XSync(xDisplay, False);
5635 frameWaiting = True;
5636 signal(SIGALRM, FrameAlarm);
5637 delay.it_interval.tv_sec =
5638 delay.it_value.tv_sec = time / 1000;
5639 delay.it_interval.tv_usec =
5640 delay.it_value.tv_usec = (time % 1000) * 1000;
5641 setitimer(ITIMER_REAL, &delay, NULL);
5642 while (frameWaiting) pause();
5643 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
5644 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
5645 setitimer(ITIMER_REAL, &delay, NULL);
5655 // XSync(xDisplay, False);
5657 usleep(time * 1000);
5662 /* Convert board position to corner of screen rect and color */
5665 ScreenSquare(column, row, pt, color)
5666 int column; int row; XPoint * pt; int * color;
5669 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
5670 pt->y = lineGap + row * (squareSize + lineGap);
5672 pt->x = lineGap + column * (squareSize + lineGap);
5673 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
5675 *color = SquareColor(row, column);
5678 /* Convert window coords to square */
5681 BoardSquare(x, y, column, row)
5682 int x; int y; int * column; int * row;
5684 *column = EventToSquare(x, BOARD_WIDTH);
5685 if (flipView && *column >= 0)
5686 *column = BOARD_WIDTH - 1 - *column;
5687 *row = EventToSquare(y, BOARD_HEIGHT);
5688 if (!flipView && *row >= 0)
5689 *row = BOARD_HEIGHT - 1 - *row;
5694 #undef Max /* just in case */
5696 #define Max(a, b) ((a) > (b) ? (a) : (b))
5697 #define Min(a, b) ((a) < (b) ? (a) : (b))
5700 SetRect(rect, x, y, width, height)
5701 XRectangle * rect; int x; int y; int width; int height;
5705 rect->width = width;
5706 rect->height = height;
5709 /* Test if two frames overlap. If they do, return
5710 intersection rect within old and location of
5711 that rect within new. */
5714 Intersect(old, new, size, area, pt)
5715 XPoint * old; XPoint * new;
5716 int size; XRectangle * area; XPoint * pt;
5718 if (old->x > new->x + size || new->x > old->x + size ||
5719 old->y > new->y + size || new->y > old->y + size) {
5722 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
5723 size - abs(old->x - new->x), size - abs(old->y - new->y));
5724 pt->x = Max(old->x - new->x, 0);
5725 pt->y = Max(old->y - new->y, 0);
5730 /* For two overlapping frames, return the rect(s)
5731 in the old that do not intersect with the new. */
5734 CalcUpdateRects(old, new, size, update, nUpdates)
5735 XPoint * old; XPoint * new; int size;
5736 XRectangle update[]; int * nUpdates;
5740 /* If old = new (shouldn't happen) then nothing to draw */
5741 if (old->x == new->x && old->y == new->y) {
5745 /* Work out what bits overlap. Since we know the rects
5746 are the same size we don't need a full intersect calc. */
5748 /* Top or bottom edge? */
5749 if (new->y > old->y) {
5750 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
5752 } else if (old->y > new->y) {
5753 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
5754 size, old->y - new->y);
5757 /* Left or right edge - don't overlap any update calculated above. */
5758 if (new->x > old->x) {
5759 SetRect(&(update[count]), old->x, Max(new->y, old->y),
5760 new->x - old->x, size - abs(new->y - old->y));
5762 } else if (old->x > new->x) {
5763 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
5764 old->x - new->x, size - abs(new->y - old->y));
5771 /* Generate a series of frame coords from start->mid->finish.
5772 The movement rate doubles until the half way point is
5773 reached, then halves back down to the final destination,
5774 which gives a nice slow in/out effect. The algorithmn
5775 may seem to generate too many intermediates for short
5776 moves, but remember that the purpose is to attract the
5777 viewers attention to the piece about to be moved and
5778 then to where it ends up. Too few frames would be less
5782 Tween(start, mid, finish, factor, frames, nFrames)
5783 XPoint * start; XPoint * mid;
5784 XPoint * finish; int factor;
5785 XPoint frames[]; int * nFrames;
5787 int fraction, n, count;
5791 /* Slow in, stepping 1/16th, then 1/8th, ... */
5793 for (n = 0; n < factor; n++)
5795 for (n = 0; n < factor; n++) {
5796 frames[count].x = start->x + (mid->x - start->x) / fraction;
5797 frames[count].y = start->y + (mid->y - start->y) / fraction;
5799 fraction = fraction / 2;
5803 frames[count] = *mid;
5806 /* Slow out, stepping 1/2, then 1/4, ... */
5808 for (n = 0; n < factor; n++) {
5809 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
5810 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
5812 fraction = fraction * 2;
5817 /* Draw a piece on the screen without disturbing what's there */
5820 SelectGCMask(piece, clip, outline, mask)
5821 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
5825 /* Bitmap for piece being moved. */
5826 if (appData.monoMode) {
5827 *mask = *pieceToSolid(piece);
5828 } else if (useImages) {
5830 *mask = xpmMask[piece];
5832 *mask = ximMaskPm[piece];
5835 *mask = *pieceToSolid(piece);
5838 /* GC for piece being moved. Square color doesn't matter, but
5839 since it gets modified we make a copy of the original. */
5841 if (appData.monoMode)
5846 if (appData.monoMode)
5851 // XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
5853 /* Outline only used in mono mode and is not modified */
5855 *outline = bwPieceGC;
5857 *outline = wbPieceGC;
5861 OverlayPiece(piece, clip, outline, dest)
5862 ChessSquare piece; GC clip; GC outline; Drawable dest;
5867 /* Draw solid rectangle which will be clipped to shape of piece */
5868 // XFillRectangle(xDisplay, dest, clip,
5869 // 0, 0, squareSize, squareSize)
5871 if (appData.monoMode)
5872 /* Also draw outline in contrasting color for black
5873 on black / white on white cases */
5874 // XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
5875 // 0, 0, squareSize, squareSize, 0, 0, 1)
5878 /* Copy the piece */
5883 // XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
5885 // 0, 0, squareSize, squareSize,
5890 /* Animate the movement of a single piece */
5893 BeginAnimation(anim, piece, startColor, start)
5901 /* The old buffer is initialised with the start square (empty) */
5902 BlankSquare(0, 0, startColor, EmptySquare, anim->saveBuf);
5903 anim->prevFrame = *start;
5905 /* The piece will be drawn using its own bitmap as a matte */
5906 // SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
5907 // XSetClipMask(xDisplay, anim->pieceGC, mask);
5911 AnimationFrame(anim, frame, piece)
5916 XRectangle updates[4];
5921 /* Save what we are about to draw into the new buffer */
5922 // XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
5923 // frame->x, frame->y, squareSize, squareSize,
5926 /* Erase bits of the previous frame */
5927 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
5928 /* Where the new frame overlapped the previous,
5929 the contents in newBuf are wrong. */
5930 // XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
5931 // overlap.x, overlap.y,
5932 // overlap.width, overlap.height,
5934 /* Repaint the areas in the old that don't overlap new */
5935 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
5936 for (i = 0; i < count; i++)
5937 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
5938 // updates[i].x - anim->prevFrame.x,
5939 // updates[i].y - anim->prevFrame.y,
5940 // updates[i].width, updates[i].height,
5941 // updates[i].x, updates[i].y)
5944 /* Easy when no overlap */
5945 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
5946 // 0, 0, squareSize, squareSize,
5947 // anim->prevFrame.x, anim->prevFrame.y);
5950 /* Save this frame for next time round */
5951 // XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
5952 // 0, 0, squareSize, squareSize,
5954 anim->prevFrame = *frame;
5956 /* Draw piece over original screen contents, not current,
5957 and copy entire rect. Wipes out overlapping piece images. */
5958 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
5959 // XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
5960 // 0, 0, squareSize, squareSize,
5961 // frame->x, frame->y);
5965 EndAnimation (anim, finish)
5969 XRectangle updates[4];
5974 /* The main code will redraw the final square, so we
5975 only need to erase the bits that don't overlap. */
5976 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
5977 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
5978 for (i = 0; i < count; i++)
5979 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
5980 // updates[i].x - anim->prevFrame.x,
5981 // updates[i].y - anim->prevFrame.y,
5982 // updates[i].width, updates[i].height,
5983 // updates[i].x, updates[i].y)
5986 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
5987 // 0, 0, squareSize, squareSize,
5988 // anim->prevFrame.x, anim->prevFrame.y);
5993 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
5995 ChessSquare piece; int startColor;
5996 XPoint * start; XPoint * finish;
5997 XPoint frames[]; int nFrames;
6001 BeginAnimation(anim, piece, startColor, start);
6002 for (n = 0; n < nFrames; n++) {
6003 AnimationFrame(anim, &(frames[n]), piece);
6004 FrameDelay(appData.animSpeed);
6006 EndAnimation(anim, finish);
6009 /* Main control logic for deciding what to animate and how */
6012 AnimateMove(board, fromX, fromY, toX, toY)
6021 XPoint start, finish, mid;
6022 XPoint frames[kFactor * 2 + 1];
6023 int nFrames, startColor, endColor;
6025 /* Are we animating? */
6026 if (!appData.animate || appData.blindfold)
6029 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
6030 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
6031 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
6033 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
6034 piece = board[fromY][fromX];
6035 if (piece >= EmptySquare) return;
6040 hop = (piece == WhiteKnight || piece == BlackKnight);
6043 if (appData.debugMode) {
6044 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
6045 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
6046 piece, fromX, fromY, toX, toY); }
6048 ScreenSquare(fromX, fromY, &start, &startColor);
6049 ScreenSquare(toX, toY, &finish, &endColor);
6052 /* Knight: make diagonal movement then straight */
6053 if (abs(toY - fromY) < abs(toX - fromX)) {
6054 mid.x = start.x + (finish.x - start.x) / 2;
6058 mid.y = start.y + (finish.y - start.y) / 2;
6061 mid.x = start.x + (finish.x - start.x) / 2;
6062 mid.y = start.y + (finish.y - start.y) / 2;
6065 /* Don't use as many frames for very short moves */
6066 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
6067 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
6069 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
6070 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
6072 /* Be sure end square is redrawn */
6073 damage[toY][toX] = True;
6077 DragPieceBegin(x, y)
6080 int boardX, boardY, color;
6083 /* Are we animating? */
6084 if (!appData.animateDragging || appData.blindfold)
6087 /* Figure out which square we start in and the
6088 mouse position relative to top left corner. */
6089 BoardSquare(x, y, &boardX, &boardY);
6090 player.startBoardX = boardX;
6091 player.startBoardY = boardY;
6092 ScreenSquare(boardX, boardY, &corner, &color);
6093 player.startSquare = corner;
6094 player.startColor = color;
6095 /* As soon as we start dragging, the piece will jump slightly to
6096 be centered over the mouse pointer. */
6097 player.mouseDelta.x = squareSize/2;
6098 player.mouseDelta.y = squareSize/2;
6099 /* Initialise animation */
6100 player.dragPiece = PieceForSquare(boardX, boardY);
6102 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
6103 player.dragActive = True;
6104 BeginAnimation(&player, player.dragPiece, color, &corner);
6105 /* Mark this square as needing to be redrawn. Note that
6106 we don't remove the piece though, since logically (ie
6107 as seen by opponent) the move hasn't been made yet. */
6108 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
6109 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
6110 // XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
6111 // corner.x, corner.y, squareSize, squareSize,
6112 // 0, 0); // [HGM] zh: unstack in stead of grab
6113 damage[boardY][boardX] = True;
6115 player.dragActive = False;
6125 /* Are we animating? */
6126 if (!appData.animateDragging || appData.blindfold)
6130 if (! player.dragActive)
6132 /* Move piece, maintaining same relative position
6133 of mouse within square */
6134 corner.x = x - player.mouseDelta.x;
6135 corner.y = y - player.mouseDelta.y;
6136 AnimationFrame(&player, &corner, player.dragPiece);
6138 if (appData.highlightDragging) {
6140 BoardSquare(x, y, &boardX, &boardY);
6141 SetHighlights(fromX, fromY, boardX, boardY);
6150 int boardX, boardY, color;
6153 /* Are we animating? */
6154 if (!appData.animateDragging || appData.blindfold)
6158 if (! player.dragActive)
6160 /* Last frame in sequence is square piece is
6161 placed on, which may not match mouse exactly. */
6162 BoardSquare(x, y, &boardX, &boardY);
6163 ScreenSquare(boardX, boardY, &corner, &color);
6164 EndAnimation(&player, &corner);
6166 /* Be sure end square is redrawn */
6167 damage[boardY][boardX] = True;
6169 /* This prevents weird things happening with fast successive
6170 clicks which on my Sun at least can cause motion events
6171 without corresponding press/release. */
6172 player.dragActive = False;
6175 /* Handle expose event while piece being dragged */
6180 if (!player.dragActive || appData.blindfold)
6183 /* What we're doing: logically, the move hasn't been made yet,
6184 so the piece is still in it's original square. But visually
6185 it's being dragged around the board. So we erase the square
6186 that the piece is on and draw it at the last known drag point. */
6187 BlankSquare(player.startSquare.x, player.startSquare.y,
6188 player.startColor, EmptySquare, xBoardWindow);
6189 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
6190 damage[player.startBoardY][player.startBoardX] = TRUE;
6193 #include <sys/ioctl.h>
6194 int get_term_width()
6196 int fd, default_width;
6199 default_width = 79; // this is FICS default anyway...
6201 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
6203 if (!ioctl(fd, TIOCGSIZE, &win))
6204 default_width = win.ts_cols;
6205 #elif defined(TIOCGWINSZ)
6207 if (!ioctl(fd, TIOCGWINSZ, &win))
6208 default_width = win.ws_col;
6210 return default_width;
6213 void update_ics_width()
6215 static int old_width = 0;
6216 int new_width = get_term_width();
6218 if (old_width != new_width)
6219 ics_printf("set width %d\n", new_width);
6220 old_width = new_width;
6223 void NotifyFrontendLogin()