2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
65 # if HAVE_SYS_SOCKET_H
66 # include <sys/socket.h>
67 # include <netinet/in.h>
69 # else /* not HAVE_SYS_SOCKET_H */
70 # if HAVE_LAN_SOCKET_H
71 # include <lan/socket.h>
73 # include <lan/netdb.h>
74 # else /* not HAVE_LAN_SOCKET_H */
75 # define OMIT_SOCKETS 1
76 # endif /* not HAVE_LAN_SOCKET_H */
77 # endif /* not HAVE_SYS_SOCKET_H */
78 #endif /* !OMIT_SOCKETS */
83 #else /* not STDC_HEADERS */
84 extern char *getenv();
87 # else /* not HAVE_STRING_H */
89 # endif /* not HAVE_STRING_H */
90 #endif /* not STDC_HEADERS */
93 # include <sys/fcntl.h>
94 #else /* not HAVE_SYS_FCNTL_H */
97 # endif /* HAVE_FCNTL_H */
98 #endif /* not HAVE_SYS_FCNTL_H */
100 #if HAVE_SYS_SYSTEMINFO_H
101 # include <sys/systeminfo.h>
102 #endif /* HAVE_SYS_SYSTEMINFO_H */
104 #if TIME_WITH_SYS_TIME
105 # include <sys/time.h>
109 # include <sys/time.h>
120 # include <sys/wait.h>
125 # define NAMLEN(dirent) strlen((dirent)->d_name)
126 # define HAVE_DIR_STRUCT
128 # define dirent direct
129 # define NAMLEN(dirent) (dirent)->d_namlen
131 # include <sys/ndir.h>
132 # define HAVE_DIR_STRUCT
135 # include <sys/dir.h>
136 # define HAVE_DIR_STRUCT
140 # define HAVE_DIR_STRUCT
144 #include <X11/Intrinsic.h>
145 #include <X11/StringDefs.h>
146 #include <X11/Shell.h>
147 #include <X11/cursorfont.h>
148 #include <X11/Xatom.h>
149 #include <X11/Xmu/Atoms.h>
151 #include <X11/Xaw3d/Dialog.h>
152 #include <X11/Xaw3d/Form.h>
153 #include <X11/Xaw3d/List.h>
154 #include <X11/Xaw3d/Label.h>
155 #include <X11/Xaw3d/SimpleMenu.h>
156 #include <X11/Xaw3d/SmeBSB.h>
157 #include <X11/Xaw3d/SmeLine.h>
158 #include <X11/Xaw3d/Box.h>
159 #include <X11/Xaw3d/MenuButton.h>
160 #include <X11/Xaw3d/Text.h>
161 #include <X11/Xaw3d/AsciiText.h>
163 #include <X11/Xaw/Dialog.h>
164 #include <X11/Xaw/Form.h>
165 #include <X11/Xaw/List.h>
166 #include <X11/Xaw/Label.h>
167 #include <X11/Xaw/SimpleMenu.h>
168 #include <X11/Xaw/SmeBSB.h>
169 #include <X11/Xaw/SmeLine.h>
170 #include <X11/Xaw/Box.h>
171 #include <X11/Xaw/MenuButton.h>
172 #include <X11/Xaw/Text.h>
173 #include <X11/Xaw/AsciiText.h>
176 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
181 #include "pixmaps/pixmaps.h"
182 #define IMAGE_EXT "xpm"
184 #define IMAGE_EXT "xim"
185 #include "bitmaps/bitmaps.h"
190 #include <gdk-pixbuf/gdk-pixbuf.h>
192 #include "bitmaps/icon_white.bm"
193 #include "bitmaps/icon_black.bm"
194 #include "bitmaps/checkmark.bm"
196 #include "frontend.h"
201 #include "xgamelist.h"
202 #include "xhistory.h"
203 #include "xedittags.h"
205 #include "callback.h"
206 #include "interface.h"
208 // must be moved to xengineoutput.h
210 void EngineOutputProc P((Widget w, XEvent *event,
211 String *prms, Cardinal *nprms));
212 void EvalGraphProc P((Widget w, XEvent *event,
213 String *prms, Cardinal *nprms));
220 #define usleep(t) _sleep2(((t)+500)/1000)
224 # define _(s) gettext (s)
225 # define N_(s) gettext_noop (s)
248 int main P((int argc, char **argv));
249 RETSIGTYPE CmailSigHandler P((int sig));
250 RETSIGTYPE IntSigHandler P((int sig));
251 RETSIGTYPE TermSizeSigHandler P((int sig));
252 void CreateGCs P((void));
253 void CreateXIMPieces P((void));
254 void CreateXPMPieces P((void));
255 void CreatePieces P((void));
256 void CreatePieceMenus P((void));
257 Widget CreateMenuBar P((Menu *mb));
258 char *FindFont P((char *pattern, int targetPxlSize));
259 void PieceMenuPopup P((Widget w, XEvent *event,
260 String *params, Cardinal *num_params));
261 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
262 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
263 int EventToSquare P((int x, int limit));
264 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
265 void AnimateUserMove P((Widget w, XEvent * event,
266 String * params, Cardinal * nParams));
267 void HandlePV P((Widget w, XEvent * event,
268 String * params, Cardinal * nParams));
269 void CommentPopUp P((char *title, char *label));
270 void CommentPopDown P((void));
271 void CommentCallback P((Widget w, XtPointer client_data,
272 XtPointer call_data));
273 void ICSInputBoxPopUp P((void));
274 void ICSInputBoxPopDown P((void));
275 void AskQuestionReplyAction P((Widget w, XEvent *event,
276 String *prms, Cardinal *nprms));
277 void AskQuestionProc P((Widget w, XEvent *event,
278 String *prms, Cardinal *nprms));
279 void AskQuestionPopDown P((void));
280 void PromotionPopDown P((void));
281 void PromotionCallback P((Widget w, XtPointer client_data,
282 XtPointer call_data));
283 void EditCommentPopDown P((void));
284 void EditCommentCallback P((Widget w, XtPointer client_data,
285 XtPointer call_data));
286 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
287 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
289 void PastePositionProc P((Widget w, XEvent *event, String *prms,
291 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
292 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
293 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
294 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
296 void EditCommentProc P((Widget w, XEvent *event,
297 String *prms, Cardinal *nprms));
298 void IcsInputBoxProc P((Widget w, XEvent *event,
299 String *prms, Cardinal *nprms));
300 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
301 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
302 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
303 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
304 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
305 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
306 void Iconify P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
307 void DisplayMove P((int moveNumber));
308 void DisplayTitle P((char *title));
309 void ICSInitScript P((void));
310 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
311 void ErrorPopUp P((char *title, char *text, int modal));
312 void ErrorPopDown P((void));
313 static char *ExpandPathName P((char *path));
314 static void CreateAnimVars P((void));
315 static void DragPieceMove P((int x, int y));
316 static void DrawDragPiece P((void));
317 char *ModeToWidgetName P((GameMode mode));
318 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
319 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
320 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
321 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
322 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
323 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
324 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
325 void ShufflePopDown P(());
326 void EnginePopDown P(());
327 void UciPopDown P(());
328 void TimeControlPopDown P(());
329 void NewVariantPopDown P(());
330 void SettingsPopDown P(());
331 void SetMenuEnables P((Enables *enab));
332 void update_ics_width P(());
333 int get_term_width P(());
334 int CopyMemoProc P(());
336 * XBoard depends on Xt R4 or higher
338 int xtVersion = XtSpecificationRelease;
343 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
344 jailSquareColor, highlightSquareColor, premoveHighlightColor;
345 Pixel lowTimeWarningColor;
347 #define LINE_TYPE_NORMAL 0
348 #define LINE_TYPE_HIGHLIGHT 1
349 #define LINE_TYPE_PRE 2
352 GC lightSquareGC, darkSquareGC, jailSquareGC, wdPieceGC, wlPieceGC,
353 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC,
354 wjPieceGC, bjPieceGC;
355 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
356 Widget layoutWidget, formWidget, boardWidget, messageWidget,
357 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
358 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
359 menuBarWidget, editShell, errorShell, analysisShell,
360 ICSInputShell, fileNameShell, askQuestionShell;
362 Widget evalGraphShell, gameListShell;
363 //XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
364 //XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
366 Font clockFontID, coordFontID, countFontID;
367 XFontStruct *clockFontStruct, *coordFontStruct, *countFontStruct;
368 XtAppContext appContext;
370 char *oldICSInteractionTitle;
374 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
376 Position commentX = -1, commentY = -1;
377 Dimension commentW, commentH;
378 typedef unsigned int BoardSize;
380 Boolean chessProgram;
382 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
383 int squareSize, smallLayout = 0, tinyLayout = 0,
384 marginW, marginH, // [HGM] for run-time resizing
385 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
386 ICSInputBoxUp = False, askQuestionUp = False,
387 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
388 editUp = False, errorUp = False, errorExitStatus = -1, lineGap;
389 Pixel timerForegroundPixel, timerBackgroundPixel;
390 Pixel buttonForegroundPixel, buttonBackgroundPixel;
391 char *chessDir, *programName, *programVersion,
392 *gameCopyFilename, *gamePasteFilename;
393 Boolean alwaysOnTop = False;
394 Boolean saveSettingsOnExit;
395 char *settingsFileName;
396 char *icsTextMenuString;
398 char *firstChessProgramNames;
399 char *secondChessProgramNames;
401 WindowPlacement wpMain;
402 WindowPlacement wpConsole;
403 WindowPlacement wpComment;
404 WindowPlacement wpMoveHistory;
405 WindowPlacement wpEvalGraph;
406 WindowPlacement wpEngineOutput;
407 WindowPlacement wpGameList;
408 WindowPlacement wpTags;
412 Pixmap pieceBitmap[2][(int)BlackPawn];
413 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
414 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
415 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
416 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
417 int useImages=0, useImageSqs;
418 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
419 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
420 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
421 XImage *ximLightSquare, *ximDarkSquare;
424 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
425 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
427 #define White(piece) ((int)(piece) < (int)BlackPawn)
429 /* Variables for doing smooth animation. This whole thing
430 would be much easier if the board was double-buffered,
431 but that would require a fairly major rewrite. */
436 GC blitGC, pieceGC, outlineGC;
437 XPoint startSquare, prevFrame, mouseDelta;
441 int startBoardX, startBoardY;
444 /* There can be two pieces being animated at once: a player
445 can begin dragging a piece before the remote opponent has moved. */
447 static AnimState game, player;
449 /* Bitmaps for use as masks when drawing XPM pieces.
450 Need one for each black and white piece. */
451 static Pixmap xpmMask[BlackKing + 1];
453 /* This magic number is the number of intermediate frames used
454 in each half of the animation. For short moves it's reduced
455 by 1. The total number of frames will be factor * 2 + 1. */
458 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
460 Enables icsEnables[] = {
461 { "menuFile.Mail Move", False },
462 { "menuFile.Reload CMail Message", False },
463 { "menuMode.Machine Black", False },
464 { "menuMode.Machine White", False },
465 { "menuMode.Analysis Mode", False },
466 { "menuMode.Analyze File", False },
467 { "menuMode.Two Machines", False },
469 { "menuHelp.Hint", False },
470 { "menuHelp.Book", False },
471 { "menuStep.Move Now", False },
472 { "menuOptions.Periodic Updates", False },
473 { "menuOptions.Hide Thinking", False },
474 { "menuOptions.Ponder Next Move", False },
479 Enables ncpEnables[] = {
480 { "menuFile.Mail Move", False },
481 { "menuFile.Reload CMail Message", False },
482 { "menuMode.Machine White", False },
483 { "menuMode.Machine Black", False },
484 { "menuMode.Analysis Mode", False },
485 { "menuMode.Analyze File", False },
486 { "menuMode.Two Machines", False },
487 { "menuMode.ICS Client", False },
488 { "menuMode.ICS Input Box", False },
490 { "menuStep.Revert", False },
491 { "menuStep.Move Now", False },
492 { "menuStep.Retract Move", False },
493 { "menuOptions.Auto Comment", False },
494 { "menuOptions.Auto Flag", False },
495 { "menuOptions.Auto Flip View", False },
496 { "menuOptions.Auto Observe", False },
497 { "menuOptions.Auto Raise Board", False },
498 { "menuOptions.Get Move List", False },
499 { "menuOptions.ICS Alarm", False },
500 { "menuOptions.Move Sound", False },
501 { "menuOptions.Quiet Play", False },
502 { "menuOptions.Hide Thinking", False },
503 { "menuOptions.Periodic Updates", False },
504 { "menuOptions.Ponder Next Move", False },
505 { "menuHelp.Hint", False },
506 { "menuHelp.Book", False },
510 Enables gnuEnables[] = {
511 { "menuMode.ICS Client", False },
512 { "menuMode.ICS Input Box", False },
513 { "menuAction.Accept", False },
514 { "menuAction.Decline", False },
515 { "menuAction.Rematch", False },
516 { "menuAction.Adjourn", False },
517 { "menuAction.Stop Examining", False },
518 { "menuAction.Stop Observing", False },
519 { "menuStep.Revert", False },
520 { "menuOptions.Auto Comment", False },
521 { "menuOptions.Auto Observe", False },
522 { "menuOptions.Auto Raise Board", False },
523 { "menuOptions.Get Move List", False },
524 { "menuOptions.Premove", False },
525 { "menuOptions.Quiet Play", False },
527 /* The next two options rely on SetCmailMode being called *after* */
528 /* SetGNUMode so that when GNU is being used to give hints these */
529 /* menu options are still available */
531 { "menuFile.Mail Move", False },
532 { "menuFile.Reload CMail Message", False },
536 Enables cmailEnables[] = {
538 { "menuAction.Call Flag", False },
539 { "menuAction.Draw", True },
540 { "menuAction.Adjourn", False },
541 { "menuAction.Abort", False },
542 { "menuAction.Stop Observing", False },
543 { "menuAction.Stop Examining", False },
544 { "menuFile.Mail Move", True },
545 { "menuFile.Reload CMail Message", True },
549 Enables trainingOnEnables[] = {
550 { "menuMode.Edit Comment", False },
551 { "menuMode.Pause", False },
552 { "menuStep.Forward", False },
553 { "menuStep.Backward", False },
554 { "menuStep.Forward to End", False },
555 { "menuStep.Back to Start", False },
556 { "menuStep.Move Now", False },
557 { "menuStep.Truncate Game", False },
561 Enables trainingOffEnables[] = {
562 { "menuMode.Edit Comment", True },
563 { "menuMode.Pause", True },
564 { "menuStep.Forward", True },
565 { "menuStep.Backward", True },
566 { "menuStep.Forward to End", True },
567 { "menuStep.Back to Start", True },
568 { "menuStep.Move Now", True },
569 { "menuStep.Truncate Game", True },
573 Enables machineThinkingEnables[] = {
574 { "menuFile.Load Game", False },
575 { "menuFile.Load Next Game", False },
576 { "menuFile.Load Previous Game", False },
577 { "menuFile.Reload Same Game", False },
578 { "menuFile.Paste Game", False },
579 { "menuFile.Load Position", False },
580 { "menuFile.Load Next Position", False },
581 { "menuFile.Load Previous Position", False },
582 { "menuFile.Reload Same Position", False },
583 { "menuFile.Paste Position", False },
584 { "menuMode.Machine White", False },
585 { "menuMode.Machine Black", False },
586 { "menuMode.Two Machines", False },
587 { "menuStep.Retract Move", False },
591 Enables userThinkingEnables[] = {
592 { "menuFile.Load Game", True },
593 { "menuFile.Load Next Game", True },
594 { "menuFile.Load Previous Game", True },
595 { "menuFile.Reload Same Game", True },
596 { "menuFile.Paste Game", True },
597 { "menuFile.Load Position", True },
598 { "menuFile.Load Next Position", True },
599 { "menuFile.Load Previous Position", True },
600 { "menuFile.Reload Same Position", True },
601 { "menuFile.Paste Position", True },
602 { "menuMode.Machine White", True },
603 { "menuMode.Machine Black", True },
604 { "menuMode.Two Machines", True },
605 { "menuStep.Retract Move", True },
611 MenuItem fileMenu[] = {
612 {N_("New Shuffle Game ..."), ShuffleMenuProc},
613 {N_("New Variant ..."), NewVariantProc}, // [HGM] variant: not functional yet
614 // {"----", NothingProc},
615 // {N_("Save Game"), SaveGameProc},
616 // {"----", NothingProc},
617 {N_("Copy Game"), CopyGameProc},
618 {N_("Paste Game"), PasteGameProc},
619 // {"----", NothingProc},
620 // {N_("Load Position"), LoadPositionProc},
621 // {N_("Load Next Position"), LoadNextPositionProc},
622 // {N_("Load Previous Position"), LoadPrevPositionProc},
623 // {N_("Reload Same Position"), ReloadPositionProc},
624 // {N_("Save Position"), SavePositionProc},
625 // {"----", NothingProc},
626 {N_("Copy Position"), CopyPositionProc},
627 {N_("Paste Position"), PastePositionProc},
628 // {"----", NothingProc},
629 {N_("Mail Move"), MailMoveProc},
630 {N_("Reload CMail Message"), ReloadCmailMsgProc},
631 // {"----", NothingProc},
635 MenuItem modeMenu[] = {
636 // {N_("Machine White"), MachineWhiteProc},
637 // {N_("Machine Black"), MachineBlackProc},
638 // {N_("Two Machines"), TwoMachinesProc},
639 // {N_("Analysis Mode"), AnalyzeModeProc},
640 // {N_("Analyze File"), AnalyzeFileProc },
641 // {N_("ICS Client"), IcsClientProc},
642 // {N_("Edit Game"), EditGameProc},
643 // {N_("Edit Position"), EditPositionProc},
644 // {N_("Training"), TrainingProc},
645 // {"----", NothingProc},
646 {N_("Show Engine Output"), EngineOutputProc},
647 {N_("Show Evaluation Graph"), EvalGraphProc},
648 {N_("Show Game List"), ShowGameListProc},
649 // {"Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
650 // {"----", NothingProc},
651 // {N_("Edit Tags"), EditTagsProc},
652 {N_("Edit Comment"), EditCommentProc},
653 {N_("ICS Input Box"), IcsInputBoxProc},
657 MenuItem optionsMenu[] = {
658 // {N_("Flip View"), FlipViewProc},
659 // {"----", NothingProc},
660 {N_("Adjudications ..."), EngineMenuProc},
661 {N_("General Settings ..."), UciMenuProc},
662 {N_("Engine #1 Settings ..."), FirstSettingsProc},
663 {N_("Engine #2 Settings ..."), SecondSettingsProc},
664 {N_("Time Control ..."), TimeControlProc},
665 {"----", NothingProc},
666 // {N_("Always Queen"), AlwaysQueenProc},
667 // {N_("Animate Dragging"), AnimateDraggingProc},
668 // {N_("Animate Moving"), AnimateMovingProc},
669 // {N_("Auto Comment"), AutocommProc},
670 // {N_("Auto Flag"), AutoflagProc},
671 // {N_("Auto Flip View"), AutoflipProc},
672 // {N_("Auto Observe"), AutobsProc},
673 // {N_("Auto Raise Board"), AutoraiseProc},
674 // {N_("Auto Save"), AutosaveProc},
675 // {N_("Blindfold"), BlindfoldProc},
676 // {N_("Flash Moves"), FlashMovesProc},
677 // {N_("Get Move List"), GetMoveListProc},
679 // {N_("Highlight Dragging"), HighlightDraggingProc},
681 // {N_("Highlight Last Move"), HighlightLastMoveProc},
682 // {N_("Move Sound"), MoveSoundProc},
683 // {N_("ICS Alarm"), IcsAlarmProc},
684 // {N_("Old Save Style"), OldSaveStyleProc},
685 // {N_("Periodic Updates"), PeriodicUpdatesProc},
686 // {N_("Ponder Next Move"), PonderNextMoveProc},
687 // {N_("Popup Exit Message"), PopupExitMessageProc},
688 // {N_("Popup Move Errors"), PopupMoveErrorsProc},
689 // {N_("Premove"), PremoveProc},
690 // {N_("Quiet Play"), QuietPlayProc},
691 // {N_("Hide Thinking"), HideThinkingProc},
692 // {N_("Test Legality"), TestLegalityProc},
693 // {N_("Show Coords"), ShowCoordsProc},
694 {"----", NothingProc},
695 {N_("Save Settings Now"), SaveSettingsProc},
696 {N_("Save Settings on Exit"), SaveOnExitProc},
701 {N_("File"), fileMenu},
702 {N_("Mode"), modeMenu},
703 {N_("Options"), optionsMenu},
707 #define PIECE_MENU_SIZE 18
708 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
709 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
710 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
711 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
712 N_("Empty square"), N_("Clear board") },
713 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
714 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
715 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
716 N_("Empty square"), N_("Clear board") }
718 /* must be in same order as PieceMenuStrings! */
719 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
720 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
721 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
722 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
723 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
724 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
725 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
726 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
727 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
730 #define DROP_MENU_SIZE 6
731 String dropMenuStrings[DROP_MENU_SIZE] = {
732 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
734 /* must be in same order as PieceMenuStrings! */
735 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
736 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
737 WhiteRook, WhiteQueen
745 DropMenuEnables dmEnables[] = {
754 { XtNborderWidth, 0 },
755 { XtNdefaultDistance, 0 },
759 { XtNborderWidth, 0 },
760 { XtNresizable, (XtArgVal) True },
764 { XtNborderWidth, 0 },
769 XtResource clientResources[] = {
770 { "flashCount", "flashCount", XtRInt, sizeof(int),
771 XtOffset(AppDataPtr, flashCount), XtRImmediate,
772 (XtPointer) FLASH_COUNT },
775 XtActionsRec boardActions[] = {
776 // { "HandleUserMove", HandleUserMove },
777 { "AnimateUserMove", AnimateUserMove },
778 // { "FileNameAction", FileNameAction },
779 { "HandlePV", HandlePV },
780 { "UnLoadPV", UnLoadPV },
781 { "AskQuestionProc", AskQuestionProc },
782 { "AskQuestionReplyAction", AskQuestionReplyAction },
783 { "PieceMenuPopup", PieceMenuPopup },
784 // { "WhiteClock", WhiteClock },
785 // { "BlackClock", BlackClock },
786 { "Iconify", Iconify },
787 { "LoadSelectedProc", LoadSelectedProc },
788 // { "LoadPositionProc", LoadPositionProc },
789 // { "LoadNextPositionProc", LoadNextPositionProc },
790 // { "LoadPrevPositionProc", LoadPrevPositionProc },
791 // { "ReloadPositionProc", ReloadPositionProc },
792 { "CopyPositionProc", CopyPositionProc },
793 { "PastePositionProc", PastePositionProc },
794 { "CopyGameProc", CopyGameProc },
795 { "PasteGameProc", PasteGameProc },
796 // { "SaveGameProc", SaveGameProc },
797 // { "SavePositionProc", SavePositionProc },
798 { "MailMoveProc", MailMoveProc },
799 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
800 // { "MachineWhiteProc", MachineWhiteProc },
801 // { "MachineBlackProc", MachineBlackProc },
802 // { "AnalysisModeProc", AnalyzeModeProc },
803 // { "AnalyzeFileProc", AnalyzeFileProc },
804 // { "TwoMachinesProc", TwoMachinesProc },
805 // { "IcsClientProc", IcsClientProc },
806 // { "EditGameProc", EditGameProc },
807 // { "EditPositionProc", EditPositionProc },
808 // { "TrainingProc", EditPositionProc },
809 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
810 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
811 { "ShowGameListProc", ShowGameListProc },
812 // { "ShowMoveListProc", HistoryShowProc},
813 // { "EditTagsProc", EditCommentProc },
814 { "EditCommentProc", EditCommentProc },
815 // { "IcsAlarmProc", IcsAlarmProc },
816 { "IcsInputBoxProc", IcsInputBoxProc },
817 // { "AcceptProc", AcceptProc },
818 // { "DeclineProc", DeclineProc },
819 // { "RematchProc", RematchProc },
820 // { "CallFlagProc", CallFlagProc },
821 // { "DrawProc", DrawProc },
822 // { "AdjournProc", AdjournProc },
823 // { "AbortProc", AbortProc },
824 // { "ResignProc", ResignProc },
825 // { "AdjuWhiteProc", AdjuWhiteProc },
826 // { "AdjuBlackProc", AdjuBlackProc },
827 // { "AdjuDrawProc", AdjuDrawProc },
828 { "EnterKeyProc", EnterKeyProc },
829 // { "StopObservingProc", StopObservingProc },
830 // { "StopExaminingProc", StopExaminingProc },
831 // { "BackwardProc", BackwardProc },
832 // { "ForwardProc", ForwardProc },
833 // { "ToStartProc", ToStartProc },
834 // { "ToEndProc", ToEndProc },
835 // { "RevertProc", RevertProc },
836 // { "TruncateGameProc", TruncateGameProc },
837 // { "MoveNowProc", MoveNowProc },
838 // { "RetractMoveProc", RetractMoveProc },
839 // { "AlwaysQueenProc", AlwaysQueenProc },
840 // { "AnimateDraggingProc", AnimateDraggingProc },
841 // { "AnimateMovingProc", AnimateMovingProc },
842 // { "AutoflagProc", AutoflagProc },
843 // { "AutoflipProc", AutoflipProc },
844 // { "AutobsProc", AutobsProc },
845 // { "AutoraiseProc", AutoraiseProc },
846 // { "AutosaveProc", AutosaveProc },
847 // { "BlindfoldProc", BlindfoldProc },
848 // { "FlashMovesProc", FlashMovesProc },
849 // { "FlipViewProc", FlipViewProc },
850 // { "GetMoveListProc", GetMoveListProc },
852 // { "HighlightDraggingProc", HighlightDraggingProc },
854 // { "HighlightLastMoveProc", HighlightLastMoveProc },
855 // { "IcsAlarmProc", IcsAlarmProc },
856 // { "MoveSoundProc", MoveSoundProc },
857 // { "OldSaveStyleProc", OldSaveStyleProc },
858 // { "PeriodicUpdatesProc", PeriodicUpdatesProc },
859 // { "PonderNextMoveProc", PonderNextMoveProc },
860 // { "PopupExitMessageProc", PopupExitMessageProc },
861 // { "PopupMoveErrorsProc", PopupMoveErrorsProc },
862 // { "PremoveProc", PremoveProc },
863 // { "QuietPlayProc", QuietPlayProc },
864 // { "ShowThinkingProc", ShowThinkingProc },
865 // { "HideThinkingProc", HideThinkingProc },
866 // { "TestLegalityProc", TestLegalityProc },
867 { "SaveSettingsProc", SaveSettingsProc },
868 { "SaveOnExitProc", SaveOnExitProc },
869 // { "InfoProc", InfoProc },
870 // { "ManProc", ManProc },
871 // { "HintProc", HintProc },
872 // { "BookProc", BookProc },
873 { "AboutGameProc", AboutGameProc },
874 { "DebugProc", DebugProc },
875 { "NothingProc", NothingProc },
876 { "CommentPopDown", (XtActionProc) CommentPopDown },
877 { "EditCommentPopDown", (XtActionProc) EditCommentPopDown },
878 { "TagsPopDown", (XtActionProc) TagsPopDown },
879 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
880 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
881 // { "FileNamePopDown", (XtActionProc) FileNamePopDown },
882 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
883 { "GameListPopDown", (XtActionProc) GameListPopDown },
884 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
885 // { "HistoryPopDown", (XtActionProc) HistoryPopDown },
886 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
887 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
888 { "ShufflePopDown", (XtActionProc) ShufflePopDown },
889 { "EnginePopDown", (XtActionProc) EnginePopDown },
890 { "UciPopDown", (XtActionProc) UciPopDown },
891 { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
892 { "NewVariantPopDown", (XtActionProc) NewVariantPopDown },
893 { "SettingsPopDown", (XtActionProc) SettingsPopDown },
894 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
897 //char globalTranslations[] =
898 // ":<Key>R: ResignProc() \n \
899 // :<Key>r: ResetProc() \n \
900 // :<Key>g: LoadGameProc() \n \
901 // :<Key>N: LoadNextGameProc() \n \
902 // :<Key>P: LoadPrevGameProc() \n \
903 // :<Key>Q: QuitProc() \n \
904 // :<Key>F: ToEndProc() \n \
905 // :<Key>f: ForwardProc() \n \
906 // :<Key>B: ToStartProc() \n \
907 // :<Key>b: BackwardProc() \n \
908 // :<Key>p: PauseProc() \n \
909 // :<Key>d: DrawProc() \n \
910 // :<Key>t: CallFlagProc() \n \
911 // :<Key>i: Iconify() \n \
912 // :<Key>c: Iconify() \n \
913 // :<Key>v: FlipViewProc() \n \
914 // <KeyDown>Control_L: BackwardProc() \n \
915 // <KeyUp>Control_L: ForwardProc() \n \
916 // <KeyDown>Control_R: BackwardProc() \n \
917 // <KeyUp>Control_R: ForwardProc() \n \
918 // Shift<Key>1: AskQuestionProc(\"Direct command\",\
919 // \"Send to chess program:\",,1) \n \
920 // Shift<Key>2: AskQuestionProc(\"Direct command\",\
921 // \"Send to second chess program:\",,2) \n";
923 //char boardTranslations[] =
924 // "<Btn1Down>: HandleUserMove() \n \
925 // <Btn1Up>: HandleUserMove() \n \
926 // <Btn1Motion>: AnimateUserMove() \n \
927 // <Btn3Motion>: HandlePV() \n \
928 // <Btn3Up>: UnLoadPV() \n \
929 // Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
930 // PieceMenuPopup(menuB) \n \
931 // Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
932 // PieceMenuPopup(menuW) \n \
933 // Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
934 // PieceMenuPopup(menuW) \n \
935 // Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
936 // PieceMenuPopup(menuB) \n";
938 //char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
939 //char blackTranslations[] = "<BtnDown>: BlackClock()\n";
941 char ICSInputTranslations[] =
942 "<Key>Return: EnterKeyProc() \n";
944 String xboardResources[] = {
945 // "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
946 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
947 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
951 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
952 "magenta", "cyan", "white" };
956 TextColors textColors[(int)NColorClasses];
958 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
960 parse_color(str, which)
964 char *p, buf[100], *d;
967 if (strlen(str) > 99) /* watch bounds on buf */
972 for (i=0; i<which; ++i) {
979 /* Could be looking at something like:
981 .. in which case we want to stop on a comma also */
982 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
986 return -1; /* Use default for empty field */
989 if (which == 2 || isdigit(*p))
992 while (*p && isalpha(*p))
997 for (i=0; i<8; ++i) {
998 if (!StrCaseCmp(buf, cnames[i]))
999 return which? (i+40) : (i+30);
1001 if (!StrCaseCmp(buf, "default")) return -1;
1003 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1008 parse_cpair(cc, str)
1012 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1013 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1018 /* bg and attr are optional */
1019 textColors[(int)cc].bg = parse_color(str, 1);
1020 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1021 textColors[(int)cc].attr = 0;
1030 /* this should raise the board to the top */
1031 gtk_window_present(GTK_WINDOW(GUI_Window));
1035 //---------------------------------------------------------------------------------------------------------
1036 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1039 #define CW_USEDEFAULT (1<<31)
1040 #define ICS_TEXT_MENU_SIZE 90
1041 #define DEBUG_FILE "xboard.debug"
1042 #define SetCurrentDirectory chdir
1043 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1047 // these two must some day move to frontend.h, when they are implemented
1048 Boolean GameListIsUp();
1050 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1053 // front-end part of option handling
1055 // [HGM] This platform-dependent table provides the location for storing the color info
1056 extern char *crWhite, * crBlack;
1060 &appData.whitePieceColor,
1061 &appData.blackPieceColor,
1062 &appData.lightSquareColor,
1063 &appData.darkSquareColor,
1064 &appData.highlightSquareColor,
1065 &appData.premoveHighlightColor,
1066 &appData.lowTimeWarningColor,
1078 ParseFont(char *name, int number)
1079 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1081 case 0: // CLOCK_FONT
1082 appData.clockFont = strdup(name);
1084 case 1: // MESSAGE_FONT
1085 appData.font = strdup(name);
1087 case 2: // COORD_FONT
1088 appData.coordFont = strdup(name);
1097 { // only 2 fonts currently
1098 appData.clockFont = CLOCK_FONT_NAME;
1099 appData.coordFont = COORD_FONT_NAME;
1100 appData.font = DEFAULT_FONT_NAME;
1105 { // no-op, until we identify the code for this already in XBoard and move it here
1109 ParseColor(int n, char *name)
1110 { // in XBoard, just copy the color-name string
1111 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1115 ParseTextAttribs(ColorClass cc, char *s)
1117 (&appData.colorShout)[cc] = strdup(s);
1121 ParseBoardSize(void *addr, char *name)
1123 appData.boardSize = strdup(name);
1128 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1132 SetCommPortDefaults()
1133 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1136 // [HGM] args: these three cases taken out to stay in front-end
1138 SaveFontArg(FILE *f, ArgDescriptor *ad)
1141 switch((int)ad->argLoc) {
1142 case 0: // CLOCK_FONT
1143 name = appData.clockFont;
1145 case 1: // MESSAGE_FONT
1146 name = appData.font;
1148 case 2: // COORD_FONT
1149 name = appData.coordFont;
1154 // Do not save fonts for now, as the saved font would be board-size specific
1155 // and not suitable for a re-start at another board size
1156 // fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, name);
1161 { // nothing to do, as the sounds are at all times represented by their text-string names already
1165 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1166 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1167 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)ad->argLoc]);
1171 SaveColor(FILE *f, ArgDescriptor *ad)
1172 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1173 if(colorVariable[(int)ad->argLoc])
1174 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)ad->argLoc]);
1178 SaveBoardSize(FILE *f, char *name, void *addr)
1179 { // wrapper to shield back-end from BoardSize & sizeInfo
1180 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1184 ParseCommPortSettings(char *s)
1185 { // no such option in XBoard (yet)
1188 extern Widget engineOutputShell;
1189 extern Widget tagsShell, editTagsShell;
1192 GetActualPlacement(Widget wg, WindowPlacement *wp)
1202 XtSetArg(args[i], XtNx, &x); i++;
1203 XtSetArg(args[i], XtNy, &y); i++;
1204 XtSetArg(args[i], XtNwidth, &w); i++;
1205 XtSetArg(args[i], XtNheight, &h); i++;
1206 XtGetValues(wg, args, i);
1215 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1216 // In XBoard this will have to wait until awareness of window parameters is implemented
1218 // GetActualPlacement(shellWidget, &wpMain);
1219 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1220 // if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1221 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1222 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1223 if(commentShell) GetActualPlacement(commentShell, &wpComment);
1224 else GetActualPlacement(editShell, &wpComment);
1225 if(tagsShell) GetActualPlacement(tagsShell, &wpTags);
1226 else GetActualPlacement(editTagsShell, &wpTags);
1230 PrintCommPortSettings(FILE *f, char *name)
1231 { // This option does not exist in XBoard
1235 MySearchPath(char *installDir, char *name, char *fullname)
1236 { // just append installDir and name. Perhaps ExpandPath should be used here?
1237 name = ExpandPathName(name);
1238 if(name && name[0] == '/') strcpy(fullname, name); else {
1239 sprintf(fullname, "%s%c%s", installDir, '/', name);
1245 MyGetFullPathName(char *name, char *fullname)
1246 { // should use ExpandPath?
1247 name = ExpandPathName(name);
1248 strcpy(fullname, name);
1253 EnsureOnScreen(int *x, int *y, int minX, int minY)
1260 { // [HGM] args: allows testing if main window is realized from back-end
1261 return xBoardWindow != 0;
1265 PopUpStartupDialog()
1266 { // start menu not implemented in XBoard
1269 ConvertToLine(int argc, char **argv)
1271 static char line[128*1024], buf[1024];
1275 for(i=1; i<argc; i++) {
1276 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1277 && argv[i][0] != '{' )
1278 sprintf(buf, "{%s} ", argv[i]);
1279 else sprintf(buf, "%s ", argv[i]);
1282 line[strlen(line)-1] = NULLCHAR;
1286 //--------------------------------------------------------------------------------------------
1289 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1292 #define BoardSize int
1293 void InitDrawingSizes(BoardSize boardSize, int flags)
1294 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1295 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1297 XtGeometryResult gres;
1300 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1301 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1303 timerWidth = (boardWidth - sep) / 2;
1305 if (appData.titleInWindow)
1310 w = boardWidth - 2*bor;
1314 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1318 if(!formWidget) return;
1321 * Inhibit shell resizing.
1324 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1327 for(i=0; i<4; i++) {
1329 for(p=0; p<=(int)WhiteKing; p++)
1330 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1331 if(gameInfo.variant == VariantShogi) {
1332 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1333 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1334 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1335 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1336 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1339 if(gameInfo.variant == VariantGothic) {
1340 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1344 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1345 for(p=0; p<=(int)WhiteKing; p++)
1346 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1347 if(gameInfo.variant == VariantShogi) {
1348 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1349 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1350 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1351 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1352 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1355 if(gameInfo.variant == VariantGothic) {
1356 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1362 for(i=0; i<2; i++) {
1364 for(p=0; p<=(int)WhiteKing; p++)
1365 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1366 if(gameInfo.variant == VariantShogi) {
1367 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1368 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1369 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1370 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1371 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1374 if(gameInfo.variant == VariantGothic) {
1375 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1386 void EscapeExpand(char *p, char *q)
1387 { // [HGM] initstring: routine to shape up string arguments
1388 while(*p++ = *q++) if(p[-1] == '\\')
1390 case 'n': p[-1] = '\n'; break;
1391 case 'r': p[-1] = '\r'; break;
1392 case 't': p[-1] = '\t'; break;
1393 case '\\': p[-1] = '\\'; break;
1394 case 0: *p = 0; return;
1395 default: p[-1] = q[-1]; break;
1404 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1405 XSetWindowAttributes window_attributes;
1407 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1408 XrmValue vFrom, vTo;
1409 XtGeometryResult gres;
1412 int forceMono = False;
1414 srandom(time(0)); // [HGM] book: make random truly random
1416 setbuf(stdout, NULL);
1417 setbuf(stderr, NULL);
1420 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1421 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1425 programName = strrchr(argv[0], '/');
1426 if (programName == NULL)
1427 programName = argv[0];
1432 XtSetLanguageProc(NULL, NULL, NULL);
1433 bindtextdomain(PACKAGE, LOCALEDIR);
1434 textdomain(PACKAGE);
1438 gtk_init (&argc, &argv);
1440 /* parse glade file to build widgets */
1442 builder = gtk_builder_new ();
1443 gtk_builder_add_from_file (builder, "gtk-interface.xml", NULL);
1445 /* test if everything worked ok */
1447 GUI_Window = GTK_WIDGET (gtk_builder_get_object (builder, "MainWindow"));
1448 if(!GUI_Window) printf("Error: gtk_builder didn't work!\n");
1450 GUI_Aspect = GTK_WIDGET (gtk_builder_get_object (builder, "Aspectframe"));
1451 if(!GUI_Aspect) printf("Error: gtk_builder didn't work!\n");
1453 GUI_History = GTK_WIDGET (gtk_builder_get_object (builder, "MoveHistory"));
1454 if(!GUI_History) printf("Error: gtk_builder didn't work!\n");
1456 GUI_GameList = GTK_WIDGET (gtk_builder_get_object (builder, "GameList"));
1457 if(!GUI_GameList) printf("Error: gtk_builder didn't work!\n");
1459 GUI_Menubar = GTK_WIDGET (gtk_builder_get_object (builder, "MenuBar"));
1460 if(!GUI_Menubar) printf("Error: gtk_builder didn't work!\n");
1461 GUI_Timer = GTK_WIDGET (gtk_builder_get_object (builder, "Timer"));
1462 if(!GUI_Timer) printf("Error: gtk_builder didn't work!\n");
1463 GUI_Buttonbar = GTK_WIDGET (gtk_builder_get_object (builder, "ButtonBar"));
1464 if(!GUI_Buttonbar) printf("Error: gtk_builder didn't work!\n");
1465 GUI_Board = GTK_WIDGET (gtk_builder_get_object (builder, "Board"));
1466 if(!GUI_Board) printf("Error: gtk_builder didn't work!\n");
1468 GUI_Whiteclock = GTK_WIDGET (gtk_builder_get_object (builder, "WhiteClock"));
1469 if(!GUI_Whiteclock) printf("Error: gtk_builder didn't work!\n");
1471 GUI_Blackclock = GTK_WIDGET (gtk_builder_get_object (builder, "BlackClock"));
1472 if(!GUI_Blackclock) printf("Error: gtk_builder didn't work!\n");
1474 LIST_MoveHistory = GTK_LIST_STORE (gtk_builder_get_object (builder, "MoveHistoryStore"));
1475 if(!LIST_MoveHistory) printf("Error: gtk_builder didn't work!\n");
1477 LIST_GameList = GTK_LIST_STORE (gtk_builder_get_object (builder, "GameListStore"));
1478 if(!LIST_GameList) printf("Error: gtk_builder didn't work!\n");
1480 /* EditTags window */
1481 GUI_EditTags = GTK_WIDGET (gtk_builder_get_object (builder, "EditTags"));
1482 if(!GUI_EditTags) printf("Error: gtk_builder didn't work!\n");
1484 GUI_EditTagsTextArea = GTK_WIDGET (gtk_builder_get_object (builder, "EditTagsTextArea"));
1485 if(!GUI_EditTagsTextArea) printf("Error: gtk_builder didn't work!\n");
1488 gtk_builder_connect_signals (builder, NULL);
1490 // don't unref the builder, since we use it to get references to widgets
1491 // g_object_unref (G_OBJECT (builder));
1493 /* end parse glade file */
1495 appData.boardSize = "";
1496 InitAppData(ConvertToLine(argc, argv));
1499 if (p == NULL) p = "/tmp";
1500 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1501 gameCopyFilename = (char*) malloc(i);
1502 gamePasteFilename = (char*) malloc(i);
1503 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1504 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1506 // XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1507 // clientResources, XtNumber(clientResources),
1510 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1511 static char buf[MSG_SIZ];
1512 EscapeExpand(buf, appData.initString);
1513 appData.initString = strdup(buf);
1514 EscapeExpand(buf, appData.secondInitString);
1515 appData.secondInitString = strdup(buf);
1516 EscapeExpand(buf, appData.firstComputerString);
1517 appData.firstComputerString = strdup(buf);
1518 EscapeExpand(buf, appData.secondComputerString);
1519 appData.secondComputerString = strdup(buf);
1522 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1525 if (chdir(chessDir) != 0) {
1526 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1532 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1533 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1534 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1535 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1538 setbuf(debugFP, NULL);
1543 /* This feature does not work; animation needs a rewrite */
1544 appData.highlightDragging = FALSE;
1548 gameInfo.variant = StringToVariant(appData.variant);
1549 InitPosition(FALSE);
1554 clockFontPxlSize = 20;
1555 coordFontPxlSize = 20;
1561 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1562 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1565 * Determine what fonts to use.
1567 // appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1568 // clockFontID = XLoadFont(xDisplay, appData.clockFont);
1569 // clockFontStruct = XQueryFont(xDisplay, clockFontID);
1570 // appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1571 // coordFontID = XLoadFont(xDisplay, appData.coordFont);
1572 // coordFontStruct = XQueryFont(xDisplay, coordFontID);
1573 // appData.font = FindFont(appData.font, fontPxlSize);
1574 // countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
1575 // countFontStruct = XQueryFont(xDisplay, countFontID);
1576 // appData.font = FindFont(appData.font, fontPxlSize);
1578 // xdb = XtDatabase(xDisplay);
1579 // XrmPutStringResource(&xdb, "*font", appData.font);
1582 * Detect if there are not enough colors available and adapt.
1584 // if (DefaultDepth(xDisplay, xScreen) <= 2) {
1585 // appData.monoMode = True;
1588 if (!appData.monoMode) {
1589 vFrom.addr = (caddr_t) appData.lightSquareColor;
1590 vFrom.size = strlen(appData.lightSquareColor);
1591 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1592 if (vTo.addr == NULL) {
1593 appData.monoMode = True;
1596 lightSquareColor = *(Pixel *) vTo.addr;
1599 if (!appData.monoMode) {
1600 vFrom.addr = (caddr_t) appData.darkSquareColor;
1601 vFrom.size = strlen(appData.darkSquareColor);
1602 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1603 if (vTo.addr == NULL) {
1604 appData.monoMode = True;
1607 darkSquareColor = *(Pixel *) vTo.addr;
1610 if (!appData.monoMode) {
1611 vFrom.addr = (caddr_t) appData.whitePieceColor;
1612 vFrom.size = strlen(appData.whitePieceColor);
1613 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1614 if (vTo.addr == NULL) {
1615 appData.monoMode = True;
1618 whitePieceColor = *(Pixel *) vTo.addr;
1621 if (!appData.monoMode) {
1622 vFrom.addr = (caddr_t) appData.blackPieceColor;
1623 vFrom.size = strlen(appData.blackPieceColor);
1624 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1625 if (vTo.addr == NULL) {
1626 appData.monoMode = True;
1629 blackPieceColor = *(Pixel *) vTo.addr;
1633 if (!appData.monoMode) {
1634 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1635 vFrom.size = strlen(appData.highlightSquareColor);
1636 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1637 if (vTo.addr == NULL) {
1638 appData.monoMode = True;
1641 highlightSquareColor = *(Pixel *) vTo.addr;
1645 if (!appData.monoMode) {
1646 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1647 vFrom.size = strlen(appData.premoveHighlightColor);
1648 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1649 if (vTo.addr == NULL) {
1650 appData.monoMode = True;
1653 premoveHighlightColor = *(Pixel *) vTo.addr;
1658 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1661 if (appData.bitmapDirectory == NULL ||
1662 appData.bitmapDirectory[0] == NULLCHAR)
1663 appData.bitmapDirectory = DEF_BITMAP_DIR;
1666 if (appData.lowTimeWarning && !appData.monoMode) {
1667 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1668 vFrom.size = strlen(appData.lowTimeWarningColor);
1669 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1670 if (vTo.addr == NULL)
1671 appData.monoMode = True;
1673 lowTimeWarningColor = *(Pixel *) vTo.addr;
1676 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1677 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1678 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1679 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1680 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1681 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1682 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1683 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1684 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1685 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1687 if (appData.colorize) {
1689 _("%s: can't parse color names; disabling colorization\n"),
1692 appData.colorize = FALSE;
1694 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
1695 textColors[ColorNone].attr = 0;
1697 // XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1703 layoutName = "tinyLayout";
1704 } else if (smallLayout) {
1705 layoutName = "smallLayout";
1707 layoutName = "normalLayout";
1710 if (appData.titleInWindow) {
1711 /* todo check what this appdata does */
1714 if (appData.showButtonBar) {
1715 /* TODO hide button bar if requested */
1719 if (appData.titleInWindow)
1724 if (appData.showButtonBar)
1731 if (appData.showButtonBar)
1741 /* set some checkboxes in the menu according to appData */
1743 if (appData.alwaysPromoteToQueen)
1744 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Always Queen")),TRUE);
1746 if (appData.animateDragging)
1747 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Animate Dragging")),TRUE);
1749 if (appData.animate)
1750 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Animate Moving")),TRUE);
1752 if (appData.autoComment)
1753 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Comment")),TRUE);
1755 if (appData.autoCallFlag)
1756 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Flag")),TRUE);
1758 if (appData.autoFlipView)
1759 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Flip View")),TRUE);
1761 if (appData.autoObserve)
1762 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Observe")),TRUE);
1764 if (appData.autoRaiseBoard)
1765 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Raise Board")),TRUE);
1767 if (appData.autoSaveGames)
1768 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Save")),TRUE);
1770 if (appData.saveGameFile[0] != NULLCHAR)
1772 /* Can't turn this off from menu */
1773 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Save")),TRUE);
1774 gtk_action_set_sensitive(GTK_ACTION (gtk_builder_get_object (builder, "menuOptions.Auto Save")),FALSE);
1777 if (appData.blindfold)
1778 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Blindfold")),TRUE);
1780 if (appData.flashCount > 0)
1781 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Flash Moves")),TRUE);
1783 if (appData.getMoveList)
1784 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Get Move List")),TRUE);
1787 if (appData.highlightDragging)
1788 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Highlight Dragging")),TRUE);
1791 if (appData.highlightLastMove)
1792 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Highlight Last Move")),TRUE);
1794 if (appData.icsAlarm)
1795 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.ICS Alarm")),TRUE);
1797 if (appData.ringBellAfterMoves)
1798 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Move Sound")),TRUE);
1800 if (appData.oldSaveStyle)
1801 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Old Save Style")),TRUE);
1803 if (appData.periodicUpdates)
1804 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Periodic Updates")),TRUE);
1806 if (appData.ponderNextMove)
1807 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Ponder Next Move")),TRUE);
1809 if (appData.popupExitMessage)
1810 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Popup Exit Message")),TRUE);
1812 if (appData.popupMoveErrors)
1813 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Popup Move Errors")),TRUE);
1815 if (appData.premove)
1816 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Premove")),TRUE);
1818 if (appData.quietPlay)
1819 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Quit Play")),TRUE);
1821 if (appData.showCoords)
1822 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Show Coords")),TRUE);
1824 if (appData.showThinking)
1825 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Hide Thinking")),TRUE);
1827 if (appData.testLegality)
1828 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Test Legality")),TRUE);
1831 // if (saveSettingsOnExit) {
1832 // XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
1837 /* end setting check boxes */
1839 /* load square colors */
1840 SVGLightSquare = load_pixbuf("svg/LightSquare.svg",squareSize);
1841 SVGDarkSquare = load_pixbuf("svg/DarkSquare.svg",squareSize);
1842 SVGNeutralSquare = load_pixbuf("svg/NeutralSquare.svg",squareSize);
1844 /* use two icons to indicate if it is white's or black's turn */
1845 WhiteIcon = load_pixbuf("svg/icon_white.svg",0);
1846 BlackIcon = load_pixbuf("svg/icon_black.svg",0);
1847 WindowIcon = WhiteIcon;
1848 gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
1851 /* realize window */
1852 gtk_widget_show (GUI_Window);
1854 /* recalc boardsize */
1859 if (appData.animate || appData.animateDragging)
1864 if (errorExitStatus == -1) {
1865 if (appData.icsActive) {
1866 /* We now wait until we see "login:" from the ICS before
1867 sending the logon script (problems with timestamp otherwise) */
1868 /*ICSInitScript();*/
1869 if (appData.icsInputBox) ICSInputBoxPopUp();
1873 signal(SIGWINCH, TermSizeSigHandler);
1875 signal(SIGINT, IntSigHandler);
1876 signal(SIGTERM, IntSigHandler);
1877 if (*appData.cmailGameName != NULLCHAR) {
1878 signal(SIGUSR1, CmailSigHandler);
1881 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1885 * Create a cursor for the board widget.
1886 * (This needs to be called after the window has been created to have access to board-window)
1889 BoardCursor = gdk_cursor_new(GDK_HAND2);
1890 gdk_window_set_cursor(GUI_Board->window, BoardCursor);
1891 gdk_cursor_destroy(BoardCursor);
1896 if (appData.debugMode) fclose(debugFP); // [DM] debug
1903 if (appData.icsActive && oldICSInteractionTitle != NULL) {
1904 DisplayIcsInteractionTitle(oldICSInteractionTitle);
1906 if (saveSettingsOnExit) SaveSettings(settingsFileName);
1907 unlink(gameCopyFilename);
1908 unlink(gamePasteFilename);
1911 RETSIGTYPE TermSizeSigHandler(int sig)
1924 CmailSigHandler(sig)
1930 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1932 /* Activate call-back function CmailSigHandlerCallBack() */
1933 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1935 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1939 CmailSigHandlerCallBack(isr, closure, message, count, error)
1947 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1949 /**** end signal code ****/
1959 f = fopen(appData.icsLogon, "r");
1965 strcat(buf, appData.icsLogon);
1966 f = fopen(buf, "r");
1970 ProcessICSInitScript(f);
1977 EditCommentPopDown();
1987 if (!menuBarWidget) return;
1988 w = XtNameToWidget(menuBarWidget, "menuStep.Revert");
1990 DisplayError("menuStep.Revert", 0);
1992 XtSetSensitive(w, !grey);
1997 SetMenuEnables(enab)
2002 if (!builder) return;
2003 while (enab->name != NULL) {
2004 o = gtk_builder_get_object(builder, enab->name);
2005 if(GTK_IS_WIDGET(o))
2006 gtk_widget_set_sensitive(GTK_WIDGET (o),enab->value);
2009 if(GTK_IS_ACTION(o))
2010 gtk_action_set_sensitive(GTK_ACTION (o),enab->value);
2012 DisplayError(enab->name, 0);
2020 SetMenuEnables(icsEnables);
2023 if (appData.zippyPlay && !appData.noChessProgram) /* [DM] icsEngineAnalyze */
2024 {}; // XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2031 SetMenuEnables(ncpEnables);
2037 SetMenuEnables(gnuEnables);
2043 SetMenuEnables(cmailEnables);
2049 SetMenuEnables(trainingOnEnables);
2050 if (appData.showButtonBar) {
2051 // XtSetSensitive(buttonBarWidget, False);
2057 SetTrainingModeOff()
2059 SetMenuEnables(trainingOffEnables);
2060 if (appData.showButtonBar) {
2061 // XtSetSensitive(buttonBarWidget, True);
2066 SetUserThinkingEnables()
2068 if (appData.noChessProgram) return;
2069 SetMenuEnables(userThinkingEnables);
2073 SetMachineThinkingEnables()
2075 if (appData.noChessProgram) return;
2076 SetMenuEnables(machineThinkingEnables);
2078 case MachinePlaysBlack:
2079 case MachinePlaysWhite:
2080 case TwoMachinesPlay:
2081 // XtSetSensitive(XtNameToWidget(menuBarWidget,
2082 // ModeToWidgetName(gameMode)), True);
2089 #define Abs(n) ((n)<0 ? -(n) : (n))
2092 * Find a font that matches "pattern" that is as close as
2093 * possible to the targetPxlSize. Prefer fonts that are k
2094 * pixels smaller to fonts that are k pixels larger. The
2095 * pattern must be in the X Consortium standard format,
2096 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2097 * The return value should be freed with XtFree when no
2100 char *FindFont(pattern, targetPxlSize)
2104 char **fonts, *p, *best, *scalable, *scalableTail;
2105 int i, j, nfonts, minerr, err, pxlSize;
2108 char **missing_list;
2110 char *def_string, *base_fnt_lst, strInt[3];
2112 XFontStruct **fnt_list;
2114 base_fnt_lst = calloc(1, strlen(pattern) + 3);
2115 sprintf(strInt, "%d", targetPxlSize);
2116 p = strstr(pattern, "--");
2117 strncpy(base_fnt_lst, pattern, p - pattern + 2);
2118 strcat(base_fnt_lst, strInt);
2119 strcat(base_fnt_lst, strchr(p + 2, '-'));
2121 if ((fntSet = XCreateFontSet(xDisplay,
2125 &def_string)) == NULL) {
2127 fprintf(stderr, _("Unable to create font set.\n"));
2131 nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
2133 // fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2134 // if (nfonts < 1) {
2135 // fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2136 // programName, pattern);
2144 for (i=0; i<nfonts; i++) {
2147 if (*p != '-') continue;
2149 if (*p == NULLCHAR) break;
2150 if (*p++ == '-') j++;
2152 if (j < 7) continue;
2155 scalable = fonts[i];
2158 err = pxlSize - targetPxlSize;
2159 if (Abs(err) < Abs(minerr) ||
2160 (minerr > 0 && err < 0 && -err == minerr)) {
2166 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2167 /* If the error is too big and there is a scalable font,
2168 use the scalable font. */
2169 int headlen = scalableTail - scalable;
2170 p = (char *) XtMalloc(strlen(scalable) + 10);
2171 while (isdigit(*scalableTail)) scalableTail++;
2172 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2174 p = (char *) XtMalloc(strlen(best) + 1);
2177 if (appData.debugMode) {
2178 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2179 pattern, targetPxlSize, p);
2182 if (missing_count > 0)
2183 XFreeStringList(missing_list);
2184 // XFreeFontSet(xDisplay, fntSet);
2186 XFreeFontNames(fonts);
2193 /* 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*/
2202 for(i=0;i<MAXPIECES;i++)
2206 g_free(SVGpieces[i]);
2213 SVGLightSquare = load_pixbuf("svg/LightSquare.svg",squareSize);
2214 SVGDarkSquare = load_pixbuf("svg/DarkSquare.svg",squareSize);
2215 SVGNeutralSquare = load_pixbuf("svg/NeutralSquare.svg",squareSize);
2218 /* get some defaults going */
2219 for(i=WhitePawn; i<DemotePiece+1; i++)
2220 SVGpieces[i] = load_pixbuf("svg/NeutralSquare.svg",squareSize);
2222 SVGpieces[WhitePawn] = load_pixbuf("svg/WhitePawn.svg",squareSize);
2223 SVGpieces[WhiteKnight] = load_pixbuf("svg/WhiteKnight.svg",squareSize);
2224 SVGpieces[WhiteBishop] = load_pixbuf("svg/WhiteBishop.svg",squareSize);
2225 SVGpieces[WhiteRook] = load_pixbuf("svg/WhiteRook.svg",squareSize);
2226 SVGpieces[WhiteQueen] = load_pixbuf("svg/WhiteQueen.svg",squareSize);
2227 SVGpieces[WhiteKing] = load_pixbuf("svg/WhiteKing.svg",squareSize);
2229 SVGpieces[BlackPawn] = load_pixbuf("svg/BlackPawn.svg",squareSize);
2230 SVGpieces[BlackKnight] = load_pixbuf("svg/BlackKnight.svg",squareSize);
2231 SVGpieces[BlackBishop] = load_pixbuf("svg/BlackBishop.svg",squareSize);
2232 SVGpieces[BlackRook] = load_pixbuf("svg/BlackRook.svg",squareSize);
2233 SVGpieces[BlackQueen] = load_pixbuf("svg/BlackQueen.svg",squareSize);
2234 SVGpieces[BlackKing] = load_pixbuf("svg/BlackKing.svg",squareSize);
2240 static void MenuBarSelect(w, addr, index)
2245 XtActionProc proc = (XtActionProc) addr;
2247 (proc)(NULL, NULL, NULL, NULL);
2250 void CreateMenuBarPopup(parent, name, mb)
2260 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2263 XtSetArg(args[j], XtNleftMargin, 20); j++;
2264 XtSetArg(args[j], XtNrightMargin, 20); j++;
2266 while (mi->string != NULL) {
2267 if (strcmp(mi->string, "----") == 0) {
2268 entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
2271 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
2272 entry = XtCreateManagedWidget(mi->string, smeBSBObjectClass,
2274 XtAddCallback(entry, XtNcallback,
2275 (XtCallbackProc) MenuBarSelect,
2276 (caddr_t) mi->proc);
2282 Widget CreateMenuBar(mb)
2286 Widget anchor, menuBar;
2288 char menuName[MSG_SIZ];
2291 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2292 XtSetArg(args[j], XtNvSpace, 0); j++;
2293 XtSetArg(args[j], XtNborderWidth, 0); j++;
2294 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
2295 formWidget, args, j);
2297 while (mb->name != NULL) {
2298 strcpy(menuName, "menu");
2299 strcat(menuName, mb->name);
2301 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
2304 shortName[0] = _(mb->name)[0];
2305 shortName[1] = NULLCHAR;
2306 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
2309 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
2312 XtSetArg(args[j], XtNborderWidth, 0); j++;
2313 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
2315 CreateMenuBarPopup(menuBar, menuName, mb);
2323 CreatePieceMenu(name, color)
2330 ChessSquare selection;
2332 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2333 boardWidget, args, 0);
2335 for (i = 0; i < PIECE_MENU_SIZE; i++) {
2336 String item = pieceMenuStrings[color][i];
2338 if (strcmp(item, "----") == 0) {
2339 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2342 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2343 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2345 selection = pieceMenuTranslation[color][i];
2346 XtAddCallback(entry, XtNcallback,
2347 (XtCallbackProc) PieceMenuSelect,
2348 (caddr_t) selection);
2349 if (selection == WhitePawn || selection == BlackPawn) {
2350 XtSetArg(args[0], XtNpopupOnEntry, entry);
2351 XtSetValues(menu, args, 1);
2364 ChessSquare selection;
2366 // whitePieceMenu = CreatePieceMenu("menuW", 0);
2367 // blackPieceMenu = CreatePieceMenu("menuB", 1);
2369 // XtRegisterGrabAction(PieceMenuPopup, True,
2370 // (unsigned)(ButtonPressMask|ButtonReleaseMask),
2371 // GrabModeAsync, GrabModeAsync);
2373 // XtSetArg(args[0], XtNlabel, _("Drop"));
2374 // dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
2375 // boardWidget, args, 1);
2376 // for (i = 0; i < DROP_MENU_SIZE; i++) {
2377 // String item = dropMenuStrings[i];
2379 // if (strcmp(item, "----") == 0) {
2380 // entry = XtCreateManagedWidget(item, smeLineObjectClass,
2381 // dropMenu, NULL, 0);
2383 // XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2384 // entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2385 // dropMenu, args, 1);
2386 // selection = dropMenuTranslation[i];
2387 // XtAddCallback(entry, XtNcallback,
2388 // (XtCallbackProc) DropMenuSelect,
2389 // (caddr_t) selection);
2394 void SetupDropMenu()
2402 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2403 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2404 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2405 dmEnables[i].piece);
2406 XtSetSensitive(entry, p != NULL || !appData.testLegality
2407 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2408 && !appData.icsActive));
2410 while (p && *p++ == dmEnables[i].piece) count++;
2411 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2413 XtSetArg(args[j], XtNlabel, label); j++;
2414 XtSetValues(entry, args, j);
2418 void PieceMenuPopup(w, event, params, num_params)
2422 Cardinal *num_params;
2426 if (event->type != ButtonRelease) UnLoadPV(); // [HGM] pv
2427 if (event->type != ButtonPress) return;
2428 if (errorUp) ErrorPopDown();
2432 whichMenu = params[0];
2435 if(!appData.icsEngineAnalyze) return;
2436 case IcsPlayingWhite:
2437 case IcsPlayingBlack:
2438 if(!appData.zippyPlay) goto noZip;
2441 case MachinePlaysWhite:
2442 case MachinePlaysBlack:
2443 case TwoMachinesPlay: // [HGM] pv: use for showing PV
2444 if (!appData.dropMenu) {
2445 LoadPV(event->xbutton.x, event->xbutton.y);
2448 if(gameMode == TwoMachinesPlay || gameMode == AnalyzeMode ||
2449 gameMode == AnalyzeFile || gameMode == IcsObserving) return;
2452 if (!appData.dropMenu || appData.testLegality &&
2453 gameInfo.variant != VariantBughouse &&
2454 gameInfo.variant != VariantCrazyhouse) return;
2456 whichMenu = "menuD";
2462 if (((pmFromX = EventToSquare(event->xbutton.x, BOARD_WIDTH)) < 0) ||
2463 ((pmFromY = EventToSquare(event->xbutton.y, BOARD_HEIGHT)) < 0)) {
2464 pmFromX = pmFromY = -1;
2468 pmFromX = BOARD_WIDTH - 1 - pmFromX;
2470 pmFromY = BOARD_HEIGHT - 1 - pmFromY;
2472 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
2475 static void PieceMenuSelect(w, piece, junk)
2480 if (pmFromX < 0 || pmFromY < 0) return;
2481 EditPositionMenuEvent(piece, pmFromX, pmFromY);
2484 static void DropMenuSelect(w, piece, junk)
2489 if (pmFromX < 0 || pmFromY < 0) return;
2490 DropMenuEvent(piece, pmFromX, pmFromY);
2494 * If the user selects on a border boundary, return -1; if off the board,
2495 * return -2. Otherwise map the event coordinate to the square.
2497 int EventToSquare(x, limit)
2505 if ((x % (squareSize + lineGap)) >= squareSize)
2507 x /= (squareSize + lineGap);
2513 static void do_flash_delay(msec)
2519 static void drawHighlight(file, rank, line_type)
2520 int file, rank, line_type;
2525 if (lineGap == 0 || appData.blindfold) return;
2529 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
2530 (squareSize + lineGap);
2531 y = lineGap/2 + rank * (squareSize + lineGap);
2535 x = lineGap/2 + file * (squareSize + lineGap);
2536 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
2537 (squareSize + lineGap);
2541 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2543 /* draw the highlight */
2544 cairo_move_to (cr, x, y);
2545 cairo_rel_line_to (cr, 0,squareSize+lineGap);
2546 cairo_rel_line_to (cr, squareSize+lineGap,0);
2547 cairo_rel_line_to (cr, 0,-squareSize-lineGap);
2548 cairo_close_path (cr);
2550 cairo_set_line_width (cr, lineGap);
2553 /* TODO: use appdata colors */
2554 case LINE_TYPE_HIGHLIGHT:
2555 cairo_set_source_rgba (cr, 1, 1, 0, 1.0);
2558 cairo_set_source_rgba (cr, 1, 0, 0, 1.0);
2560 case LINE_TYPE_NORMAL:
2562 cairo_set_source_rgba (cr, 0, 1, 0, 1.0);
2573 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
2574 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
2577 SetHighlights(fromX, fromY, toX, toY)
2578 int fromX, fromY, toX, toY;
2580 if (hi1X != fromX || hi1Y != fromY)
2582 if (hi1X >= 0 && hi1Y >= 0)
2584 drawHighlight(hi1X, hi1Y, LINE_TYPE_NORMAL);
2586 if (fromX >= 0 && fromY >= 0)
2588 drawHighlight(fromX, fromY, LINE_TYPE_HIGHLIGHT);
2591 if (hi2X != toX || hi2Y != toY)
2593 if (hi2X >= 0 && hi2Y >= 0)
2595 drawHighlight(hi2X, hi2Y, LINE_TYPE_NORMAL);
2597 if (toX >= 0 && toY >= 0)
2599 drawHighlight(toX, toY, LINE_TYPE_HIGHLIGHT);
2613 SetHighlights(-1, -1, -1, -1);
2618 SetPremoveHighlights(fromX, fromY, toX, toY)
2619 int fromX, fromY, toX, toY;
2621 if (pm1X != fromX || pm1Y != fromY)
2623 if (pm1X >= 0 && pm1Y >= 0)
2625 drawHighlight(pm1X, pm1Y, LINE_TYPE_NORMAL);
2627 if (fromX >= 0 && fromY >= 0)
2629 drawHighlight(fromX, fromY, LINE_TYPE_PRE);
2632 if (pm2X != toX || pm2Y != toY)
2634 if (pm2X >= 0 && pm2Y >= 0)
2636 drawHighlight(pm2X, pm2Y, LINE_TYPE_NORMAL);
2638 if (toX >= 0 && toY >= 0)
2640 drawHighlight(toX, toY, LINE_TYPE_PRE);
2653 ClearPremoveHighlights()
2655 SetPremoveHighlights(-1, -1, -1, -1);
2658 static void BlankSquare(x, y, color, piece, dest)
2671 pb = SVGLightSquare;
2673 case 2: /* neutral */
2675 pb = SVGNeutralSquare;
2678 gdk_draw_pixbuf(GDK_WINDOW(GUI_Board->window),NULL,pb,0,0,x,y,-1,-1, GDK_RGB_DITHER_NORMAL, 0, 0);
2682 static void DrawPiece(piece, square_color, x, y, dest)
2684 int square_color, x, y;
2687 /* redraw background, since piece might be transparent in some areas */
2688 BlankSquare(x,y,square_color,piece,dest);
2691 gdk_draw_pixbuf(GDK_WINDOW(GUI_Board->window),NULL,
2692 GDK_PIXBUF(SVGpieces[piece]),0,0,x,y,-1,-1,
2693 GDK_RGB_DITHER_NORMAL, 0, 0);
2697 /* [HR] determine square color depending on chess variant. */
2698 static int SquareColor(row, column)
2703 if (gameInfo.variant == VariantXiangqi) {
2704 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
2706 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
2708 } else if (row <= 4) {
2714 square_color = ((column + row) % 2) == 1;
2717 /* [hgm] holdings: next line makes all holdings squares light */
2718 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
2720 return square_color;
2723 void DrawSquare(row, column, piece, do_flash)
2724 int row, column, do_flash;
2727 int square_color, x, y;
2732 /* Calculate delay in milliseconds (2-delays per complete flash) */
2733 flash_delay = 500 / appData.flashRate;
2735 /* calculate x and y coordinates from row and column */
2738 x = lineGap + ((BOARD_WIDTH-1)-column) *
2739 (squareSize + lineGap);
2740 y = lineGap + row * (squareSize + lineGap);
2744 x = lineGap + column * (squareSize + lineGap);
2745 y = lineGap + ((BOARD_HEIGHT-1)-row) *
2746 (squareSize + lineGap);
2749 square_color = SquareColor(row, column);
2751 // [HGM] holdings: blank out area between board and holdings
2752 if ( column == BOARD_LEFT-1 || column == BOARD_RGHT
2753 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
2754 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) )
2756 BlankSquare(x, y, 2, EmptySquare, xBoardWindow);
2758 // [HGM] print piece counts next to holdings
2759 string[1] = NULLCHAR;
2762 cairo_text_extents_t extents;
2767 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2769 string[0] = '0' + piece;
2771 /* TODO this has to go into the font-selection */
2772 cairo_select_font_face (cr, "Sans",
2773 CAIRO_FONT_SLANT_NORMAL,
2774 CAIRO_FONT_WEIGHT_NORMAL);
2776 cairo_set_font_size (cr, 12.0);
2777 cairo_text_extents (cr, string, &extents);
2779 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) )
2781 xpos= x + squareSize - extents.width - 2;
2782 ypos= y + extents.y_bearing + 1;
2784 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1)
2787 ypos = y + extents.y_bearing + 1;
2790 /* TODO mono mode? */
2791 cairo_move_to (cr, xpos, ypos);
2792 cairo_text_path (cr, string);
2793 cairo_set_source_rgb (cr, 1.0, 1.0, 1);
2794 cairo_fill_preserve (cr);
2795 cairo_set_source_rgb (cr, 0, 0, 0);
2796 cairo_set_line_width (cr, 0.1);
2805 /* square on the board */
2806 if (piece == EmptySquare || appData.blindfold)
2808 BlankSquare(x, y, square_color, piece, xBoardWindow);
2812 if (do_flash && appData.flashCount > 0)
2814 for (i=0; i<appData.flashCount; ++i)
2817 DrawPiece(piece, square_color, x, y, xBoardWindow);
2818 do_flash_delay(flash_delay);
2820 BlankSquare(x, y, square_color, piece, xBoardWindow);
2821 do_flash_delay(flash_delay);
2824 DrawPiece(piece, square_color, x, y, xBoardWindow);
2828 /* show coordinates if necessary */
2829 if(appData.showCoords)
2831 cairo_text_extents_t extents;
2835 /* TODO this has to go into the font-selection */
2836 cairo_select_font_face (cr, "Sans",
2837 CAIRO_FONT_SLANT_NORMAL,
2838 CAIRO_FONT_WEIGHT_NORMAL);
2839 cairo_set_font_size (cr, 12.0);
2841 string[1] = NULLCHAR;
2844 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2846 if (row == (flipView ? BOARD_HEIGHT-1 : 0) &&
2847 column >= BOARD_LEFT && column < BOARD_RGHT)
2849 string[0] = 'a' + column - BOARD_LEFT;
2850 cairo_text_extents (cr, string, &extents);
2852 xpos = x + squareSize - extents.width - 2;
2853 ypos = y + squareSize - extents.height - extents.y_bearing - 1;
2855 if (appData.monoMode)
2862 cairo_move_to (cr, xpos, ypos);
2863 cairo_text_path (cr, string);
2864 cairo_set_source_rgb (cr, 0.0, 0.0, 0);
2865 cairo_fill_preserve (cr);
2866 cairo_set_source_rgb (cr, 0, 1.0, 0);
2867 cairo_set_line_width (cr, 0.1);
2870 if ( column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT))
2873 string[0] = ONE + row;
2874 cairo_text_extents (cr, string, &extents);
2877 ypos = y + extents.height + 1;
2879 if (appData.monoMode)
2886 cairo_move_to (cr, xpos, ypos);
2887 cairo_text_path (cr, string);
2888 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
2889 cairo_fill_preserve (cr);
2890 cairo_set_source_rgb (cr, 0, 0, 1.0);
2891 cairo_set_line_width (cr, 0.1);
2903 /* Returns 1 if there are "too many" differences between b1 and b2
2904 (i.e. more than 1 move was made) */
2905 static int too_many_diffs(b1, b2)
2911 for (i=0; i<BOARD_HEIGHT; ++i) {
2912 for (j=0; j<BOARD_WIDTH; ++j) {
2913 if (b1[i][j] != b2[i][j]) {
2914 if (++c > 4) /* Castling causes 4 diffs */
2923 /* Matrix describing castling maneuvers */
2924 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
2925 static int castling_matrix[4][5] = {
2926 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
2927 { 0, 7, 4, 5, 6 }, /* 0-0, white */
2928 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
2929 { 7, 7, 4, 5, 6 } /* 0-0, black */
2932 /* Checks whether castling occurred. If it did, *rrow and *rcol
2933 are set to the destination (row,col) of the rook that moved.
2935 Returns 1 if castling occurred, 0 if not.
2937 Note: Only handles a max of 1 castling move, so be sure
2938 to call too_many_diffs() first.
2940 static int check_castle_draw(newb, oldb, rrow, rcol)
2947 /* For each type of castling... */
2948 for (i=0; i<4; ++i) {
2949 r = castling_matrix[i];
2951 /* Check the 4 squares involved in the castling move */
2953 for (j=1; j<=4; ++j) {
2954 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
2961 /* All 4 changed, so it must be a castling move */
2970 static int damage[BOARD_RANKS][BOARD_FILES];
2973 * event handler for redrawing the board
2975 void DrawPosition( repaint, board)
2976 /*Boolean*/int repaint;
2980 static int lastFlipView = 0;
2981 static int lastBoardValid = 0;
2982 static Board lastBoard;
2985 if (board == NULL) {
2986 if (!lastBoardValid) return;
2989 if (!lastBoardValid || lastFlipView != flipView) {
2990 // XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
2991 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flip View"),
2996 * It would be simpler to clear the window with XClearWindow()
2997 * but this causes a very distracting flicker.
3000 if (!repaint && lastBoardValid && lastFlipView == flipView)
3002 /* If too much changes (begin observing new game, etc.), don't
3004 do_flash = too_many_diffs(board, lastBoard) ? 0 : 1;
3006 /* Special check for castling so we don't flash both the king
3007 and the rook (just flash the king). */
3010 if (check_castle_draw(board, lastBoard, &rrow, &rcol))
3012 /* Draw rook with NO flashing. King will be drawn flashing later */
3013 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
3014 lastBoard[rrow][rcol] = board[rrow][rcol];
3018 /* First pass -- Draw (newly) empty squares and repair damage.
3019 This prevents you from having a piece show up twice while it
3020 is flashing on its new square */
3021 for (i = 0; i < BOARD_HEIGHT; i++)
3022 for (j = 0; j < BOARD_WIDTH; j++)
3023 if ((board[i][j] != lastBoard[i][j] && board[i][j] == EmptySquare)
3026 DrawSquare(i, j, board[i][j], 0);
3027 damage[i][j] = False;
3030 /* Second pass -- Draw piece(s) in new position and flash them */
3031 for (i = 0; i < BOARD_HEIGHT; i++)
3032 for (j = 0; j < BOARD_WIDTH; j++)
3033 if (board[i][j] != lastBoard[i][j])
3035 DrawSquare(i, j, board[i][j], do_flash);
3047 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
3049 cairo_set_line_width (cr, lineGap);
3051 /* TODO: use appdata colors */
3052 cairo_set_source_rgba (cr, 0, 1, 0, 1.0);
3056 for (i = 0; i < BOARD_HEIGHT + 1; i++)
3059 x2 = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3060 y1 = y2 = lineGap / 2 + (i * (squareSize + lineGap));
3062 cairo_move_to (cr, x1, y1);
3063 cairo_rel_line_to (cr, x2,0);
3067 for (j = 0; j < BOARD_WIDTH + 1; j++)
3070 y2 = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3071 x1 = x2 = lineGap / 2 + (j * (squareSize + lineGap));
3073 cairo_move_to (cr, x1, y1);
3074 cairo_rel_line_to (cr, 0, y2);
3083 for (i = 0; i < BOARD_HEIGHT; i++)
3084 for (j = 0; j < BOARD_WIDTH; j++)
3086 DrawSquare(i, j, board[i][j], 0);
3087 damage[i][j] = False;
3091 CopyBoard(lastBoard, board);
3093 lastFlipView = flipView;
3095 /* Draw highlights */
3096 if (pm1X >= 0 && pm1Y >= 0)
3098 drawHighlight(pm1X, pm1Y, LINE_TYPE_PRE);
3100 if (pm2X >= 0 && pm2Y >= 0)
3102 drawHighlight(pm2X, pm2Y, LINE_TYPE_PRE);
3104 if (hi1X >= 0 && hi1Y >= 0)
3106 drawHighlight(hi1X, hi1Y, LINE_TYPE_HIGHLIGHT);
3108 if (hi2X >= 0 && hi2Y >= 0)
3110 drawHighlight(hi2X, hi2Y, LINE_TYPE_HIGHLIGHT);
3113 /* If piece being dragged around board, must redraw that too */
3119 void AnimateUserMove (Widget w, XEvent * event,
3120 String * params, Cardinal * nParams)
3122 DragPieceMove(event->xmotion.x, event->xmotion.y);
3125 void HandlePV (Widget w, XEvent * event,
3126 String * params, Cardinal * nParams)
3127 { // [HGM] pv: walk PV
3128 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3131 Widget CommentCreate(name, text, mutable, callback, lines)
3133 int /*Boolean*/ mutable;
3134 XtCallbackProc callback;
3138 Widget shell, layout, form, edit, b_ok, b_cancel, b_clear, b_close, b_edit;
3143 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3144 XtGetValues(boardWidget, args, j);
3147 XtSetArg(args[j], XtNresizable, True); j++;
3150 // XtCreatePopupShell(name, topLevelShellWidgetClass,
3151 // shellWidget, args, j);
3154 // XtCreatePopupShell(name, transientShellWidgetClass,
3155 // shellWidget, args, j);
3158 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
3159 layoutArgs, XtNumber(layoutArgs));
3161 XtCreateManagedWidget("form", formWidgetClass, layout,
3162 formArgs, XtNumber(formArgs));
3166 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
3167 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
3169 XtSetArg(args[j], XtNstring, text); j++;
3170 XtSetArg(args[j], XtNtop, XtChainTop); j++;
3171 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3172 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3173 XtSetArg(args[j], XtNright, XtChainRight); j++;
3174 XtSetArg(args[j], XtNresizable, True); j++;
3175 XtSetArg(args[j], XtNwidth, bw_width); j++; /*force wider than buttons*/
3176 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
3177 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
3178 XtSetArg(args[j], XtNautoFill, True); j++;
3179 XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
3181 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
3185 XtSetArg(args[j], XtNfromVert, edit); j++;
3186 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3187 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3188 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3189 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3191 XtCreateManagedWidget(_("ok"), commandWidgetClass, form, args, j);
3192 XtAddCallback(b_ok, XtNcallback, callback, (XtPointer) 0);
3195 XtSetArg(args[j], XtNfromVert, edit); j++;
3196 XtSetArg(args[j], XtNfromHoriz, b_ok); j++;
3197 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3198 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3199 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3200 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3202 XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
3203 XtAddCallback(b_cancel, XtNcallback, callback, (XtPointer) 0);
3206 XtSetArg(args[j], XtNfromVert, edit); j++;
3207 XtSetArg(args[j], XtNfromHoriz, b_cancel); j++;
3208 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3209 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3210 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3211 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3213 XtCreateManagedWidget(_("clear"), commandWidgetClass, form, args, j);
3214 XtAddCallback(b_clear, XtNcallback, callback, (XtPointer) 0);
3217 XtSetArg(args[j], XtNfromVert, edit); j++;
3218 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3219 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3220 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3221 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3223 XtCreateManagedWidget(_("close"), commandWidgetClass, form, args, j);
3224 XtAddCallback(b_close, XtNcallback, callback, (XtPointer) 0);
3227 XtSetArg(args[j], XtNfromVert, edit); j++;
3228 XtSetArg(args[j], XtNfromHoriz, b_close); j++;
3229 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3230 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3231 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3232 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3234 XtCreateManagedWidget(_("edit"), commandWidgetClass, form, args, j);
3235 XtAddCallback(b_edit, XtNcallback, callback, (XtPointer) 0);
3238 XtRealizeWidget(shell);
3240 if (commentX == -1) {
3243 Dimension pw_height;
3244 Dimension ew_height;
3247 XtSetArg(args[j], XtNheight, &ew_height); j++;
3248 XtGetValues(edit, args, j);
3251 XtSetArg(args[j], XtNheight, &pw_height); j++;
3252 XtGetValues(shell, args, j);
3253 commentH = pw_height + (lines - 1) * ew_height;
3254 commentW = bw_width - 16;
3256 // XSync(xDisplay, False);
3258 /* This code seems to tickle an X bug if it is executed too soon
3259 after xboard starts up. The coordinates get transformed as if
3260 the main window was positioned at (0, 0).
3262 // XtTranslateCoords(shellWidget,
3263 // (bw_width - commentW) / 2, 0 - commentH / 2,
3264 // &commentX, &commentY);
3266 // XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
3267 // RootWindowOfScreen(XtScreen(shellWidget)),
3268 // (bw_width - commentW) / 2, 0 - commentH / 2,
3269 // &xx, &yy, &junk);
3273 if (commentY < 0) commentY = 0; /*avoid positioning top offscreen*/
3276 if(wpComment.width > 0) {
3277 commentX = wpComment.x;
3278 commentY = wpComment.y;
3279 commentW = wpComment.width;
3280 commentH = wpComment.height;
3284 XtSetArg(args[j], XtNheight, commentH); j++;
3285 XtSetArg(args[j], XtNwidth, commentW); j++;
3286 XtSetArg(args[j], XtNx, commentX); j++;
3287 XtSetArg(args[j], XtNy, commentY); j++;
3288 XtSetValues(shell, args, j);
3289 XtSetKeyboardFocus(shell, edit);
3294 /* Used for analysis window and ICS input window */
3295 Widget MiscCreate(name, text, mutable, callback, lines)
3297 int /*Boolean*/ mutable;
3298 XtCallbackProc callback;
3302 Widget shell, layout, form, edit;
3304 Dimension bw_width, pw_height, ew_height, w, h;
3310 XtSetArg(args[j], XtNresizable, True); j++;
3313 // XtCreatePopupShell(name, topLevelShellWidgetClass,
3314 // shellWidget, args, j);
3317 // XtCreatePopupShell(name, transientShellWidgetClass,
3318 // shellWidget, args, j);
3321 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
3322 layoutArgs, XtNumber(layoutArgs));
3324 XtCreateManagedWidget("form", formWidgetClass, layout,
3325 formArgs, XtNumber(formArgs));
3329 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
3330 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
3332 XtSetArg(args[j], XtNstring, text); j++;
3333 XtSetArg(args[j], XtNtop, XtChainTop); j++;
3334 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3335 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3336 XtSetArg(args[j], XtNright, XtChainRight); j++;
3337 XtSetArg(args[j], XtNresizable, True); j++;
3338 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
3339 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
3340 XtSetArg(args[j], XtNautoFill, True); j++;
3341 XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
3343 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
3345 XtRealizeWidget(shell);
3348 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3349 XtGetValues(boardWidget, args, j);
3352 XtSetArg(args[j], XtNheight, &ew_height); j++;
3353 XtGetValues(edit, args, j);
3356 XtSetArg(args[j], XtNheight, &pw_height); j++;
3357 XtGetValues(shell, args, j);
3358 h = pw_height + (lines - 1) * ew_height;
3361 // XSync(xDisplay, False);
3363 /* This code seems to tickle an X bug if it is executed too soon
3364 after xboard starts up. The coordinates get transformed as if
3365 the main window was positioned at (0, 0).
3367 // XtTranslateCoords(shellWidget, (bw_width - w) / 2, 0 - h / 2, &x, &y);
3369 // XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
3370 // RootWindowOfScreen(XtScreen(shellWidget)),
3371 // (bw_width - w) / 2, 0 - h / 2, &xx, &yy, &junk);
3375 if (y < 0) y = 0; /*avoid positioning top offscreen*/
3378 XtSetArg(args[j], XtNheight, h); j++;
3379 XtSetArg(args[j], XtNwidth, w); j++;
3380 XtSetArg(args[j], XtNx, x); j++;
3381 XtSetArg(args[j], XtNy, y); j++;
3382 XtSetValues(shell, args, j);
3388 static int savedIndex; /* gross that this is global */
3390 void EditCommentPopUp(index, title, text)
3399 if (text == NULL) text = "";
3401 if (editShell == NULL) {
3403 CommentCreate(title, text, True, EditCommentCallback, 4);
3404 XtRealizeWidget(editShell);
3405 // CatchDeleteWindow(editShell, "EditCommentPopDown");
3407 edit = XtNameToWidget(editShell, "*form.text");
3409 XtSetArg(args[j], XtNstring, text); j++;
3410 XtSetValues(edit, args, j);
3412 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
3413 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
3414 XtSetValues(editShell, args, j);
3417 XtPopup(editShell, XtGrabNone);
3421 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
3422 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
3426 void EditCommentCallback(w, client_data, call_data)
3428 XtPointer client_data, call_data;
3436 XtSetArg(args[j], XtNlabel, &name); j++;
3437 XtGetValues(w, args, j);
3439 if (strcmp(name, _("ok")) == 0) {
3440 edit = XtNameToWidget(editShell, "*form.text");
3442 XtSetArg(args[j], XtNstring, &val); j++;
3443 XtGetValues(edit, args, j);
3444 ReplaceComment(savedIndex, val);
3445 EditCommentPopDown();
3446 } else if (strcmp(name, _("cancel")) == 0) {
3447 EditCommentPopDown();
3448 } else if (strcmp(name, _("clear")) == 0) {
3449 edit = XtNameToWidget(editShell, "*form.text");
3450 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
3451 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
3455 void EditCommentPopDown()
3460 if (!editUp) return;
3462 XtSetArg(args[j], XtNx, &commentX); j++;
3463 XtSetArg(args[j], XtNy, &commentY); j++;
3464 XtSetArg(args[j], XtNheight, &commentH); j++;
3465 XtSetArg(args[j], XtNwidth, &commentW); j++;
3466 XtGetValues(editShell, args, j);
3467 XtPopdown(editShell);
3470 XtSetArg(args[j], XtNleftBitmap, None); j++;
3471 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
3475 void ICSInputBoxPopUp()
3480 char *title = _("ICS Input");
3483 if (ICSInputShell == NULL) {
3484 ICSInputShell = MiscCreate(title, "", True, NULL, 1);
3485 tr = XtParseTranslationTable(ICSInputTranslations);
3486 edit = XtNameToWidget(ICSInputShell, "*form.text");
3487 XtOverrideTranslations(edit, tr);
3488 XtRealizeWidget(ICSInputShell);
3489 // CatchDeleteWindow(ICSInputShell, "ICSInputBoxPopDown");
3492 edit = XtNameToWidget(ICSInputShell, "*form.text");
3494 XtSetArg(args[j], XtNstring, ""); j++;
3495 XtSetValues(edit, args, j);
3497 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
3498 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
3499 XtSetValues(ICSInputShell, args, j);
3502 XtPopup(ICSInputShell, XtGrabNone);
3503 XtSetKeyboardFocus(ICSInputShell, edit);
3505 ICSInputBoxUp = True;
3507 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
3508 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
3512 void ICSInputSendText()
3519 edit = XtNameToWidget(ICSInputShell, "*form.text");
3521 XtSetArg(args[j], XtNstring, &val); j++;
3522 XtGetValues(edit, args, j);
3523 SendMultiLineToICS(val);
3524 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
3525 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
3528 void ICSInputBoxPopDown()
3533 if (!ICSInputBoxUp) return;
3535 XtPopdown(ICSInputShell);
3536 ICSInputBoxUp = False;
3538 XtSetArg(args[j], XtNleftBitmap, None); j++;
3539 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
3543 void CommentPopUp(title, text)
3550 if (commentShell == NULL) {
3552 CommentCreate(title, text, False, CommentCallback, 4);
3553 XtRealizeWidget(commentShell);
3554 // CatchDeleteWindow(commentShell, "CommentPopDown");
3556 edit = XtNameToWidget(commentShell, "*form.text");
3558 XtSetArg(args[j], XtNstring, text); j++;
3559 XtSetValues(edit, args, j);
3561 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
3562 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
3563 XtSetValues(commentShell, args, j);
3566 XtPopup(commentShell, XtGrabNone);
3567 // XSync(xDisplay, False);
3572 void CommentCallback(w, client_data, call_data)
3574 XtPointer client_data, call_data;
3581 XtSetArg(args[j], XtNlabel, &name); j++;
3582 XtGetValues(w, args, j);
3584 if (strcmp(name, _("close")) == 0) {
3586 } else if (strcmp(name, _("edit")) == 0) {
3593 void CommentPopDown()
3598 if (!commentUp) return;
3600 XtSetArg(args[j], XtNx, &commentX); j++;
3601 XtSetArg(args[j], XtNy, &commentY); j++;
3602 XtSetArg(args[j], XtNwidth, &commentW); j++;
3603 XtSetArg(args[j], XtNheight, &commentH); j++;
3604 XtGetValues(commentShell, args, j);
3605 XtPopdown(commentShell);
3606 // XSync(xDisplay, False);
3610 void PromotionPopUp()
3613 Widget dialog, layout;
3615 Dimension bw_width, pw_width;
3619 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3620 XtGetValues(boardWidget, args, j);
3623 XtSetArg(args[j], XtNresizable, True); j++;
3624 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
3626 // XtCreatePopupShell("Promotion", transientShellWidgetClass,
3627 // shellWidget, args, j);
3629 // XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
3630 // layoutArgs, XtNumber(layoutArgs));
3633 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
3634 XtSetArg(args[j], XtNborderWidth, 0); j++;
3635 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
3638 if(gameInfo.variant != VariantShogi) {
3639 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
3640 (XtPointer) dialog);
3641 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
3642 (XtPointer) dialog);
3643 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
3644 (XtPointer) dialog);
3645 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
3646 (XtPointer) dialog);
3647 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
3648 gameInfo.variant == VariantGiveaway) {
3649 XawDialogAddButton(dialog, _("King"), PromotionCallback,
3650 (XtPointer) dialog);
3652 if(gameInfo.variant == VariantCapablanca ||
3653 gameInfo.variant == VariantGothic ||
3654 gameInfo.variant == VariantCapaRandom) {
3655 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
3656 (XtPointer) dialog);
3657 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
3658 (XtPointer) dialog);
3660 } else // [HGM] shogi
3662 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
3663 (XtPointer) dialog);
3664 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
3665 (XtPointer) dialog);
3667 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
3668 (XtPointer) dialog);
3670 XtRealizeWidget(promotionShell);
3671 // CatchDeleteWindow(promotionShell, "PromotionPopDown");
3674 XtSetArg(args[j], XtNwidth, &pw_width); j++;
3675 XtGetValues(promotionShell, args, j);
3677 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3678 lineGap + squareSize/3 +
3679 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
3680 0 : 6*(squareSize + lineGap)), &x, &y);
3683 XtSetArg(args[j], XtNx, x); j++;
3684 XtSetArg(args[j], XtNy, y); j++;
3685 XtSetValues(promotionShell, args, j);
3687 XtPopup(promotionShell, XtGrabNone);
3692 void PromotionPopDown()
3694 if (!promotionUp) return;
3695 XtPopdown(promotionShell);
3696 XtDestroyWidget(promotionShell);
3697 promotionUp = False;
3700 void PromotionCallback(w, client_data, call_data)
3702 XtPointer client_data, call_data;
3708 XtSetArg(args[0], XtNlabel, &name);
3709 XtGetValues(w, args, 1);
3713 if (fromX == -1) return;
3715 if (strcmp(name, _("cancel")) == 0) {
3719 } else if (strcmp(name, _("Knight")) == 0) {
3721 } else if (strcmp(name, _("Promote")) == 0) {
3723 } else if (strcmp(name, _("Defer")) == 0) {
3726 promoChar = ToLower(name[0]);
3729 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
3731 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
3732 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3737 void ErrorCallback(w, client_data, call_data)
3739 XtPointer client_data, call_data;
3742 XtPopdown(w = XtParent(XtParent(XtParent(w))));
3744 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3750 if (!errorUp) return;
3754 gtk_widget_destroy(GTK_WIDGET(GUI_Error));
3756 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3761 void ErrorPopUp(title, label, modal)
3762 char *title, *label;
3765 GUI_Error = gtk_message_dialog_new(GTK_WINDOW(GUI_Window),
3766 GTK_DIALOG_DESTROY_WITH_PARENT,
3771 gtk_window_set_title(GTK_WINDOW(GUI_Error),(gchar *) title);
3774 gtk_dialog_run(GTK_DIALOG(GUI_Error));
3775 gtk_widget_destroy(GTK_WIDGET(GUI_Error));
3779 g_signal_connect_swapped (GUI_Error, "response",
3780 G_CALLBACK (ErrorPopDownProc),
3783 gtk_widget_show(GTK_WIDGET(GUI_Error));
3789 /* Disable all user input other than deleting the window */
3790 static int frozen = 0;
3794 /* Grab by a widget that doesn't accept input */
3795 // XtAddGrab(messageWidget, TRUE, FALSE);
3799 /* Undo a FreezeUI */
3802 if (!frozen) return;
3803 // XtRemoveGrab(messageWidget);
3807 char *ModeToWidgetName(mode)
3811 case BeginningOfGame:
3812 if (appData.icsActive)
3813 return "menuMode.ICS Client";
3814 else if (appData.noChessProgram ||
3815 *appData.cmailGameName != NULLCHAR)
3816 return "menuMode.Edit Game";
3818 return "menuMode.Machine Black";
3819 case MachinePlaysBlack:
3820 return "menuMode.Machine Black";
3821 case MachinePlaysWhite:
3822 return "menuMode.Machine White";
3824 return "menuMode.Analysis Mode";
3826 return "menuMode.Analyze File";
3827 case TwoMachinesPlay:
3828 return "menuMode.Two Machines";
3830 return "menuMode.Edit Game";
3831 case PlayFromGameFile:
3832 return "menuFile.Load Game";
3834 return "menuMode.Edit Position";
3836 return "menuMode.Training";
3837 case IcsPlayingWhite:
3838 case IcsPlayingBlack:
3842 return "menuMode.ICS Client";
3849 void ModeHighlight()
3851 static int oldPausing = FALSE;
3852 static GameMode oldmode = (GameMode) -1;
3855 // todo this toggling of the pause button doesn't seem to work?
3856 // e.g. select pause from buttonbar doesn't activate menumode.pause
3858 // if (!boardWidget || !XtIsRealized(boardWidget)) return;
3860 if (pausing != oldPausing) {
3861 oldPausing = pausing;
3862 gtk_button_set_relief(GTK_BUTTON (gtk_builder_get_object (builder, "menuMode.Pause")),pausing?GTK_RELIEF_NORMAL:GTK_RELIEF_NONE);
3863 /* toggle background color in showbuttonbar */
3864 if (appData.showButtonBar) {
3866 gtk_button_pressed(GTK_BUTTON (gtk_builder_get_object (builder, "buttonbar.Pause")));
3868 gtk_button_released(GTK_BUTTON (gtk_builder_get_object (builder, "buttonbar.Pause")));
3873 // probably not needed anymore
3874 // wname = ModeToWidgetName(oldmode);
3876 // gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, wname)),True);
3880 /* Maybe all the enables should be handled here, not just this one */
3881 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuMode.Training")),
3882 gameMode == Training || gameMode == PlayFromGameFile);
3887 * Button/menu procedures
3890 int LoadGamePopUp(f, gameNumber, title)
3895 cmailMsgLoaded = FALSE;
3897 if (gameNumber == 0)
3899 int error = GameListBuild(f);
3903 DisplayError(_("Cannot build game list"), error);
3905 else if (!ListEmpty(&gameList)
3906 && ((ListGame *) gameList.tailPred)->number > 1)
3908 /* we need an answer which game to load, so let's make it modal for a while*/
3909 gtk_window_set_modal(GTK_WINDOW(GUI_GameList) , TRUE);
3910 GameListPopUp(f, title);
3911 gtk_window_set_modal(GTK_WINDOW(GUI_GameList) , FALSE);
3920 return LoadGame(f, gameNumber, title, FALSE);
3923 void ReloadCmailMsgProc(w, event, prms, nprms)
3929 ReloadCmailMsgEvent(FALSE);
3932 void MailMoveProc(w, event, prms, nprms)
3941 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3942 char *selected_fen_position=NULL;
3945 SendPositionSelection(Widget w, Atom *selection, Atom *target,
3946 Atom *type_return, XtPointer *value_return,
3947 unsigned long *length_return, int *format_return)
3949 char *selection_tmp;
3951 if (!selected_fen_position) return False; /* should never happen */
3952 // if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3953 // /* note: since no XtSelectionDoneProc was registered, Xt will
3954 // * automatically call XtFree on the value returned. So have to
3955 // * make a copy of it allocated with XtMalloc */
3956 // selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3957 // strcpy(selection_tmp, selected_fen_position);
3959 // *value_return=selection_tmp;
3960 // *length_return=strlen(selection_tmp);
3961 // *type_return=*target;
3962 // *format_return = 8; /* bits per byte */
3964 // } else if (*target == XA_TARGETS(xDisplay)) {
3965 // Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3966 // targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3967 // targets_tmp[1] = XA_STRING;
3968 // *value_return = targets_tmp;
3969 // *type_return = XA_ATOM;
3970 // *length_return = 2;
3971 // *format_return = 8 * sizeof(Atom);
3972 // if (*format_return > 32) {
3973 // *length_return *= *format_return / 32;
3974 // *format_return = 32;
3982 /* note: when called from menu all parameters are NULL, so no clue what the
3983 * Widget which was clicked on was, or what the click event was
3985 void CopyPositionProc(w, event, prms, nprms)
3992 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3993 * have a notion of a position that is selected but not copied.
3994 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3996 if(gameMode == EditPosition) EditPositionDone(TRUE);
3997 if (selected_fen_position) free(selected_fen_position);
3998 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
3999 if (!selected_fen_position) return;
4000 // XtOwnSelection(menuBarWidget, XA_PRIMARY,
4002 // SendPositionSelection,
4003 // NULL/* lose_ownership_proc */ ,
4004 // NULL/* transfer_done_proc */);
4005 // XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4007 // SendPositionSelection,
4008 // NULL/* lose_ownership_proc */ ,
4009 // NULL/* transfer_done_proc */);
4012 /* function called when the data to Paste is ready */
4014 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
4015 Atom *type, XtPointer value, unsigned long *len, int *format)
4018 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
4019 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
4020 EditPositionPasteFEN(fenstr);
4024 /* called when Paste Position button is pressed,
4025 * all parameters will be NULL */
4026 void PastePositionProc(w, event, prms, nprms)
4032 // XtGetSelectionValue(menuBarWidget,
4033 // appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4034 // /* (XtSelectionCallbackProc) */ PastePositionCB,
4035 // NULL, /* client_data passed to PastePositionCB */
4037 // /* better to use the time field from the event that triggered the
4038 // * call to this function, but that isn't trivial to get
4046 SendGameSelection(Widget w, Atom *selection, Atom *target,
4047 Atom *type_return, XtPointer *value_return,
4048 unsigned long *length_return, int *format_return)
4050 char *selection_tmp;
4052 // if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4053 // FILE* f = fopen(gameCopyFilename, "r");
4056 // if (f == NULL) return False;
4060 // selection_tmp = XtMalloc(len + 1);
4061 // count = fread(selection_tmp, 1, len, f);
4062 // if (len != count) {
4063 // XtFree(selection_tmp);
4066 // selection_tmp[len] = NULLCHAR;
4067 // *value_return = selection_tmp;
4068 // *length_return = len;
4069 // *type_return = *target;
4070 // *format_return = 8; /* bits per byte */
4072 // } else if (*target == XA_TARGETS(xDisplay)) {
4073 // Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4074 // targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4075 // targets_tmp[1] = XA_STRING;
4076 // *value_return = targets_tmp;
4077 // *type_return = XA_ATOM;
4078 // *length_return = 2;
4079 // *format_return = 8 * sizeof(Atom);
4080 // if (*format_return > 32) {
4081 // *length_return *= *format_return / 32;
4082 // *format_return = 32;
4090 /* note: when called from menu all parameters are NULL, so no clue what the
4091 * Widget which was clicked on was, or what the click event was
4093 void CopyGameProc(w, event, prms, nprms)
4101 ret = SaveGameToFile(gameCopyFilename, FALSE);
4105 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4106 * have a notion of a game that is selected but not copied.
4107 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4109 // XtOwnSelection(menuBarWidget, XA_PRIMARY,
4111 // SendGameSelection,
4112 // NULL/* lose_ownership_proc */ ,
4113 // NULL/* transfer_done_proc */);
4114 // XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4116 // SendGameSelection,
4117 // NULL/* lose_ownership_proc */ ,
4118 // NULL/* transfer_done_proc */);
4121 /* function called when the data to Paste is ready */
4123 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
4124 Atom *type, XtPointer value, unsigned long *len, int *format)
4127 if (value == NULL || *len == 0) {
4128 return; /* nothing had been selected to copy */
4130 f = fopen(gamePasteFilename, "w");
4132 DisplayError(_("Can't open temp file"), errno);
4135 fwrite(value, 1, *len, f);
4138 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
4141 /* called when Paste Game button is pressed,
4142 * all parameters will be NULL */
4143 void PasteGameProc(w, event, prms, nprms)
4149 // XtGetSelectionValue(menuBarWidget,
4150 // appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4151 // /* (XtSelectionCallbackProc) */ PasteGameCB,
4152 // NULL, /* client_data passed to PasteGameCB */
4154 // /* better to use the time field from the event that triggered the
4155 // * call to this function, but that isn't trivial to get
4162 void SaveOnExitProc(w, event, prms, nprms)
4170 saveSettingsOnExit = !saveSettingsOnExit;
4172 if (saveSettingsOnExit) {
4173 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
4175 XtSetArg(args[0], XtNleftBitmap, None);
4177 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
4181 void SaveSettingsProc(w, event, prms, nprms)
4187 SaveSettings(settingsFileName);
4193 SaveGameProc(NULL, NULL);
4198 void EditCommentProc(w, event, prms, nprms)
4205 EditCommentPopDown();
4211 void IcsInputBoxProc(w, event, prms, nprms)
4217 if (ICSInputBoxUp) {
4218 ICSInputBoxPopDown();
4225 void EnterKeyProc(w, event, prms, nprms)
4231 if (ICSInputBoxUp == True)
4236 void DebugProc(w, event, prms, nprms)
4242 appData.debugMode = !appData.debugMode;
4245 void AboutGameProc(w, event, prms, nprms)
4254 void NothingProc(w, event, prms, nprms)
4263 void Iconify(w, event, prms, nprms)
4271 // fromX = fromY = -1;
4272 // XtSetArg(args[0], XtNiconic, True);
4273 // XtSetValues(shellWidget, args, 1);
4276 void DisplayMessage(message, extMessage)
4277 gchar *message, *extMessage;
4284 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
4287 message = extMessage;
4290 gtk_label_set_text( GTK_LABEL(gtk_builder_get_object (builder, "Messages")),message);
4295 void DisplayTitle(text)
4298 gchar title[MSG_SIZ];
4300 if (text == NULL) text = "";
4302 if (appData.titleInWindow)
4307 if (*text != NULLCHAR)
4309 strcpy(title, text);
4311 else if (appData.icsActive)
4313 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
4315 else if (appData.cmailGameName[0] != NULLCHAR)
4317 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
4319 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
4321 else if (gameInfo.variant == VariantGothic)
4323 strcpy(title, GOTHIC);
4327 else if (gameInfo.variant == VariantFalcon)
4329 strcpy(title, FALCON);
4332 else if (appData.noChessProgram)
4334 strcpy(title, programName);
4338 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
4340 gtk_window_set_title(GTK_WINDOW(GUI_Window),title);
4346 void DisplayError(message, error)
4353 if (appData.debugMode || appData.matchMode) {
4354 fprintf(stderr, "%s: %s\n", programName, message);
4357 if (appData.debugMode || appData.matchMode) {
4358 fprintf(stderr, "%s: %s: %s\n",
4359 programName, message, strerror(error));
4361 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
4364 ErrorPopUp(_("Error"), message, FALSE);
4368 void DisplayMoveError(message)
4373 DrawPosition(FALSE, NULL);
4374 if (appData.debugMode || appData.matchMode) {
4375 fprintf(stderr, "%s: %s\n", programName, message);
4377 if (appData.popupMoveErrors) {
4378 ErrorPopUp(_("Error"), message, FALSE);
4380 DisplayMessage(message, "");
4385 void DisplayFatalError(message, error, status)
4391 errorExitStatus = status;
4393 fprintf(stderr, "%s: %s\n", programName, message);
4395 fprintf(stderr, "%s: %s: %s\n",
4396 programName, message, strerror(error));
4397 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
4400 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
4401 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
4407 void DisplayInformation(message)
4411 ErrorPopUp(_("Information"), message, TRUE);
4414 void DisplayNote(message)
4418 ErrorPopUp(_("Note"), message, FALSE);
4422 NullXErrorCheck(dpy, error_event)
4424 XErrorEvent *error_event;
4429 void DisplayIcsInteractionTitle(message)
4432 if (oldICSInteractionTitle == NULL) {
4433 /* Magic to find the old window title, adapted from vim */
4434 char *wina = getenv("WINDOWID");
4436 Window win = (Window) atoi(wina);
4437 Window root, parent, *children;
4438 unsigned int nchildren;
4439 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4441 // if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4442 // if (!XQueryTree(xDisplay, win, &root, &parent,
4443 // &children, &nchildren)) break;
4444 // if (children) XFree((void *)children);
4445 // if (parent == root || parent == 0) break;
4448 XSetErrorHandler(oldHandler);
4450 if (oldICSInteractionTitle == NULL) {
4451 oldICSInteractionTitle = "xterm";
4454 printf("\033]0;%s\007", message);
4458 char pendingReplyPrefix[MSG_SIZ];
4459 ProcRef pendingReplyPR;
4461 void AskQuestionProc(w, event, prms, nprms)
4468 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
4472 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
4475 void AskQuestionPopDown()
4477 if (!askQuestionUp) return;
4478 XtPopdown(askQuestionShell);
4479 XtDestroyWidget(askQuestionShell);
4480 askQuestionUp = False;
4483 void AskQuestionReplyAction(w, event, prms, nprms)
4493 reply = XawDialogGetValueString(w = XtParent(w));
4494 strcpy(buf, pendingReplyPrefix);
4495 if (*buf) strcat(buf, " ");
4498 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
4499 AskQuestionPopDown();
4501 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
4504 void AskQuestionCallback(w, client_data, call_data)
4506 XtPointer client_data, call_data;
4511 XtSetArg(args[0], XtNlabel, &name);
4512 XtGetValues(w, args, 1);
4514 if (strcmp(name, _("cancel")) == 0) {
4515 AskQuestionPopDown();
4517 AskQuestionReplyAction(w, NULL, NULL, NULL);
4521 void AskQuestion(title, question, replyPrefix, pr)
4522 char *title, *question, *replyPrefix;
4526 Widget popup, layout, dialog, edit;
4532 strcpy(pendingReplyPrefix, replyPrefix);
4533 pendingReplyPR = pr;
4536 XtSetArg(args[i], XtNresizable, True); i++;
4537 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
4538 // askQuestionShell = popup =
4539 // XtCreatePopupShell(title, transientShellWidgetClass,
4540 // shellWidget, args, i);
4543 // XtCreateManagedWidget(layoutName, formWidgetClass, popup,
4544 // layoutArgs, XtNumber(layoutArgs));
4547 XtSetArg(args[i], XtNlabel, question); i++;
4548 XtSetArg(args[i], XtNvalue, ""); i++;
4549 XtSetArg(args[i], XtNborderWidth, 0); i++;
4550 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
4553 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
4554 (XtPointer) dialog);
4555 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
4556 (XtPointer) dialog);
4558 XtRealizeWidget(popup);
4559 // CatchDeleteWindow(popup, "AskQuestionPopDown");
4561 // XQueryPointer(xDisplay, xBoardWindow, &root, &child,
4562 // &x, &y, &win_x, &win_y, &mask);
4564 // XtSetArg(args[0], XtNx, x - 10);
4565 // XtSetArg(args[1], XtNy, y - 30);
4566 // XtSetValues(popup, args, 2);
4568 // XtPopup(popup, XtGrabExclusive);
4569 // askQuestionUp = True;
4571 // edit = XtNameToWidget(dialog, "*value");
4572 // XtSetKeyboardFocus(popup, edit);
4580 if (*name == NULLCHAR) {
4582 } else if (strcmp(name, "$") == 0) {
4583 putc(BELLCHAR, stderr);
4586 snprintf(buf, sizeof(buf), "%s '%s' &", appData.soundProgram, name);
4594 PlaySound(appData.soundMove);
4600 PlaySound(appData.soundIcsWin);
4606 PlaySound(appData.soundIcsLoss);
4612 PlaySound(appData.soundIcsDraw);
4616 PlayIcsUnfinishedSound()
4618 PlaySound(appData.soundIcsUnfinished);
4624 PlaySound(appData.soundIcsAlarm);
4630 system("stty echo");
4636 system("stty -echo");
4640 Colorize(cc, continuation)
4645 int count, outCount, error;
4647 if (textColors[(int)cc].bg > 0) {
4648 if (textColors[(int)cc].fg > 0) {
4649 sprintf(buf, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
4650 textColors[(int)cc].fg, textColors[(int)cc].bg);
4652 sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
4653 textColors[(int)cc].bg);
4656 if (textColors[(int)cc].fg > 0) {
4657 sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
4658 textColors[(int)cc].fg);
4660 sprintf(buf, "\033[0;%dm", textColors[(int)cc].attr);
4663 count = strlen(buf);
4664 outCount = OutputToProcess(NoProc, buf, count, &error);
4665 if (outCount < count) {
4666 DisplayFatalError(_("Error writing to display"), error, 1);
4669 if (continuation) return;
4672 PlaySound(appData.soundShout);
4675 PlaySound(appData.soundSShout);
4678 PlaySound(appData.soundChannel1);
4681 PlaySound(appData.soundChannel);
4684 PlaySound(appData.soundKibitz);
4687 PlaySound(appData.soundTell);
4689 case ColorChallenge:
4690 PlaySound(appData.soundChallenge);
4693 PlaySound(appData.soundRequest);
4696 PlaySound(appData.soundSeek);
4707 return getpwuid(getuid())->pw_name;
4710 static char *ExpandPathName(path)
4713 static char static_buf[2000];
4714 char *d, *s, buf[2000];
4720 while (*s && isspace(*s))
4729 if (*(s+1) == '/') {
4730 strcpy(d, getpwuid(getuid())->pw_dir);
4735 *strchr(buf, '/') = 0;
4736 pwd = getpwnam(buf);
4739 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
4743 strcpy(d, pwd->pw_dir);
4744 strcat(d, strchr(s+1, '/'));
4755 static char host_name[MSG_SIZ];
4757 #if HAVE_GETHOSTNAME
4758 gethostname(host_name, MSG_SIZ);
4760 #else /* not HAVE_GETHOSTNAME */
4761 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
4762 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
4764 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4766 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4767 #endif /* not HAVE_GETHOSTNAME */
4770 guint delayedEventTimerTag = 0;
4771 DelayedEventCallback delayedEventCallback = 0;
4774 FireDelayedEvent(data)
4778 g_source_remove(delayedEventTimerTag);
4779 delayedEventTimerTag = 0;
4782 delayedEventCallback();
4788 ScheduleDelayedEvent(cb, millisec)
4789 DelayedEventCallback cb; guint millisec;
4791 if(delayedEventTimerTag && delayedEventCallback == cb)
4792 // [HGM] alive: replace, rather than add or flush identical event
4793 g_source_remove(delayedEventTimerTag);
4794 delayedEventCallback = cb;
4795 delayedEventTimerTag = g_timeout_add(millisec,(GSourceFunc) FireDelayedEvent, NULL);
4799 DelayedEventCallback
4802 if (delayedEventTimerTag)
4804 return delayedEventCallback;
4813 CancelDelayedEvent()
4815 if (delayedEventTimerTag)
4817 g_source_remove(delayedEventTimerTag);
4818 delayedEventTimerTag = 0;
4824 guint loadGameTimerTag = 0;
4826 int LoadGameTimerRunning()
4828 return loadGameTimerTag != 0;
4831 int StopLoadGameTimer()
4833 if (loadGameTimerTag != 0) {
4834 g_source_remove(loadGameTimerTag);
4835 loadGameTimerTag = 0;
4843 LoadGameTimerCallback(data)
4847 g_source_remove(loadGameTimerTag);
4848 loadGameTimerTag = 0;
4855 StartLoadGameTimer(millisec)
4859 g_timeout_add( millisec, (GSourceFunc) LoadGameTimerCallback, NULL);
4863 guint analysisClockTag = 0;
4866 AnalysisClockCallback(data)
4869 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
4870 || appData.icsEngineAnalyze)
4872 AnalysisPeriodicEvent(0);
4873 return 1; /* keep on going */
4875 return 0; /* stop timer */
4879 StartAnalysisClock()
4882 g_timeout_add( 2000,(GSourceFunc) AnalysisClockCallback, NULL);
4886 guint clockTimerTag = 0;
4888 int ClockTimerRunning()
4890 return clockTimerTag != 0;
4893 int StopClockTimer()
4895 if (clockTimerTag != 0)
4897 g_source_remove(clockTimerTag);
4908 ClockTimerCallback(data)
4912 g_source_remove(clockTimerTag);
4920 StartClockTimer(millisec)
4923 clockTimerTag = g_timeout_add(millisec,(GSourceFunc) ClockTimerCallback,NULL);
4928 DisplayTimerLabel(w, color, timer, highlight)
4937 if (appData.clockMode) {
4938 sprintf(buf, "%s: %s", color, TimeString(timer));
4940 sprintf(buf, "%s ", color);
4942 gtk_label_set_text(GTK_LABEL(w),buf);
4944 /* check for low time warning */
4945 // Pixel foregroundOrWarningColor = timerForegroundPixel;
4948 // appData.lowTimeWarning &&
4949 // (timer / 1000) < appData.icsAlarmTime)
4950 // foregroundOrWarningColor = lowTimeWarningColor;
4952 // if (appData.clockMode) {
4953 // sprintf(buf, "%s: %s", color, TimeString(timer));
4954 // XtSetArg(args[0], XtNlabel, buf);
4956 // sprintf(buf, "%s ", color);
4957 // XtSetArg(args[0], XtNlabel, buf);
4962 // XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
4963 // XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
4965 // XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
4966 // XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
4969 // XtSetValues(w, args, 3);
4974 DisplayWhiteClock(timeRemaining, highlight)
4978 if(appData.noGUI) return;
4980 DisplayTimerLabel(GUI_Whiteclock, _("White"), timeRemaining, highlight);
4981 if (highlight && WindowIcon == BlackIcon)
4983 WindowIcon = WhiteIcon;
4984 gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
4989 DisplayBlackClock(timeRemaining, highlight)
4993 if(appData.noGUI) return;
4995 DisplayTimerLabel(GUI_Blackclock, _("Black"), timeRemaining, highlight);
4996 if (highlight && WindowIcon == WhiteIcon)
4998 WindowIcon = BlackIcon;
4999 gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
5017 int StartChildProcess(cmdLine, dir, pr)
5024 int to_prog[2], from_prog[2];
5028 if (appData.debugMode) {
5029 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
5032 /* We do NOT feed the cmdLine to the shell; we just
5033 parse it into blank-separated arguments in the
5034 most simple-minded way possible.
5037 strcpy(buf, cmdLine);
5040 while(*p == ' ') p++;
5042 if(*p == '"' || *p == '\'')
5043 p = strchr(++argv[i-1], *p);
5044 else p = strchr(p, ' ');
5045 if (p == NULL) break;
5050 SetUpChildIO(to_prog, from_prog);
5052 if ((pid = fork()) == 0) {
5054 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
5055 close(to_prog[1]); // first close the unused pipe ends
5056 close(from_prog[0]);
5057 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
5058 dup2(from_prog[1], 1);
5059 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
5060 close(from_prog[1]); // and closing again loses one of the pipes!
5061 if(fileno(stderr) >= 2) // better safe than sorry...
5062 dup2(1, fileno(stderr)); /* force stderr to the pipe */
5064 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
5069 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
5071 execvp(argv[0], argv);
5073 /* If we get here, exec failed */
5078 /* Parent process */
5080 close(from_prog[1]);
5082 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5085 cp->fdFrom = from_prog[0];
5086 cp->fdTo = to_prog[1];
5091 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
5092 static RETSIGTYPE AlarmCallBack(int n)
5098 DestroyChildProcess(pr, signalType)
5102 ChildProc *cp = (ChildProc *) pr;
5104 if (cp->kind != CPReal) return;
5106 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
5107 signal(SIGALRM, AlarmCallBack);
5109 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
5110 kill(cp->pid, SIGKILL); // kill it forcefully
5111 wait((int *) 0); // and wait again
5115 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
5117 /* Process is exiting either because of the kill or because of
5118 a quit command sent by the backend; either way, wait for it to die.
5127 InterruptChildProcess(pr)
5130 ChildProc *cp = (ChildProc *) pr;
5132 if (cp->kind != CPReal) return;
5133 (void) kill(cp->pid, SIGINT); /* stop it thinking */
5136 int OpenTelnet(host, port, pr)
5141 char cmdLine[MSG_SIZ];
5143 if (port[0] == NULLCHAR) {
5144 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
5146 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
5148 return StartChildProcess(cmdLine, "", pr);
5151 int OpenTCP(host, port, pr)
5157 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
5158 #else /* !OMIT_SOCKETS */
5160 struct sockaddr_in sa;
5162 unsigned short uport;
5165 if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
5169 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
5170 sa.sin_family = AF_INET;
5171 sa.sin_addr.s_addr = INADDR_ANY;
5172 uport = (unsigned short) 0;
5173 sa.sin_port = htons(uport);
5174 if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
5178 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
5179 if (!(hp = gethostbyname(host))) {
5181 if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
5182 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
5183 hp->h_addrtype = AF_INET;
5185 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
5186 hp->h_addr_list[0] = (char *) malloc(4);
5187 hp->h_addr_list[0][0] = b0;
5188 hp->h_addr_list[0][1] = b1;
5189 hp->h_addr_list[0][2] = b2;
5190 hp->h_addr_list[0][3] = b3;
5195 sa.sin_family = hp->h_addrtype;
5196 uport = (unsigned short) atoi(port);
5197 sa.sin_port = htons(uport);
5198 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
5200 if (connect(s, (struct sockaddr *) &sa,
5201 sizeof(struct sockaddr_in)) < 0) {
5205 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5212 #endif /* !OMIT_SOCKETS */
5217 int OpenCommPort(name, pr)
5224 fd = open(name, 2, 0);
5225 if (fd < 0) return errno;
5227 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5237 int OpenLoopback(pr)
5243 SetUpChildIO(to, from);
5245 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5248 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
5255 int OpenRcmd(host, user, cmd, pr)
5256 char *host, *user, *cmd;
5259 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
5263 #define INPUT_SOURCE_BUF_SIZE 8192
5272 char buf[INPUT_SOURCE_BUF_SIZE];
5277 DoInputCallback(io,cond,data)
5282 /* read input from one of the input source (for example a chess program, ICS, etc).
5283 * and call a function that will handle the input
5286 int count; /* how many bytes did we read */
5290 /* All information (callback function, file descriptor, etc) is
5291 * saved in an InputSource structure
5293 InputSource *is = (InputSource *) data;
5297 count = read(is->fd, is->unused,
5298 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
5302 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
5305 is->unused += count;
5307 /* break input into lines and call the callback function on each
5310 while (p < is->unused)
5312 q = memchr(p, '\n', is->unused - p);
5313 if (q == NULL) break;
5315 (is->func)(is, is->closure, p, q - p, 0);
5318 /* remember not yet used part of the buffer */
5320 while (p < is->unused)
5328 /* read maximum length of input buffer and send the whole buffer
5329 * to the callback function
5331 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
5336 (is->func)(is, is->closure, is->buf, count, error);
5342 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
5349 GIOChannel *channel;
5350 ChildProc *cp = (ChildProc *) pr;
5352 is = (InputSource *) calloc(1, sizeof(InputSource));
5353 is->lineByLine = lineByLine;
5357 is->fd = fileno(stdin);
5359 is->kind = cp->kind;
5360 is->fd = cp->fdFrom;
5363 is->unused = is->buf;
5367 // is->xid = XtAppAddInput(appContext, is->fd,
5368 // (XtPointer) (XtInputReadMask),
5369 // (XtInputCallbackProc) DoInputCallback,
5373 /* TODO: will this work on windows?*/
5375 channel = g_io_channel_unix_new(is->fd);
5376 g_io_channel_set_close_on_unref (channel, TRUE);
5377 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
5378 is->closure = closure;
5379 return (InputSourceRef) is;
5383 RemoveInputSource(isr)
5386 InputSource *is = (InputSource *) isr;
5388 if (is->sid == 0) return;
5389 g_source_remove(is->sid);
5394 int OutputToProcess(pr, message, count, outError)
5400 static int line = 0;
5401 ChildProc *cp = (ChildProc *) pr;
5406 if (appData.noJoin || !appData.useInternalWrap)
5407 outCount = fwrite(message, 1, count, stdout);
5410 int width = get_term_width();
5411 int len = wrap(NULL, message, count, width, &line);
5412 char *msg = malloc(len);
5416 outCount = fwrite(message, 1, count, stdout);
5419 dbgchk = wrap(msg, message, count, width, &line);
5420 if (dbgchk != len && appData.debugMode)
5421 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
5422 outCount = fwrite(msg, 1, dbgchk, stdout);
5428 outCount = write(cp->fdTo, message, count);
5438 /* Output message to process, with "ms" milliseconds of delay
5439 between each character. This is needed when sending the logon
5440 script to ICC, which for some reason doesn't like the
5441 instantaneous send. */
5442 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
5449 ChildProc *cp = (ChildProc *) pr;
5454 r = write(cp->fdTo, message++, 1);
5467 /**** Animation code by Hugh Fisher, DCS, ANU.
5469 Known problem: if a window overlapping the board is
5470 moved away while a piece is being animated underneath,
5471 the newly exposed area won't be updated properly.
5472 I can live with this.
5474 Known problem: if you look carefully at the animation
5475 of pieces in mono mode, they are being drawn as solid
5476 shapes without interior detail while moving. Fixing
5477 this would be a major complication for minimal return.
5480 /* Masks for XPM pieces. Black and white pieces can have
5481 different shapes, but in the interest of retaining my
5482 sanity pieces must have the same outline on both light
5483 and dark squares, and all pieces must use the same
5484 background square colors/images. */
5486 static int xpmDone = 0;
5489 CreateAnimMasks (pieceDepth)
5496 unsigned long plane;
5499 /* just return for gtk at the moment */
5502 /* Need a bitmap just to get a GC with right depth */
5503 // buf = XCreatePixmap(xDisplay, xBoardWindow,
5505 values.foreground = 1;
5506 values.background = 0;
5507 /* Don't use XtGetGC, not read only */
5508 // maskGC = XCreateGC(xDisplay, buf,
5509 // GCForeground | GCBackground, &values);
5510 // XFreePixmap(xDisplay, buf);
5512 // buf = XCreatePixmap(xDisplay, xBoardWindow,
5513 // squareSize, squareSize, pieceDepth);
5514 // values.foreground = XBlackPixel(xDisplay, xScreen);
5515 // values.background = XWhitePixel(xDisplay, xScreen);
5516 // bufGC = XCreateGC(xDisplay, buf,
5517 // GCForeground | GCBackground, &values);
5519 for (piece = WhitePawn; piece <= BlackKing; piece++) {
5520 /* Begin with empty mask */
5521 // if(!xpmDone) // [HGM] pieces: keep using existing
5522 // xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
5523 // squareSize, squareSize, 1);
5524 // XSetFunction(xDisplay, maskGC, GXclear);
5525 // XFillRectangle(xDisplay, xpmMask[piece], maskGC,
5526 // 0, 0, squareSize, squareSize);
5528 /* Take a copy of the piece */
5533 // XSetFunction(xDisplay, bufGC, GXcopy);
5534 // XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
5536 // 0, 0, squareSize, squareSize, 0, 0);
5538 /* XOR the background (light) over the piece */
5539 // XSetFunction(xDisplay, bufGC, GXxor);
5541 // XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
5542 // 0, 0, squareSize, squareSize, 0, 0);
5544 // XSetForeground(xDisplay, bufGC, lightSquareColor);
5545 // XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
5548 /* We now have an inverted piece image with the background
5549 erased. Construct mask by just selecting all the non-zero
5550 pixels - no need to reconstruct the original image. */
5551 // XSetFunction(xDisplay, maskGC, GXor);
5553 /* Might be quicker to download an XImage and create bitmap
5554 data from it rather than this N copies per piece, but it
5555 only takes a fraction of a second and there is a much
5556 longer delay for loading the pieces. */
5557 // for (n = 0; n < pieceDepth; n ++) {
5558 // XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
5559 // 0, 0, squareSize, squareSize,
5561 // plane = plane << 1;
5565 // XFreePixmap(xDisplay, buf);
5566 // XFreeGC(xDisplay, bufGC);
5567 // XFreeGC(xDisplay, maskGC);
5571 InitAnimState (anim, info)
5573 XWindowAttributes * info;
5578 /* Each buffer is square size, same depth as window */
5579 // anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
5580 // squareSize, squareSize, info->depth);
5581 // anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
5582 // squareSize, squareSize, info->depth);
5584 // /* Create a plain GC for blitting */
5585 // mask = GCForeground | GCBackground | GCFunction |
5586 // GCPlaneMask | GCGraphicsExposures;
5587 // values.foreground = XBlackPixel(xDisplay, xScreen);
5588 // values.background = XWhitePixel(xDisplay, xScreen);
5589 // values.function = GXcopy;
5590 // values.plane_mask = AllPlanes;
5591 // values.graphics_exposures = False;
5592 // anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
5594 // /* Piece will be copied from an existing context at
5595 // the start of each new animation/drag. */
5596 // anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
5598 // /* Outline will be a read-only copy of an existing */
5599 // anim->outlineGC = None;
5605 static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
5606 XWindowAttributes info;
5608 /* for gtk at the moment just ... */
5611 if (xpmDone && gameInfo.variant == old) return;
5612 if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet
5613 // XGetWindowAttributes(xDisplay, xBoardWindow, &info);
5615 // InitAnimState(&game, &info);
5616 // InitAnimState(&player, &info);
5618 /* For XPM pieces, we need bitmaps to use as masks. */
5620 // CreateAnimMasks(info.depth);
5626 static Boolean frameWaiting;
5628 static RETSIGTYPE FrameAlarm (sig)
5631 frameWaiting = False;
5632 /* In case System-V style signals. Needed?? */
5633 signal(SIGALRM, FrameAlarm);
5640 struct itimerval delay;
5642 XSync(xDisplay, False);
5645 frameWaiting = True;
5646 signal(SIGALRM, FrameAlarm);
5647 delay.it_interval.tv_sec =
5648 delay.it_value.tv_sec = time / 1000;
5649 delay.it_interval.tv_usec =
5650 delay.it_value.tv_usec = (time % 1000) * 1000;
5651 setitimer(ITIMER_REAL, &delay, NULL);
5652 while (frameWaiting) pause();
5653 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
5654 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
5655 setitimer(ITIMER_REAL, &delay, NULL);
5665 // XSync(xDisplay, False);
5667 usleep(time * 1000);
5672 /* Convert board position to corner of screen rect and color */
5675 ScreenSquare(column, row, pt, color)
5676 int column; int row; XPoint * pt; int * color;
5679 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
5680 pt->y = lineGap + row * (squareSize + lineGap);
5682 pt->x = lineGap + column * (squareSize + lineGap);
5683 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
5685 *color = SquareColor(row, column);
5688 /* Convert window coords to square */
5691 BoardSquare(x, y, column, row)
5692 int x; int y; int * column; int * row;
5694 *column = EventToSquare(x, BOARD_WIDTH);
5695 if (flipView && *column >= 0)
5696 *column = BOARD_WIDTH - 1 - *column;
5697 *row = EventToSquare(y, BOARD_HEIGHT);
5698 if (!flipView && *row >= 0)
5699 *row = BOARD_HEIGHT - 1 - *row;
5704 #undef Max /* just in case */
5706 #define Max(a, b) ((a) > (b) ? (a) : (b))
5707 #define Min(a, b) ((a) < (b) ? (a) : (b))
5710 SetRect(rect, x, y, width, height)
5711 XRectangle * rect; int x; int y; int width; int height;
5715 rect->width = width;
5716 rect->height = height;
5719 /* Test if two frames overlap. If they do, return
5720 intersection rect within old and location of
5721 that rect within new. */
5724 Intersect(old, new, size, area, pt)
5725 XPoint * old; XPoint * new;
5726 int size; XRectangle * area; XPoint * pt;
5728 if (old->x > new->x + size || new->x > old->x + size ||
5729 old->y > new->y + size || new->y > old->y + size) {
5732 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
5733 size - abs(old->x - new->x), size - abs(old->y - new->y));
5734 pt->x = Max(old->x - new->x, 0);
5735 pt->y = Max(old->y - new->y, 0);
5740 /* For two overlapping frames, return the rect(s)
5741 in the old that do not intersect with the new. */
5744 CalcUpdateRects(old, new, size, update, nUpdates)
5745 XPoint * old; XPoint * new; int size;
5746 XRectangle update[]; int * nUpdates;
5750 /* If old = new (shouldn't happen) then nothing to draw */
5751 if (old->x == new->x && old->y == new->y) {
5755 /* Work out what bits overlap. Since we know the rects
5756 are the same size we don't need a full intersect calc. */
5758 /* Top or bottom edge? */
5759 if (new->y > old->y) {
5760 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
5762 } else if (old->y > new->y) {
5763 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
5764 size, old->y - new->y);
5767 /* Left or right edge - don't overlap any update calculated above. */
5768 if (new->x > old->x) {
5769 SetRect(&(update[count]), old->x, Max(new->y, old->y),
5770 new->x - old->x, size - abs(new->y - old->y));
5772 } else if (old->x > new->x) {
5773 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
5774 old->x - new->x, size - abs(new->y - old->y));
5781 /* Generate a series of frame coords from start->mid->finish.
5782 The movement rate doubles until the half way point is
5783 reached, then halves back down to the final destination,
5784 which gives a nice slow in/out effect. The algorithmn
5785 may seem to generate too many intermediates for short
5786 moves, but remember that the purpose is to attract the
5787 viewers attention to the piece about to be moved and
5788 then to where it ends up. Too few frames would be less
5792 Tween(start, mid, finish, factor, frames, nFrames)
5793 XPoint * start; XPoint * mid;
5794 XPoint * finish; int factor;
5795 XPoint frames[]; int * nFrames;
5797 int fraction, n, count;
5801 /* Slow in, stepping 1/16th, then 1/8th, ... */
5803 for (n = 0; n < factor; n++)
5805 for (n = 0; n < factor; n++) {
5806 frames[count].x = start->x + (mid->x - start->x) / fraction;
5807 frames[count].y = start->y + (mid->y - start->y) / fraction;
5809 fraction = fraction / 2;
5813 frames[count] = *mid;
5816 /* Slow out, stepping 1/2, then 1/4, ... */
5818 for (n = 0; n < factor; n++) {
5819 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
5820 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
5822 fraction = fraction * 2;
5827 /* Draw a piece on the screen without disturbing what's there */
5830 SelectGCMask(piece, clip, outline, mask)
5831 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
5835 /* Bitmap for piece being moved. */
5836 if (appData.monoMode) {
5837 *mask = *pieceToSolid(piece);
5838 } else if (useImages) {
5840 *mask = xpmMask[piece];
5842 *mask = ximMaskPm[piece];
5845 *mask = *pieceToSolid(piece);
5848 /* GC for piece being moved. Square color doesn't matter, but
5849 since it gets modified we make a copy of the original. */
5851 if (appData.monoMode)
5856 if (appData.monoMode)
5861 // XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
5863 /* Outline only used in mono mode and is not modified */
5865 *outline = bwPieceGC;
5867 *outline = wbPieceGC;
5871 OverlayPiece(piece, clip, outline, dest)
5872 ChessSquare piece; GC clip; GC outline; Drawable dest;
5877 /* Draw solid rectangle which will be clipped to shape of piece */
5878 // XFillRectangle(xDisplay, dest, clip,
5879 // 0, 0, squareSize, squareSize)
5881 if (appData.monoMode)
5882 /* Also draw outline in contrasting color for black
5883 on black / white on white cases */
5884 // XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
5885 // 0, 0, squareSize, squareSize, 0, 0, 1)
5888 /* Copy the piece */
5893 // XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
5895 // 0, 0, squareSize, squareSize,
5900 /* Animate the movement of a single piece */
5903 BeginAnimation(anim, piece, startColor, start)
5911 /* The old buffer is initialised with the start square (empty) */
5912 BlankSquare(0, 0, startColor, EmptySquare, anim->saveBuf);
5913 anim->prevFrame = *start;
5915 /* The piece will be drawn using its own bitmap as a matte */
5916 // SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
5917 // XSetClipMask(xDisplay, anim->pieceGC, mask);
5921 AnimationFrame(anim, frame, piece)
5926 XRectangle updates[4];
5931 /* Save what we are about to draw into the new buffer */
5932 // XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
5933 // frame->x, frame->y, squareSize, squareSize,
5936 /* Erase bits of the previous frame */
5937 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
5938 /* Where the new frame overlapped the previous,
5939 the contents in newBuf are wrong. */
5940 // XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
5941 // overlap.x, overlap.y,
5942 // overlap.width, overlap.height,
5944 /* Repaint the areas in the old that don't overlap new */
5945 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
5946 for (i = 0; i < count; i++)
5947 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
5948 // updates[i].x - anim->prevFrame.x,
5949 // updates[i].y - anim->prevFrame.y,
5950 // updates[i].width, updates[i].height,
5951 // updates[i].x, updates[i].y)
5954 /* Easy when no overlap */
5955 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
5956 // 0, 0, squareSize, squareSize,
5957 // anim->prevFrame.x, anim->prevFrame.y);
5960 /* Save this frame for next time round */
5961 // XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
5962 // 0, 0, squareSize, squareSize,
5964 anim->prevFrame = *frame;
5966 /* Draw piece over original screen contents, not current,
5967 and copy entire rect. Wipes out overlapping piece images. */
5968 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
5969 // XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
5970 // 0, 0, squareSize, squareSize,
5971 // frame->x, frame->y);
5975 EndAnimation (anim, finish)
5979 XRectangle updates[4];
5984 /* The main code will redraw the final square, so we
5985 only need to erase the bits that don't overlap. */
5986 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
5987 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
5988 for (i = 0; i < count; i++)
5989 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
5990 // updates[i].x - anim->prevFrame.x,
5991 // updates[i].y - anim->prevFrame.y,
5992 // updates[i].width, updates[i].height,
5993 // updates[i].x, updates[i].y)
5996 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
5997 // 0, 0, squareSize, squareSize,
5998 // anim->prevFrame.x, anim->prevFrame.y);
6003 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
6005 ChessSquare piece; int startColor;
6006 XPoint * start; XPoint * finish;
6007 XPoint frames[]; int nFrames;
6011 BeginAnimation(anim, piece, startColor, start);
6012 for (n = 0; n < nFrames; n++) {
6013 AnimationFrame(anim, &(frames[n]), piece);
6014 FrameDelay(appData.animSpeed);
6016 EndAnimation(anim, finish);
6019 /* Main control logic for deciding what to animate and how */
6022 AnimateMove(board, fromX, fromY, toX, toY)
6031 XPoint start, finish, mid;
6032 XPoint frames[kFactor * 2 + 1];
6033 int nFrames, startColor, endColor;
6035 /* Are we animating? */
6036 if (!appData.animate || appData.blindfold)
6039 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
6040 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
6041 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
6043 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
6044 piece = board[fromY][fromX];
6045 if (piece >= EmptySquare) return;
6050 hop = (piece == WhiteKnight || piece == BlackKnight);
6053 if (appData.debugMode) {
6054 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
6055 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
6056 piece, fromX, fromY, toX, toY); }
6058 ScreenSquare(fromX, fromY, &start, &startColor);
6059 ScreenSquare(toX, toY, &finish, &endColor);
6062 /* Knight: make diagonal movement then straight */
6063 if (abs(toY - fromY) < abs(toX - fromX)) {
6064 mid.x = start.x + (finish.x - start.x) / 2;
6068 mid.y = start.y + (finish.y - start.y) / 2;
6071 mid.x = start.x + (finish.x - start.x) / 2;
6072 mid.y = start.y + (finish.y - start.y) / 2;
6075 /* Don't use as many frames for very short moves */
6076 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
6077 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
6079 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
6080 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
6082 /* Be sure end square is redrawn */
6083 damage[toY][toX] = True;
6087 DragPieceBegin(x, y)
6090 int boardX, boardY, color;
6093 /* Are we animating? */
6094 if (!appData.animateDragging || appData.blindfold)
6097 /* Figure out which square we start in and the
6098 mouse position relative to top left corner. */
6099 BoardSquare(x, y, &boardX, &boardY);
6100 player.startBoardX = boardX;
6101 player.startBoardY = boardY;
6102 ScreenSquare(boardX, boardY, &corner, &color);
6103 player.startSquare = corner;
6104 player.startColor = color;
6105 /* As soon as we start dragging, the piece will jump slightly to
6106 be centered over the mouse pointer. */
6107 player.mouseDelta.x = squareSize/2;
6108 player.mouseDelta.y = squareSize/2;
6109 /* Initialise animation */
6110 player.dragPiece = PieceForSquare(boardX, boardY);
6112 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
6113 player.dragActive = True;
6114 BeginAnimation(&player, player.dragPiece, color, &corner);
6115 /* Mark this square as needing to be redrawn. Note that
6116 we don't remove the piece though, since logically (ie
6117 as seen by opponent) the move hasn't been made yet. */
6118 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
6119 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
6120 // XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
6121 // corner.x, corner.y, squareSize, squareSize,
6122 // 0, 0); // [HGM] zh: unstack in stead of grab
6123 damage[boardY][boardX] = True;
6125 player.dragActive = False;
6135 /* Are we animating? */
6136 if (!appData.animateDragging || appData.blindfold)
6140 if (! player.dragActive)
6142 /* Move piece, maintaining same relative position
6143 of mouse within square */
6144 corner.x = x - player.mouseDelta.x;
6145 corner.y = y - player.mouseDelta.y;
6146 AnimationFrame(&player, &corner, player.dragPiece);
6148 if (appData.highlightDragging) {
6150 BoardSquare(x, y, &boardX, &boardY);
6151 SetHighlights(fromX, fromY, boardX, boardY);
6160 int boardX, boardY, color;
6163 /* Are we animating? */
6164 if (!appData.animateDragging || appData.blindfold)
6168 if (! player.dragActive)
6170 /* Last frame in sequence is square piece is
6171 placed on, which may not match mouse exactly. */
6172 BoardSquare(x, y, &boardX, &boardY);
6173 ScreenSquare(boardX, boardY, &corner, &color);
6174 EndAnimation(&player, &corner);
6176 /* Be sure end square is redrawn */
6177 damage[boardY][boardX] = True;
6179 /* This prevents weird things happening with fast successive
6180 clicks which on my Sun at least can cause motion events
6181 without corresponding press/release. */
6182 player.dragActive = False;
6185 /* Handle expose event while piece being dragged */
6190 if (!player.dragActive || appData.blindfold)
6193 /* What we're doing: logically, the move hasn't been made yet,
6194 so the piece is still in it's original square. But visually
6195 it's being dragged around the board. So we erase the square
6196 that the piece is on and draw it at the last known drag point. */
6197 BlankSquare(player.startSquare.x, player.startSquare.y,
6198 player.startColor, EmptySquare, xBoardWindow);
6199 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
6200 damage[player.startBoardY][player.startBoardX] = TRUE;
6203 #include <sys/ioctl.h>
6204 int get_term_width()
6206 int fd, default_width;
6209 default_width = 79; // this is FICS default anyway...
6211 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
6213 if (!ioctl(fd, TIOCGSIZE, &win))
6214 default_width = win.ts_cols;
6215 #elif defined(TIOCGWINSZ)
6217 if (!ioctl(fd, TIOCGWINSZ, &win))
6218 default_width = win.ws_col;
6220 return default_width;
6223 void update_ics_width()
6225 static int old_width = 0;
6226 int new_width = get_term_width();
6228 if (old_width != new_width)
6229 ics_printf("set width %d\n", new_width);
6230 old_width = new_width;
6233 void NotifyFrontendLogin()