2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
65 # if HAVE_SYS_SOCKET_H
66 # include <sys/socket.h>
67 # include <netinet/in.h>
69 # else /* not HAVE_SYS_SOCKET_H */
70 # if HAVE_LAN_SOCKET_H
71 # include <lan/socket.h>
73 # include <lan/netdb.h>
74 # else /* not HAVE_LAN_SOCKET_H */
75 # define OMIT_SOCKETS 1
76 # endif /* not HAVE_LAN_SOCKET_H */
77 # endif /* not HAVE_SYS_SOCKET_H */
78 #endif /* !OMIT_SOCKETS */
83 #else /* not STDC_HEADERS */
84 extern char *getenv();
87 # else /* not HAVE_STRING_H */
89 # endif /* not HAVE_STRING_H */
90 #endif /* not STDC_HEADERS */
93 # include <sys/fcntl.h>
94 #else /* not HAVE_SYS_FCNTL_H */
97 # endif /* HAVE_FCNTL_H */
98 #endif /* not HAVE_SYS_FCNTL_H */
100 #if HAVE_SYS_SYSTEMINFO_H
101 # include <sys/systeminfo.h>
102 #endif /* HAVE_SYS_SYSTEMINFO_H */
104 #if TIME_WITH_SYS_TIME
105 # include <sys/time.h>
109 # include <sys/time.h>
120 # include <sys/wait.h>
125 # define NAMLEN(dirent) strlen((dirent)->d_name)
126 # define HAVE_DIR_STRUCT
128 # define dirent direct
129 # define NAMLEN(dirent) (dirent)->d_namlen
131 # include <sys/ndir.h>
132 # define HAVE_DIR_STRUCT
135 # include <sys/dir.h>
136 # define HAVE_DIR_STRUCT
140 # define HAVE_DIR_STRUCT
144 #include <X11/Intrinsic.h>
145 #include <X11/StringDefs.h>
146 #include <X11/Shell.h>
147 #include <X11/cursorfont.h>
148 #include <X11/Xatom.h>
149 #include <X11/Xmu/Atoms.h>
151 #include <X11/Xaw3d/Dialog.h>
152 #include <X11/Xaw3d/Form.h>
153 #include <X11/Xaw3d/List.h>
154 #include <X11/Xaw3d/Label.h>
155 #include <X11/Xaw3d/SimpleMenu.h>
156 #include <X11/Xaw3d/SmeBSB.h>
157 #include <X11/Xaw3d/SmeLine.h>
158 #include <X11/Xaw3d/Box.h>
159 #include <X11/Xaw3d/MenuButton.h>
160 #include <X11/Xaw3d/Text.h>
161 #include <X11/Xaw3d/AsciiText.h>
163 #include <X11/Xaw/Dialog.h>
164 #include <X11/Xaw/Form.h>
165 #include <X11/Xaw/List.h>
166 #include <X11/Xaw/Label.h>
167 #include <X11/Xaw/SimpleMenu.h>
168 #include <X11/Xaw/SmeBSB.h>
169 #include <X11/Xaw/SmeLine.h>
170 #include <X11/Xaw/Box.h>
171 #include <X11/Xaw/MenuButton.h>
172 #include <X11/Xaw/Text.h>
173 #include <X11/Xaw/AsciiText.h>
176 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
181 #include "pixmaps/pixmaps.h"
182 #define IMAGE_EXT "xpm"
184 #define IMAGE_EXT "xim"
185 #include "bitmaps/bitmaps.h"
190 #include <gdk-pixbuf/gdk-pixbuf.h>
192 #include "bitmaps/icon_white.bm"
193 #include "bitmaps/icon_black.bm"
194 #include "bitmaps/checkmark.bm"
196 #include "frontend.h"
201 #include "xgamelist.h"
202 #include "xhistory.h"
203 #include "xedittags.h"
205 #include "callback.h"
206 #include "interface.h"
208 // must be moved to xengineoutput.h
210 void EngineOutputProc P((Widget w, XEvent *event,
211 String *prms, Cardinal *nprms));
212 void EvalGraphProc P((Widget w, XEvent *event,
213 String *prms, Cardinal *nprms));
220 #define usleep(t) _sleep2(((t)+500)/1000)
224 # define _(s) gettext (s)
225 # define N_(s) gettext_noop (s)
248 int main P((int argc, char **argv));
249 RETSIGTYPE CmailSigHandler P((int sig));
250 RETSIGTYPE IntSigHandler P((int sig));
251 RETSIGTYPE TermSizeSigHandler P((int sig));
252 void CreateGCs P((void));
253 void CreateXIMPieces P((void));
254 void CreateXPMPieces P((void));
255 void CreatePieces P((void));
256 void CreatePieceMenus P((void));
257 Widget CreateMenuBar P((Menu *mb));
258 char *FindFont P((char *pattern, int targetPxlSize));
259 void PieceMenuPopup P((Widget w, XEvent *event,
260 String *params, Cardinal *num_params));
261 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
262 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
263 int EventToSquare P((int x, int limit));
264 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
265 void AnimateUserMove P((Widget w, XEvent * event,
266 String * params, Cardinal * nParams));
267 void HandlePV P((Widget w, XEvent * event,
268 String * params, Cardinal * nParams));
269 void CommentPopUp P((char *title, char *label));
270 void CommentPopDown P((void));
271 void CommentCallback P((Widget w, XtPointer client_data,
272 XtPointer call_data));
273 void ICSInputBoxPopUp P((void));
274 void ICSInputBoxPopDown P((void));
275 void AskQuestionReplyAction P((Widget w, XEvent *event,
276 String *prms, Cardinal *nprms));
277 void AskQuestionProc P((Widget w, XEvent *event,
278 String *prms, Cardinal *nprms));
279 void AskQuestionPopDown P((void));
280 void PromotionPopDown P((void));
281 void PromotionCallback P((Widget w, XtPointer client_data,
282 XtPointer call_data));
283 void EditCommentPopDown P((void));
284 void EditCommentCallback P((Widget w, XtPointer client_data,
285 XtPointer call_data));
286 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
287 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
289 void PastePositionProc P((Widget w, XEvent *event, String *prms,
291 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
292 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
293 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
294 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
296 void EditCommentProc P((Widget w, XEvent *event,
297 String *prms, Cardinal *nprms));
298 void IcsInputBoxProc P((Widget w, XEvent *event,
299 String *prms, Cardinal *nprms));
300 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
301 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
302 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
303 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
304 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
305 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
306 void Iconify P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
307 void DisplayMove P((int moveNumber));
308 void DisplayTitle P((char *title));
309 void ICSInitScript P((void));
310 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
311 void ErrorPopUp P((char *title, char *text, int modal));
312 void ErrorPopDown P((void));
313 static char *ExpandPathName P((char *path));
314 static void CreateAnimVars P((void));
315 static void DragPieceMove P((int x, int y));
316 static void DrawDragPiece P((void));
317 char *ModeToWidgetName P((GameMode mode));
318 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
319 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
320 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
321 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
322 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
323 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
324 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
325 void ShufflePopDown P(());
326 void EnginePopDown P(());
327 void UciPopDown P(());
328 void TimeControlPopDown P(());
329 void NewVariantPopDown P(());
330 void SettingsPopDown P(());
331 void SetMenuEnables P((Enables *enab));
332 void update_ics_width P(());
333 int get_term_width P(());
334 int CopyMemoProc P(());
336 * XBoard depends on Xt R4 or higher
338 int xtVersion = XtSpecificationRelease;
343 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
344 jailSquareColor, highlightSquareColor, premoveHighlightColor;
345 Pixel lowTimeWarningColor;
347 #define LINE_TYPE_NORMAL 0
348 #define LINE_TYPE_HIGHLIGHT 1
349 #define LINE_TYPE_PRE 2
352 GC lightSquareGC, darkSquareGC, jailSquareGC, wdPieceGC, wlPieceGC,
353 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC,
354 wjPieceGC, bjPieceGC;
355 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
356 Widget layoutWidget, formWidget, boardWidget, messageWidget,
357 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
358 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
359 menuBarWidget, editShell, errorShell, analysisShell,
360 ICSInputShell, fileNameShell, askQuestionShell;
362 Widget historyShell, evalGraphShell, gameListShell;
363 //XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
364 //XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
366 Font clockFontID, coordFontID, countFontID;
367 XFontStruct *clockFontStruct, *coordFontStruct, *countFontStruct;
368 XtAppContext appContext;
370 char *oldICSInteractionTitle;
374 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
376 Position commentX = -1, commentY = -1;
377 Dimension commentW, commentH;
378 typedef unsigned int BoardSize;
380 Boolean chessProgram;
382 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
383 int squareSize, smallLayout = 0, tinyLayout = 0,
384 marginW, marginH, // [HGM] for run-time resizing
385 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
386 ICSInputBoxUp = False, askQuestionUp = False,
387 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
388 editUp = False, errorUp = False, errorExitStatus = -1, lineGap;
389 Pixel timerForegroundPixel, timerBackgroundPixel;
390 Pixel buttonForegroundPixel, buttonBackgroundPixel;
391 char *chessDir, *programName, *programVersion,
392 *gameCopyFilename, *gamePasteFilename;
393 Boolean alwaysOnTop = False;
394 Boolean saveSettingsOnExit;
395 char *settingsFileName;
396 char *icsTextMenuString;
398 char *firstChessProgramNames;
399 char *secondChessProgramNames;
401 WindowPlacement wpMain;
402 WindowPlacement wpConsole;
403 WindowPlacement wpComment;
404 WindowPlacement wpMoveHistory;
405 WindowPlacement wpEvalGraph;
406 WindowPlacement wpEngineOutput;
407 WindowPlacement wpGameList;
408 WindowPlacement wpTags;
412 Pixmap pieceBitmap[2][(int)BlackPawn];
413 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
414 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
415 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
416 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
417 int useImages=0, useImageSqs;
418 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
419 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
420 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
421 XImage *ximLightSquare, *ximDarkSquare;
424 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
425 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
427 #define White(piece) ((int)(piece) < (int)BlackPawn)
429 /* Variables for doing smooth animation. This whole thing
430 would be much easier if the board was double-buffered,
431 but that would require a fairly major rewrite. */
436 GC blitGC, pieceGC, outlineGC;
437 XPoint startSquare, prevFrame, mouseDelta;
441 int startBoardX, startBoardY;
444 /* There can be two pieces being animated at once: a player
445 can begin dragging a piece before the remote opponent has moved. */
447 static AnimState game, player;
449 /* Bitmaps for use as masks when drawing XPM pieces.
450 Need one for each black and white piece. */
451 static Pixmap xpmMask[BlackKing + 1];
453 /* This magic number is the number of intermediate frames used
454 in each half of the animation. For short moves it's reduced
455 by 1. The total number of frames will be factor * 2 + 1. */
458 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
460 Enables icsEnables[] = {
461 { "menuFile.Mail Move", False },
462 { "menuFile.Reload CMail Message", False },
463 { "menuMode.Machine Black", False },
464 { "menuMode.Machine White", False },
465 { "menuMode.Analysis Mode", False },
466 { "menuMode.Analyze File", False },
467 { "menuMode.Two Machines", False },
469 { "menuHelp.Hint", False },
470 { "menuHelp.Book", False },
471 { "menuStep.Move Now", False },
472 { "menuOptions.Periodic Updates", False },
473 { "menuOptions.Hide Thinking", False },
474 { "menuOptions.Ponder Next Move", False },
479 Enables ncpEnables[] = {
480 { "menuFile.Mail Move", False },
481 { "menuFile.Reload CMail Message", False },
482 { "menuMode.Machine White", False },
483 { "menuMode.Machine Black", False },
484 { "menuMode.Analysis Mode", False },
485 { "menuMode.Analyze File", False },
486 { "menuMode.Two Machines", False },
487 { "menuMode.ICS Client", False },
488 { "menuMode.ICS Input Box", False },
490 { "menuStep.Revert", False },
491 { "menuStep.Move Now", False },
492 { "menuStep.Retract Move", False },
493 { "menuOptions.Auto Comment", False },
494 { "menuOptions.Auto Flag", False },
495 { "menuOptions.Auto Flip View", False },
496 { "menuOptions.Auto Observe", False },
497 { "menuOptions.Auto Raise Board", False },
498 { "menuOptions.Get Move List", False },
499 { "menuOptions.ICS Alarm", False },
500 { "menuOptions.Move Sound", False },
501 { "menuOptions.Quiet Play", False },
502 { "menuOptions.Hide Thinking", False },
503 { "menuOptions.Periodic Updates", False },
504 { "menuOptions.Ponder Next Move", False },
505 { "menuHelp.Hint", False },
506 { "menuHelp.Book", False },
510 Enables gnuEnables[] = {
511 { "menuMode.ICS Client", False },
512 { "menuMode.ICS Input Box", False },
513 { "menuAction.Accept", False },
514 { "menuAction.Decline", False },
515 { "menuAction.Rematch", False },
516 { "menuAction.Adjourn", False },
517 { "menuAction.Stop Examining", False },
518 { "menuAction.Stop Observing", False },
519 { "menuStep.Revert", False },
520 { "menuOptions.Auto Comment", False },
521 { "menuOptions.Auto Observe", False },
522 { "menuOptions.Auto Raise Board", False },
523 { "menuOptions.Get Move List", False },
524 { "menuOptions.Premove", False },
525 { "menuOptions.Quiet Play", False },
527 /* The next two options rely on SetCmailMode being called *after* */
528 /* SetGNUMode so that when GNU is being used to give hints these */
529 /* menu options are still available */
531 { "menuFile.Mail Move", False },
532 { "menuFile.Reload CMail Message", False },
536 Enables cmailEnables[] = {
538 { "menuAction.Call Flag", False },
539 { "menuAction.Draw", True },
540 { "menuAction.Adjourn", False },
541 { "menuAction.Abort", False },
542 { "menuAction.Stop Observing", False },
543 { "menuAction.Stop Examining", False },
544 { "menuFile.Mail Move", True },
545 { "menuFile.Reload CMail Message", True },
549 Enables trainingOnEnables[] = {
550 { "menuMode.Edit Comment", False },
551 { "menuMode.Pause", False },
552 { "menuStep.Forward", False },
553 { "menuStep.Backward", False },
554 { "menuStep.Forward to End", False },
555 { "menuStep.Back to Start", False },
556 { "menuStep.Move Now", False },
557 { "menuStep.Truncate Game", False },
561 Enables trainingOffEnables[] = {
562 { "menuMode.Edit Comment", True },
563 { "menuMode.Pause", True },
564 { "menuStep.Forward", True },
565 { "menuStep.Backward", True },
566 { "menuStep.Forward to End", True },
567 { "menuStep.Back to Start", True },
568 { "menuStep.Move Now", True },
569 { "menuStep.Truncate Game", True },
573 Enables machineThinkingEnables[] = {
574 { "menuFile.Load Game", False },
575 { "menuFile.Load Next Game", False },
576 { "menuFile.Load Previous Game", False },
577 { "menuFile.Reload Same Game", False },
578 { "menuFile.Paste Game", False },
579 { "menuFile.Load Position", False },
580 { "menuFile.Load Next Position", False },
581 { "menuFile.Load Previous Position", False },
582 { "menuFile.Reload Same Position", False },
583 { "menuFile.Paste Position", False },
584 { "menuMode.Machine White", False },
585 { "menuMode.Machine Black", False },
586 { "menuMode.Two Machines", False },
587 { "menuStep.Retract Move", False },
591 Enables userThinkingEnables[] = {
592 { "menuFile.Load Game", True },
593 { "menuFile.Load Next Game", True },
594 { "menuFile.Load Previous Game", True },
595 { "menuFile.Reload Same Game", True },
596 { "menuFile.Paste Game", True },
597 { "menuFile.Load Position", True },
598 { "menuFile.Load Next Position", True },
599 { "menuFile.Load Previous Position", True },
600 { "menuFile.Reload Same Position", True },
601 { "menuFile.Paste Position", True },
602 { "menuMode.Machine White", True },
603 { "menuMode.Machine Black", True },
604 { "menuMode.Two Machines", True },
605 { "menuStep.Retract Move", True },
611 MenuItem fileMenu[] = {
612 {N_("New Shuffle Game ..."), ShuffleMenuProc},
613 {N_("New Variant ..."), NewVariantProc}, // [HGM] variant: not functional yet
614 // {"----", NothingProc},
615 // {N_("Save Game"), SaveGameProc},
616 // {"----", NothingProc},
617 {N_("Copy Game"), CopyGameProc},
618 {N_("Paste Game"), PasteGameProc},
619 // {"----", NothingProc},
620 // {N_("Load Position"), LoadPositionProc},
621 // {N_("Load Next Position"), LoadNextPositionProc},
622 // {N_("Load Previous Position"), LoadPrevPositionProc},
623 // {N_("Reload Same Position"), ReloadPositionProc},
624 // {N_("Save Position"), SavePositionProc},
625 // {"----", NothingProc},
626 {N_("Copy Position"), CopyPositionProc},
627 {N_("Paste Position"), PastePositionProc},
628 // {"----", NothingProc},
629 {N_("Mail Move"), MailMoveProc},
630 {N_("Reload CMail Message"), ReloadCmailMsgProc},
631 // {"----", NothingProc},
635 MenuItem modeMenu[] = {
636 // {N_("Machine White"), MachineWhiteProc},
637 // {N_("Machine Black"), MachineBlackProc},
638 // {N_("Two Machines"), TwoMachinesProc},
639 // {N_("Analysis Mode"), AnalyzeModeProc},
640 // {N_("Analyze File"), AnalyzeFileProc },
641 // {N_("ICS Client"), IcsClientProc},
642 // {N_("Edit Game"), EditGameProc},
643 // {N_("Edit Position"), EditPositionProc},
644 // {N_("Training"), TrainingProc},
645 // {"----", NothingProc},
646 {N_("Show Engine Output"), EngineOutputProc},
647 {N_("Show Evaluation Graph"), EvalGraphProc},
648 {N_("Show Game List"), ShowGameListProc},
649 // {"Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
650 // {"----", NothingProc},
651 // {N_("Edit Tags"), EditTagsProc},
652 {N_("Edit Comment"), EditCommentProc},
653 {N_("ICS Input Box"), IcsInputBoxProc},
657 MenuItem optionsMenu[] = {
658 // {N_("Flip View"), FlipViewProc},
659 // {"----", NothingProc},
660 {N_("Adjudications ..."), EngineMenuProc},
661 {N_("General Settings ..."), UciMenuProc},
662 {N_("Engine #1 Settings ..."), FirstSettingsProc},
663 {N_("Engine #2 Settings ..."), SecondSettingsProc},
664 {N_("Time Control ..."), TimeControlProc},
665 {"----", NothingProc},
666 // {N_("Always Queen"), AlwaysQueenProc},
667 // {N_("Animate Dragging"), AnimateDraggingProc},
668 // {N_("Animate Moving"), AnimateMovingProc},
669 // {N_("Auto Comment"), AutocommProc},
670 // {N_("Auto Flag"), AutoflagProc},
671 // {N_("Auto Flip View"), AutoflipProc},
672 // {N_("Auto Observe"), AutobsProc},
673 // {N_("Auto Raise Board"), AutoraiseProc},
674 // {N_("Auto Save"), AutosaveProc},
675 // {N_("Blindfold"), BlindfoldProc},
676 // {N_("Flash Moves"), FlashMovesProc},
677 // {N_("Get Move List"), GetMoveListProc},
679 // {N_("Highlight Dragging"), HighlightDraggingProc},
681 // {N_("Highlight Last Move"), HighlightLastMoveProc},
682 // {N_("Move Sound"), MoveSoundProc},
683 // {N_("ICS Alarm"), IcsAlarmProc},
684 // {N_("Old Save Style"), OldSaveStyleProc},
685 // {N_("Periodic Updates"), PeriodicUpdatesProc},
686 // {N_("Ponder Next Move"), PonderNextMoveProc},
687 // {N_("Popup Exit Message"), PopupExitMessageProc},
688 // {N_("Popup Move Errors"), PopupMoveErrorsProc},
689 // {N_("Premove"), PremoveProc},
690 // {N_("Quiet Play"), QuietPlayProc},
691 // {N_("Hide Thinking"), HideThinkingProc},
692 // {N_("Test Legality"), TestLegalityProc},
693 // {N_("Show Coords"), ShowCoordsProc},
694 {"----", NothingProc},
695 {N_("Save Settings Now"), SaveSettingsProc},
696 {N_("Save Settings on Exit"), SaveOnExitProc},
701 {N_("File"), fileMenu},
702 {N_("Mode"), modeMenu},
703 {N_("Options"), optionsMenu},
707 #define PIECE_MENU_SIZE 18
708 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
709 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
710 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
711 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
712 N_("Empty square"), N_("Clear board") },
713 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
714 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
715 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
716 N_("Empty square"), N_("Clear board") }
718 /* must be in same order as PieceMenuStrings! */
719 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
720 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
721 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
722 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
723 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
724 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
725 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
726 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
727 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
730 #define DROP_MENU_SIZE 6
731 String dropMenuStrings[DROP_MENU_SIZE] = {
732 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
734 /* must be in same order as PieceMenuStrings! */
735 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
736 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
737 WhiteRook, WhiteQueen
745 DropMenuEnables dmEnables[] = {
754 { XtNborderWidth, 0 },
755 { XtNdefaultDistance, 0 },
759 { XtNborderWidth, 0 },
760 { XtNresizable, (XtArgVal) True },
764 { XtNborderWidth, 0 },
769 XtResource clientResources[] = {
770 { "flashCount", "flashCount", XtRInt, sizeof(int),
771 XtOffset(AppDataPtr, flashCount), XtRImmediate,
772 (XtPointer) FLASH_COUNT },
775 XtActionsRec boardActions[] = {
776 // { "HandleUserMove", HandleUserMove },
777 { "AnimateUserMove", AnimateUserMove },
778 // { "FileNameAction", FileNameAction },
779 { "HandlePV", HandlePV },
780 { "UnLoadPV", UnLoadPV },
781 { "AskQuestionProc", AskQuestionProc },
782 { "AskQuestionReplyAction", AskQuestionReplyAction },
783 { "PieceMenuPopup", PieceMenuPopup },
784 // { "WhiteClock", WhiteClock },
785 // { "BlackClock", BlackClock },
786 { "Iconify", Iconify },
787 { "LoadSelectedProc", LoadSelectedProc },
788 // { "LoadPositionProc", LoadPositionProc },
789 // { "LoadNextPositionProc", LoadNextPositionProc },
790 // { "LoadPrevPositionProc", LoadPrevPositionProc },
791 // { "ReloadPositionProc", ReloadPositionProc },
792 { "CopyPositionProc", CopyPositionProc },
793 { "PastePositionProc", PastePositionProc },
794 { "CopyGameProc", CopyGameProc },
795 { "PasteGameProc", PasteGameProc },
796 // { "SaveGameProc", SaveGameProc },
797 // { "SavePositionProc", SavePositionProc },
798 { "MailMoveProc", MailMoveProc },
799 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
800 // { "MachineWhiteProc", MachineWhiteProc },
801 // { "MachineBlackProc", MachineBlackProc },
802 // { "AnalysisModeProc", AnalyzeModeProc },
803 // { "AnalyzeFileProc", AnalyzeFileProc },
804 // { "TwoMachinesProc", TwoMachinesProc },
805 // { "IcsClientProc", IcsClientProc },
806 // { "EditGameProc", EditGameProc },
807 // { "EditPositionProc", EditPositionProc },
808 // { "TrainingProc", EditPositionProc },
809 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
810 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
811 { "ShowGameListProc", ShowGameListProc },
812 // { "ShowMoveListProc", HistoryShowProc},
813 // { "EditTagsProc", EditCommentProc },
814 { "EditCommentProc", EditCommentProc },
815 // { "IcsAlarmProc", IcsAlarmProc },
816 { "IcsInputBoxProc", IcsInputBoxProc },
817 // { "AcceptProc", AcceptProc },
818 // { "DeclineProc", DeclineProc },
819 // { "RematchProc", RematchProc },
820 // { "CallFlagProc", CallFlagProc },
821 // { "DrawProc", DrawProc },
822 // { "AdjournProc", AdjournProc },
823 // { "AbortProc", AbortProc },
824 // { "ResignProc", ResignProc },
825 // { "AdjuWhiteProc", AdjuWhiteProc },
826 // { "AdjuBlackProc", AdjuBlackProc },
827 // { "AdjuDrawProc", AdjuDrawProc },
828 { "EnterKeyProc", EnterKeyProc },
829 // { "StopObservingProc", StopObservingProc },
830 // { "StopExaminingProc", StopExaminingProc },
831 // { "BackwardProc", BackwardProc },
832 // { "ForwardProc", ForwardProc },
833 // { "ToStartProc", ToStartProc },
834 // { "ToEndProc", ToEndProc },
835 // { "RevertProc", RevertProc },
836 // { "TruncateGameProc", TruncateGameProc },
837 // { "MoveNowProc", MoveNowProc },
838 // { "RetractMoveProc", RetractMoveProc },
839 // { "AlwaysQueenProc", AlwaysQueenProc },
840 // { "AnimateDraggingProc", AnimateDraggingProc },
841 // { "AnimateMovingProc", AnimateMovingProc },
842 // { "AutoflagProc", AutoflagProc },
843 // { "AutoflipProc", AutoflipProc },
844 // { "AutobsProc", AutobsProc },
845 // { "AutoraiseProc", AutoraiseProc },
846 // { "AutosaveProc", AutosaveProc },
847 // { "BlindfoldProc", BlindfoldProc },
848 // { "FlashMovesProc", FlashMovesProc },
849 // { "FlipViewProc", FlipViewProc },
850 // { "GetMoveListProc", GetMoveListProc },
852 // { "HighlightDraggingProc", HighlightDraggingProc },
854 // { "HighlightLastMoveProc", HighlightLastMoveProc },
855 // { "IcsAlarmProc", IcsAlarmProc },
856 // { "MoveSoundProc", MoveSoundProc },
857 // { "OldSaveStyleProc", OldSaveStyleProc },
858 // { "PeriodicUpdatesProc", PeriodicUpdatesProc },
859 // { "PonderNextMoveProc", PonderNextMoveProc },
860 // { "PopupExitMessageProc", PopupExitMessageProc },
861 // { "PopupMoveErrorsProc", PopupMoveErrorsProc },
862 // { "PremoveProc", PremoveProc },
863 // { "QuietPlayProc", QuietPlayProc },
864 // { "ShowThinkingProc", ShowThinkingProc },
865 // { "HideThinkingProc", HideThinkingProc },
866 // { "TestLegalityProc", TestLegalityProc },
867 { "SaveSettingsProc", SaveSettingsProc },
868 { "SaveOnExitProc", SaveOnExitProc },
869 // { "InfoProc", InfoProc },
870 // { "ManProc", ManProc },
871 // { "HintProc", HintProc },
872 // { "BookProc", BookProc },
873 { "AboutGameProc", AboutGameProc },
874 { "DebugProc", DebugProc },
875 { "NothingProc", NothingProc },
876 { "CommentPopDown", (XtActionProc) CommentPopDown },
877 { "EditCommentPopDown", (XtActionProc) EditCommentPopDown },
878 { "TagsPopDown", (XtActionProc) TagsPopDown },
879 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
880 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
881 // { "FileNamePopDown", (XtActionProc) FileNamePopDown },
882 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
883 { "GameListPopDown", (XtActionProc) GameListPopDown },
884 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
885 // { "HistoryPopDown", (XtActionProc) HistoryPopDown },
886 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
887 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
888 { "ShufflePopDown", (XtActionProc) ShufflePopDown },
889 { "EnginePopDown", (XtActionProc) EnginePopDown },
890 { "UciPopDown", (XtActionProc) UciPopDown },
891 { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
892 { "NewVariantPopDown", (XtActionProc) NewVariantPopDown },
893 { "SettingsPopDown", (XtActionProc) SettingsPopDown },
894 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
897 //char globalTranslations[] =
898 // ":<Key>R: ResignProc() \n \
899 // :<Key>r: ResetProc() \n \
900 // :<Key>g: LoadGameProc() \n \
901 // :<Key>N: LoadNextGameProc() \n \
902 // :<Key>P: LoadPrevGameProc() \n \
903 // :<Key>Q: QuitProc() \n \
904 // :<Key>F: ToEndProc() \n \
905 // :<Key>f: ForwardProc() \n \
906 // :<Key>B: ToStartProc() \n \
907 // :<Key>b: BackwardProc() \n \
908 // :<Key>p: PauseProc() \n \
909 // :<Key>d: DrawProc() \n \
910 // :<Key>t: CallFlagProc() \n \
911 // :<Key>i: Iconify() \n \
912 // :<Key>c: Iconify() \n \
913 // :<Key>v: FlipViewProc() \n \
914 // <KeyDown>Control_L: BackwardProc() \n \
915 // <KeyUp>Control_L: ForwardProc() \n \
916 // <KeyDown>Control_R: BackwardProc() \n \
917 // <KeyUp>Control_R: ForwardProc() \n \
918 // Shift<Key>1: AskQuestionProc(\"Direct command\",\
919 // \"Send to chess program:\",,1) \n \
920 // Shift<Key>2: AskQuestionProc(\"Direct command\",\
921 // \"Send to second chess program:\",,2) \n";
923 //char boardTranslations[] =
924 // "<Btn1Down>: HandleUserMove() \n \
925 // <Btn1Up>: HandleUserMove() \n \
926 // <Btn1Motion>: AnimateUserMove() \n \
927 // <Btn3Motion>: HandlePV() \n \
928 // <Btn3Up>: UnLoadPV() \n \
929 // Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
930 // PieceMenuPopup(menuB) \n \
931 // Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
932 // PieceMenuPopup(menuW) \n \
933 // Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
934 // PieceMenuPopup(menuW) \n \
935 // Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
936 // PieceMenuPopup(menuB) \n";
938 //char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
939 //char blackTranslations[] = "<BtnDown>: BlackClock()\n";
941 char ICSInputTranslations[] =
942 "<Key>Return: EnterKeyProc() \n";
944 String xboardResources[] = {
945 // "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
946 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
947 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
951 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
952 "magenta", "cyan", "white" };
956 TextColors textColors[(int)NColorClasses];
958 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
960 parse_color(str, which)
964 char *p, buf[100], *d;
967 if (strlen(str) > 99) /* watch bounds on buf */
972 for (i=0; i<which; ++i) {
979 /* Could be looking at something like:
981 .. in which case we want to stop on a comma also */
982 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
986 return -1; /* Use default for empty field */
989 if (which == 2 || isdigit(*p))
992 while (*p && isalpha(*p))
997 for (i=0; i<8; ++i) {
998 if (!StrCaseCmp(buf, cnames[i]))
999 return which? (i+40) : (i+30);
1001 if (!StrCaseCmp(buf, "default")) return -1;
1003 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1008 parse_cpair(cc, str)
1012 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1013 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1018 /* bg and attr are optional */
1019 textColors[(int)cc].bg = parse_color(str, 1);
1020 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1021 textColors[(int)cc].attr = 0;
1030 /* this should raise the board to the top */
1031 gtk_window_present(GTK_WINDOW(GUI_Window));
1035 //---------------------------------------------------------------------------------------------------------
1036 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1039 #define CW_USEDEFAULT (1<<31)
1040 #define ICS_TEXT_MENU_SIZE 90
1041 #define DEBUG_FILE "xboard.debug"
1042 #define SetCurrentDirectory chdir
1043 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1047 // these two must some day move to frontend.h, when they are implemented
1048 Boolean GameListIsUp();
1050 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1053 // front-end part of option handling
1055 // [HGM] This platform-dependent table provides the location for storing the color info
1056 extern char *crWhite, * crBlack;
1060 &appData.whitePieceColor,
1061 &appData.blackPieceColor,
1062 &appData.lightSquareColor,
1063 &appData.darkSquareColor,
1064 &appData.highlightSquareColor,
1065 &appData.premoveHighlightColor,
1066 &appData.lowTimeWarningColor,
1078 ParseFont(char *name, int number)
1079 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1081 case 0: // CLOCK_FONT
1082 appData.clockFont = strdup(name);
1084 case 1: // MESSAGE_FONT
1085 appData.font = strdup(name);
1087 case 2: // COORD_FONT
1088 appData.coordFont = strdup(name);
1097 { // only 2 fonts currently
1098 appData.clockFont = CLOCK_FONT_NAME;
1099 appData.coordFont = COORD_FONT_NAME;
1100 appData.font = DEFAULT_FONT_NAME;
1105 { // no-op, until we identify the code for this already in XBoard and move it here
1109 ParseColor(int n, char *name)
1110 { // in XBoard, just copy the color-name string
1111 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1115 ParseTextAttribs(ColorClass cc, char *s)
1117 (&appData.colorShout)[cc] = strdup(s);
1121 ParseBoardSize(void *addr, char *name)
1123 appData.boardSize = strdup(name);
1128 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1132 SetCommPortDefaults()
1133 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1136 // [HGM] args: these three cases taken out to stay in front-end
1138 SaveFontArg(FILE *f, ArgDescriptor *ad)
1141 switch((int)ad->argLoc) {
1142 case 0: // CLOCK_FONT
1143 name = appData.clockFont;
1145 case 1: // MESSAGE_FONT
1146 name = appData.font;
1148 case 2: // COORD_FONT
1149 name = appData.coordFont;
1154 // Do not save fonts for now, as the saved font would be board-size specific
1155 // and not suitable for a re-start at another board size
1156 // fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, name);
1161 { // nothing to do, as the sounds are at all times represented by their text-string names already
1165 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1166 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1167 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)ad->argLoc]);
1171 SaveColor(FILE *f, ArgDescriptor *ad)
1172 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1173 if(colorVariable[(int)ad->argLoc])
1174 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)ad->argLoc]);
1178 SaveBoardSize(FILE *f, char *name, void *addr)
1179 { // wrapper to shield back-end from BoardSize & sizeInfo
1180 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1184 ParseCommPortSettings(char *s)
1185 { // no such option in XBoard (yet)
1188 extern Widget engineOutputShell;
1189 extern Widget tagsShell, editTagsShell;
1191 GetActualPlacement(Widget wg, WindowPlacement *wp)
1201 XtSetArg(args[i], XtNx, &x); i++;
1202 XtSetArg(args[i], XtNy, &y); i++;
1203 XtSetArg(args[i], XtNwidth, &w); i++;
1204 XtSetArg(args[i], XtNheight, &h); i++;
1205 XtGetValues(wg, args, i);
1214 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1215 // In XBoard this will have to wait until awareness of window parameters is implemented
1217 // GetActualPlacement(shellWidget, &wpMain);
1218 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1219 if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1220 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1221 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1222 if(commentShell) GetActualPlacement(commentShell, &wpComment);
1223 else GetActualPlacement(editShell, &wpComment);
1224 if(tagsShell) GetActualPlacement(tagsShell, &wpTags);
1225 else GetActualPlacement(editTagsShell, &wpTags);
1229 PrintCommPortSettings(FILE *f, char *name)
1230 { // This option does not exist in XBoard
1234 MySearchPath(char *installDir, char *name, char *fullname)
1235 { // just append installDir and name. Perhaps ExpandPath should be used here?
1236 name = ExpandPathName(name);
1237 if(name && name[0] == '/') strcpy(fullname, name); else {
1238 sprintf(fullname, "%s%c%s", installDir, '/', name);
1244 MyGetFullPathName(char *name, char *fullname)
1245 { // should use ExpandPath?
1246 name = ExpandPathName(name);
1247 strcpy(fullname, name);
1252 EnsureOnScreen(int *x, int *y, int minX, int minY)
1259 { // [HGM] args: allows testing if main window is realized from back-end
1260 return xBoardWindow != 0;
1264 PopUpStartupDialog()
1265 { // start menu not implemented in XBoard
1268 ConvertToLine(int argc, char **argv)
1270 static char line[128*1024], buf[1024];
1274 for(i=1; i<argc; i++) {
1275 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1276 && argv[i][0] != '{' )
1277 sprintf(buf, "{%s} ", argv[i]);
1278 else sprintf(buf, "%s ", argv[i]);
1281 line[strlen(line)-1] = NULLCHAR;
1285 //--------------------------------------------------------------------------------------------
1288 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1291 #define BoardSize int
1292 void InitDrawingSizes(BoardSize boardSize, int flags)
1293 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1294 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1296 XtGeometryResult gres;
1299 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1300 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1302 timerWidth = (boardWidth - sep) / 2;
1304 if (appData.titleInWindow)
1309 w = boardWidth - 2*bor;
1313 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1317 if(!formWidget) return;
1320 * Inhibit shell resizing.
1323 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1326 for(i=0; i<4; i++) {
1328 for(p=0; p<=(int)WhiteKing; p++)
1329 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1330 if(gameInfo.variant == VariantShogi) {
1331 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1332 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1333 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1334 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1335 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1338 if(gameInfo.variant == VariantGothic) {
1339 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1343 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1344 for(p=0; p<=(int)WhiteKing; p++)
1345 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1346 if(gameInfo.variant == VariantShogi) {
1347 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1348 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1349 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1350 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1351 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1354 if(gameInfo.variant == VariantGothic) {
1355 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1361 for(i=0; i<2; i++) {
1363 for(p=0; p<=(int)WhiteKing; p++)
1364 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1365 if(gameInfo.variant == VariantShogi) {
1366 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1367 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1368 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1369 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1370 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1373 if(gameInfo.variant == VariantGothic) {
1374 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1385 void EscapeExpand(char *p, char *q)
1386 { // [HGM] initstring: routine to shape up string arguments
1387 while(*p++ = *q++) if(p[-1] == '\\')
1389 case 'n': p[-1] = '\n'; break;
1390 case 'r': p[-1] = '\r'; break;
1391 case 't': p[-1] = '\t'; break;
1392 case '\\': p[-1] = '\\'; break;
1393 case 0: *p = 0; return;
1394 default: p[-1] = q[-1]; break;
1403 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1404 XSetWindowAttributes window_attributes;
1406 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1407 XrmValue vFrom, vTo;
1408 XtGeometryResult gres;
1411 int forceMono = False;
1413 srandom(time(0)); // [HGM] book: make random truly random
1415 setbuf(stdout, NULL);
1416 setbuf(stderr, NULL);
1419 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1420 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1424 programName = strrchr(argv[0], '/');
1425 if (programName == NULL)
1426 programName = argv[0];
1431 XtSetLanguageProc(NULL, NULL, NULL);
1432 bindtextdomain(PACKAGE, LOCALEDIR);
1433 textdomain(PACKAGE);
1437 gtk_init (&argc, &argv);
1439 /* parse glade file to build widgets */
1441 builder = gtk_builder_new ();
1442 gtk_builder_add_from_file (builder, "gtk-interface.xml", NULL);
1444 /* test if everything worked ok */
1446 GUI_Window = GTK_WIDGET (gtk_builder_get_object (builder, "MainWindow"));
1447 if(!GUI_Window) printf("Error: gtk_builder didn't work!\n");
1449 GUI_Aspect = GTK_WIDGET (gtk_builder_get_object (builder, "Aspectframe"));
1450 if(!GUI_Aspect) printf("Error: gtk_builder didn't work!\n");
1452 GUI_History = GTK_WIDGET (gtk_builder_get_object (builder, "MoveHistory"));
1453 if(!GUI_History) printf("Error: gtk_builder didn't work!\n");
1455 GUI_Menubar = GTK_WIDGET (gtk_builder_get_object (builder, "MenuBar"));
1456 if(!GUI_Menubar) printf("Error: gtk_builder didn't work!\n");
1457 GUI_Timer = GTK_WIDGET (gtk_builder_get_object (builder, "Timer"));
1458 if(!GUI_Timer) printf("Error: gtk_builder didn't work!\n");
1459 GUI_Buttonbar = GTK_WIDGET (gtk_builder_get_object (builder, "ButtonBar"));
1460 if(!GUI_Buttonbar) printf("Error: gtk_builder didn't work!\n");
1461 GUI_Board = GTK_WIDGET (gtk_builder_get_object (builder, "Board"));
1462 if(!GUI_Board) printf("Error: gtk_builder didn't work!\n");
1464 GUI_Whiteclock = GTK_WIDGET (gtk_builder_get_object (builder, "WhiteClock"));
1465 if(!GUI_Whiteclock) printf("Error: gtk_builder didn't work!\n");
1467 GUI_Blackclock = GTK_WIDGET (gtk_builder_get_object (builder, "BlackClock"));
1468 if(!GUI_Blackclock) printf("Error: gtk_builder didn't work!\n");
1470 LIST_MoveHistory = GTK_LIST_STORE (gtk_builder_get_object (builder, "MoveHistoryStore"));
1471 if(!LIST_MoveHistory) printf("Error: gtk_builder didn't work!\n");
1473 /* EditTags window */
1474 GUI_EditTags = GTK_WIDGET (gtk_builder_get_object (builder, "EditTags"));
1475 if(!GUI_EditTags) printf("Error: gtk_builder didn't work!\n");
1477 GUI_EditTagsTextArea = GTK_WIDGET (gtk_builder_get_object (builder, "EditTagsTextArea"));
1478 if(!GUI_EditTagsTextArea) printf("Error: gtk_builder didn't work!\n");
1481 gtk_builder_connect_signals (builder, NULL);
1483 // don't unref the builder, since we use it to get references to widgets
1484 // g_object_unref (G_OBJECT (builder));
1486 /* end parse glade file */
1488 appData.boardSize = "";
1489 InitAppData(ConvertToLine(argc, argv));
1492 if (p == NULL) p = "/tmp";
1493 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1494 gameCopyFilename = (char*) malloc(i);
1495 gamePasteFilename = (char*) malloc(i);
1496 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1497 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1499 // XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1500 // clientResources, XtNumber(clientResources),
1503 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1504 static char buf[MSG_SIZ];
1505 EscapeExpand(buf, appData.initString);
1506 appData.initString = strdup(buf);
1507 EscapeExpand(buf, appData.secondInitString);
1508 appData.secondInitString = strdup(buf);
1509 EscapeExpand(buf, appData.firstComputerString);
1510 appData.firstComputerString = strdup(buf);
1511 EscapeExpand(buf, appData.secondComputerString);
1512 appData.secondComputerString = strdup(buf);
1515 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1518 if (chdir(chessDir) != 0) {
1519 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1525 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1526 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1527 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1528 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1531 setbuf(debugFP, NULL);
1536 /* This feature does not work; animation needs a rewrite */
1537 appData.highlightDragging = FALSE;
1541 gameInfo.variant = StringToVariant(appData.variant);
1542 InitPosition(FALSE);
1547 clockFontPxlSize = 20;
1548 coordFontPxlSize = 20;
1554 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1555 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1558 * Determine what fonts to use.
1560 // appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1561 // clockFontID = XLoadFont(xDisplay, appData.clockFont);
1562 // clockFontStruct = XQueryFont(xDisplay, clockFontID);
1563 // appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1564 // coordFontID = XLoadFont(xDisplay, appData.coordFont);
1565 // coordFontStruct = XQueryFont(xDisplay, coordFontID);
1566 // appData.font = FindFont(appData.font, fontPxlSize);
1567 // countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
1568 // countFontStruct = XQueryFont(xDisplay, countFontID);
1569 // appData.font = FindFont(appData.font, fontPxlSize);
1571 // xdb = XtDatabase(xDisplay);
1572 // XrmPutStringResource(&xdb, "*font", appData.font);
1575 * Detect if there are not enough colors available and adapt.
1577 // if (DefaultDepth(xDisplay, xScreen) <= 2) {
1578 // appData.monoMode = True;
1581 if (!appData.monoMode) {
1582 vFrom.addr = (caddr_t) appData.lightSquareColor;
1583 vFrom.size = strlen(appData.lightSquareColor);
1584 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1585 if (vTo.addr == NULL) {
1586 appData.monoMode = True;
1589 lightSquareColor = *(Pixel *) vTo.addr;
1592 if (!appData.monoMode) {
1593 vFrom.addr = (caddr_t) appData.darkSquareColor;
1594 vFrom.size = strlen(appData.darkSquareColor);
1595 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1596 if (vTo.addr == NULL) {
1597 appData.monoMode = True;
1600 darkSquareColor = *(Pixel *) vTo.addr;
1603 if (!appData.monoMode) {
1604 vFrom.addr = (caddr_t) appData.whitePieceColor;
1605 vFrom.size = strlen(appData.whitePieceColor);
1606 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1607 if (vTo.addr == NULL) {
1608 appData.monoMode = True;
1611 whitePieceColor = *(Pixel *) vTo.addr;
1614 if (!appData.monoMode) {
1615 vFrom.addr = (caddr_t) appData.blackPieceColor;
1616 vFrom.size = strlen(appData.blackPieceColor);
1617 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1618 if (vTo.addr == NULL) {
1619 appData.monoMode = True;
1622 blackPieceColor = *(Pixel *) vTo.addr;
1626 if (!appData.monoMode) {
1627 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1628 vFrom.size = strlen(appData.highlightSquareColor);
1629 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1630 if (vTo.addr == NULL) {
1631 appData.monoMode = True;
1634 highlightSquareColor = *(Pixel *) vTo.addr;
1638 if (!appData.monoMode) {
1639 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1640 vFrom.size = strlen(appData.premoveHighlightColor);
1641 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1642 if (vTo.addr == NULL) {
1643 appData.monoMode = True;
1646 premoveHighlightColor = *(Pixel *) vTo.addr;
1651 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1654 if (appData.bitmapDirectory == NULL ||
1655 appData.bitmapDirectory[0] == NULLCHAR)
1656 appData.bitmapDirectory = DEF_BITMAP_DIR;
1659 if (appData.lowTimeWarning && !appData.monoMode) {
1660 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1661 vFrom.size = strlen(appData.lowTimeWarningColor);
1662 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1663 if (vTo.addr == NULL)
1664 appData.monoMode = True;
1666 lowTimeWarningColor = *(Pixel *) vTo.addr;
1669 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1670 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1671 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1672 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1673 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1674 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1675 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1676 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1677 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1678 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1680 if (appData.colorize) {
1682 _("%s: can't parse color names; disabling colorization\n"),
1685 appData.colorize = FALSE;
1687 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
1688 textColors[ColorNone].attr = 0;
1690 // XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1696 layoutName = "tinyLayout";
1697 } else if (smallLayout) {
1698 layoutName = "smallLayout";
1700 layoutName = "normalLayout";
1703 if (appData.titleInWindow) {
1704 /* todo check what this appdata does */
1707 if (appData.showButtonBar) {
1708 /* TODO hide button bar if requested */
1712 if (appData.titleInWindow)
1717 if (appData.showButtonBar)
1724 if (appData.showButtonBar)
1734 /* set some checkboxes in the menu according to appData */
1736 if (appData.alwaysPromoteToQueen)
1737 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Always Queen")),TRUE);
1739 if (appData.animateDragging)
1740 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Animate Dragging")),TRUE);
1742 if (appData.animate)
1743 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Animate Moving")),TRUE);
1745 if (appData.autoComment)
1746 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Comment")),TRUE);
1748 if (appData.autoCallFlag)
1749 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Flag")),TRUE);
1751 if (appData.autoFlipView)
1752 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Flip View")),TRUE);
1754 if (appData.autoObserve)
1755 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Observe")),TRUE);
1757 if (appData.autoRaiseBoard)
1758 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Raise Board")),TRUE);
1760 if (appData.autoSaveGames)
1761 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Save")),TRUE);
1763 if (appData.saveGameFile[0] != NULLCHAR)
1765 /* Can't turn this off from menu */
1766 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Save")),TRUE);
1767 gtk_action_set_sensitive(GTK_ACTION (gtk_builder_get_object (builder, "menuOptions.Auto Save")),FALSE);
1770 if (appData.blindfold)
1771 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Blindfold")),TRUE);
1773 if (appData.flashCount > 0)
1774 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Flash Moves")),TRUE);
1776 if (appData.getMoveList)
1777 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Get Move List")),TRUE);
1780 if (appData.highlightDragging)
1781 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Highlight Dragging")),TRUE);
1784 if (appData.highlightLastMove)
1785 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Highlight Last Move")),TRUE);
1787 if (appData.icsAlarm)
1788 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.ICS Alarm")),TRUE);
1790 if (appData.ringBellAfterMoves)
1791 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Move Sound")),TRUE);
1793 if (appData.oldSaveStyle)
1794 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Old Save Style")),TRUE);
1796 if (appData.periodicUpdates)
1797 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Periodic Updates")),TRUE);
1799 if (appData.ponderNextMove)
1800 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Ponder Next Move")),TRUE);
1802 if (appData.popupExitMessage)
1803 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Popup Exit Message")),TRUE);
1805 if (appData.popupMoveErrors)
1806 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Popup Move Errors")),TRUE);
1808 if (appData.premove)
1809 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Premove")),TRUE);
1811 if (appData.quietPlay)
1812 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Quit Play")),TRUE);
1814 if (appData.showCoords)
1815 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Show Coords")),TRUE);
1817 if (appData.showThinking)
1818 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Hide Thinking")),TRUE);
1820 if (appData.testLegality)
1821 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Test Legality")),TRUE);
1824 // if (saveSettingsOnExit) {
1825 // XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
1830 /* end setting check boxes */
1832 /* load square colors */
1833 SVGLightSquare = load_pixbuf("svg/LightSquare.svg",squareSize);
1834 SVGDarkSquare = load_pixbuf("svg/DarkSquare.svg",squareSize);
1835 SVGNeutralSquare = load_pixbuf("svg/NeutralSquare.svg",squareSize);
1837 /* use two icons to indicate if it is white's or black's turn */
1838 WhiteIcon = load_pixbuf("svg/icon_white.svg",0);
1839 BlackIcon = load_pixbuf("svg/icon_black.svg",0);
1840 WindowIcon = WhiteIcon;
1841 gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
1844 /* realize window */
1845 gtk_widget_show (GUI_Window);
1847 /* recalc boardsize */
1852 if (appData.animate || appData.animateDragging)
1857 if (errorExitStatus == -1) {
1858 if (appData.icsActive) {
1859 /* We now wait until we see "login:" from the ICS before
1860 sending the logon script (problems with timestamp otherwise) */
1861 /*ICSInitScript();*/
1862 if (appData.icsInputBox) ICSInputBoxPopUp();
1866 signal(SIGWINCH, TermSizeSigHandler);
1868 signal(SIGINT, IntSigHandler);
1869 signal(SIGTERM, IntSigHandler);
1870 if (*appData.cmailGameName != NULLCHAR) {
1871 signal(SIGUSR1, CmailSigHandler);
1874 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1878 * Create a cursor for the board widget.
1879 * (This needs to be called after the window has been created to have access to board-window)
1882 BoardCursor = gdk_cursor_new(GDK_HAND2);
1883 gdk_window_set_cursor(GUI_Board->window, BoardCursor);
1884 gdk_cursor_destroy(BoardCursor);
1889 if (appData.debugMode) fclose(debugFP); // [DM] debug
1896 if (appData.icsActive && oldICSInteractionTitle != NULL) {
1897 DisplayIcsInteractionTitle(oldICSInteractionTitle);
1899 if (saveSettingsOnExit) SaveSettings(settingsFileName);
1900 unlink(gameCopyFilename);
1901 unlink(gamePasteFilename);
1904 RETSIGTYPE TermSizeSigHandler(int sig)
1917 CmailSigHandler(sig)
1923 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1925 /* Activate call-back function CmailSigHandlerCallBack() */
1926 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1928 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1932 CmailSigHandlerCallBack(isr, closure, message, count, error)
1940 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1942 /**** end signal code ****/
1952 f = fopen(appData.icsLogon, "r");
1958 strcat(buf, appData.icsLogon);
1959 f = fopen(buf, "r");
1963 ProcessICSInitScript(f);
1970 EditCommentPopDown();
1980 if (!menuBarWidget) return;
1981 w = XtNameToWidget(menuBarWidget, "menuStep.Revert");
1983 DisplayError("menuStep.Revert", 0);
1985 XtSetSensitive(w, !grey);
1990 SetMenuEnables(enab)
1995 if (!builder) return;
1996 while (enab->name != NULL) {
1997 o = gtk_builder_get_object(builder, enab->name);
1998 if(GTK_IS_WIDGET(o))
1999 gtk_widget_set_sensitive(GTK_WIDGET (o),enab->value);
2002 if(GTK_IS_ACTION(o))
2003 gtk_action_set_sensitive(GTK_ACTION (o),enab->value);
2005 DisplayError(enab->name, 0);
2013 SetMenuEnables(icsEnables);
2016 if (appData.zippyPlay && !appData.noChessProgram) /* [DM] icsEngineAnalyze */
2017 {}; // XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2024 SetMenuEnables(ncpEnables);
2030 SetMenuEnables(gnuEnables);
2036 SetMenuEnables(cmailEnables);
2042 SetMenuEnables(trainingOnEnables);
2043 if (appData.showButtonBar) {
2044 // XtSetSensitive(buttonBarWidget, False);
2050 SetTrainingModeOff()
2052 SetMenuEnables(trainingOffEnables);
2053 if (appData.showButtonBar) {
2054 // XtSetSensitive(buttonBarWidget, True);
2059 SetUserThinkingEnables()
2061 if (appData.noChessProgram) return;
2062 SetMenuEnables(userThinkingEnables);
2066 SetMachineThinkingEnables()
2068 if (appData.noChessProgram) return;
2069 SetMenuEnables(machineThinkingEnables);
2071 case MachinePlaysBlack:
2072 case MachinePlaysWhite:
2073 case TwoMachinesPlay:
2074 // XtSetSensitive(XtNameToWidget(menuBarWidget,
2075 // ModeToWidgetName(gameMode)), True);
2082 #define Abs(n) ((n)<0 ? -(n) : (n))
2085 * Find a font that matches "pattern" that is as close as
2086 * possible to the targetPxlSize. Prefer fonts that are k
2087 * pixels smaller to fonts that are k pixels larger. The
2088 * pattern must be in the X Consortium standard format,
2089 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2090 * The return value should be freed with XtFree when no
2093 char *FindFont(pattern, targetPxlSize)
2097 char **fonts, *p, *best, *scalable, *scalableTail;
2098 int i, j, nfonts, minerr, err, pxlSize;
2101 char **missing_list;
2103 char *def_string, *base_fnt_lst, strInt[3];
2105 XFontStruct **fnt_list;
2107 base_fnt_lst = calloc(1, strlen(pattern) + 3);
2108 sprintf(strInt, "%d", targetPxlSize);
2109 p = strstr(pattern, "--");
2110 strncpy(base_fnt_lst, pattern, p - pattern + 2);
2111 strcat(base_fnt_lst, strInt);
2112 strcat(base_fnt_lst, strchr(p + 2, '-'));
2114 if ((fntSet = XCreateFontSet(xDisplay,
2118 &def_string)) == NULL) {
2120 fprintf(stderr, _("Unable to create font set.\n"));
2124 nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
2126 // fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2127 // if (nfonts < 1) {
2128 // fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2129 // programName, pattern);
2137 for (i=0; i<nfonts; i++) {
2140 if (*p != '-') continue;
2142 if (*p == NULLCHAR) break;
2143 if (*p++ == '-') j++;
2145 if (j < 7) continue;
2148 scalable = fonts[i];
2151 err = pxlSize - targetPxlSize;
2152 if (Abs(err) < Abs(minerr) ||
2153 (minerr > 0 && err < 0 && -err == minerr)) {
2159 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2160 /* If the error is too big and there is a scalable font,
2161 use the scalable font. */
2162 int headlen = scalableTail - scalable;
2163 p = (char *) XtMalloc(strlen(scalable) + 10);
2164 while (isdigit(*scalableTail)) scalableTail++;
2165 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2167 p = (char *) XtMalloc(strlen(best) + 1);
2170 if (appData.debugMode) {
2171 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2172 pattern, targetPxlSize, p);
2175 if (missing_count > 0)
2176 XFreeStringList(missing_list);
2177 // XFreeFontSet(xDisplay, fntSet);
2179 XFreeFontNames(fonts);
2186 /* GCs are not needed anymore for GTK just left them in here for the moment, since there is a lot of X-code still around that's wants them*/
2195 for(i=0;i<MAXPIECES;i++)
2199 g_free(SVGpieces[i]);
2206 SVGLightSquare = load_pixbuf("svg/LightSquare.svg",squareSize);
2207 SVGDarkSquare = load_pixbuf("svg/DarkSquare.svg",squareSize);
2208 SVGNeutralSquare = load_pixbuf("svg/NeutralSquare.svg",squareSize);
2211 /* get some defaults going */
2212 for(i=WhitePawn; i<DemotePiece+1; i++)
2213 SVGpieces[i] = load_pixbuf("svg/NeutralSquare.svg",squareSize);
2215 SVGpieces[WhitePawn] = load_pixbuf("svg/WhitePawn.svg",squareSize);
2216 SVGpieces[WhiteKnight] = load_pixbuf("svg/WhiteKnight.svg",squareSize);
2217 SVGpieces[WhiteBishop] = load_pixbuf("svg/WhiteBishop.svg",squareSize);
2218 SVGpieces[WhiteRook] = load_pixbuf("svg/WhiteRook.svg",squareSize);
2219 SVGpieces[WhiteQueen] = load_pixbuf("svg/WhiteQueen.svg",squareSize);
2220 SVGpieces[WhiteKing] = load_pixbuf("svg/WhiteKing.svg",squareSize);
2222 SVGpieces[BlackPawn] = load_pixbuf("svg/BlackPawn.svg",squareSize);
2223 SVGpieces[BlackKnight] = load_pixbuf("svg/BlackKnight.svg",squareSize);
2224 SVGpieces[BlackBishop] = load_pixbuf("svg/BlackBishop.svg",squareSize);
2225 SVGpieces[BlackRook] = load_pixbuf("svg/BlackRook.svg",squareSize);
2226 SVGpieces[BlackQueen] = load_pixbuf("svg/BlackQueen.svg",squareSize);
2227 SVGpieces[BlackKing] = load_pixbuf("svg/BlackKing.svg",squareSize);
2233 static void MenuBarSelect(w, addr, index)
2238 XtActionProc proc = (XtActionProc) addr;
2240 (proc)(NULL, NULL, NULL, NULL);
2243 void CreateMenuBarPopup(parent, name, mb)
2253 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2256 XtSetArg(args[j], XtNleftMargin, 20); j++;
2257 XtSetArg(args[j], XtNrightMargin, 20); j++;
2259 while (mi->string != NULL) {
2260 if (strcmp(mi->string, "----") == 0) {
2261 entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
2264 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
2265 entry = XtCreateManagedWidget(mi->string, smeBSBObjectClass,
2267 XtAddCallback(entry, XtNcallback,
2268 (XtCallbackProc) MenuBarSelect,
2269 (caddr_t) mi->proc);
2275 Widget CreateMenuBar(mb)
2279 Widget anchor, menuBar;
2281 char menuName[MSG_SIZ];
2284 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2285 XtSetArg(args[j], XtNvSpace, 0); j++;
2286 XtSetArg(args[j], XtNborderWidth, 0); j++;
2287 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
2288 formWidget, args, j);
2290 while (mb->name != NULL) {
2291 strcpy(menuName, "menu");
2292 strcat(menuName, mb->name);
2294 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
2297 shortName[0] = _(mb->name)[0];
2298 shortName[1] = NULLCHAR;
2299 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
2302 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
2305 XtSetArg(args[j], XtNborderWidth, 0); j++;
2306 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
2308 CreateMenuBarPopup(menuBar, menuName, mb);
2316 CreatePieceMenu(name, color)
2323 ChessSquare selection;
2325 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2326 boardWidget, args, 0);
2328 for (i = 0; i < PIECE_MENU_SIZE; i++) {
2329 String item = pieceMenuStrings[color][i];
2331 if (strcmp(item, "----") == 0) {
2332 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2335 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2336 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2338 selection = pieceMenuTranslation[color][i];
2339 XtAddCallback(entry, XtNcallback,
2340 (XtCallbackProc) PieceMenuSelect,
2341 (caddr_t) selection);
2342 if (selection == WhitePawn || selection == BlackPawn) {
2343 XtSetArg(args[0], XtNpopupOnEntry, entry);
2344 XtSetValues(menu, args, 1);
2357 ChessSquare selection;
2359 // whitePieceMenu = CreatePieceMenu("menuW", 0);
2360 // blackPieceMenu = CreatePieceMenu("menuB", 1);
2362 // XtRegisterGrabAction(PieceMenuPopup, True,
2363 // (unsigned)(ButtonPressMask|ButtonReleaseMask),
2364 // GrabModeAsync, GrabModeAsync);
2366 // XtSetArg(args[0], XtNlabel, _("Drop"));
2367 // dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
2368 // boardWidget, args, 1);
2369 // for (i = 0; i < DROP_MENU_SIZE; i++) {
2370 // String item = dropMenuStrings[i];
2372 // if (strcmp(item, "----") == 0) {
2373 // entry = XtCreateManagedWidget(item, smeLineObjectClass,
2374 // dropMenu, NULL, 0);
2376 // XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2377 // entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2378 // dropMenu, args, 1);
2379 // selection = dropMenuTranslation[i];
2380 // XtAddCallback(entry, XtNcallback,
2381 // (XtCallbackProc) DropMenuSelect,
2382 // (caddr_t) selection);
2387 void SetupDropMenu()
2395 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2396 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2397 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2398 dmEnables[i].piece);
2399 XtSetSensitive(entry, p != NULL || !appData.testLegality
2400 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2401 && !appData.icsActive));
2403 while (p && *p++ == dmEnables[i].piece) count++;
2404 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2406 XtSetArg(args[j], XtNlabel, label); j++;
2407 XtSetValues(entry, args, j);
2411 void PieceMenuPopup(w, event, params, num_params)
2415 Cardinal *num_params;
2419 if (event->type != ButtonRelease) UnLoadPV(); // [HGM] pv
2420 if (event->type != ButtonPress) return;
2421 if (errorUp) ErrorPopDown();
2425 whichMenu = params[0];
2428 if(!appData.icsEngineAnalyze) return;
2429 case IcsPlayingWhite:
2430 case IcsPlayingBlack:
2431 if(!appData.zippyPlay) goto noZip;
2434 case MachinePlaysWhite:
2435 case MachinePlaysBlack:
2436 case TwoMachinesPlay: // [HGM] pv: use for showing PV
2437 if (!appData.dropMenu) {
2438 LoadPV(event->xbutton.x, event->xbutton.y);
2441 if(gameMode == TwoMachinesPlay || gameMode == AnalyzeMode ||
2442 gameMode == AnalyzeFile || gameMode == IcsObserving) return;
2445 if (!appData.dropMenu || appData.testLegality &&
2446 gameInfo.variant != VariantBughouse &&
2447 gameInfo.variant != VariantCrazyhouse) return;
2449 whichMenu = "menuD";
2455 if (((pmFromX = EventToSquare(event->xbutton.x, BOARD_WIDTH)) < 0) ||
2456 ((pmFromY = EventToSquare(event->xbutton.y, BOARD_HEIGHT)) < 0)) {
2457 pmFromX = pmFromY = -1;
2461 pmFromX = BOARD_WIDTH - 1 - pmFromX;
2463 pmFromY = BOARD_HEIGHT - 1 - pmFromY;
2465 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
2468 static void PieceMenuSelect(w, piece, junk)
2473 if (pmFromX < 0 || pmFromY < 0) return;
2474 EditPositionMenuEvent(piece, pmFromX, pmFromY);
2477 static void DropMenuSelect(w, piece, junk)
2482 if (pmFromX < 0 || pmFromY < 0) return;
2483 DropMenuEvent(piece, pmFromX, pmFromY);
2487 * If the user selects on a border boundary, return -1; if off the board,
2488 * return -2. Otherwise map the event coordinate to the square.
2490 int EventToSquare(x, limit)
2498 if ((x % (squareSize + lineGap)) >= squareSize)
2500 x /= (squareSize + lineGap);
2506 static void do_flash_delay(msec)
2512 static void drawHighlight(file, rank, line_type)
2513 int file, rank, line_type;
2518 if (lineGap == 0 || appData.blindfold) return;
2522 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
2523 (squareSize + lineGap);
2524 y = lineGap/2 + rank * (squareSize + lineGap);
2528 x = lineGap/2 + file * (squareSize + lineGap);
2529 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
2530 (squareSize + lineGap);
2534 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2536 /* draw the highlight */
2537 cairo_move_to (cr, x, y);
2538 cairo_rel_line_to (cr, 0,squareSize+lineGap);
2539 cairo_rel_line_to (cr, squareSize+lineGap,0);
2540 cairo_rel_line_to (cr, 0,-squareSize-lineGap);
2541 cairo_close_path (cr);
2543 cairo_set_line_width (cr, lineGap);
2546 /* TODO: use appdata colors */
2547 case LINE_TYPE_HIGHLIGHT:
2548 cairo_set_source_rgba (cr, 1, 1, 0, 1.0);
2551 cairo_set_source_rgba (cr, 1, 0, 0, 1.0);
2553 case LINE_TYPE_NORMAL:
2555 cairo_set_source_rgba (cr, 0, 1, 0, 1.0);
2566 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
2567 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
2570 SetHighlights(fromX, fromY, toX, toY)
2571 int fromX, fromY, toX, toY;
2573 if (hi1X != fromX || hi1Y != fromY)
2575 if (hi1X >= 0 && hi1Y >= 0)
2577 drawHighlight(hi1X, hi1Y, LINE_TYPE_NORMAL);
2579 if (fromX >= 0 && fromY >= 0)
2581 drawHighlight(fromX, fromY, LINE_TYPE_HIGHLIGHT);
2584 if (hi2X != toX || hi2Y != toY)
2586 if (hi2X >= 0 && hi2Y >= 0)
2588 drawHighlight(hi2X, hi2Y, LINE_TYPE_NORMAL);
2590 if (toX >= 0 && toY >= 0)
2592 drawHighlight(toX, toY, LINE_TYPE_HIGHLIGHT);
2606 SetHighlights(-1, -1, -1, -1);
2611 SetPremoveHighlights(fromX, fromY, toX, toY)
2612 int fromX, fromY, toX, toY;
2614 if (pm1X != fromX || pm1Y != fromY)
2616 if (pm1X >= 0 && pm1Y >= 0)
2618 drawHighlight(pm1X, pm1Y, LINE_TYPE_NORMAL);
2620 if (fromX >= 0 && fromY >= 0)
2622 drawHighlight(fromX, fromY, LINE_TYPE_PRE);
2625 if (pm2X != toX || pm2Y != toY)
2627 if (pm2X >= 0 && pm2Y >= 0)
2629 drawHighlight(pm2X, pm2Y, LINE_TYPE_NORMAL);
2631 if (toX >= 0 && toY >= 0)
2633 drawHighlight(toX, toY, LINE_TYPE_PRE);
2646 ClearPremoveHighlights()
2648 SetPremoveHighlights(-1, -1, -1, -1);
2651 static void BlankSquare(x, y, color, piece, dest)
2664 pb = SVGLightSquare;
2666 case 2: /* neutral */
2668 pb = SVGNeutralSquare;
2671 gdk_draw_pixbuf(GDK_WINDOW(GUI_Board->window),NULL,pb,0,0,x,y,-1,-1, GDK_RGB_DITHER_NORMAL, 0, 0);
2675 static void DrawPiece(piece, square_color, x, y, dest)
2677 int square_color, x, y;
2680 /* redraw background, since piece might be transparent in some areas */
2681 BlankSquare(x,y,square_color,piece,dest);
2684 gdk_draw_pixbuf(GDK_WINDOW(GUI_Board->window),NULL,
2685 GDK_PIXBUF(SVGpieces[piece]),0,0,x,y,-1,-1,
2686 GDK_RGB_DITHER_NORMAL, 0, 0);
2690 /* [HR] determine square color depending on chess variant. */
2691 static int SquareColor(row, column)
2696 if (gameInfo.variant == VariantXiangqi) {
2697 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
2699 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
2701 } else if (row <= 4) {
2707 square_color = ((column + row) % 2) == 1;
2710 /* [hgm] holdings: next line makes all holdings squares light */
2711 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
2713 return square_color;
2716 void DrawSquare(row, column, piece, do_flash)
2717 int row, column, do_flash;
2720 int square_color, x, y;
2725 /* Calculate delay in milliseconds (2-delays per complete flash) */
2726 flash_delay = 500 / appData.flashRate;
2728 /* calculate x and y coordinates from row and column */
2731 x = lineGap + ((BOARD_WIDTH-1)-column) *
2732 (squareSize + lineGap);
2733 y = lineGap + row * (squareSize + lineGap);
2737 x = lineGap + column * (squareSize + lineGap);
2738 y = lineGap + ((BOARD_HEIGHT-1)-row) *
2739 (squareSize + lineGap);
2742 square_color = SquareColor(row, column);
2744 // [HGM] holdings: blank out area between board and holdings
2745 if ( column == BOARD_LEFT-1 || column == BOARD_RGHT
2746 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
2747 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) )
2749 BlankSquare(x, y, 2, EmptySquare, xBoardWindow);
2751 // [HGM] print piece counts next to holdings
2752 string[1] = NULLCHAR;
2755 cairo_text_extents_t extents;
2760 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2762 string[0] = '0' + piece;
2764 /* TODO this has to go into the font-selection */
2765 cairo_select_font_face (cr, "Sans",
2766 CAIRO_FONT_SLANT_NORMAL,
2767 CAIRO_FONT_WEIGHT_NORMAL);
2769 cairo_set_font_size (cr, 12.0);
2770 cairo_text_extents (cr, string, &extents);
2772 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) )
2774 xpos= x + squareSize - extents.width - 2;
2775 ypos= y + extents.y_bearing + 1;
2777 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1)
2780 ypos = y + extents.y_bearing + 1;
2783 /* TODO mono mode? */
2784 cairo_move_to (cr, xpos, ypos);
2785 cairo_text_path (cr, string);
2786 cairo_set_source_rgb (cr, 1.0, 1.0, 1);
2787 cairo_fill_preserve (cr);
2788 cairo_set_source_rgb (cr, 0, 0, 0);
2789 cairo_set_line_width (cr, 0.1);
2798 /* square on the board */
2799 if (piece == EmptySquare || appData.blindfold)
2801 BlankSquare(x, y, square_color, piece, xBoardWindow);
2805 if (do_flash && appData.flashCount > 0)
2807 for (i=0; i<appData.flashCount; ++i)
2810 DrawPiece(piece, square_color, x, y, xBoardWindow);
2811 do_flash_delay(flash_delay);
2813 BlankSquare(x, y, square_color, piece, xBoardWindow);
2814 do_flash_delay(flash_delay);
2817 DrawPiece(piece, square_color, x, y, xBoardWindow);
2821 /* show coordinates if necessary */
2822 if(appData.showCoords)
2824 cairo_text_extents_t extents;
2828 /* TODO this has to go into the font-selection */
2829 cairo_select_font_face (cr, "Sans",
2830 CAIRO_FONT_SLANT_NORMAL,
2831 CAIRO_FONT_WEIGHT_NORMAL);
2832 cairo_set_font_size (cr, 12.0);
2834 string[1] = NULLCHAR;
2837 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2839 if (row == (flipView ? BOARD_HEIGHT-1 : 0) &&
2840 column >= BOARD_LEFT && column < BOARD_RGHT)
2842 string[0] = 'a' + column - BOARD_LEFT;
2843 cairo_text_extents (cr, string, &extents);
2845 xpos = x + squareSize - extents.width - 2;
2846 ypos = y + squareSize - extents.height - extents.y_bearing - 1;
2848 if (appData.monoMode)
2855 cairo_move_to (cr, xpos, ypos);
2856 cairo_text_path (cr, string);
2857 cairo_set_source_rgb (cr, 0.0, 0.0, 0);
2858 cairo_fill_preserve (cr);
2859 cairo_set_source_rgb (cr, 0, 1.0, 0);
2860 cairo_set_line_width (cr, 0.1);
2863 if ( column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT))
2866 string[0] = ONE + row;
2867 cairo_text_extents (cr, string, &extents);
2870 ypos = y + extents.height + 1;
2872 if (appData.monoMode)
2879 cairo_move_to (cr, xpos, ypos);
2880 cairo_text_path (cr, string);
2881 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
2882 cairo_fill_preserve (cr);
2883 cairo_set_source_rgb (cr, 0, 0, 1.0);
2884 cairo_set_line_width (cr, 0.1);
2896 /* Returns 1 if there are "too many" differences between b1 and b2
2897 (i.e. more than 1 move was made) */
2898 static int too_many_diffs(b1, b2)
2904 for (i=0; i<BOARD_HEIGHT; ++i) {
2905 for (j=0; j<BOARD_WIDTH; ++j) {
2906 if (b1[i][j] != b2[i][j]) {
2907 if (++c > 4) /* Castling causes 4 diffs */
2916 /* Matrix describing castling maneuvers */
2917 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
2918 static int castling_matrix[4][5] = {
2919 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
2920 { 0, 7, 4, 5, 6 }, /* 0-0, white */
2921 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
2922 { 7, 7, 4, 5, 6 } /* 0-0, black */
2925 /* Checks whether castling occurred. If it did, *rrow and *rcol
2926 are set to the destination (row,col) of the rook that moved.
2928 Returns 1 if castling occurred, 0 if not.
2930 Note: Only handles a max of 1 castling move, so be sure
2931 to call too_many_diffs() first.
2933 static int check_castle_draw(newb, oldb, rrow, rcol)
2940 /* For each type of castling... */
2941 for (i=0; i<4; ++i) {
2942 r = castling_matrix[i];
2944 /* Check the 4 squares involved in the castling move */
2946 for (j=1; j<=4; ++j) {
2947 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
2954 /* All 4 changed, so it must be a castling move */
2963 static int damage[BOARD_RANKS][BOARD_FILES];
2966 * event handler for redrawing the board
2968 void DrawPosition( repaint, board)
2969 /*Boolean*/int repaint;
2973 static int lastFlipView = 0;
2974 static int lastBoardValid = 0;
2975 static Board lastBoard;
2978 if (board == NULL) {
2979 if (!lastBoardValid) return;
2982 if (!lastBoardValid || lastFlipView != flipView) {
2983 // XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
2984 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flip View"),
2989 * It would be simpler to clear the window with XClearWindow()
2990 * but this causes a very distracting flicker.
2993 if (!repaint && lastBoardValid && lastFlipView == flipView)
2995 /* If too much changes (begin observing new game, etc.), don't
2997 do_flash = too_many_diffs(board, lastBoard) ? 0 : 1;
2999 /* Special check for castling so we don't flash both the king
3000 and the rook (just flash the king). */
3003 if (check_castle_draw(board, lastBoard, &rrow, &rcol))
3005 /* Draw rook with NO flashing. King will be drawn flashing later */
3006 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
3007 lastBoard[rrow][rcol] = board[rrow][rcol];
3011 /* First pass -- Draw (newly) empty squares and repair damage.
3012 This prevents you from having a piece show up twice while it
3013 is flashing on its new square */
3014 for (i = 0; i < BOARD_HEIGHT; i++)
3015 for (j = 0; j < BOARD_WIDTH; j++)
3016 if ((board[i][j] != lastBoard[i][j] && board[i][j] == EmptySquare)
3019 DrawSquare(i, j, board[i][j], 0);
3020 damage[i][j] = False;
3023 /* Second pass -- Draw piece(s) in new position and flash them */
3024 for (i = 0; i < BOARD_HEIGHT; i++)
3025 for (j = 0; j < BOARD_WIDTH; j++)
3026 if (board[i][j] != lastBoard[i][j])
3028 DrawSquare(i, j, board[i][j], do_flash);
3040 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
3042 cairo_set_line_width (cr, lineGap);
3044 /* TODO: use appdata colors */
3045 cairo_set_source_rgba (cr, 0, 1, 0, 1.0);
3049 for (i = 0; i < BOARD_HEIGHT + 1; i++)
3052 x2 = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3053 y1 = y2 = lineGap / 2 + (i * (squareSize + lineGap));
3055 cairo_move_to (cr, x1, y1);
3056 cairo_rel_line_to (cr, x2,0);
3060 for (j = 0; j < BOARD_WIDTH + 1; j++)
3063 y2 = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3064 x1 = x2 = lineGap / 2 + (j * (squareSize + lineGap));
3066 cairo_move_to (cr, x1, y1);
3067 cairo_rel_line_to (cr, 0, y2);
3076 for (i = 0; i < BOARD_HEIGHT; i++)
3077 for (j = 0; j < BOARD_WIDTH; j++)
3079 DrawSquare(i, j, board[i][j], 0);
3080 damage[i][j] = False;
3084 CopyBoard(lastBoard, board);
3086 lastFlipView = flipView;
3088 /* Draw highlights */
3089 if (pm1X >= 0 && pm1Y >= 0)
3091 drawHighlight(pm1X, pm1Y, LINE_TYPE_PRE);
3093 if (pm2X >= 0 && pm2Y >= 0)
3095 drawHighlight(pm2X, pm2Y, LINE_TYPE_PRE);
3097 if (hi1X >= 0 && hi1Y >= 0)
3099 drawHighlight(hi1X, hi1Y, LINE_TYPE_HIGHLIGHT);
3101 if (hi2X >= 0 && hi2Y >= 0)
3103 drawHighlight(hi2X, hi2Y, LINE_TYPE_HIGHLIGHT);
3106 /* If piece being dragged around board, must redraw that too */
3112 void AnimateUserMove (Widget w, XEvent * event,
3113 String * params, Cardinal * nParams)
3115 DragPieceMove(event->xmotion.x, event->xmotion.y);
3118 void HandlePV (Widget w, XEvent * event,
3119 String * params, Cardinal * nParams)
3120 { // [HGM] pv: walk PV
3121 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3124 Widget CommentCreate(name, text, mutable, callback, lines)
3126 int /*Boolean*/ mutable;
3127 XtCallbackProc callback;
3131 Widget shell, layout, form, edit, b_ok, b_cancel, b_clear, b_close, b_edit;
3136 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3137 XtGetValues(boardWidget, args, j);
3140 XtSetArg(args[j], XtNresizable, True); j++;
3143 // XtCreatePopupShell(name, topLevelShellWidgetClass,
3144 // shellWidget, args, j);
3147 // XtCreatePopupShell(name, transientShellWidgetClass,
3148 // shellWidget, args, j);
3151 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
3152 layoutArgs, XtNumber(layoutArgs));
3154 XtCreateManagedWidget("form", formWidgetClass, layout,
3155 formArgs, XtNumber(formArgs));
3159 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
3160 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
3162 XtSetArg(args[j], XtNstring, text); j++;
3163 XtSetArg(args[j], XtNtop, XtChainTop); j++;
3164 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3165 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3166 XtSetArg(args[j], XtNright, XtChainRight); j++;
3167 XtSetArg(args[j], XtNresizable, True); j++;
3168 XtSetArg(args[j], XtNwidth, bw_width); j++; /*force wider than buttons*/
3169 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
3170 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
3171 XtSetArg(args[j], XtNautoFill, True); j++;
3172 XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
3174 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
3178 XtSetArg(args[j], XtNfromVert, edit); j++;
3179 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3180 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3181 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3182 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3184 XtCreateManagedWidget(_("ok"), commandWidgetClass, form, args, j);
3185 XtAddCallback(b_ok, XtNcallback, callback, (XtPointer) 0);
3188 XtSetArg(args[j], XtNfromVert, edit); j++;
3189 XtSetArg(args[j], XtNfromHoriz, b_ok); j++;
3190 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3191 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3192 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3193 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3195 XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
3196 XtAddCallback(b_cancel, XtNcallback, callback, (XtPointer) 0);
3199 XtSetArg(args[j], XtNfromVert, edit); j++;
3200 XtSetArg(args[j], XtNfromHoriz, b_cancel); j++;
3201 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3202 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3203 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3204 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3206 XtCreateManagedWidget(_("clear"), commandWidgetClass, form, args, j);
3207 XtAddCallback(b_clear, XtNcallback, callback, (XtPointer) 0);
3210 XtSetArg(args[j], XtNfromVert, edit); j++;
3211 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3212 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3213 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3214 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3216 XtCreateManagedWidget(_("close"), commandWidgetClass, form, args, j);
3217 XtAddCallback(b_close, XtNcallback, callback, (XtPointer) 0);
3220 XtSetArg(args[j], XtNfromVert, edit); j++;
3221 XtSetArg(args[j], XtNfromHoriz, b_close); j++;
3222 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3223 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3224 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3225 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3227 XtCreateManagedWidget(_("edit"), commandWidgetClass, form, args, j);
3228 XtAddCallback(b_edit, XtNcallback, callback, (XtPointer) 0);
3231 XtRealizeWidget(shell);
3233 if (commentX == -1) {
3236 Dimension pw_height;
3237 Dimension ew_height;
3240 XtSetArg(args[j], XtNheight, &ew_height); j++;
3241 XtGetValues(edit, args, j);
3244 XtSetArg(args[j], XtNheight, &pw_height); j++;
3245 XtGetValues(shell, args, j);
3246 commentH = pw_height + (lines - 1) * ew_height;
3247 commentW = bw_width - 16;
3249 // XSync(xDisplay, False);
3251 /* This code seems to tickle an X bug if it is executed too soon
3252 after xboard starts up. The coordinates get transformed as if
3253 the main window was positioned at (0, 0).
3255 // XtTranslateCoords(shellWidget,
3256 // (bw_width - commentW) / 2, 0 - commentH / 2,
3257 // &commentX, &commentY);
3259 // XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
3260 // RootWindowOfScreen(XtScreen(shellWidget)),
3261 // (bw_width - commentW) / 2, 0 - commentH / 2,
3262 // &xx, &yy, &junk);
3266 if (commentY < 0) commentY = 0; /*avoid positioning top offscreen*/
3269 if(wpComment.width > 0) {
3270 commentX = wpComment.x;
3271 commentY = wpComment.y;
3272 commentW = wpComment.width;
3273 commentH = wpComment.height;
3277 XtSetArg(args[j], XtNheight, commentH); j++;
3278 XtSetArg(args[j], XtNwidth, commentW); j++;
3279 XtSetArg(args[j], XtNx, commentX); j++;
3280 XtSetArg(args[j], XtNy, commentY); j++;
3281 XtSetValues(shell, args, j);
3282 XtSetKeyboardFocus(shell, edit);
3287 /* Used for analysis window and ICS input window */
3288 Widget MiscCreate(name, text, mutable, callback, lines)
3290 int /*Boolean*/ mutable;
3291 XtCallbackProc callback;
3295 Widget shell, layout, form, edit;
3297 Dimension bw_width, pw_height, ew_height, w, h;
3303 XtSetArg(args[j], XtNresizable, True); j++;
3306 // XtCreatePopupShell(name, topLevelShellWidgetClass,
3307 // shellWidget, args, j);
3310 // XtCreatePopupShell(name, transientShellWidgetClass,
3311 // shellWidget, args, j);
3314 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
3315 layoutArgs, XtNumber(layoutArgs));
3317 XtCreateManagedWidget("form", formWidgetClass, layout,
3318 formArgs, XtNumber(formArgs));
3322 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
3323 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
3325 XtSetArg(args[j], XtNstring, text); j++;
3326 XtSetArg(args[j], XtNtop, XtChainTop); j++;
3327 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3328 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3329 XtSetArg(args[j], XtNright, XtChainRight); j++;
3330 XtSetArg(args[j], XtNresizable, True); j++;
3331 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
3332 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
3333 XtSetArg(args[j], XtNautoFill, True); j++;
3334 XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
3336 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
3338 XtRealizeWidget(shell);
3341 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3342 XtGetValues(boardWidget, args, j);
3345 XtSetArg(args[j], XtNheight, &ew_height); j++;
3346 XtGetValues(edit, args, j);
3349 XtSetArg(args[j], XtNheight, &pw_height); j++;
3350 XtGetValues(shell, args, j);
3351 h = pw_height + (lines - 1) * ew_height;
3354 // XSync(xDisplay, False);
3356 /* This code seems to tickle an X bug if it is executed too soon
3357 after xboard starts up. The coordinates get transformed as if
3358 the main window was positioned at (0, 0).
3360 // XtTranslateCoords(shellWidget, (bw_width - w) / 2, 0 - h / 2, &x, &y);
3362 // XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
3363 // RootWindowOfScreen(XtScreen(shellWidget)),
3364 // (bw_width - w) / 2, 0 - h / 2, &xx, &yy, &junk);
3368 if (y < 0) y = 0; /*avoid positioning top offscreen*/
3371 XtSetArg(args[j], XtNheight, h); j++;
3372 XtSetArg(args[j], XtNwidth, w); j++;
3373 XtSetArg(args[j], XtNx, x); j++;
3374 XtSetArg(args[j], XtNy, y); j++;
3375 XtSetValues(shell, args, j);
3381 static int savedIndex; /* gross that this is global */
3383 void EditCommentPopUp(index, title, text)
3392 if (text == NULL) text = "";
3394 if (editShell == NULL) {
3396 CommentCreate(title, text, True, EditCommentCallback, 4);
3397 XtRealizeWidget(editShell);
3398 // CatchDeleteWindow(editShell, "EditCommentPopDown");
3400 edit = XtNameToWidget(editShell, "*form.text");
3402 XtSetArg(args[j], XtNstring, text); j++;
3403 XtSetValues(edit, args, j);
3405 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
3406 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
3407 XtSetValues(editShell, args, j);
3410 XtPopup(editShell, XtGrabNone);
3414 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
3415 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
3419 void EditCommentCallback(w, client_data, call_data)
3421 XtPointer client_data, call_data;
3429 XtSetArg(args[j], XtNlabel, &name); j++;
3430 XtGetValues(w, args, j);
3432 if (strcmp(name, _("ok")) == 0) {
3433 edit = XtNameToWidget(editShell, "*form.text");
3435 XtSetArg(args[j], XtNstring, &val); j++;
3436 XtGetValues(edit, args, j);
3437 ReplaceComment(savedIndex, val);
3438 EditCommentPopDown();
3439 } else if (strcmp(name, _("cancel")) == 0) {
3440 EditCommentPopDown();
3441 } else if (strcmp(name, _("clear")) == 0) {
3442 edit = XtNameToWidget(editShell, "*form.text");
3443 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
3444 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
3448 void EditCommentPopDown()
3453 if (!editUp) return;
3455 XtSetArg(args[j], XtNx, &commentX); j++;
3456 XtSetArg(args[j], XtNy, &commentY); j++;
3457 XtSetArg(args[j], XtNheight, &commentH); j++;
3458 XtSetArg(args[j], XtNwidth, &commentW); j++;
3459 XtGetValues(editShell, args, j);
3460 XtPopdown(editShell);
3463 XtSetArg(args[j], XtNleftBitmap, None); j++;
3464 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
3468 void ICSInputBoxPopUp()
3473 char *title = _("ICS Input");
3476 if (ICSInputShell == NULL) {
3477 ICSInputShell = MiscCreate(title, "", True, NULL, 1);
3478 tr = XtParseTranslationTable(ICSInputTranslations);
3479 edit = XtNameToWidget(ICSInputShell, "*form.text");
3480 XtOverrideTranslations(edit, tr);
3481 XtRealizeWidget(ICSInputShell);
3482 // CatchDeleteWindow(ICSInputShell, "ICSInputBoxPopDown");
3485 edit = XtNameToWidget(ICSInputShell, "*form.text");
3487 XtSetArg(args[j], XtNstring, ""); j++;
3488 XtSetValues(edit, args, j);
3490 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
3491 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
3492 XtSetValues(ICSInputShell, args, j);
3495 XtPopup(ICSInputShell, XtGrabNone);
3496 XtSetKeyboardFocus(ICSInputShell, edit);
3498 ICSInputBoxUp = True;
3500 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
3501 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
3505 void ICSInputSendText()
3512 edit = XtNameToWidget(ICSInputShell, "*form.text");
3514 XtSetArg(args[j], XtNstring, &val); j++;
3515 XtGetValues(edit, args, j);
3516 SendMultiLineToICS(val);
3517 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
3518 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
3521 void ICSInputBoxPopDown()
3526 if (!ICSInputBoxUp) return;
3528 XtPopdown(ICSInputShell);
3529 ICSInputBoxUp = False;
3531 XtSetArg(args[j], XtNleftBitmap, None); j++;
3532 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
3536 void CommentPopUp(title, text)
3543 if (commentShell == NULL) {
3545 CommentCreate(title, text, False, CommentCallback, 4);
3546 XtRealizeWidget(commentShell);
3547 // CatchDeleteWindow(commentShell, "CommentPopDown");
3549 edit = XtNameToWidget(commentShell, "*form.text");
3551 XtSetArg(args[j], XtNstring, text); j++;
3552 XtSetValues(edit, args, j);
3554 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
3555 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
3556 XtSetValues(commentShell, args, j);
3559 XtPopup(commentShell, XtGrabNone);
3560 // XSync(xDisplay, False);
3565 void CommentCallback(w, client_data, call_data)
3567 XtPointer client_data, call_data;
3574 XtSetArg(args[j], XtNlabel, &name); j++;
3575 XtGetValues(w, args, j);
3577 if (strcmp(name, _("close")) == 0) {
3579 } else if (strcmp(name, _("edit")) == 0) {
3586 void CommentPopDown()
3591 if (!commentUp) return;
3593 XtSetArg(args[j], XtNx, &commentX); j++;
3594 XtSetArg(args[j], XtNy, &commentY); j++;
3595 XtSetArg(args[j], XtNwidth, &commentW); j++;
3596 XtSetArg(args[j], XtNheight, &commentH); j++;
3597 XtGetValues(commentShell, args, j);
3598 XtPopdown(commentShell);
3599 // XSync(xDisplay, False);
3603 void PromotionPopUp()
3606 Widget dialog, layout;
3608 Dimension bw_width, pw_width;
3612 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3613 XtGetValues(boardWidget, args, j);
3616 XtSetArg(args[j], XtNresizable, True); j++;
3617 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
3619 // XtCreatePopupShell("Promotion", transientShellWidgetClass,
3620 // shellWidget, args, j);
3622 // XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
3623 // layoutArgs, XtNumber(layoutArgs));
3626 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
3627 XtSetArg(args[j], XtNborderWidth, 0); j++;
3628 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
3631 if(gameInfo.variant != VariantShogi) {
3632 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
3633 (XtPointer) dialog);
3634 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
3635 (XtPointer) dialog);
3636 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
3637 (XtPointer) dialog);
3638 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
3639 (XtPointer) dialog);
3640 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
3641 gameInfo.variant == VariantGiveaway) {
3642 XawDialogAddButton(dialog, _("King"), PromotionCallback,
3643 (XtPointer) dialog);
3645 if(gameInfo.variant == VariantCapablanca ||
3646 gameInfo.variant == VariantGothic ||
3647 gameInfo.variant == VariantCapaRandom) {
3648 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
3649 (XtPointer) dialog);
3650 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
3651 (XtPointer) dialog);
3653 } else // [HGM] shogi
3655 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
3656 (XtPointer) dialog);
3657 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
3658 (XtPointer) dialog);
3660 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
3661 (XtPointer) dialog);
3663 XtRealizeWidget(promotionShell);
3664 // CatchDeleteWindow(promotionShell, "PromotionPopDown");
3667 XtSetArg(args[j], XtNwidth, &pw_width); j++;
3668 XtGetValues(promotionShell, args, j);
3670 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3671 lineGap + squareSize/3 +
3672 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
3673 0 : 6*(squareSize + lineGap)), &x, &y);
3676 XtSetArg(args[j], XtNx, x); j++;
3677 XtSetArg(args[j], XtNy, y); j++;
3678 XtSetValues(promotionShell, args, j);
3680 XtPopup(promotionShell, XtGrabNone);
3685 void PromotionPopDown()
3687 if (!promotionUp) return;
3688 XtPopdown(promotionShell);
3689 XtDestroyWidget(promotionShell);
3690 promotionUp = False;
3693 void PromotionCallback(w, client_data, call_data)
3695 XtPointer client_data, call_data;
3701 XtSetArg(args[0], XtNlabel, &name);
3702 XtGetValues(w, args, 1);
3706 if (fromX == -1) return;
3708 if (strcmp(name, _("cancel")) == 0) {
3712 } else if (strcmp(name, _("Knight")) == 0) {
3714 } else if (strcmp(name, _("Promote")) == 0) {
3716 } else if (strcmp(name, _("Defer")) == 0) {
3719 promoChar = ToLower(name[0]);
3722 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
3724 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
3725 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3730 void ErrorCallback(w, client_data, call_data)
3732 XtPointer client_data, call_data;
3735 XtPopdown(w = XtParent(XtParent(XtParent(w))));
3737 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3743 if (!errorUp) return;
3747 gtk_widget_destroy(GTK_WIDGET(GUI_Error));
3749 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3754 void ErrorPopUp(title, label, modal)
3755 char *title, *label;
3758 GUI_Error = gtk_message_dialog_new(GTK_WINDOW(GUI_Window),
3759 GTK_DIALOG_DESTROY_WITH_PARENT,
3764 gtk_window_set_title(GTK_WINDOW(GUI_Error),(gchar *) title);
3767 gtk_dialog_run(GTK_DIALOG(GUI_Error));
3768 gtk_widget_destroy(GTK_WIDGET(GUI_Error));
3772 g_signal_connect_swapped (GUI_Error, "response",
3773 G_CALLBACK (ErrorPopDownProc),
3776 gtk_widget_show(GTK_WIDGET(GUI_Error));
3782 /* Disable all user input other than deleting the window */
3783 static int frozen = 0;
3787 /* Grab by a widget that doesn't accept input */
3788 // XtAddGrab(messageWidget, TRUE, FALSE);
3792 /* Undo a FreezeUI */
3795 if (!frozen) return;
3796 // XtRemoveGrab(messageWidget);
3800 char *ModeToWidgetName(mode)
3804 case BeginningOfGame:
3805 if (appData.icsActive)
3806 return "menuMode.ICS Client";
3807 else if (appData.noChessProgram ||
3808 *appData.cmailGameName != NULLCHAR)
3809 return "menuMode.Edit Game";
3811 return "menuMode.Machine Black";
3812 case MachinePlaysBlack:
3813 return "menuMode.Machine Black";
3814 case MachinePlaysWhite:
3815 return "menuMode.Machine White";
3817 return "menuMode.Analysis Mode";
3819 return "menuMode.Analyze File";
3820 case TwoMachinesPlay:
3821 return "menuMode.Two Machines";
3823 return "menuMode.Edit Game";
3824 case PlayFromGameFile:
3825 return "menuFile.Load Game";
3827 return "menuMode.Edit Position";
3829 return "menuMode.Training";
3830 case IcsPlayingWhite:
3831 case IcsPlayingBlack:
3835 return "menuMode.ICS Client";
3842 void ModeHighlight()
3844 static int oldPausing = FALSE;
3845 static GameMode oldmode = (GameMode) -1;
3848 // todo this toggling of the pause button doesn't seem to work?
3849 // e.g. select pause from buttonbar doesn't activate menumode.pause
3851 // if (!boardWidget || !XtIsRealized(boardWidget)) return;
3853 if (pausing != oldPausing) {
3854 oldPausing = pausing;
3855 gtk_button_set_relief(GTK_BUTTON (gtk_builder_get_object (builder, "menuMode.Pause")),pausing?GTK_RELIEF_NORMAL:GTK_RELIEF_NONE);
3856 /* toggle background color in showbuttonbar */
3857 if (appData.showButtonBar) {
3859 gtk_button_pressed(GTK_BUTTON (gtk_builder_get_object (builder, "buttonbar.Pause")));
3861 gtk_button_released(GTK_BUTTON (gtk_builder_get_object (builder, "buttonbar.Pause")));
3866 wname = ModeToWidgetName(oldmode);
3868 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, wname)),True);
3872 /* Maybe all the enables should be handled here, not just this one */
3873 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuMode.Training")),
3874 gameMode == Training || gameMode == PlayFromGameFile);
3879 * Button/menu procedures
3882 int LoadGamePopUp(f, gameNumber, title)
3887 cmailMsgLoaded = FALSE;
3889 if (gameNumber == 0)
3891 int error = GameListBuild(f);
3895 DisplayError(_("Cannot build game list"), error);
3897 else if (!ListEmpty(&gameList)
3898 && ((ListGame *) gameList.tailPred)->number > 1)
3900 // TODO convert to GTK
3901 // GameListPopUp(f, title);
3909 return LoadGame(f, gameNumber, title, FALSE);
3912 void ReloadCmailMsgProc(w, event, prms, nprms)
3918 ReloadCmailMsgEvent(FALSE);
3921 void MailMoveProc(w, event, prms, nprms)
3930 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3931 char *selected_fen_position=NULL;
3934 SendPositionSelection(Widget w, Atom *selection, Atom *target,
3935 Atom *type_return, XtPointer *value_return,
3936 unsigned long *length_return, int *format_return)
3938 char *selection_tmp;
3940 if (!selected_fen_position) return False; /* should never happen */
3941 // if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3942 // /* note: since no XtSelectionDoneProc was registered, Xt will
3943 // * automatically call XtFree on the value returned. So have to
3944 // * make a copy of it allocated with XtMalloc */
3945 // selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3946 // strcpy(selection_tmp, selected_fen_position);
3948 // *value_return=selection_tmp;
3949 // *length_return=strlen(selection_tmp);
3950 // *type_return=*target;
3951 // *format_return = 8; /* bits per byte */
3953 // } else if (*target == XA_TARGETS(xDisplay)) {
3954 // Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3955 // targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3956 // targets_tmp[1] = XA_STRING;
3957 // *value_return = targets_tmp;
3958 // *type_return = XA_ATOM;
3959 // *length_return = 2;
3960 // *format_return = 8 * sizeof(Atom);
3961 // if (*format_return > 32) {
3962 // *length_return *= *format_return / 32;
3963 // *format_return = 32;
3971 /* note: when called from menu all parameters are NULL, so no clue what the
3972 * Widget which was clicked on was, or what the click event was
3974 void CopyPositionProc(w, event, prms, nprms)
3981 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
3982 * have a notion of a position that is selected but not copied.
3983 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
3985 if(gameMode == EditPosition) EditPositionDone(TRUE);
3986 if (selected_fen_position) free(selected_fen_position);
3987 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
3988 if (!selected_fen_position) return;
3989 // XtOwnSelection(menuBarWidget, XA_PRIMARY,
3991 // SendPositionSelection,
3992 // NULL/* lose_ownership_proc */ ,
3993 // NULL/* transfer_done_proc */);
3994 // XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
3996 // SendPositionSelection,
3997 // NULL/* lose_ownership_proc */ ,
3998 // NULL/* transfer_done_proc */);
4001 /* function called when the data to Paste is ready */
4003 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
4004 Atom *type, XtPointer value, unsigned long *len, int *format)
4007 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
4008 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
4009 EditPositionPasteFEN(fenstr);
4013 /* called when Paste Position button is pressed,
4014 * all parameters will be NULL */
4015 void PastePositionProc(w, event, prms, nprms)
4021 // XtGetSelectionValue(menuBarWidget,
4022 // appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4023 // /* (XtSelectionCallbackProc) */ PastePositionCB,
4024 // NULL, /* client_data passed to PastePositionCB */
4026 // /* better to use the time field from the event that triggered the
4027 // * call to this function, but that isn't trivial to get
4035 SendGameSelection(Widget w, Atom *selection, Atom *target,
4036 Atom *type_return, XtPointer *value_return,
4037 unsigned long *length_return, int *format_return)
4039 char *selection_tmp;
4041 // if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4042 // FILE* f = fopen(gameCopyFilename, "r");
4045 // if (f == NULL) return False;
4049 // selection_tmp = XtMalloc(len + 1);
4050 // count = fread(selection_tmp, 1, len, f);
4051 // if (len != count) {
4052 // XtFree(selection_tmp);
4055 // selection_tmp[len] = NULLCHAR;
4056 // *value_return = selection_tmp;
4057 // *length_return = len;
4058 // *type_return = *target;
4059 // *format_return = 8; /* bits per byte */
4061 // } else if (*target == XA_TARGETS(xDisplay)) {
4062 // Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4063 // targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4064 // targets_tmp[1] = XA_STRING;
4065 // *value_return = targets_tmp;
4066 // *type_return = XA_ATOM;
4067 // *length_return = 2;
4068 // *format_return = 8 * sizeof(Atom);
4069 // if (*format_return > 32) {
4070 // *length_return *= *format_return / 32;
4071 // *format_return = 32;
4079 /* note: when called from menu all parameters are NULL, so no clue what the
4080 * Widget which was clicked on was, or what the click event was
4082 void CopyGameProc(w, event, prms, nprms)
4090 ret = SaveGameToFile(gameCopyFilename, FALSE);
4094 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4095 * have a notion of a game that is selected but not copied.
4096 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4098 // XtOwnSelection(menuBarWidget, XA_PRIMARY,
4100 // SendGameSelection,
4101 // NULL/* lose_ownership_proc */ ,
4102 // NULL/* transfer_done_proc */);
4103 // XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4105 // SendGameSelection,
4106 // NULL/* lose_ownership_proc */ ,
4107 // NULL/* transfer_done_proc */);
4110 /* function called when the data to Paste is ready */
4112 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
4113 Atom *type, XtPointer value, unsigned long *len, int *format)
4116 if (value == NULL || *len == 0) {
4117 return; /* nothing had been selected to copy */
4119 f = fopen(gamePasteFilename, "w");
4121 DisplayError(_("Can't open temp file"), errno);
4124 fwrite(value, 1, *len, f);
4127 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
4130 /* called when Paste Game button is pressed,
4131 * all parameters will be NULL */
4132 void PasteGameProc(w, event, prms, nprms)
4138 // XtGetSelectionValue(menuBarWidget,
4139 // appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4140 // /* (XtSelectionCallbackProc) */ PasteGameCB,
4141 // NULL, /* client_data passed to PasteGameCB */
4143 // /* better to use the time field from the event that triggered the
4144 // * call to this function, but that isn't trivial to get
4151 void SaveOnExitProc(w, event, prms, nprms)
4159 saveSettingsOnExit = !saveSettingsOnExit;
4161 if (saveSettingsOnExit) {
4162 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
4164 XtSetArg(args[0], XtNleftBitmap, None);
4166 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
4170 void SaveSettingsProc(w, event, prms, nprms)
4176 SaveSettings(settingsFileName);
4182 SaveGameProc(NULL, NULL);
4187 void EditCommentProc(w, event, prms, nprms)
4194 EditCommentPopDown();
4200 void IcsInputBoxProc(w, event, prms, nprms)
4206 if (ICSInputBoxUp) {
4207 ICSInputBoxPopDown();
4214 void EnterKeyProc(w, event, prms, nprms)
4220 if (ICSInputBoxUp == True)
4225 void DebugProc(w, event, prms, nprms)
4231 appData.debugMode = !appData.debugMode;
4234 void AboutGameProc(w, event, prms, nprms)
4243 void NothingProc(w, event, prms, nprms)
4252 void Iconify(w, event, prms, nprms)
4260 // fromX = fromY = -1;
4261 // XtSetArg(args[0], XtNiconic, True);
4262 // XtSetValues(shellWidget, args, 1);
4265 void DisplayMessage(message, extMessage)
4266 gchar *message, *extMessage;
4273 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
4276 message = extMessage;
4279 gtk_label_set_text( GTK_LABEL(gtk_builder_get_object (builder, "Messages")),message);
4284 void DisplayTitle(text)
4287 gchar title[MSG_SIZ];
4289 if (text == NULL) text = "";
4291 if (appData.titleInWindow)
4296 if (*text != NULLCHAR)
4298 strcpy(title, text);
4300 else if (appData.icsActive)
4302 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
4304 else if (appData.cmailGameName[0] != NULLCHAR)
4306 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
4308 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
4310 else if (gameInfo.variant == VariantGothic)
4312 strcpy(title, GOTHIC);
4316 else if (gameInfo.variant == VariantFalcon)
4318 strcpy(title, FALCON);
4321 else if (appData.noChessProgram)
4323 strcpy(title, programName);
4327 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
4329 gtk_window_set_title(GTK_WINDOW(GUI_Window),title);
4335 void DisplayError(message, error)
4342 if (appData.debugMode || appData.matchMode) {
4343 fprintf(stderr, "%s: %s\n", programName, message);
4346 if (appData.debugMode || appData.matchMode) {
4347 fprintf(stderr, "%s: %s: %s\n",
4348 programName, message, strerror(error));
4350 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
4353 ErrorPopUp(_("Error"), message, FALSE);
4357 void DisplayMoveError(message)
4362 DrawPosition(FALSE, NULL);
4363 if (appData.debugMode || appData.matchMode) {
4364 fprintf(stderr, "%s: %s\n", programName, message);
4366 if (appData.popupMoveErrors) {
4367 ErrorPopUp(_("Error"), message, FALSE);
4369 DisplayMessage(message, "");
4374 void DisplayFatalError(message, error, status)
4380 errorExitStatus = status;
4382 fprintf(stderr, "%s: %s\n", programName, message);
4384 fprintf(stderr, "%s: %s: %s\n",
4385 programName, message, strerror(error));
4386 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
4389 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
4390 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
4396 void DisplayInformation(message)
4400 ErrorPopUp(_("Information"), message, TRUE);
4403 void DisplayNote(message)
4407 ErrorPopUp(_("Note"), message, FALSE);
4411 NullXErrorCheck(dpy, error_event)
4413 XErrorEvent *error_event;
4418 void DisplayIcsInteractionTitle(message)
4421 if (oldICSInteractionTitle == NULL) {
4422 /* Magic to find the old window title, adapted from vim */
4423 char *wina = getenv("WINDOWID");
4425 Window win = (Window) atoi(wina);
4426 Window root, parent, *children;
4427 unsigned int nchildren;
4428 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4430 // if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4431 // if (!XQueryTree(xDisplay, win, &root, &parent,
4432 // &children, &nchildren)) break;
4433 // if (children) XFree((void *)children);
4434 // if (parent == root || parent == 0) break;
4437 XSetErrorHandler(oldHandler);
4439 if (oldICSInteractionTitle == NULL) {
4440 oldICSInteractionTitle = "xterm";
4443 printf("\033]0;%s\007", message);
4447 char pendingReplyPrefix[MSG_SIZ];
4448 ProcRef pendingReplyPR;
4450 void AskQuestionProc(w, event, prms, nprms)
4457 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
4461 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
4464 void AskQuestionPopDown()
4466 if (!askQuestionUp) return;
4467 XtPopdown(askQuestionShell);
4468 XtDestroyWidget(askQuestionShell);
4469 askQuestionUp = False;
4472 void AskQuestionReplyAction(w, event, prms, nprms)
4482 reply = XawDialogGetValueString(w = XtParent(w));
4483 strcpy(buf, pendingReplyPrefix);
4484 if (*buf) strcat(buf, " ");
4487 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
4488 AskQuestionPopDown();
4490 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
4493 void AskQuestionCallback(w, client_data, call_data)
4495 XtPointer client_data, call_data;
4500 XtSetArg(args[0], XtNlabel, &name);
4501 XtGetValues(w, args, 1);
4503 if (strcmp(name, _("cancel")) == 0) {
4504 AskQuestionPopDown();
4506 AskQuestionReplyAction(w, NULL, NULL, NULL);
4510 void AskQuestion(title, question, replyPrefix, pr)
4511 char *title, *question, *replyPrefix;
4515 Widget popup, layout, dialog, edit;
4521 strcpy(pendingReplyPrefix, replyPrefix);
4522 pendingReplyPR = pr;
4525 XtSetArg(args[i], XtNresizable, True); i++;
4526 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
4527 // askQuestionShell = popup =
4528 // XtCreatePopupShell(title, transientShellWidgetClass,
4529 // shellWidget, args, i);
4532 // XtCreateManagedWidget(layoutName, formWidgetClass, popup,
4533 // layoutArgs, XtNumber(layoutArgs));
4536 XtSetArg(args[i], XtNlabel, question); i++;
4537 XtSetArg(args[i], XtNvalue, ""); i++;
4538 XtSetArg(args[i], XtNborderWidth, 0); i++;
4539 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
4542 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
4543 (XtPointer) dialog);
4544 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
4545 (XtPointer) dialog);
4547 XtRealizeWidget(popup);
4548 // CatchDeleteWindow(popup, "AskQuestionPopDown");
4550 // XQueryPointer(xDisplay, xBoardWindow, &root, &child,
4551 // &x, &y, &win_x, &win_y, &mask);
4553 // XtSetArg(args[0], XtNx, x - 10);
4554 // XtSetArg(args[1], XtNy, y - 30);
4555 // XtSetValues(popup, args, 2);
4557 // XtPopup(popup, XtGrabExclusive);
4558 // askQuestionUp = True;
4560 // edit = XtNameToWidget(dialog, "*value");
4561 // XtSetKeyboardFocus(popup, edit);
4569 if (*name == NULLCHAR) {
4571 } else if (strcmp(name, "$") == 0) {
4572 putc(BELLCHAR, stderr);
4575 snprintf(buf, sizeof(buf), "%s '%s' &", appData.soundProgram, name);
4583 PlaySound(appData.soundMove);
4589 PlaySound(appData.soundIcsWin);
4595 PlaySound(appData.soundIcsLoss);
4601 PlaySound(appData.soundIcsDraw);
4605 PlayIcsUnfinishedSound()
4607 PlaySound(appData.soundIcsUnfinished);
4613 PlaySound(appData.soundIcsAlarm);
4619 system("stty echo");
4625 system("stty -echo");
4629 Colorize(cc, continuation)
4634 int count, outCount, error;
4636 if (textColors[(int)cc].bg > 0) {
4637 if (textColors[(int)cc].fg > 0) {
4638 sprintf(buf, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
4639 textColors[(int)cc].fg, textColors[(int)cc].bg);
4641 sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
4642 textColors[(int)cc].bg);
4645 if (textColors[(int)cc].fg > 0) {
4646 sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
4647 textColors[(int)cc].fg);
4649 sprintf(buf, "\033[0;%dm", textColors[(int)cc].attr);
4652 count = strlen(buf);
4653 outCount = OutputToProcess(NoProc, buf, count, &error);
4654 if (outCount < count) {
4655 DisplayFatalError(_("Error writing to display"), error, 1);
4658 if (continuation) return;
4661 PlaySound(appData.soundShout);
4664 PlaySound(appData.soundSShout);
4667 PlaySound(appData.soundChannel1);
4670 PlaySound(appData.soundChannel);
4673 PlaySound(appData.soundKibitz);
4676 PlaySound(appData.soundTell);
4678 case ColorChallenge:
4679 PlaySound(appData.soundChallenge);
4682 PlaySound(appData.soundRequest);
4685 PlaySound(appData.soundSeek);
4696 return getpwuid(getuid())->pw_name;
4699 static char *ExpandPathName(path)
4702 static char static_buf[2000];
4703 char *d, *s, buf[2000];
4709 while (*s && isspace(*s))
4718 if (*(s+1) == '/') {
4719 strcpy(d, getpwuid(getuid())->pw_dir);
4724 *strchr(buf, '/') = 0;
4725 pwd = getpwnam(buf);
4728 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
4732 strcpy(d, pwd->pw_dir);
4733 strcat(d, strchr(s+1, '/'));
4744 static char host_name[MSG_SIZ];
4746 #if HAVE_GETHOSTNAME
4747 gethostname(host_name, MSG_SIZ);
4749 #else /* not HAVE_GETHOSTNAME */
4750 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
4751 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
4753 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4755 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4756 #endif /* not HAVE_GETHOSTNAME */
4759 guint delayedEventTimerTag = 0;
4760 DelayedEventCallback delayedEventCallback = 0;
4763 FireDelayedEvent(data)
4767 g_source_remove(delayedEventTimerTag);
4768 delayedEventTimerTag = 0;
4771 delayedEventCallback();
4777 ScheduleDelayedEvent(cb, millisec)
4778 DelayedEventCallback cb; guint millisec;
4780 if(delayedEventTimerTag && delayedEventCallback == cb)
4781 // [HGM] alive: replace, rather than add or flush identical event
4782 g_source_remove(delayedEventTimerTag);
4783 delayedEventCallback = cb;
4784 delayedEventTimerTag = g_timeout_add(millisec,(GSourceFunc) FireDelayedEvent, NULL);
4788 DelayedEventCallback
4791 if (delayedEventTimerTag)
4793 return delayedEventCallback;
4802 CancelDelayedEvent()
4804 if (delayedEventTimerTag)
4806 g_source_remove(delayedEventTimerTag);
4807 delayedEventTimerTag = 0;
4813 guint loadGameTimerTag = 0;
4815 int LoadGameTimerRunning()
4817 return loadGameTimerTag != 0;
4820 int StopLoadGameTimer()
4822 if (loadGameTimerTag != 0) {
4823 g_source_remove(loadGameTimerTag);
4824 loadGameTimerTag = 0;
4832 LoadGameTimerCallback(data)
4836 g_source_remove(loadGameTimerTag);
4837 loadGameTimerTag = 0;
4844 StartLoadGameTimer(millisec)
4848 g_timeout_add( millisec, (GSourceFunc) LoadGameTimerCallback, NULL);
4852 guint analysisClockTag = 0;
4855 AnalysisClockCallback(data)
4858 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
4859 || appData.icsEngineAnalyze)
4861 AnalysisPeriodicEvent(0);
4862 return 1; /* keep on going */
4864 return 0; /* stop timer */
4868 StartAnalysisClock()
4871 g_timeout_add( 2000,(GSourceFunc) AnalysisClockCallback, NULL);
4875 guint clockTimerTag = 0;
4877 int ClockTimerRunning()
4879 return clockTimerTag != 0;
4882 int StopClockTimer()
4884 if (clockTimerTag != 0)
4886 g_source_remove(clockTimerTag);
4897 ClockTimerCallback(data)
4901 g_source_remove(clockTimerTag);
4909 StartClockTimer(millisec)
4912 clockTimerTag = g_timeout_add(millisec,(GSourceFunc) ClockTimerCallback,NULL);
4917 DisplayTimerLabel(w, color, timer, highlight)
4926 if (appData.clockMode) {
4927 sprintf(buf, "%s: %s", color, TimeString(timer));
4929 sprintf(buf, "%s ", color);
4931 gtk_label_set_text(GTK_LABEL(w),buf);
4933 /* check for low time warning */
4934 // Pixel foregroundOrWarningColor = timerForegroundPixel;
4937 // appData.lowTimeWarning &&
4938 // (timer / 1000) < appData.icsAlarmTime)
4939 // foregroundOrWarningColor = lowTimeWarningColor;
4941 // if (appData.clockMode) {
4942 // sprintf(buf, "%s: %s", color, TimeString(timer));
4943 // XtSetArg(args[0], XtNlabel, buf);
4945 // sprintf(buf, "%s ", color);
4946 // XtSetArg(args[0], XtNlabel, buf);
4951 // XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
4952 // XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
4954 // XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
4955 // XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
4958 // XtSetValues(w, args, 3);
4963 DisplayWhiteClock(timeRemaining, highlight)
4967 if(appData.noGUI) return;
4969 DisplayTimerLabel(GUI_Whiteclock, _("White"), timeRemaining, highlight);
4970 if (highlight && WindowIcon == BlackIcon)
4972 WindowIcon = WhiteIcon;
4973 gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
4978 DisplayBlackClock(timeRemaining, highlight)
4982 if(appData.noGUI) return;
4984 DisplayTimerLabel(GUI_Blackclock, _("Black"), timeRemaining, highlight);
4985 if (highlight && WindowIcon == WhiteIcon)
4987 WindowIcon = BlackIcon;
4988 gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
5006 int StartChildProcess(cmdLine, dir, pr)
5013 int to_prog[2], from_prog[2];
5017 if (appData.debugMode) {
5018 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
5021 /* We do NOT feed the cmdLine to the shell; we just
5022 parse it into blank-separated arguments in the
5023 most simple-minded way possible.
5026 strcpy(buf, cmdLine);
5029 while(*p == ' ') p++;
5031 if(*p == '"' || *p == '\'')
5032 p = strchr(++argv[i-1], *p);
5033 else p = strchr(p, ' ');
5034 if (p == NULL) break;
5039 SetUpChildIO(to_prog, from_prog);
5041 if ((pid = fork()) == 0) {
5043 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
5044 close(to_prog[1]); // first close the unused pipe ends
5045 close(from_prog[0]);
5046 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
5047 dup2(from_prog[1], 1);
5048 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
5049 close(from_prog[1]); // and closing again loses one of the pipes!
5050 if(fileno(stderr) >= 2) // better safe than sorry...
5051 dup2(1, fileno(stderr)); /* force stderr to the pipe */
5053 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
5058 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
5060 execvp(argv[0], argv);
5062 /* If we get here, exec failed */
5067 /* Parent process */
5069 close(from_prog[1]);
5071 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5074 cp->fdFrom = from_prog[0];
5075 cp->fdTo = to_prog[1];
5080 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
5081 static RETSIGTYPE AlarmCallBack(int n)
5087 DestroyChildProcess(pr, signalType)
5091 ChildProc *cp = (ChildProc *) pr;
5093 if (cp->kind != CPReal) return;
5095 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
5096 signal(SIGALRM, AlarmCallBack);
5098 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
5099 kill(cp->pid, SIGKILL); // kill it forcefully
5100 wait((int *) 0); // and wait again
5104 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
5106 /* Process is exiting either because of the kill or because of
5107 a quit command sent by the backend; either way, wait for it to die.
5116 InterruptChildProcess(pr)
5119 ChildProc *cp = (ChildProc *) pr;
5121 if (cp->kind != CPReal) return;
5122 (void) kill(cp->pid, SIGINT); /* stop it thinking */
5125 int OpenTelnet(host, port, pr)
5130 char cmdLine[MSG_SIZ];
5132 if (port[0] == NULLCHAR) {
5133 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
5135 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
5137 return StartChildProcess(cmdLine, "", pr);
5140 int OpenTCP(host, port, pr)
5146 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
5147 #else /* !OMIT_SOCKETS */
5149 struct sockaddr_in sa;
5151 unsigned short uport;
5154 if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
5158 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
5159 sa.sin_family = AF_INET;
5160 sa.sin_addr.s_addr = INADDR_ANY;
5161 uport = (unsigned short) 0;
5162 sa.sin_port = htons(uport);
5163 if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
5167 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
5168 if (!(hp = gethostbyname(host))) {
5170 if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
5171 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
5172 hp->h_addrtype = AF_INET;
5174 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
5175 hp->h_addr_list[0] = (char *) malloc(4);
5176 hp->h_addr_list[0][0] = b0;
5177 hp->h_addr_list[0][1] = b1;
5178 hp->h_addr_list[0][2] = b2;
5179 hp->h_addr_list[0][3] = b3;
5184 sa.sin_family = hp->h_addrtype;
5185 uport = (unsigned short) atoi(port);
5186 sa.sin_port = htons(uport);
5187 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
5189 if (connect(s, (struct sockaddr *) &sa,
5190 sizeof(struct sockaddr_in)) < 0) {
5194 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5201 #endif /* !OMIT_SOCKETS */
5206 int OpenCommPort(name, pr)
5213 fd = open(name, 2, 0);
5214 if (fd < 0) return errno;
5216 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5226 int OpenLoopback(pr)
5232 SetUpChildIO(to, from);
5234 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5237 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
5244 int OpenRcmd(host, user, cmd, pr)
5245 char *host, *user, *cmd;
5248 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
5252 #define INPUT_SOURCE_BUF_SIZE 8192
5261 char buf[INPUT_SOURCE_BUF_SIZE];
5266 DoInputCallback(io,cond,data)
5271 /* read input from one of the input source (for example a chess program, ICS, etc).
5272 * and call a function that will handle the input
5275 int count; /* how many bytes did we read */
5279 /* All information (callback function, file descriptor, etc) is
5280 * saved in an InputSource structure
5282 InputSource *is = (InputSource *) data;
5286 count = read(is->fd, is->unused,
5287 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
5291 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
5294 is->unused += count;
5296 /* break input into lines and call the callback function on each
5299 while (p < is->unused)
5301 q = memchr(p, '\n', is->unused - p);
5302 if (q == NULL) break;
5304 (is->func)(is, is->closure, p, q - p, 0);
5307 /* remember not yet used part of the buffer */
5309 while (p < is->unused)
5317 /* read maximum length of input buffer and send the whole buffer
5318 * to the callback function
5320 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
5325 (is->func)(is, is->closure, is->buf, count, error);
5331 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
5338 GIOChannel *channel;
5339 ChildProc *cp = (ChildProc *) pr;
5341 is = (InputSource *) calloc(1, sizeof(InputSource));
5342 is->lineByLine = lineByLine;
5346 is->fd = fileno(stdin);
5348 is->kind = cp->kind;
5349 is->fd = cp->fdFrom;
5352 is->unused = is->buf;
5356 // is->xid = XtAppAddInput(appContext, is->fd,
5357 // (XtPointer) (XtInputReadMask),
5358 // (XtInputCallbackProc) DoInputCallback,
5362 /* TODO: will this work on windows?*/
5364 channel = g_io_channel_unix_new(is->fd);
5365 g_io_channel_set_close_on_unref (channel, TRUE);
5366 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
5367 is->closure = closure;
5368 return (InputSourceRef) is;
5372 RemoveInputSource(isr)
5375 InputSource *is = (InputSource *) isr;
5377 if (is->sid == 0) return;
5378 g_source_remove(is->sid);
5383 int OutputToProcess(pr, message, count, outError)
5389 static int line = 0;
5390 ChildProc *cp = (ChildProc *) pr;
5395 if (appData.noJoin || !appData.useInternalWrap)
5396 outCount = fwrite(message, 1, count, stdout);
5399 int width = get_term_width();
5400 int len = wrap(NULL, message, count, width, &line);
5401 char *msg = malloc(len);
5405 outCount = fwrite(message, 1, count, stdout);
5408 dbgchk = wrap(msg, message, count, width, &line);
5409 if (dbgchk != len && appData.debugMode)
5410 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
5411 outCount = fwrite(msg, 1, dbgchk, stdout);
5417 outCount = write(cp->fdTo, message, count);
5427 /* Output message to process, with "ms" milliseconds of delay
5428 between each character. This is needed when sending the logon
5429 script to ICC, which for some reason doesn't like the
5430 instantaneous send. */
5431 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
5438 ChildProc *cp = (ChildProc *) pr;
5443 r = write(cp->fdTo, message++, 1);
5456 /**** Animation code by Hugh Fisher, DCS, ANU.
5458 Known problem: if a window overlapping the board is
5459 moved away while a piece is being animated underneath,
5460 the newly exposed area won't be updated properly.
5461 I can live with this.
5463 Known problem: if you look carefully at the animation
5464 of pieces in mono mode, they are being drawn as solid
5465 shapes without interior detail while moving. Fixing
5466 this would be a major complication for minimal return.
5469 /* Masks for XPM pieces. Black and white pieces can have
5470 different shapes, but in the interest of retaining my
5471 sanity pieces must have the same outline on both light
5472 and dark squares, and all pieces must use the same
5473 background square colors/images. */
5475 static int xpmDone = 0;
5478 CreateAnimMasks (pieceDepth)
5485 unsigned long plane;
5488 /* just return for gtk at the moment */
5491 /* Need a bitmap just to get a GC with right depth */
5492 // buf = XCreatePixmap(xDisplay, xBoardWindow,
5494 values.foreground = 1;
5495 values.background = 0;
5496 /* Don't use XtGetGC, not read only */
5497 // maskGC = XCreateGC(xDisplay, buf,
5498 // GCForeground | GCBackground, &values);
5499 // XFreePixmap(xDisplay, buf);
5501 // buf = XCreatePixmap(xDisplay, xBoardWindow,
5502 // squareSize, squareSize, pieceDepth);
5503 // values.foreground = XBlackPixel(xDisplay, xScreen);
5504 // values.background = XWhitePixel(xDisplay, xScreen);
5505 // bufGC = XCreateGC(xDisplay, buf,
5506 // GCForeground | GCBackground, &values);
5508 for (piece = WhitePawn; piece <= BlackKing; piece++) {
5509 /* Begin with empty mask */
5510 // if(!xpmDone) // [HGM] pieces: keep using existing
5511 // xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
5512 // squareSize, squareSize, 1);
5513 // XSetFunction(xDisplay, maskGC, GXclear);
5514 // XFillRectangle(xDisplay, xpmMask[piece], maskGC,
5515 // 0, 0, squareSize, squareSize);
5517 /* Take a copy of the piece */
5522 // XSetFunction(xDisplay, bufGC, GXcopy);
5523 // XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
5525 // 0, 0, squareSize, squareSize, 0, 0);
5527 /* XOR the background (light) over the piece */
5528 // XSetFunction(xDisplay, bufGC, GXxor);
5530 // XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
5531 // 0, 0, squareSize, squareSize, 0, 0);
5533 // XSetForeground(xDisplay, bufGC, lightSquareColor);
5534 // XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
5537 /* We now have an inverted piece image with the background
5538 erased. Construct mask by just selecting all the non-zero
5539 pixels - no need to reconstruct the original image. */
5540 // XSetFunction(xDisplay, maskGC, GXor);
5542 /* Might be quicker to download an XImage and create bitmap
5543 data from it rather than this N copies per piece, but it
5544 only takes a fraction of a second and there is a much
5545 longer delay for loading the pieces. */
5546 // for (n = 0; n < pieceDepth; n ++) {
5547 // XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
5548 // 0, 0, squareSize, squareSize,
5550 // plane = plane << 1;
5554 // XFreePixmap(xDisplay, buf);
5555 // XFreeGC(xDisplay, bufGC);
5556 // XFreeGC(xDisplay, maskGC);
5560 InitAnimState (anim, info)
5562 XWindowAttributes * info;
5567 /* Each buffer is square size, same depth as window */
5568 // anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
5569 // squareSize, squareSize, info->depth);
5570 // anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
5571 // squareSize, squareSize, info->depth);
5573 // /* Create a plain GC for blitting */
5574 // mask = GCForeground | GCBackground | GCFunction |
5575 // GCPlaneMask | GCGraphicsExposures;
5576 // values.foreground = XBlackPixel(xDisplay, xScreen);
5577 // values.background = XWhitePixel(xDisplay, xScreen);
5578 // values.function = GXcopy;
5579 // values.plane_mask = AllPlanes;
5580 // values.graphics_exposures = False;
5581 // anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
5583 // /* Piece will be copied from an existing context at
5584 // the start of each new animation/drag. */
5585 // anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
5587 // /* Outline will be a read-only copy of an existing */
5588 // anim->outlineGC = None;
5594 static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
5595 XWindowAttributes info;
5597 /* for gtk at the moment just ... */
5600 if (xpmDone && gameInfo.variant == old) return;
5601 if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet
5602 // XGetWindowAttributes(xDisplay, xBoardWindow, &info);
5604 // InitAnimState(&game, &info);
5605 // InitAnimState(&player, &info);
5607 /* For XPM pieces, we need bitmaps to use as masks. */
5609 // CreateAnimMasks(info.depth);
5615 static Boolean frameWaiting;
5617 static RETSIGTYPE FrameAlarm (sig)
5620 frameWaiting = False;
5621 /* In case System-V style signals. Needed?? */
5622 signal(SIGALRM, FrameAlarm);
5629 struct itimerval delay;
5631 XSync(xDisplay, False);
5634 frameWaiting = True;
5635 signal(SIGALRM, FrameAlarm);
5636 delay.it_interval.tv_sec =
5637 delay.it_value.tv_sec = time / 1000;
5638 delay.it_interval.tv_usec =
5639 delay.it_value.tv_usec = (time % 1000) * 1000;
5640 setitimer(ITIMER_REAL, &delay, NULL);
5641 while (frameWaiting) pause();
5642 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
5643 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
5644 setitimer(ITIMER_REAL, &delay, NULL);
5654 // XSync(xDisplay, False);
5656 usleep(time * 1000);
5661 /* Convert board position to corner of screen rect and color */
5664 ScreenSquare(column, row, pt, color)
5665 int column; int row; XPoint * pt; int * color;
5668 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
5669 pt->y = lineGap + row * (squareSize + lineGap);
5671 pt->x = lineGap + column * (squareSize + lineGap);
5672 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
5674 *color = SquareColor(row, column);
5677 /* Convert window coords to square */
5680 BoardSquare(x, y, column, row)
5681 int x; int y; int * column; int * row;
5683 *column = EventToSquare(x, BOARD_WIDTH);
5684 if (flipView && *column >= 0)
5685 *column = BOARD_WIDTH - 1 - *column;
5686 *row = EventToSquare(y, BOARD_HEIGHT);
5687 if (!flipView && *row >= 0)
5688 *row = BOARD_HEIGHT - 1 - *row;
5693 #undef Max /* just in case */
5695 #define Max(a, b) ((a) > (b) ? (a) : (b))
5696 #define Min(a, b) ((a) < (b) ? (a) : (b))
5699 SetRect(rect, x, y, width, height)
5700 XRectangle * rect; int x; int y; int width; int height;
5704 rect->width = width;
5705 rect->height = height;
5708 /* Test if two frames overlap. If they do, return
5709 intersection rect within old and location of
5710 that rect within new. */
5713 Intersect(old, new, size, area, pt)
5714 XPoint * old; XPoint * new;
5715 int size; XRectangle * area; XPoint * pt;
5717 if (old->x > new->x + size || new->x > old->x + size ||
5718 old->y > new->y + size || new->y > old->y + size) {
5721 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
5722 size - abs(old->x - new->x), size - abs(old->y - new->y));
5723 pt->x = Max(old->x - new->x, 0);
5724 pt->y = Max(old->y - new->y, 0);
5729 /* For two overlapping frames, return the rect(s)
5730 in the old that do not intersect with the new. */
5733 CalcUpdateRects(old, new, size, update, nUpdates)
5734 XPoint * old; XPoint * new; int size;
5735 XRectangle update[]; int * nUpdates;
5739 /* If old = new (shouldn't happen) then nothing to draw */
5740 if (old->x == new->x && old->y == new->y) {
5744 /* Work out what bits overlap. Since we know the rects
5745 are the same size we don't need a full intersect calc. */
5747 /* Top or bottom edge? */
5748 if (new->y > old->y) {
5749 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
5751 } else if (old->y > new->y) {
5752 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
5753 size, old->y - new->y);
5756 /* Left or right edge - don't overlap any update calculated above. */
5757 if (new->x > old->x) {
5758 SetRect(&(update[count]), old->x, Max(new->y, old->y),
5759 new->x - old->x, size - abs(new->y - old->y));
5761 } else if (old->x > new->x) {
5762 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
5763 old->x - new->x, size - abs(new->y - old->y));
5770 /* Generate a series of frame coords from start->mid->finish.
5771 The movement rate doubles until the half way point is
5772 reached, then halves back down to the final destination,
5773 which gives a nice slow in/out effect. The algorithmn
5774 may seem to generate too many intermediates for short
5775 moves, but remember that the purpose is to attract the
5776 viewers attention to the piece about to be moved and
5777 then to where it ends up. Too few frames would be less
5781 Tween(start, mid, finish, factor, frames, nFrames)
5782 XPoint * start; XPoint * mid;
5783 XPoint * finish; int factor;
5784 XPoint frames[]; int * nFrames;
5786 int fraction, n, count;
5790 /* Slow in, stepping 1/16th, then 1/8th, ... */
5792 for (n = 0; n < factor; n++)
5794 for (n = 0; n < factor; n++) {
5795 frames[count].x = start->x + (mid->x - start->x) / fraction;
5796 frames[count].y = start->y + (mid->y - start->y) / fraction;
5798 fraction = fraction / 2;
5802 frames[count] = *mid;
5805 /* Slow out, stepping 1/2, then 1/4, ... */
5807 for (n = 0; n < factor; n++) {
5808 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
5809 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
5811 fraction = fraction * 2;
5816 /* Draw a piece on the screen without disturbing what's there */
5819 SelectGCMask(piece, clip, outline, mask)
5820 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
5824 /* Bitmap for piece being moved. */
5825 if (appData.monoMode) {
5826 *mask = *pieceToSolid(piece);
5827 } else if (useImages) {
5829 *mask = xpmMask[piece];
5831 *mask = ximMaskPm[piece];
5834 *mask = *pieceToSolid(piece);
5837 /* GC for piece being moved. Square color doesn't matter, but
5838 since it gets modified we make a copy of the original. */
5840 if (appData.monoMode)
5845 if (appData.monoMode)
5850 // XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
5852 /* Outline only used in mono mode and is not modified */
5854 *outline = bwPieceGC;
5856 *outline = wbPieceGC;
5860 OverlayPiece(piece, clip, outline, dest)
5861 ChessSquare piece; GC clip; GC outline; Drawable dest;
5866 /* Draw solid rectangle which will be clipped to shape of piece */
5867 // XFillRectangle(xDisplay, dest, clip,
5868 // 0, 0, squareSize, squareSize)
5870 if (appData.monoMode)
5871 /* Also draw outline in contrasting color for black
5872 on black / white on white cases */
5873 // XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
5874 // 0, 0, squareSize, squareSize, 0, 0, 1)
5877 /* Copy the piece */
5882 // XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
5884 // 0, 0, squareSize, squareSize,
5889 /* Animate the movement of a single piece */
5892 BeginAnimation(anim, piece, startColor, start)
5900 /* The old buffer is initialised with the start square (empty) */
5901 BlankSquare(0, 0, startColor, EmptySquare, anim->saveBuf);
5902 anim->prevFrame = *start;
5904 /* The piece will be drawn using its own bitmap as a matte */
5905 // SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
5906 // XSetClipMask(xDisplay, anim->pieceGC, mask);
5910 AnimationFrame(anim, frame, piece)
5915 XRectangle updates[4];
5920 /* Save what we are about to draw into the new buffer */
5921 // XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
5922 // frame->x, frame->y, squareSize, squareSize,
5925 /* Erase bits of the previous frame */
5926 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
5927 /* Where the new frame overlapped the previous,
5928 the contents in newBuf are wrong. */
5929 // XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
5930 // overlap.x, overlap.y,
5931 // overlap.width, overlap.height,
5933 /* Repaint the areas in the old that don't overlap new */
5934 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
5935 for (i = 0; i < count; i++)
5936 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
5937 // updates[i].x - anim->prevFrame.x,
5938 // updates[i].y - anim->prevFrame.y,
5939 // updates[i].width, updates[i].height,
5940 // updates[i].x, updates[i].y)
5943 /* Easy when no overlap */
5944 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
5945 // 0, 0, squareSize, squareSize,
5946 // anim->prevFrame.x, anim->prevFrame.y);
5949 /* Save this frame for next time round */
5950 // XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
5951 // 0, 0, squareSize, squareSize,
5953 anim->prevFrame = *frame;
5955 /* Draw piece over original screen contents, not current,
5956 and copy entire rect. Wipes out overlapping piece images. */
5957 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
5958 // XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
5959 // 0, 0, squareSize, squareSize,
5960 // frame->x, frame->y);
5964 EndAnimation (anim, finish)
5968 XRectangle updates[4];
5973 /* The main code will redraw the final square, so we
5974 only need to erase the bits that don't overlap. */
5975 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
5976 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
5977 for (i = 0; i < count; i++)
5978 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
5979 // updates[i].x - anim->prevFrame.x,
5980 // updates[i].y - anim->prevFrame.y,
5981 // updates[i].width, updates[i].height,
5982 // updates[i].x, updates[i].y)
5985 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
5986 // 0, 0, squareSize, squareSize,
5987 // anim->prevFrame.x, anim->prevFrame.y);
5992 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
5994 ChessSquare piece; int startColor;
5995 XPoint * start; XPoint * finish;
5996 XPoint frames[]; int nFrames;
6000 BeginAnimation(anim, piece, startColor, start);
6001 for (n = 0; n < nFrames; n++) {
6002 AnimationFrame(anim, &(frames[n]), piece);
6003 FrameDelay(appData.animSpeed);
6005 EndAnimation(anim, finish);
6008 /* Main control logic for deciding what to animate and how */
6011 AnimateMove(board, fromX, fromY, toX, toY)
6020 XPoint start, finish, mid;
6021 XPoint frames[kFactor * 2 + 1];
6022 int nFrames, startColor, endColor;
6024 /* Are we animating? */
6025 if (!appData.animate || appData.blindfold)
6028 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
6029 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
6030 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
6032 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
6033 piece = board[fromY][fromX];
6034 if (piece >= EmptySquare) return;
6039 hop = (piece == WhiteKnight || piece == BlackKnight);
6042 if (appData.debugMode) {
6043 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
6044 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
6045 piece, fromX, fromY, toX, toY); }
6047 ScreenSquare(fromX, fromY, &start, &startColor);
6048 ScreenSquare(toX, toY, &finish, &endColor);
6051 /* Knight: make diagonal movement then straight */
6052 if (abs(toY - fromY) < abs(toX - fromX)) {
6053 mid.x = start.x + (finish.x - start.x) / 2;
6057 mid.y = start.y + (finish.y - start.y) / 2;
6060 mid.x = start.x + (finish.x - start.x) / 2;
6061 mid.y = start.y + (finish.y - start.y) / 2;
6064 /* Don't use as many frames for very short moves */
6065 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
6066 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
6068 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
6069 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
6071 /* Be sure end square is redrawn */
6072 damage[toY][toX] = True;
6076 DragPieceBegin(x, y)
6079 int boardX, boardY, color;
6082 /* Are we animating? */
6083 if (!appData.animateDragging || appData.blindfold)
6086 /* Figure out which square we start in and the
6087 mouse position relative to top left corner. */
6088 BoardSquare(x, y, &boardX, &boardY);
6089 player.startBoardX = boardX;
6090 player.startBoardY = boardY;
6091 ScreenSquare(boardX, boardY, &corner, &color);
6092 player.startSquare = corner;
6093 player.startColor = color;
6094 /* As soon as we start dragging, the piece will jump slightly to
6095 be centered over the mouse pointer. */
6096 player.mouseDelta.x = squareSize/2;
6097 player.mouseDelta.y = squareSize/2;
6098 /* Initialise animation */
6099 player.dragPiece = PieceForSquare(boardX, boardY);
6101 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
6102 player.dragActive = True;
6103 BeginAnimation(&player, player.dragPiece, color, &corner);
6104 /* Mark this square as needing to be redrawn. Note that
6105 we don't remove the piece though, since logically (ie
6106 as seen by opponent) the move hasn't been made yet. */
6107 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
6108 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
6109 // XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
6110 // corner.x, corner.y, squareSize, squareSize,
6111 // 0, 0); // [HGM] zh: unstack in stead of grab
6112 damage[boardY][boardX] = True;
6114 player.dragActive = False;
6124 /* Are we animating? */
6125 if (!appData.animateDragging || appData.blindfold)
6129 if (! player.dragActive)
6131 /* Move piece, maintaining same relative position
6132 of mouse within square */
6133 corner.x = x - player.mouseDelta.x;
6134 corner.y = y - player.mouseDelta.y;
6135 AnimationFrame(&player, &corner, player.dragPiece);
6137 if (appData.highlightDragging) {
6139 BoardSquare(x, y, &boardX, &boardY);
6140 SetHighlights(fromX, fromY, boardX, boardY);
6149 int boardX, boardY, color;
6152 /* Are we animating? */
6153 if (!appData.animateDragging || appData.blindfold)
6157 if (! player.dragActive)
6159 /* Last frame in sequence is square piece is
6160 placed on, which may not match mouse exactly. */
6161 BoardSquare(x, y, &boardX, &boardY);
6162 ScreenSquare(boardX, boardY, &corner, &color);
6163 EndAnimation(&player, &corner);
6165 /* Be sure end square is redrawn */
6166 damage[boardY][boardX] = True;
6168 /* This prevents weird things happening with fast successive
6169 clicks which on my Sun at least can cause motion events
6170 without corresponding press/release. */
6171 player.dragActive = False;
6174 /* Handle expose event while piece being dragged */
6179 if (!player.dragActive || appData.blindfold)
6182 /* What we're doing: logically, the move hasn't been made yet,
6183 so the piece is still in it's original square. But visually
6184 it's being dragged around the board. So we erase the square
6185 that the piece is on and draw it at the last known drag point. */
6186 BlankSquare(player.startSquare.x, player.startSquare.y,
6187 player.startColor, EmptySquare, xBoardWindow);
6188 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
6189 damage[player.startBoardY][player.startBoardX] = TRUE;
6192 #include <sys/ioctl.h>
6193 int get_term_width()
6195 int fd, default_width;
6198 default_width = 79; // this is FICS default anyway...
6200 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
6202 if (!ioctl(fd, TIOCGSIZE, &win))
6203 default_width = win.ts_cols;
6204 #elif defined(TIOCGWINSZ)
6206 if (!ioctl(fd, TIOCGWINSZ, &win))
6207 default_width = win.ws_col;
6209 return default_width;
6212 void update_ics_width()
6214 static int old_width = 0;
6215 int new_width = get_term_width();
6217 if (old_width != new_width)
6218 ics_printf("set width %d\n", new_width);
6219 old_width = new_width;
6222 void NotifyFrontendLogin()