2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
65 # if HAVE_SYS_SOCKET_H
66 # include <sys/socket.h>
67 # include <netinet/in.h>
69 # else /* not HAVE_SYS_SOCKET_H */
70 # if HAVE_LAN_SOCKET_H
71 # include <lan/socket.h>
73 # include <lan/netdb.h>
74 # else /* not HAVE_LAN_SOCKET_H */
75 # define OMIT_SOCKETS 1
76 # endif /* not HAVE_LAN_SOCKET_H */
77 # endif /* not HAVE_SYS_SOCKET_H */
78 #endif /* !OMIT_SOCKETS */
83 #else /* not STDC_HEADERS */
84 extern char *getenv();
87 # else /* not HAVE_STRING_H */
89 # endif /* not HAVE_STRING_H */
90 #endif /* not STDC_HEADERS */
93 # include <sys/fcntl.h>
94 #else /* not HAVE_SYS_FCNTL_H */
97 # endif /* HAVE_FCNTL_H */
98 #endif /* not HAVE_SYS_FCNTL_H */
100 #if HAVE_SYS_SYSTEMINFO_H
101 # include <sys/systeminfo.h>
102 #endif /* HAVE_SYS_SYSTEMINFO_H */
104 #if TIME_WITH_SYS_TIME
105 # include <sys/time.h>
109 # include <sys/time.h>
120 # include <sys/wait.h>
125 # define NAMLEN(dirent) strlen((dirent)->d_name)
126 # define HAVE_DIR_STRUCT
128 # define dirent direct
129 # define NAMLEN(dirent) (dirent)->d_namlen
131 # include <sys/ndir.h>
132 # define HAVE_DIR_STRUCT
135 # include <sys/dir.h>
136 # define HAVE_DIR_STRUCT
140 # define HAVE_DIR_STRUCT
144 #include <X11/Intrinsic.h>
145 #include <X11/StringDefs.h>
146 #include <X11/Shell.h>
147 #include <X11/cursorfont.h>
148 #include <X11/Xatom.h>
149 #include <X11/Xmu/Atoms.h>
151 #include <X11/Xaw3d/Dialog.h>
152 #include <X11/Xaw3d/Form.h>
153 #include <X11/Xaw3d/List.h>
154 #include <X11/Xaw3d/Label.h>
155 #include <X11/Xaw3d/SimpleMenu.h>
156 #include <X11/Xaw3d/SmeBSB.h>
157 #include <X11/Xaw3d/SmeLine.h>
158 #include <X11/Xaw3d/Box.h>
159 #include <X11/Xaw3d/MenuButton.h>
160 #include <X11/Xaw3d/Text.h>
161 #include <X11/Xaw3d/AsciiText.h>
163 #include <X11/Xaw/Dialog.h>
164 #include <X11/Xaw/Form.h>
165 #include <X11/Xaw/List.h>
166 #include <X11/Xaw/Label.h>
167 #include <X11/Xaw/SimpleMenu.h>
168 #include <X11/Xaw/SmeBSB.h>
169 #include <X11/Xaw/SmeLine.h>
170 #include <X11/Xaw/Box.h>
171 #include <X11/Xaw/MenuButton.h>
172 #include <X11/Xaw/Text.h>
173 #include <X11/Xaw/AsciiText.h>
176 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
181 #include "pixmaps/pixmaps.h"
182 #define IMAGE_EXT "xpm"
184 #define IMAGE_EXT "xim"
185 #include "bitmaps/bitmaps.h"
190 #include <gdk-pixbuf/gdk-pixbuf.h>
192 #include "bitmaps/icon_white.bm"
193 #include "bitmaps/icon_black.bm"
194 #include "bitmaps/checkmark.bm"
196 #include "frontend.h"
201 #include "xgamelist.h"
202 #include "xhistory.h"
203 #include "xedittags.h"
205 #include "callback.h"
206 #include "interface.h"
208 // must be moved to xengineoutput.h
210 void EngineOutputProc P((Widget w, XEvent *event,
211 String *prms, Cardinal *nprms));
212 void EvalGraphProc P((Widget w, XEvent *event,
213 String *prms, Cardinal *nprms));
220 #define usleep(t) _sleep2(((t)+500)/1000)
224 # define _(s) gettext (s)
225 # define N_(s) gettext_noop (s)
248 int main P((int argc, char **argv));
249 RETSIGTYPE CmailSigHandler P((int sig));
250 RETSIGTYPE IntSigHandler P((int sig));
251 RETSIGTYPE TermSizeSigHandler P((int sig));
252 void CreateGCs P((void));
253 void CreateXIMPieces P((void));
254 void CreateXPMPieces P((void));
255 void CreatePieces P((void));
256 void CreatePieceMenus P((void));
257 Widget CreateMenuBar P((Menu *mb));
258 char *FindFont P((char *pattern, int targetPxlSize));
259 void PieceMenuPopup P((Widget w, XEvent *event,
260 String *params, Cardinal *num_params));
261 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
262 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
263 int EventToSquare P((int x, int limit));
264 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
265 void AnimateUserMove P((Widget w, XEvent * event,
266 String * params, Cardinal * nParams));
267 void HandlePV P((Widget w, XEvent * event,
268 String * params, Cardinal * nParams));
269 void CommentPopUp P((char *title, char *label));
270 void CommentPopDown P((void));
271 void CommentCallback P((Widget w, XtPointer client_data,
272 XtPointer call_data));
273 void ICSInputBoxPopUp P((void));
274 void ICSInputBoxPopDown P((void));
275 void AskQuestionReplyAction P((Widget w, XEvent *event,
276 String *prms, Cardinal *nprms));
277 void AskQuestionProc P((Widget w, XEvent *event,
278 String *prms, Cardinal *nprms));
279 void AskQuestionPopDown P((void));
280 void PromotionPopDown P((void));
281 void PromotionCallback P((Widget w, XtPointer client_data,
282 XtPointer call_data));
283 void EditCommentPopDown P((void));
284 void EditCommentCallback P((Widget w, XtPointer client_data,
285 XtPointer call_data));
286 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
287 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
289 void PastePositionProc P((Widget w, XEvent *event, String *prms,
291 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
292 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
293 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
294 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
296 void EditCommentProc P((Widget w, XEvent *event,
297 String *prms, Cardinal *nprms));
298 void IcsInputBoxProc P((Widget w, XEvent *event,
299 String *prms, Cardinal *nprms));
300 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
301 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
302 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
303 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
304 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
305 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
306 void Iconify P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
307 void DisplayMove P((int moveNumber));
308 void DisplayTitle P((char *title));
309 void ICSInitScript P((void));
310 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
311 void ErrorPopUp P((char *title, char *text, int modal));
312 void ErrorPopDown P((void));
313 static char *ExpandPathName P((char *path));
314 static void CreateAnimVars P((void));
315 static void DragPieceMove P((int x, int y));
316 static void DrawDragPiece P((void));
317 char *ModeToWidgetName P((GameMode mode));
318 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
319 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
320 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
321 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
322 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
323 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
324 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
325 void ShufflePopDown P(());
326 void EnginePopDown P(());
327 void UciPopDown P(());
328 void TimeControlPopDown P(());
329 void NewVariantPopDown P(());
330 void SettingsPopDown P(());
331 void SetMenuEnables P((Enables *enab));
332 void update_ics_width P(());
333 int get_term_width P(());
334 int CopyMemoProc P(());
336 * XBoard depends on Xt R4 or higher
338 int xtVersion = XtSpecificationRelease;
343 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
344 jailSquareColor, highlightSquareColor, premoveHighlightColor;
345 Pixel lowTimeWarningColor;
347 #define LINE_TYPE_NORMAL 0
348 #define LINE_TYPE_HIGHLIGHT 1
349 #define LINE_TYPE_PRE 2
352 GC lightSquareGC, darkSquareGC, jailSquareGC, wdPieceGC, wlPieceGC,
353 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC,
354 wjPieceGC, bjPieceGC;
355 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
356 Widget layoutWidget, formWidget, boardWidget, messageWidget,
357 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
358 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
359 menuBarWidget, editShell, errorShell, analysisShell,
360 ICSInputShell, fileNameShell, askQuestionShell;
362 Widget evalGraphShell, gameListShell;
363 //XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
364 //XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
366 Font clockFontID, coordFontID, countFontID;
367 XFontStruct *clockFontStruct, *coordFontStruct, *countFontStruct;
368 XtAppContext appContext;
370 char *oldICSInteractionTitle;
374 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
376 Position commentX = -1, commentY = -1;
377 Dimension commentW, commentH;
378 typedef unsigned int BoardSize;
380 Boolean chessProgram;
382 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
383 int squareSize, smallLayout = 0, tinyLayout = 0,
384 marginW, marginH, // [HGM] for run-time resizing
385 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
386 ICSInputBoxUp = False, askQuestionUp = False,
387 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
388 editUp = False, errorUp = False, errorExitStatus = -1, lineGap;
389 Pixel timerForegroundPixel, timerBackgroundPixel;
390 Pixel buttonForegroundPixel, buttonBackgroundPixel;
391 char *chessDir, *programName, *programVersion,
392 *gameCopyFilename, *gamePasteFilename;
393 Boolean alwaysOnTop = False;
394 Boolean saveSettingsOnExit;
395 char *settingsFileName;
396 char *icsTextMenuString;
398 char *firstChessProgramNames;
399 char *secondChessProgramNames;
401 WindowPlacement wpMain;
402 WindowPlacement wpConsole;
403 WindowPlacement wpComment;
404 WindowPlacement wpMoveHistory;
405 WindowPlacement wpEvalGraph;
406 WindowPlacement wpEngineOutput;
407 WindowPlacement wpGameList;
408 WindowPlacement wpTags;
412 Pixmap pieceBitmap[2][(int)BlackPawn];
413 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
414 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
415 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
416 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
417 int useImages=0, useImageSqs;
418 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
419 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
420 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
421 XImage *ximLightSquare, *ximDarkSquare;
424 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
425 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
427 #define White(piece) ((int)(piece) < (int)BlackPawn)
429 /* Variables for doing smooth animation. This whole thing
430 would be much easier if the board was double-buffered,
431 but that would require a fairly major rewrite. */
436 GC blitGC, pieceGC, outlineGC;
437 XPoint startSquare, prevFrame, mouseDelta;
441 int startBoardX, startBoardY;
444 /* There can be two pieces being animated at once: a player
445 can begin dragging a piece before the remote opponent has moved. */
447 static AnimState game, player;
449 /* Bitmaps for use as masks when drawing XPM pieces.
450 Need one for each black and white piece. */
451 static Pixmap xpmMask[BlackKing + 1];
453 /* This magic number is the number of intermediate frames used
454 in each half of the animation. For short moves it's reduced
455 by 1. The total number of frames will be factor * 2 + 1. */
458 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
460 Enables icsEnables[] = {
461 { "menuFile.Mail Move", False },
462 { "menuFile.Reload CMail Message", False },
463 { "menuMode.Machine Black", False },
464 { "menuMode.Machine White", False },
465 { "menuMode.Analysis Mode", False },
466 { "menuMode.Analyze File", False },
467 { "menuMode.Two Machines", False },
469 { "menuHelp.Hint", False },
470 { "menuHelp.Book", False },
471 { "menuStep.Move Now", False },
472 { "menuOptions.Periodic Updates", False },
473 { "menuOptions.Hide Thinking", False },
474 { "menuOptions.Ponder Next Move", False },
479 Enables ncpEnables[] = {
480 { "menuFile.Mail Move", False },
481 { "menuFile.Reload CMail Message", False },
482 { "menuMode.Machine White", False },
483 { "menuMode.Machine Black", False },
484 { "menuMode.Analysis Mode", False },
485 { "menuMode.Analyze File", False },
486 { "menuMode.Two Machines", False },
487 { "menuMode.ICS Client", False },
488 { "menuMode.ICS Input Box", False },
490 { "menuStep.Revert", False },
491 { "menuStep.Move Now", False },
492 { "menuStep.Retract Move", False },
493 { "menuOptions.Auto Comment", False },
494 { "menuOptions.Auto Flag", False },
495 { "menuOptions.Auto Flip View", False },
496 { "menuOptions.Auto Observe", False },
497 { "menuOptions.Auto Raise Board", False },
498 { "menuOptions.Get Move List", False },
499 { "menuOptions.ICS Alarm", False },
500 { "menuOptions.Move Sound", False },
501 { "menuOptions.Quiet Play", False },
502 { "menuOptions.Hide Thinking", False },
503 { "menuOptions.Periodic Updates", False },
504 { "menuOptions.Ponder Next Move", False },
505 { "menuHelp.Hint", False },
506 { "menuHelp.Book", False },
510 Enables gnuEnables[] = {
511 { "menuMode.ICS Client", False },
512 { "menuMode.ICS Input Box", False },
513 { "menuAction.Accept", False },
514 { "menuAction.Decline", False },
515 { "menuAction.Rematch", False },
516 { "menuAction.Adjourn", False },
517 { "menuAction.Stop Examining", False },
518 { "menuAction.Stop Observing", False },
519 { "menuStep.Revert", False },
520 { "menuOptions.Auto Comment", False },
521 { "menuOptions.Auto Observe", False },
522 { "menuOptions.Auto Raise Board", False },
523 { "menuOptions.Get Move List", False },
524 { "menuOptions.Premove", False },
525 { "menuOptions.Quiet Play", False },
527 /* The next two options rely on SetCmailMode being called *after* */
528 /* SetGNUMode so that when GNU is being used to give hints these */
529 /* menu options are still available */
531 { "menuFile.Mail Move", False },
532 { "menuFile.Reload CMail Message", False },
536 Enables cmailEnables[] = {
538 { "menuAction.Call Flag", False },
539 { "menuAction.Draw", True },
540 { "menuAction.Adjourn", False },
541 { "menuAction.Abort", False },
542 { "menuAction.Stop Observing", False },
543 { "menuAction.Stop Examining", False },
544 { "menuFile.Mail Move", True },
545 { "menuFile.Reload CMail Message", True },
549 Enables trainingOnEnables[] = {
550 { "menuMode.Edit Comment", False },
551 { "menuMode.Pause", False },
552 { "menuStep.Forward", False },
553 { "menuStep.Backward", False },
554 { "menuStep.Forward to End", False },
555 { "menuStep.Back to Start", False },
556 { "menuStep.Move Now", False },
557 { "menuStep.Truncate Game", False },
561 Enables trainingOffEnables[] = {
562 { "menuMode.Edit Comment", True },
563 { "menuMode.Pause", True },
564 { "menuStep.Forward", True },
565 { "menuStep.Backward", True },
566 { "menuStep.Forward to End", True },
567 { "menuStep.Back to Start", True },
568 { "menuStep.Move Now", True },
569 { "menuStep.Truncate Game", True },
573 Enables machineThinkingEnables[] = {
574 { "menuFile.Load Game", False },
575 { "menuFile.Load Next Game", False },
576 { "menuFile.Load Previous Game", False },
577 { "menuFile.Reload Same Game", False },
578 { "menuFile.Paste Game", False },
579 { "menuFile.Load Position", False },
580 { "menuFile.Load Next Position", False },
581 { "menuFile.Load Previous Position", False },
582 { "menuFile.Reload Same Position", False },
583 { "menuFile.Paste Position", False },
584 { "menuMode.Machine White", False },
585 { "menuMode.Machine Black", False },
586 { "menuMode.Two Machines", False },
587 { "menuStep.Retract Move", False },
591 Enables userThinkingEnables[] = {
592 { "menuFile.Load Game", True },
593 { "menuFile.Load Next Game", True },
594 { "menuFile.Load Previous Game", True },
595 { "menuFile.Reload Same Game", True },
596 { "menuFile.Paste Game", True },
597 { "menuFile.Load Position", True },
598 { "menuFile.Load Next Position", True },
599 { "menuFile.Load Previous Position", True },
600 { "menuFile.Reload Same Position", True },
601 { "menuFile.Paste Position", True },
602 { "menuMode.Machine White", True },
603 { "menuMode.Machine Black", True },
604 { "menuMode.Two Machines", True },
605 { "menuStep.Retract Move", True },
611 MenuItem fileMenu[] = {
612 {N_("New Shuffle Game ..."), ShuffleMenuProc},
613 {N_("New Variant ..."), NewVariantProc}, // [HGM] variant: not functional yet
614 // {"----", NothingProc},
615 // {N_("Save Game"), SaveGameProc},
616 // {"----", NothingProc},
617 {N_("Copy Game"), CopyGameProc},
618 {N_("Paste Game"), PasteGameProc},
619 // {"----", NothingProc},
620 // {N_("Load Position"), LoadPositionProc},
621 // {N_("Load Next Position"), LoadNextPositionProc},
622 // {N_("Load Previous Position"), LoadPrevPositionProc},
623 // {N_("Reload Same Position"), ReloadPositionProc},
624 // {N_("Save Position"), SavePositionProc},
625 // {"----", NothingProc},
626 {N_("Copy Position"), CopyPositionProc},
627 {N_("Paste Position"), PastePositionProc},
628 // {"----", NothingProc},
629 {N_("Mail Move"), MailMoveProc},
630 {N_("Reload CMail Message"), ReloadCmailMsgProc},
631 // {"----", NothingProc},
635 MenuItem modeMenu[] = {
636 // {N_("Machine White"), MachineWhiteProc},
637 // {N_("Machine Black"), MachineBlackProc},
638 // {N_("Two Machines"), TwoMachinesProc},
639 // {N_("Analysis Mode"), AnalyzeModeProc},
640 // {N_("Analyze File"), AnalyzeFileProc },
641 // {N_("ICS Client"), IcsClientProc},
642 // {N_("Edit Game"), EditGameProc},
643 // {N_("Edit Position"), EditPositionProc},
644 // {N_("Training"), TrainingProc},
645 // {"----", NothingProc},
646 {N_("Show Engine Output"), EngineOutputProc},
647 {N_("Show Evaluation Graph"), EvalGraphProc},
648 {N_("Show Game List"), ShowGameListProc},
649 // {"Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
650 // {"----", NothingProc},
651 // {N_("Edit Tags"), EditTagsProc},
652 {N_("Edit Comment"), EditCommentProc},
653 {N_("ICS Input Box"), IcsInputBoxProc},
657 MenuItem optionsMenu[] = {
658 // {N_("Flip View"), FlipViewProc},
659 // {"----", NothingProc},
660 {N_("Adjudications ..."), EngineMenuProc},
661 {N_("General Settings ..."), UciMenuProc},
662 {N_("Engine #1 Settings ..."), FirstSettingsProc},
663 {N_("Engine #2 Settings ..."), SecondSettingsProc},
664 {N_("Time Control ..."), TimeControlProc},
665 {"----", NothingProc},
666 // {N_("Always Queen"), AlwaysQueenProc},
667 // {N_("Animate Dragging"), AnimateDraggingProc},
668 // {N_("Animate Moving"), AnimateMovingProc},
669 // {N_("Auto Comment"), AutocommProc},
670 // {N_("Auto Flag"), AutoflagProc},
671 // {N_("Auto Flip View"), AutoflipProc},
672 // {N_("Auto Observe"), AutobsProc},
673 // {N_("Auto Raise Board"), AutoraiseProc},
674 // {N_("Auto Save"), AutosaveProc},
675 // {N_("Blindfold"), BlindfoldProc},
676 // {N_("Flash Moves"), FlashMovesProc},
677 // {N_("Get Move List"), GetMoveListProc},
679 // {N_("Highlight Dragging"), HighlightDraggingProc},
681 // {N_("Highlight Last Move"), HighlightLastMoveProc},
682 // {N_("Move Sound"), MoveSoundProc},
683 // {N_("ICS Alarm"), IcsAlarmProc},
684 // {N_("Old Save Style"), OldSaveStyleProc},
685 // {N_("Periodic Updates"), PeriodicUpdatesProc},
686 // {N_("Ponder Next Move"), PonderNextMoveProc},
687 // {N_("Popup Exit Message"), PopupExitMessageProc},
688 // {N_("Popup Move Errors"), PopupMoveErrorsProc},
689 // {N_("Premove"), PremoveProc},
690 // {N_("Quiet Play"), QuietPlayProc},
691 // {N_("Hide Thinking"), HideThinkingProc},
692 // {N_("Test Legality"), TestLegalityProc},
693 // {N_("Show Coords"), ShowCoordsProc},
694 {"----", NothingProc},
695 {N_("Save Settings Now"), SaveSettingsProc},
696 {N_("Save Settings on Exit"), SaveOnExitProc},
701 {N_("File"), fileMenu},
702 {N_("Mode"), modeMenu},
703 {N_("Options"), optionsMenu},
707 #define PIECE_MENU_SIZE 18
708 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
709 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
710 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
711 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
712 N_("Empty square"), N_("Clear board") },
713 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
714 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
715 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
716 N_("Empty square"), N_("Clear board") }
718 /* must be in same order as PieceMenuStrings! */
719 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
720 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
721 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
722 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
723 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
724 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
725 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
726 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
727 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
730 #define DROP_MENU_SIZE 6
731 String dropMenuStrings[DROP_MENU_SIZE] = {
732 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
734 /* must be in same order as PieceMenuStrings! */
735 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
736 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
737 WhiteRook, WhiteQueen
745 DropMenuEnables dmEnables[] = {
754 { XtNborderWidth, 0 },
755 { XtNdefaultDistance, 0 },
759 { XtNborderWidth, 0 },
760 { XtNresizable, (XtArgVal) True },
764 { XtNborderWidth, 0 },
769 XtResource clientResources[] = {
770 { "flashCount", "flashCount", XtRInt, sizeof(int),
771 XtOffset(AppDataPtr, flashCount), XtRImmediate,
772 (XtPointer) FLASH_COUNT },
775 XtActionsRec boardActions[] = {
776 // { "HandleUserMove", HandleUserMove },
777 { "AnimateUserMove", AnimateUserMove },
778 // { "FileNameAction", FileNameAction },
779 { "HandlePV", HandlePV },
780 { "UnLoadPV", UnLoadPV },
781 { "AskQuestionProc", AskQuestionProc },
782 { "AskQuestionReplyAction", AskQuestionReplyAction },
783 { "PieceMenuPopup", PieceMenuPopup },
784 // { "WhiteClock", WhiteClock },
785 // { "BlackClock", BlackClock },
786 { "Iconify", Iconify },
787 { "LoadSelectedProc", LoadSelectedProc },
788 // { "LoadPositionProc", LoadPositionProc },
789 // { "LoadNextPositionProc", LoadNextPositionProc },
790 // { "LoadPrevPositionProc", LoadPrevPositionProc },
791 // { "ReloadPositionProc", ReloadPositionProc },
792 { "CopyPositionProc", CopyPositionProc },
793 { "PastePositionProc", PastePositionProc },
794 { "CopyGameProc", CopyGameProc },
795 { "PasteGameProc", PasteGameProc },
796 // { "SaveGameProc", SaveGameProc },
797 // { "SavePositionProc", SavePositionProc },
798 { "MailMoveProc", MailMoveProc },
799 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
800 // { "MachineWhiteProc", MachineWhiteProc },
801 // { "MachineBlackProc", MachineBlackProc },
802 // { "AnalysisModeProc", AnalyzeModeProc },
803 // { "AnalyzeFileProc", AnalyzeFileProc },
804 // { "TwoMachinesProc", TwoMachinesProc },
805 // { "IcsClientProc", IcsClientProc },
806 // { "EditGameProc", EditGameProc },
807 // { "EditPositionProc", EditPositionProc },
808 // { "TrainingProc", EditPositionProc },
809 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
810 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
811 { "ShowGameListProc", ShowGameListProc },
812 // { "ShowMoveListProc", HistoryShowProc},
813 // { "EditTagsProc", EditCommentProc },
814 { "EditCommentProc", EditCommentProc },
815 // { "IcsAlarmProc", IcsAlarmProc },
816 { "IcsInputBoxProc", IcsInputBoxProc },
817 // { "AcceptProc", AcceptProc },
818 // { "DeclineProc", DeclineProc },
819 // { "RematchProc", RematchProc },
820 // { "CallFlagProc", CallFlagProc },
821 // { "DrawProc", DrawProc },
822 // { "AdjournProc", AdjournProc },
823 // { "AbortProc", AbortProc },
824 // { "ResignProc", ResignProc },
825 // { "AdjuWhiteProc", AdjuWhiteProc },
826 // { "AdjuBlackProc", AdjuBlackProc },
827 // { "AdjuDrawProc", AdjuDrawProc },
828 { "EnterKeyProc", EnterKeyProc },
829 // { "StopObservingProc", StopObservingProc },
830 // { "StopExaminingProc", StopExaminingProc },
831 // { "BackwardProc", BackwardProc },
832 // { "ForwardProc", ForwardProc },
833 // { "ToStartProc", ToStartProc },
834 // { "ToEndProc", ToEndProc },
835 // { "RevertProc", RevertProc },
836 // { "TruncateGameProc", TruncateGameProc },
837 // { "MoveNowProc", MoveNowProc },
838 // { "RetractMoveProc", RetractMoveProc },
839 // { "AlwaysQueenProc", AlwaysQueenProc },
840 // { "AnimateDraggingProc", AnimateDraggingProc },
841 // { "AnimateMovingProc", AnimateMovingProc },
842 // { "AutoflagProc", AutoflagProc },
843 // { "AutoflipProc", AutoflipProc },
844 // { "AutobsProc", AutobsProc },
845 // { "AutoraiseProc", AutoraiseProc },
846 // { "AutosaveProc", AutosaveProc },
847 // { "BlindfoldProc", BlindfoldProc },
848 // { "FlashMovesProc", FlashMovesProc },
849 // { "FlipViewProc", FlipViewProc },
850 // { "GetMoveListProc", GetMoveListProc },
852 // { "HighlightDraggingProc", HighlightDraggingProc },
854 // { "HighlightLastMoveProc", HighlightLastMoveProc },
855 // { "IcsAlarmProc", IcsAlarmProc },
856 // { "MoveSoundProc", MoveSoundProc },
857 // { "OldSaveStyleProc", OldSaveStyleProc },
858 // { "PeriodicUpdatesProc", PeriodicUpdatesProc },
859 // { "PonderNextMoveProc", PonderNextMoveProc },
860 // { "PopupExitMessageProc", PopupExitMessageProc },
861 // { "PopupMoveErrorsProc", PopupMoveErrorsProc },
862 // { "PremoveProc", PremoveProc },
863 // { "QuietPlayProc", QuietPlayProc },
864 // { "ShowThinkingProc", ShowThinkingProc },
865 // { "HideThinkingProc", HideThinkingProc },
866 // { "TestLegalityProc", TestLegalityProc },
867 { "SaveSettingsProc", SaveSettingsProc },
868 { "SaveOnExitProc", SaveOnExitProc },
869 // { "InfoProc", InfoProc },
870 // { "ManProc", ManProc },
871 // { "HintProc", HintProc },
872 // { "BookProc", BookProc },
873 { "AboutGameProc", AboutGameProc },
874 { "DebugProc", DebugProc },
875 { "NothingProc", NothingProc },
876 { "CommentPopDown", (XtActionProc) CommentPopDown },
877 { "EditCommentPopDown", (XtActionProc) EditCommentPopDown },
878 { "TagsPopDown", (XtActionProc) TagsPopDown },
879 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
880 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
881 // { "FileNamePopDown", (XtActionProc) FileNamePopDown },
882 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
883 { "GameListPopDown", (XtActionProc) GameListPopDown },
884 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
885 // { "HistoryPopDown", (XtActionProc) HistoryPopDown },
886 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
887 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
888 { "ShufflePopDown", (XtActionProc) ShufflePopDown },
889 { "EnginePopDown", (XtActionProc) EnginePopDown },
890 { "UciPopDown", (XtActionProc) UciPopDown },
891 { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
892 { "NewVariantPopDown", (XtActionProc) NewVariantPopDown },
893 { "SettingsPopDown", (XtActionProc) SettingsPopDown },
894 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
897 //char globalTranslations[] =
898 // ":<Key>R: ResignProc() \n \
899 // :<Key>r: ResetProc() \n \
900 // :<Key>g: LoadGameProc() \n \
901 // :<Key>N: LoadNextGameProc() \n \
902 // :<Key>P: LoadPrevGameProc() \n \
903 // :<Key>Q: QuitProc() \n \
904 // :<Key>F: ToEndProc() \n \
905 // :<Key>f: ForwardProc() \n \
906 // :<Key>B: ToStartProc() \n \
907 // :<Key>b: BackwardProc() \n \
908 // :<Key>p: PauseProc() \n \
909 // :<Key>d: DrawProc() \n \
910 // :<Key>t: CallFlagProc() \n \
911 // :<Key>i: Iconify() \n \
912 // :<Key>c: Iconify() \n \
913 // :<Key>v: FlipViewProc() \n \
914 // <KeyDown>Control_L: BackwardProc() \n \
915 // <KeyUp>Control_L: ForwardProc() \n \
916 // <KeyDown>Control_R: BackwardProc() \n \
917 // <KeyUp>Control_R: ForwardProc() \n \
918 // Shift<Key>1: AskQuestionProc(\"Direct command\",\
919 // \"Send to chess program:\",,1) \n \
920 // Shift<Key>2: AskQuestionProc(\"Direct command\",\
921 // \"Send to second chess program:\",,2) \n";
923 //char boardTranslations[] =
924 // "<Btn1Down>: HandleUserMove() \n \
925 // <Btn1Up>: HandleUserMove() \n \
926 // <Btn1Motion>: AnimateUserMove() \n \
927 // <Btn3Motion>: HandlePV() \n \
928 // <Btn3Up>: UnLoadPV() \n \
929 // Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
930 // PieceMenuPopup(menuB) \n \
931 // Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
932 // PieceMenuPopup(menuW) \n \
933 // Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
934 // PieceMenuPopup(menuW) \n \
935 // Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
936 // PieceMenuPopup(menuB) \n";
938 //char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
939 //char blackTranslations[] = "<BtnDown>: BlackClock()\n";
941 char ICSInputTranslations[] =
942 "<Key>Return: EnterKeyProc() \n";
944 String xboardResources[] = {
945 // "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
946 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
947 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
951 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
952 "magenta", "cyan", "white" };
956 TextColors textColors[(int)NColorClasses];
958 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
960 parse_color(str, which)
964 char *p, buf[100], *d;
967 if (strlen(str) > 99) /* watch bounds on buf */
972 for (i=0; i<which; ++i) {
979 /* Could be looking at something like:
981 .. in which case we want to stop on a comma also */
982 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
986 return -1; /* Use default for empty field */
989 if (which == 2 || isdigit(*p))
992 while (*p && isalpha(*p))
997 for (i=0; i<8; ++i) {
998 if (!StrCaseCmp(buf, cnames[i]))
999 return which? (i+40) : (i+30);
1001 if (!StrCaseCmp(buf, "default")) return -1;
1003 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1008 parse_cpair(cc, str)
1012 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1013 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1018 /* bg and attr are optional */
1019 textColors[(int)cc].bg = parse_color(str, 1);
1020 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1021 textColors[(int)cc].attr = 0;
1030 /* this should raise the board to the top */
1031 gtk_window_present(GTK_WINDOW(GUI_Window));
1035 //---------------------------------------------------------------------------------------------------------
1036 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1039 #define CW_USEDEFAULT (1<<31)
1040 #define ICS_TEXT_MENU_SIZE 90
1041 #define DEBUG_FILE "xboard.debug"
1042 #define SetCurrentDirectory chdir
1043 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1047 // these two must some day move to frontend.h, when they are implemented
1048 Boolean GameListIsUp();
1050 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1053 // front-end part of option handling
1055 // [HGM] This platform-dependent table provides the location for storing the color info
1056 extern char *crWhite, * crBlack;
1060 &appData.whitePieceColor,
1061 &appData.blackPieceColor,
1062 &appData.lightSquareColor,
1063 &appData.darkSquareColor,
1064 &appData.highlightSquareColor,
1065 &appData.premoveHighlightColor,
1066 &appData.lowTimeWarningColor,
1078 ParseFont(char *name, int number)
1079 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1081 case 0: // CLOCK_FONT
1082 appData.clockFont = strdup(name);
1084 case 1: // MESSAGE_FONT
1085 appData.font = strdup(name);
1087 case 2: // COORD_FONT
1088 appData.coordFont = strdup(name);
1097 { // only 2 fonts currently
1098 appData.clockFont = CLOCK_FONT_NAME;
1099 appData.coordFont = COORD_FONT_NAME;
1100 appData.font = DEFAULT_FONT_NAME;
1105 { // no-op, until we identify the code for this already in XBoard and move it here
1109 ParseColor(int n, char *name)
1110 { // in XBoard, just copy the color-name string
1111 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1115 ParseTextAttribs(ColorClass cc, char *s)
1117 (&appData.colorShout)[cc] = strdup(s);
1121 ParseBoardSize(void *addr, char *name)
1123 appData.boardSize = strdup(name);
1128 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1132 SetCommPortDefaults()
1133 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1136 // [HGM] args: these three cases taken out to stay in front-end
1138 SaveFontArg(FILE *f, ArgDescriptor *ad)
1141 switch((int)ad->argLoc) {
1142 case 0: // CLOCK_FONT
1143 name = appData.clockFont;
1145 case 1: // MESSAGE_FONT
1146 name = appData.font;
1148 case 2: // COORD_FONT
1149 name = appData.coordFont;
1154 // Do not save fonts for now, as the saved font would be board-size specific
1155 // and not suitable for a re-start at another board size
1156 // fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, name);
1161 { // nothing to do, as the sounds are at all times represented by their text-string names already
1165 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1166 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1167 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)ad->argLoc]);
1171 SaveColor(FILE *f, ArgDescriptor *ad)
1172 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1173 if(colorVariable[(int)ad->argLoc])
1174 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)ad->argLoc]);
1178 SaveBoardSize(FILE *f, char *name, void *addr)
1179 { // wrapper to shield back-end from BoardSize & sizeInfo
1180 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1184 ParseCommPortSettings(char *s)
1185 { // no such option in XBoard (yet)
1188 extern Widget engineOutputShell;
1189 extern Widget tagsShell, editTagsShell;
1192 GetActualPlacement(Widget wg, WindowPlacement *wp)
1202 XtSetArg(args[i], XtNx, &x); i++;
1203 XtSetArg(args[i], XtNy, &y); i++;
1204 XtSetArg(args[i], XtNwidth, &w); i++;
1205 XtSetArg(args[i], XtNheight, &h); i++;
1206 XtGetValues(wg, args, i);
1215 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1216 // In XBoard this will have to wait until awareness of window parameters is implemented
1218 // GetActualPlacement(shellWidget, &wpMain);
1219 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1220 // if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1221 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1222 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1223 if(commentShell) GetActualPlacement(commentShell, &wpComment);
1224 else GetActualPlacement(editShell, &wpComment);
1225 if(tagsShell) GetActualPlacement(tagsShell, &wpTags);
1226 else GetActualPlacement(editTagsShell, &wpTags);
1230 PrintCommPortSettings(FILE *f, char *name)
1231 { // This option does not exist in XBoard
1235 MySearchPath(char *installDir, char *name, char *fullname)
1236 { // just append installDir and name. Perhaps ExpandPath should be used here?
1237 name = ExpandPathName(name);
1238 if(name && name[0] == '/') strcpy(fullname, name); else {
1239 sprintf(fullname, "%s%c%s", installDir, '/', name);
1245 MyGetFullPathName(char *name, char *fullname)
1246 { // should use ExpandPath?
1247 name = ExpandPathName(name);
1248 strcpy(fullname, name);
1253 EnsureOnScreen(int *x, int *y, int minX, int minY)
1260 { // [HGM] args: allows testing if main window is realized from back-end
1261 return xBoardWindow != 0;
1265 PopUpStartupDialog()
1266 { // start menu not implemented in XBoard
1269 ConvertToLine(int argc, char **argv)
1271 static char line[128*1024], buf[1024];
1275 for(i=1; i<argc; i++) {
1276 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1277 && argv[i][0] != '{' )
1278 sprintf(buf, "{%s} ", argv[i]);
1279 else sprintf(buf, "%s ", argv[i]);
1282 line[strlen(line)-1] = NULLCHAR;
1286 //--------------------------------------------------------------------------------------------
1289 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1292 #define BoardSize int
1293 void InitDrawingSizes(BoardSize boardSize, int flags)
1294 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1295 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1297 XtGeometryResult gres;
1300 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1301 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1303 timerWidth = (boardWidth - sep) / 2;
1305 if (appData.titleInWindow)
1310 w = boardWidth - 2*bor;
1314 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1318 if(!formWidget) return;
1321 * Inhibit shell resizing.
1324 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1327 for(i=0; i<4; i++) {
1329 for(p=0; p<=(int)WhiteKing; p++)
1330 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1331 if(gameInfo.variant == VariantShogi) {
1332 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1333 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1334 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1335 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1336 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1339 if(gameInfo.variant == VariantGothic) {
1340 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1344 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1345 for(p=0; p<=(int)WhiteKing; p++)
1346 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1347 if(gameInfo.variant == VariantShogi) {
1348 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1349 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1350 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1351 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1352 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1355 if(gameInfo.variant == VariantGothic) {
1356 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1362 for(i=0; i<2; i++) {
1364 for(p=0; p<=(int)WhiteKing; p++)
1365 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1366 if(gameInfo.variant == VariantShogi) {
1367 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1368 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1369 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1370 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1371 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1374 if(gameInfo.variant == VariantGothic) {
1375 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1386 void EscapeExpand(char *p, char *q)
1387 { // [HGM] initstring: routine to shape up string arguments
1388 while(*p++ = *q++) if(p[-1] == '\\')
1390 case 'n': p[-1] = '\n'; break;
1391 case 'r': p[-1] = '\r'; break;
1392 case 't': p[-1] = '\t'; break;
1393 case '\\': p[-1] = '\\'; break;
1394 case 0: *p = 0; return;
1395 default: p[-1] = q[-1]; break;
1404 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1405 XSetWindowAttributes window_attributes;
1407 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1408 XrmValue vFrom, vTo;
1409 XtGeometryResult gres;
1412 int forceMono = False;
1414 srandom(time(0)); // [HGM] book: make random truly random
1416 setbuf(stdout, NULL);
1417 setbuf(stderr, NULL);
1420 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1421 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1425 programName = strrchr(argv[0], '/');
1426 if (programName == NULL)
1427 programName = argv[0];
1432 XtSetLanguageProc(NULL, NULL, NULL);
1433 bindtextdomain(PACKAGE, LOCALEDIR);
1434 textdomain(PACKAGE);
1438 gtk_init (&argc, &argv);
1440 /* parse glade file to build widgets */
1442 builder = gtk_builder_new ();
1443 gtk_builder_add_from_file (builder, "gtk-interface.xml", NULL);
1445 /* test if everything worked ok */
1447 GUI_Window = GTK_WIDGET (gtk_builder_get_object (builder, "MainWindow"));
1448 if(!GUI_Window) printf("Error: gtk_builder didn't work!\n");
1450 GUI_Aspect = GTK_WIDGET (gtk_builder_get_object (builder, "Aspectframe"));
1451 if(!GUI_Aspect) printf("Error: gtk_builder didn't work!\n");
1453 GUI_Menubar = GTK_WIDGET (gtk_builder_get_object (builder, "MenuBar"));
1454 if(!GUI_Menubar) printf("Error: gtk_builder didn't work!\n");
1455 GUI_Timer = GTK_WIDGET (gtk_builder_get_object (builder, "Timer"));
1456 if(!GUI_Timer) printf("Error: gtk_builder didn't work!\n");
1457 GUI_Buttonbar = GTK_WIDGET (gtk_builder_get_object (builder, "ButtonBar"));
1458 if(!GUI_Buttonbar) printf("Error: gtk_builder didn't work!\n");
1459 GUI_Board = GTK_WIDGET (gtk_builder_get_object (builder, "Board"));
1460 if(!GUI_Board) printf("Error: gtk_builder didn't work!\n");
1462 GUI_Whiteclock = GTK_WIDGET (gtk_builder_get_object (builder, "WhiteClock"));
1463 if(!GUI_Whiteclock) printf("Error: gtk_builder didn't work!\n");
1465 GUI_Blackclock = GTK_WIDGET (gtk_builder_get_object (builder, "BlackClock"));
1466 if(!GUI_Blackclock) printf("Error: gtk_builder didn't work!\n");
1468 /* GTK lists stores*/
1469 LIST_MoveHistory = GTK_LIST_STORE (gtk_builder_get_object (builder, "MoveHistoryStore"));
1470 if(!LIST_MoveHistory) printf("Error: gtk_builder didn't work!\n");
1472 LIST_GameList = GTK_LIST_STORE (gtk_builder_get_object (builder, "GameListStore"));
1473 if(!LIST_GameList) printf("Error: gtk_builder didn't work!\n");
1475 /* EditTags window */
1476 GUI_EditTags = GTK_WIDGET (gtk_builder_get_object (builder, "EditTags"));
1477 if(!GUI_EditTags) printf("Error: gtk_builder didn't work!\n");
1479 GUI_EditTagsTextArea = GTK_WIDGET (gtk_builder_get_object (builder, "EditTagsTextArea"));
1480 if(!GUI_EditTagsTextArea) printf("Error: gtk_builder didn't work!\n");
1482 /* move history and game list windows */
1483 GUI_History = GTK_WIDGET (gtk_builder_get_object (builder, "MoveHistory"));
1484 if(!GUI_History) printf("Error: gtk_builder didn't work!\n");
1486 TREE_History = GTK_TREE_VIEW (gtk_builder_get_object (builder, "MoveHistoryView"));
1487 if(!TREE_History) printf("Error: gtk_builder didn't work!\n");
1489 GUI_GameList = GTK_WIDGET (gtk_builder_get_object (builder, "GameList"));
1490 if(!GUI_GameList) printf("Error: gtk_builder didn't work!\n");
1492 TREE_Game = GTK_TREE_VIEW (gtk_builder_get_object (builder, "GameListView"));
1493 if(!TREE_Game) printf("Error: gtk_builder didn't work!\n");
1496 /* connect lists to views */
1497 gtk_tree_view_set_model(TREE_History,GTK_TREE_MODEL(LIST_MoveHistory));
1498 gtk_tree_view_set_model(TREE_Game,GTK_TREE_MODEL(LIST_GameList));
1500 gtk_builder_connect_signals (builder, NULL);
1502 // don't unref the builder, since we use it to get references to widgets
1503 // g_object_unref (G_OBJECT (builder));
1505 /* end parse glade file */
1507 appData.boardSize = "";
1508 InitAppData(ConvertToLine(argc, argv));
1511 if (p == NULL) p = "/tmp";
1512 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1513 gameCopyFilename = (char*) malloc(i);
1514 gamePasteFilename = (char*) malloc(i);
1515 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1516 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1518 // XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1519 // clientResources, XtNumber(clientResources),
1522 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1523 static char buf[MSG_SIZ];
1524 EscapeExpand(buf, appData.initString);
1525 appData.initString = strdup(buf);
1526 EscapeExpand(buf, appData.secondInitString);
1527 appData.secondInitString = strdup(buf);
1528 EscapeExpand(buf, appData.firstComputerString);
1529 appData.firstComputerString = strdup(buf);
1530 EscapeExpand(buf, appData.secondComputerString);
1531 appData.secondComputerString = strdup(buf);
1534 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1537 if (chdir(chessDir) != 0) {
1538 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1544 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1545 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1546 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1547 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1550 setbuf(debugFP, NULL);
1555 /* This feature does not work; animation needs a rewrite */
1556 appData.highlightDragging = FALSE;
1560 gameInfo.variant = StringToVariant(appData.variant);
1561 InitPosition(FALSE);
1566 clockFontPxlSize = 20;
1567 coordFontPxlSize = 20;
1573 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1574 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1577 * Determine what fonts to use.
1579 // appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1580 // clockFontID = XLoadFont(xDisplay, appData.clockFont);
1581 // clockFontStruct = XQueryFont(xDisplay, clockFontID);
1582 // appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1583 // coordFontID = XLoadFont(xDisplay, appData.coordFont);
1584 // coordFontStruct = XQueryFont(xDisplay, coordFontID);
1585 // appData.font = FindFont(appData.font, fontPxlSize);
1586 // countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
1587 // countFontStruct = XQueryFont(xDisplay, countFontID);
1588 // appData.font = FindFont(appData.font, fontPxlSize);
1590 // xdb = XtDatabase(xDisplay);
1591 // XrmPutStringResource(&xdb, "*font", appData.font);
1594 * Detect if there are not enough colors available and adapt.
1596 // if (DefaultDepth(xDisplay, xScreen) <= 2) {
1597 // appData.monoMode = True;
1600 if (!appData.monoMode) {
1601 vFrom.addr = (caddr_t) appData.lightSquareColor;
1602 vFrom.size = strlen(appData.lightSquareColor);
1603 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1604 if (vTo.addr == NULL) {
1605 appData.monoMode = True;
1608 lightSquareColor = *(Pixel *) vTo.addr;
1611 if (!appData.monoMode) {
1612 vFrom.addr = (caddr_t) appData.darkSquareColor;
1613 vFrom.size = strlen(appData.darkSquareColor);
1614 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1615 if (vTo.addr == NULL) {
1616 appData.monoMode = True;
1619 darkSquareColor = *(Pixel *) vTo.addr;
1622 if (!appData.monoMode) {
1623 vFrom.addr = (caddr_t) appData.whitePieceColor;
1624 vFrom.size = strlen(appData.whitePieceColor);
1625 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1626 if (vTo.addr == NULL) {
1627 appData.monoMode = True;
1630 whitePieceColor = *(Pixel *) vTo.addr;
1633 if (!appData.monoMode) {
1634 vFrom.addr = (caddr_t) appData.blackPieceColor;
1635 vFrom.size = strlen(appData.blackPieceColor);
1636 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1637 if (vTo.addr == NULL) {
1638 appData.monoMode = True;
1641 blackPieceColor = *(Pixel *) vTo.addr;
1645 if (!appData.monoMode) {
1646 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1647 vFrom.size = strlen(appData.highlightSquareColor);
1648 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1649 if (vTo.addr == NULL) {
1650 appData.monoMode = True;
1653 highlightSquareColor = *(Pixel *) vTo.addr;
1657 if (!appData.monoMode) {
1658 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1659 vFrom.size = strlen(appData.premoveHighlightColor);
1660 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1661 if (vTo.addr == NULL) {
1662 appData.monoMode = True;
1665 premoveHighlightColor = *(Pixel *) vTo.addr;
1670 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1673 if (appData.bitmapDirectory == NULL ||
1674 appData.bitmapDirectory[0] == NULLCHAR)
1675 appData.bitmapDirectory = DEF_BITMAP_DIR;
1678 if (appData.lowTimeWarning && !appData.monoMode) {
1679 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1680 vFrom.size = strlen(appData.lowTimeWarningColor);
1681 // XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1682 if (vTo.addr == NULL)
1683 appData.monoMode = True;
1685 lowTimeWarningColor = *(Pixel *) vTo.addr;
1688 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1689 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1690 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1691 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1692 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1693 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1694 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1695 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1696 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1697 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1699 if (appData.colorize) {
1701 _("%s: can't parse color names; disabling colorization\n"),
1704 appData.colorize = FALSE;
1706 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
1707 textColors[ColorNone].attr = 0;
1709 // XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1715 layoutName = "tinyLayout";
1716 } else if (smallLayout) {
1717 layoutName = "smallLayout";
1719 layoutName = "normalLayout";
1722 if (appData.titleInWindow) {
1723 /* todo check what this appdata does */
1726 if (appData.showButtonBar) {
1727 /* TODO hide button bar if requested */
1731 if (appData.titleInWindow)
1736 if (appData.showButtonBar)
1743 if (appData.showButtonBar)
1753 /* set some checkboxes in the menu according to appData */
1755 if (appData.alwaysPromoteToQueen)
1756 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Always Queen")),TRUE);
1758 if (appData.animateDragging)
1759 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Animate Dragging")),TRUE);
1761 if (appData.animate)
1762 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Animate Moving")),TRUE);
1764 if (appData.autoComment)
1765 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Comment")),TRUE);
1767 if (appData.autoCallFlag)
1768 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Flag")),TRUE);
1770 if (appData.autoFlipView)
1771 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Flip View")),TRUE);
1773 if (appData.autoObserve)
1774 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Observe")),TRUE);
1776 if (appData.autoRaiseBoard)
1777 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Raise Board")),TRUE);
1779 if (appData.autoSaveGames)
1780 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Save")),TRUE);
1782 if (appData.saveGameFile[0] != NULLCHAR)
1784 /* Can't turn this off from menu */
1785 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Save")),TRUE);
1786 gtk_action_set_sensitive(GTK_ACTION (gtk_builder_get_object (builder, "menuOptions.Auto Save")),FALSE);
1789 if (appData.blindfold)
1790 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Blindfold")),TRUE);
1792 if (appData.flashCount > 0)
1793 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Flash Moves")),TRUE);
1795 if (appData.getMoveList)
1796 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Get Move List")),TRUE);
1799 if (appData.highlightDragging)
1800 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Highlight Dragging")),TRUE);
1803 if (appData.highlightLastMove)
1804 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Highlight Last Move")),TRUE);
1806 if (appData.icsAlarm)
1807 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.ICS Alarm")),TRUE);
1809 if (appData.ringBellAfterMoves)
1810 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Move Sound")),TRUE);
1812 if (appData.oldSaveStyle)
1813 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Old Save Style")),TRUE);
1815 if (appData.periodicUpdates)
1816 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Periodic Updates")),TRUE);
1818 if (appData.ponderNextMove)
1819 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Ponder Next Move")),TRUE);
1821 if (appData.popupExitMessage)
1822 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Popup Exit Message")),TRUE);
1824 if (appData.popupMoveErrors)
1825 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Popup Move Errors")),TRUE);
1827 if (appData.premove)
1828 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Premove")),TRUE);
1830 if (appData.quietPlay)
1831 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Quit Play")),TRUE);
1833 if (appData.showCoords)
1834 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Show Coords")),TRUE);
1836 if (appData.showThinking)
1837 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Hide Thinking")),TRUE);
1839 if (appData.testLegality)
1840 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Test Legality")),TRUE);
1843 // if (saveSettingsOnExit) {
1844 // XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
1849 /* end setting check boxes */
1851 /* load square colors */
1852 SVGLightSquare = load_pixbuf("svg/LightSquare.svg",squareSize);
1853 SVGDarkSquare = load_pixbuf("svg/DarkSquare.svg",squareSize);
1854 SVGNeutralSquare = load_pixbuf("svg/NeutralSquare.svg",squareSize);
1856 /* use two icons to indicate if it is white's or black's turn */
1857 WhiteIcon = load_pixbuf("svg/icon_white.svg",0);
1858 BlackIcon = load_pixbuf("svg/icon_black.svg",0);
1859 WindowIcon = WhiteIcon;
1860 gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
1863 /* realize window */
1864 gtk_widget_show (GUI_Window);
1866 /* recalc boardsize */
1871 if (appData.animate || appData.animateDragging)
1876 if (errorExitStatus == -1) {
1877 if (appData.icsActive) {
1878 /* We now wait until we see "login:" from the ICS before
1879 sending the logon script (problems with timestamp otherwise) */
1880 /*ICSInitScript();*/
1881 if (appData.icsInputBox) ICSInputBoxPopUp();
1885 signal(SIGWINCH, TermSizeSigHandler);
1887 signal(SIGINT, IntSigHandler);
1888 signal(SIGTERM, IntSigHandler);
1889 if (*appData.cmailGameName != NULLCHAR) {
1890 signal(SIGUSR1, CmailSigHandler);
1893 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1897 * Create a cursor for the board widget.
1898 * (This needs to be called after the window has been created to have access to board-window)
1901 BoardCursor = gdk_cursor_new(GDK_HAND2);
1902 gdk_window_set_cursor(GUI_Board->window, BoardCursor);
1903 gdk_cursor_destroy(BoardCursor);
1908 if (appData.debugMode) fclose(debugFP); // [DM] debug
1915 if (appData.icsActive && oldICSInteractionTitle != NULL) {
1916 DisplayIcsInteractionTitle(oldICSInteractionTitle);
1918 if (saveSettingsOnExit) SaveSettings(settingsFileName);
1919 unlink(gameCopyFilename);
1920 unlink(gamePasteFilename);
1923 RETSIGTYPE TermSizeSigHandler(int sig)
1936 CmailSigHandler(sig)
1942 signal(SIGUSR1, SIG_IGN); /* suspend handler */
1944 /* Activate call-back function CmailSigHandlerCallBack() */
1945 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1947 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1951 CmailSigHandlerCallBack(isr, closure, message, count, error)
1959 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
1961 /**** end signal code ****/
1971 f = fopen(appData.icsLogon, "r");
1977 strcat(buf, appData.icsLogon);
1978 f = fopen(buf, "r");
1982 ProcessICSInitScript(f);
1989 EditCommentPopDown();
1999 if (!menuBarWidget) return;
2000 w = XtNameToWidget(menuBarWidget, "menuStep.Revert");
2002 DisplayError("menuStep.Revert", 0);
2004 XtSetSensitive(w, !grey);
2009 SetMenuEnables(enab)
2014 if (!builder) return;
2015 while (enab->name != NULL) {
2016 o = gtk_builder_get_object(builder, enab->name);
2017 if(GTK_IS_WIDGET(o))
2018 gtk_widget_set_sensitive(GTK_WIDGET (o),enab->value);
2021 if(GTK_IS_ACTION(o))
2022 gtk_action_set_sensitive(GTK_ACTION (o),enab->value);
2024 DisplayError(enab->name, 0);
2032 SetMenuEnables(icsEnables);
2035 if (appData.zippyPlay && !appData.noChessProgram) /* [DM] icsEngineAnalyze */
2036 {}; // XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2043 SetMenuEnables(ncpEnables);
2049 SetMenuEnables(gnuEnables);
2055 SetMenuEnables(cmailEnables);
2061 SetMenuEnables(trainingOnEnables);
2062 if (appData.showButtonBar) {
2063 // XtSetSensitive(buttonBarWidget, False);
2069 SetTrainingModeOff()
2071 SetMenuEnables(trainingOffEnables);
2072 if (appData.showButtonBar) {
2073 // XtSetSensitive(buttonBarWidget, True);
2078 SetUserThinkingEnables()
2080 if (appData.noChessProgram) return;
2081 SetMenuEnables(userThinkingEnables);
2085 SetMachineThinkingEnables()
2087 if (appData.noChessProgram) return;
2088 SetMenuEnables(machineThinkingEnables);
2090 case MachinePlaysBlack:
2091 case MachinePlaysWhite:
2092 case TwoMachinesPlay:
2093 // XtSetSensitive(XtNameToWidget(menuBarWidget,
2094 // ModeToWidgetName(gameMode)), True);
2101 #define Abs(n) ((n)<0 ? -(n) : (n))
2104 * Find a font that matches "pattern" that is as close as
2105 * possible to the targetPxlSize. Prefer fonts that are k
2106 * pixels smaller to fonts that are k pixels larger. The
2107 * pattern must be in the X Consortium standard format,
2108 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2109 * The return value should be freed with XtFree when no
2112 char *FindFont(pattern, targetPxlSize)
2116 char **fonts, *p, *best, *scalable, *scalableTail;
2117 int i, j, nfonts, minerr, err, pxlSize;
2120 char **missing_list;
2122 char *def_string, *base_fnt_lst, strInt[3];
2124 XFontStruct **fnt_list;
2126 base_fnt_lst = calloc(1, strlen(pattern) + 3);
2127 sprintf(strInt, "%d", targetPxlSize);
2128 p = strstr(pattern, "--");
2129 strncpy(base_fnt_lst, pattern, p - pattern + 2);
2130 strcat(base_fnt_lst, strInt);
2131 strcat(base_fnt_lst, strchr(p + 2, '-'));
2133 if ((fntSet = XCreateFontSet(xDisplay,
2137 &def_string)) == NULL) {
2139 fprintf(stderr, _("Unable to create font set.\n"));
2143 nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
2145 // fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2146 // if (nfonts < 1) {
2147 // fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2148 // programName, pattern);
2156 for (i=0; i<nfonts; i++) {
2159 if (*p != '-') continue;
2161 if (*p == NULLCHAR) break;
2162 if (*p++ == '-') j++;
2164 if (j < 7) continue;
2167 scalable = fonts[i];
2170 err = pxlSize - targetPxlSize;
2171 if (Abs(err) < Abs(minerr) ||
2172 (minerr > 0 && err < 0 && -err == minerr)) {
2178 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2179 /* If the error is too big and there is a scalable font,
2180 use the scalable font. */
2181 int headlen = scalableTail - scalable;
2182 p = (char *) XtMalloc(strlen(scalable) + 10);
2183 while (isdigit(*scalableTail)) scalableTail++;
2184 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2186 p = (char *) XtMalloc(strlen(best) + 1);
2189 if (appData.debugMode) {
2190 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2191 pattern, targetPxlSize, p);
2194 if (missing_count > 0)
2195 XFreeStringList(missing_list);
2196 // XFreeFontSet(xDisplay, fntSet);
2198 XFreeFontNames(fonts);
2205 /* 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*/
2214 for(i=0;i<MAXPIECES;i++)
2218 g_free(SVGpieces[i]);
2225 SVGLightSquare = load_pixbuf("svg/LightSquare.svg",squareSize);
2226 SVGDarkSquare = load_pixbuf("svg/DarkSquare.svg",squareSize);
2227 SVGNeutralSquare = load_pixbuf("svg/NeutralSquare.svg",squareSize);
2230 /* get some defaults going */
2231 for(i=WhitePawn; i<DemotePiece+1; i++)
2232 SVGpieces[i] = load_pixbuf("svg/NeutralSquare.svg",squareSize);
2234 SVGpieces[WhitePawn] = load_pixbuf("svg/WhitePawn.svg",squareSize);
2235 SVGpieces[WhiteKnight] = load_pixbuf("svg/WhiteKnight.svg",squareSize);
2236 SVGpieces[WhiteBishop] = load_pixbuf("svg/WhiteBishop.svg",squareSize);
2237 SVGpieces[WhiteRook] = load_pixbuf("svg/WhiteRook.svg",squareSize);
2238 SVGpieces[WhiteQueen] = load_pixbuf("svg/WhiteQueen.svg",squareSize);
2239 SVGpieces[WhiteKing] = load_pixbuf("svg/WhiteKing.svg",squareSize);
2241 SVGpieces[BlackPawn] = load_pixbuf("svg/BlackPawn.svg",squareSize);
2242 SVGpieces[BlackKnight] = load_pixbuf("svg/BlackKnight.svg",squareSize);
2243 SVGpieces[BlackBishop] = load_pixbuf("svg/BlackBishop.svg",squareSize);
2244 SVGpieces[BlackRook] = load_pixbuf("svg/BlackRook.svg",squareSize);
2245 SVGpieces[BlackQueen] = load_pixbuf("svg/BlackQueen.svg",squareSize);
2246 SVGpieces[BlackKing] = load_pixbuf("svg/BlackKing.svg",squareSize);
2252 static void MenuBarSelect(w, addr, index)
2257 XtActionProc proc = (XtActionProc) addr;
2259 (proc)(NULL, NULL, NULL, NULL);
2262 void CreateMenuBarPopup(parent, name, mb)
2272 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2275 XtSetArg(args[j], XtNleftMargin, 20); j++;
2276 XtSetArg(args[j], XtNrightMargin, 20); j++;
2278 while (mi->string != NULL) {
2279 if (strcmp(mi->string, "----") == 0) {
2280 entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
2283 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
2284 entry = XtCreateManagedWidget(mi->string, smeBSBObjectClass,
2286 XtAddCallback(entry, XtNcallback,
2287 (XtCallbackProc) MenuBarSelect,
2288 (caddr_t) mi->proc);
2294 Widget CreateMenuBar(mb)
2298 Widget anchor, menuBar;
2300 char menuName[MSG_SIZ];
2303 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2304 XtSetArg(args[j], XtNvSpace, 0); j++;
2305 XtSetArg(args[j], XtNborderWidth, 0); j++;
2306 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
2307 formWidget, args, j);
2309 while (mb->name != NULL) {
2310 strcpy(menuName, "menu");
2311 strcat(menuName, mb->name);
2313 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
2316 shortName[0] = _(mb->name)[0];
2317 shortName[1] = NULLCHAR;
2318 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
2321 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
2324 XtSetArg(args[j], XtNborderWidth, 0); j++;
2325 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
2327 CreateMenuBarPopup(menuBar, menuName, mb);
2335 CreatePieceMenu(name, color)
2342 ChessSquare selection;
2344 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2345 boardWidget, args, 0);
2347 for (i = 0; i < PIECE_MENU_SIZE; i++) {
2348 String item = pieceMenuStrings[color][i];
2350 if (strcmp(item, "----") == 0) {
2351 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2354 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2355 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2357 selection = pieceMenuTranslation[color][i];
2358 XtAddCallback(entry, XtNcallback,
2359 (XtCallbackProc) PieceMenuSelect,
2360 (caddr_t) selection);
2361 if (selection == WhitePawn || selection == BlackPawn) {
2362 XtSetArg(args[0], XtNpopupOnEntry, entry);
2363 XtSetValues(menu, args, 1);
2376 ChessSquare selection;
2378 // whitePieceMenu = CreatePieceMenu("menuW", 0);
2379 // blackPieceMenu = CreatePieceMenu("menuB", 1);
2381 // XtRegisterGrabAction(PieceMenuPopup, True,
2382 // (unsigned)(ButtonPressMask|ButtonReleaseMask),
2383 // GrabModeAsync, GrabModeAsync);
2385 // XtSetArg(args[0], XtNlabel, _("Drop"));
2386 // dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
2387 // boardWidget, args, 1);
2388 // for (i = 0; i < DROP_MENU_SIZE; i++) {
2389 // String item = dropMenuStrings[i];
2391 // if (strcmp(item, "----") == 0) {
2392 // entry = XtCreateManagedWidget(item, smeLineObjectClass,
2393 // dropMenu, NULL, 0);
2395 // XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2396 // entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2397 // dropMenu, args, 1);
2398 // selection = dropMenuTranslation[i];
2399 // XtAddCallback(entry, XtNcallback,
2400 // (XtCallbackProc) DropMenuSelect,
2401 // (caddr_t) selection);
2406 void SetupDropMenu()
2414 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2415 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2416 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2417 dmEnables[i].piece);
2418 XtSetSensitive(entry, p != NULL || !appData.testLegality
2419 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2420 && !appData.icsActive));
2422 while (p && *p++ == dmEnables[i].piece) count++;
2423 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2425 XtSetArg(args[j], XtNlabel, label); j++;
2426 XtSetValues(entry, args, j);
2430 void PieceMenuPopup(w, event, params, num_params)
2434 Cardinal *num_params;
2438 if (event->type != ButtonRelease) UnLoadPV(); // [HGM] pv
2439 if (event->type != ButtonPress) return;
2440 if (errorUp) ErrorPopDown();
2444 whichMenu = params[0];
2447 if(!appData.icsEngineAnalyze) return;
2448 case IcsPlayingWhite:
2449 case IcsPlayingBlack:
2450 if(!appData.zippyPlay) goto noZip;
2453 case MachinePlaysWhite:
2454 case MachinePlaysBlack:
2455 case TwoMachinesPlay: // [HGM] pv: use for showing PV
2456 if (!appData.dropMenu) {
2457 LoadPV(event->xbutton.x, event->xbutton.y);
2460 if(gameMode == TwoMachinesPlay || gameMode == AnalyzeMode ||
2461 gameMode == AnalyzeFile || gameMode == IcsObserving) return;
2464 if (!appData.dropMenu || appData.testLegality &&
2465 gameInfo.variant != VariantBughouse &&
2466 gameInfo.variant != VariantCrazyhouse) return;
2468 whichMenu = "menuD";
2474 if (((pmFromX = EventToSquare(event->xbutton.x, BOARD_WIDTH)) < 0) ||
2475 ((pmFromY = EventToSquare(event->xbutton.y, BOARD_HEIGHT)) < 0)) {
2476 pmFromX = pmFromY = -1;
2480 pmFromX = BOARD_WIDTH - 1 - pmFromX;
2482 pmFromY = BOARD_HEIGHT - 1 - pmFromY;
2484 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
2487 static void PieceMenuSelect(w, piece, junk)
2492 if (pmFromX < 0 || pmFromY < 0) return;
2493 EditPositionMenuEvent(piece, pmFromX, pmFromY);
2496 static void DropMenuSelect(w, piece, junk)
2501 if (pmFromX < 0 || pmFromY < 0) return;
2502 DropMenuEvent(piece, pmFromX, pmFromY);
2506 * If the user selects on a border boundary, return -1; if off the board,
2507 * return -2. Otherwise map the event coordinate to the square.
2509 int EventToSquare(x, limit)
2517 if ((x % (squareSize + lineGap)) >= squareSize)
2519 x /= (squareSize + lineGap);
2525 static void do_flash_delay(msec)
2531 static void drawHighlight(file, rank, line_type)
2532 int file, rank, line_type;
2537 if (lineGap == 0 || appData.blindfold) return;
2541 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
2542 (squareSize + lineGap);
2543 y = lineGap/2 + rank * (squareSize + lineGap);
2547 x = lineGap/2 + file * (squareSize + lineGap);
2548 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
2549 (squareSize + lineGap);
2553 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2555 /* draw the highlight */
2556 cairo_move_to (cr, x, y);
2557 cairo_rel_line_to (cr, 0,squareSize+lineGap);
2558 cairo_rel_line_to (cr, squareSize+lineGap,0);
2559 cairo_rel_line_to (cr, 0,-squareSize-lineGap);
2560 cairo_close_path (cr);
2562 cairo_set_line_width (cr, lineGap);
2565 /* TODO: use appdata colors */
2566 case LINE_TYPE_HIGHLIGHT:
2567 cairo_set_source_rgba (cr, 1, 1, 0, 1.0);
2570 cairo_set_source_rgba (cr, 1, 0, 0, 1.0);
2572 case LINE_TYPE_NORMAL:
2574 cairo_set_source_rgba (cr, 0, 1, 0, 1.0);
2585 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
2586 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
2589 SetHighlights(fromX, fromY, toX, toY)
2590 int fromX, fromY, toX, toY;
2592 if (hi1X != fromX || hi1Y != fromY)
2594 if (hi1X >= 0 && hi1Y >= 0)
2596 drawHighlight(hi1X, hi1Y, LINE_TYPE_NORMAL);
2598 if (fromX >= 0 && fromY >= 0)
2600 drawHighlight(fromX, fromY, LINE_TYPE_HIGHLIGHT);
2603 if (hi2X != toX || hi2Y != toY)
2605 if (hi2X >= 0 && hi2Y >= 0)
2607 drawHighlight(hi2X, hi2Y, LINE_TYPE_NORMAL);
2609 if (toX >= 0 && toY >= 0)
2611 drawHighlight(toX, toY, LINE_TYPE_HIGHLIGHT);
2625 SetHighlights(-1, -1, -1, -1);
2630 SetPremoveHighlights(fromX, fromY, toX, toY)
2631 int fromX, fromY, toX, toY;
2633 if (pm1X != fromX || pm1Y != fromY)
2635 if (pm1X >= 0 && pm1Y >= 0)
2637 drawHighlight(pm1X, pm1Y, LINE_TYPE_NORMAL);
2639 if (fromX >= 0 && fromY >= 0)
2641 drawHighlight(fromX, fromY, LINE_TYPE_PRE);
2644 if (pm2X != toX || pm2Y != toY)
2646 if (pm2X >= 0 && pm2Y >= 0)
2648 drawHighlight(pm2X, pm2Y, LINE_TYPE_NORMAL);
2650 if (toX >= 0 && toY >= 0)
2652 drawHighlight(toX, toY, LINE_TYPE_PRE);
2665 ClearPremoveHighlights()
2667 SetPremoveHighlights(-1, -1, -1, -1);
2670 static void BlankSquare(x, y, color, piece, dest)
2683 pb = SVGLightSquare;
2685 case 2: /* neutral */
2687 pb = SVGNeutralSquare;
2690 gdk_draw_pixbuf(GDK_WINDOW(GUI_Board->window),NULL,pb,0,0,x,y,-1,-1, GDK_RGB_DITHER_NORMAL, 0, 0);
2694 static void DrawPiece(piece, square_color, x, y, dest)
2696 int square_color, x, y;
2699 /* redraw background, since piece might be transparent in some areas */
2700 BlankSquare(x,y,square_color,piece,dest);
2703 gdk_draw_pixbuf(GDK_WINDOW(GUI_Board->window),NULL,
2704 GDK_PIXBUF(SVGpieces[piece]),0,0,x,y,-1,-1,
2705 GDK_RGB_DITHER_NORMAL, 0, 0);
2709 /* [HR] determine square color depending on chess variant. */
2710 static int SquareColor(row, column)
2715 if (gameInfo.variant == VariantXiangqi) {
2716 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
2718 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
2720 } else if (row <= 4) {
2726 square_color = ((column + row) % 2) == 1;
2729 /* [hgm] holdings: next line makes all holdings squares light */
2730 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
2732 return square_color;
2735 void DrawSquare(row, column, piece, do_flash)
2736 int row, column, do_flash;
2739 int square_color, x, y;
2744 /* Calculate delay in milliseconds (2-delays per complete flash) */
2745 flash_delay = 500 / appData.flashRate;
2747 /* calculate x and y coordinates from row and column */
2750 x = lineGap + ((BOARD_WIDTH-1)-column) *
2751 (squareSize + lineGap);
2752 y = lineGap + row * (squareSize + lineGap);
2756 x = lineGap + column * (squareSize + lineGap);
2757 y = lineGap + ((BOARD_HEIGHT-1)-row) *
2758 (squareSize + lineGap);
2761 square_color = SquareColor(row, column);
2763 // [HGM] holdings: blank out area between board and holdings
2764 if ( column == BOARD_LEFT-1 || column == BOARD_RGHT
2765 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
2766 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) )
2768 BlankSquare(x, y, 2, EmptySquare, xBoardWindow);
2770 // [HGM] print piece counts next to holdings
2771 string[1] = NULLCHAR;
2774 cairo_text_extents_t extents;
2779 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2781 string[0] = '0' + piece;
2783 /* TODO this has to go into the font-selection */
2784 cairo_select_font_face (cr, "Sans",
2785 CAIRO_FONT_SLANT_NORMAL,
2786 CAIRO_FONT_WEIGHT_NORMAL);
2788 cairo_set_font_size (cr, 12.0);
2789 cairo_text_extents (cr, string, &extents);
2791 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) )
2793 xpos= x + squareSize - extents.width - 2;
2794 ypos= y + extents.y_bearing + 1;
2796 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1)
2799 ypos = y + extents.y_bearing + 1;
2802 /* TODO mono mode? */
2803 cairo_move_to (cr, xpos, ypos);
2804 cairo_text_path (cr, string);
2805 cairo_set_source_rgb (cr, 1.0, 1.0, 1);
2806 cairo_fill_preserve (cr);
2807 cairo_set_source_rgb (cr, 0, 0, 0);
2808 cairo_set_line_width (cr, 0.1);
2817 /* square on the board */
2818 if (piece == EmptySquare || appData.blindfold)
2820 BlankSquare(x, y, square_color, piece, xBoardWindow);
2824 if (do_flash && appData.flashCount > 0)
2826 for (i=0; i<appData.flashCount; ++i)
2829 DrawPiece(piece, square_color, x, y, xBoardWindow);
2830 do_flash_delay(flash_delay);
2832 BlankSquare(x, y, square_color, piece, xBoardWindow);
2833 do_flash_delay(flash_delay);
2836 DrawPiece(piece, square_color, x, y, xBoardWindow);
2840 /* show coordinates if necessary */
2841 if(appData.showCoords)
2843 cairo_text_extents_t extents;
2847 /* TODO this has to go into the font-selection */
2848 cairo_select_font_face (cr, "Sans",
2849 CAIRO_FONT_SLANT_NORMAL,
2850 CAIRO_FONT_WEIGHT_NORMAL);
2851 cairo_set_font_size (cr, 12.0);
2853 string[1] = NULLCHAR;
2856 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2858 if (row == (flipView ? BOARD_HEIGHT-1 : 0) &&
2859 column >= BOARD_LEFT && column < BOARD_RGHT)
2861 string[0] = 'a' + column - BOARD_LEFT;
2862 cairo_text_extents (cr, string, &extents);
2864 xpos = x + squareSize - extents.width - 2;
2865 ypos = y + squareSize - extents.height - extents.y_bearing - 1;
2867 if (appData.monoMode)
2874 cairo_move_to (cr, xpos, ypos);
2875 cairo_text_path (cr, string);
2876 cairo_set_source_rgb (cr, 0.0, 0.0, 0);
2877 cairo_fill_preserve (cr);
2878 cairo_set_source_rgb (cr, 0, 1.0, 0);
2879 cairo_set_line_width (cr, 0.1);
2882 if ( column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT))
2885 string[0] = ONE + row;
2886 cairo_text_extents (cr, string, &extents);
2889 ypos = y + extents.height + 1;
2891 if (appData.monoMode)
2898 cairo_move_to (cr, xpos, ypos);
2899 cairo_text_path (cr, string);
2900 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
2901 cairo_fill_preserve (cr);
2902 cairo_set_source_rgb (cr, 0, 0, 1.0);
2903 cairo_set_line_width (cr, 0.1);
2915 /* Returns 1 if there are "too many" differences between b1 and b2
2916 (i.e. more than 1 move was made) */
2917 static int too_many_diffs(b1, b2)
2923 for (i=0; i<BOARD_HEIGHT; ++i) {
2924 for (j=0; j<BOARD_WIDTH; ++j) {
2925 if (b1[i][j] != b2[i][j]) {
2926 if (++c > 4) /* Castling causes 4 diffs */
2935 /* Matrix describing castling maneuvers */
2936 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
2937 static int castling_matrix[4][5] = {
2938 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
2939 { 0, 7, 4, 5, 6 }, /* 0-0, white */
2940 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
2941 { 7, 7, 4, 5, 6 } /* 0-0, black */
2944 /* Checks whether castling occurred. If it did, *rrow and *rcol
2945 are set to the destination (row,col) of the rook that moved.
2947 Returns 1 if castling occurred, 0 if not.
2949 Note: Only handles a max of 1 castling move, so be sure
2950 to call too_many_diffs() first.
2952 static int check_castle_draw(newb, oldb, rrow, rcol)
2959 /* For each type of castling... */
2960 for (i=0; i<4; ++i) {
2961 r = castling_matrix[i];
2963 /* Check the 4 squares involved in the castling move */
2965 for (j=1; j<=4; ++j) {
2966 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
2973 /* All 4 changed, so it must be a castling move */
2982 static int damage[BOARD_RANKS][BOARD_FILES];
2985 * event handler for redrawing the board
2987 void DrawPosition( repaint, board)
2988 /*Boolean*/int repaint;
2992 static int lastFlipView = 0;
2993 static int lastBoardValid = 0;
2994 static Board lastBoard;
2997 if (board == NULL) {
2998 if (!lastBoardValid) return;
3001 if (!lastBoardValid || lastFlipView != flipView) {
3002 // XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
3003 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flip View"),
3008 * It would be simpler to clear the window with XClearWindow()
3009 * but this causes a very distracting flicker.
3012 if (!repaint && lastBoardValid && lastFlipView == flipView)
3014 /* If too much changes (begin observing new game, etc.), don't
3016 do_flash = too_many_diffs(board, lastBoard) ? 0 : 1;
3018 /* Special check for castling so we don't flash both the king
3019 and the rook (just flash the king). */
3022 if (check_castle_draw(board, lastBoard, &rrow, &rcol))
3024 /* Draw rook with NO flashing. King will be drawn flashing later */
3025 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
3026 lastBoard[rrow][rcol] = board[rrow][rcol];
3030 /* First pass -- Draw (newly) empty squares and repair damage.
3031 This prevents you from having a piece show up twice while it
3032 is flashing on its new square */
3033 for (i = 0; i < BOARD_HEIGHT; i++)
3034 for (j = 0; j < BOARD_WIDTH; j++)
3035 if ((board[i][j] != lastBoard[i][j] && board[i][j] == EmptySquare)
3038 DrawSquare(i, j, board[i][j], 0);
3039 damage[i][j] = False;
3042 /* Second pass -- Draw piece(s) in new position and flash them */
3043 for (i = 0; i < BOARD_HEIGHT; i++)
3044 for (j = 0; j < BOARD_WIDTH; j++)
3045 if (board[i][j] != lastBoard[i][j])
3047 DrawSquare(i, j, board[i][j], do_flash);
3059 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
3061 cairo_set_line_width (cr, lineGap);
3063 /* TODO: use appdata colors */
3064 cairo_set_source_rgba (cr, 0, 1, 0, 1.0);
3068 for (i = 0; i < BOARD_HEIGHT + 1; i++)
3071 x2 = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3072 y1 = y2 = lineGap / 2 + (i * (squareSize + lineGap));
3074 cairo_move_to (cr, x1, y1);
3075 cairo_rel_line_to (cr, x2,0);
3079 for (j = 0; j < BOARD_WIDTH + 1; j++)
3082 y2 = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3083 x1 = x2 = lineGap / 2 + (j * (squareSize + lineGap));
3085 cairo_move_to (cr, x1, y1);
3086 cairo_rel_line_to (cr, 0, y2);
3095 for (i = 0; i < BOARD_HEIGHT; i++)
3096 for (j = 0; j < BOARD_WIDTH; j++)
3098 DrawSquare(i, j, board[i][j], 0);
3099 damage[i][j] = False;
3103 CopyBoard(lastBoard, board);
3105 lastFlipView = flipView;
3107 /* Draw highlights */
3108 if (pm1X >= 0 && pm1Y >= 0)
3110 drawHighlight(pm1X, pm1Y, LINE_TYPE_PRE);
3112 if (pm2X >= 0 && pm2Y >= 0)
3114 drawHighlight(pm2X, pm2Y, LINE_TYPE_PRE);
3116 if (hi1X >= 0 && hi1Y >= 0)
3118 drawHighlight(hi1X, hi1Y, LINE_TYPE_HIGHLIGHT);
3120 if (hi2X >= 0 && hi2Y >= 0)
3122 drawHighlight(hi2X, hi2Y, LINE_TYPE_HIGHLIGHT);
3125 /* If piece being dragged around board, must redraw that too */
3131 void AnimateUserMove (Widget w, XEvent * event,
3132 String * params, Cardinal * nParams)
3134 DragPieceMove(event->xmotion.x, event->xmotion.y);
3137 void HandlePV (Widget w, XEvent * event,
3138 String * params, Cardinal * nParams)
3139 { // [HGM] pv: walk PV
3140 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3143 Widget CommentCreate(name, text, mutable, callback, lines)
3145 int /*Boolean*/ mutable;
3146 XtCallbackProc callback;
3150 Widget shell, layout, form, edit, b_ok, b_cancel, b_clear, b_close, b_edit;
3155 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3156 XtGetValues(boardWidget, args, j);
3159 XtSetArg(args[j], XtNresizable, True); j++;
3162 // XtCreatePopupShell(name, topLevelShellWidgetClass,
3163 // shellWidget, args, j);
3166 // XtCreatePopupShell(name, transientShellWidgetClass,
3167 // shellWidget, args, j);
3170 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
3171 layoutArgs, XtNumber(layoutArgs));
3173 XtCreateManagedWidget("form", formWidgetClass, layout,
3174 formArgs, XtNumber(formArgs));
3178 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
3179 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
3181 XtSetArg(args[j], XtNstring, text); j++;
3182 XtSetArg(args[j], XtNtop, XtChainTop); j++;
3183 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3184 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3185 XtSetArg(args[j], XtNright, XtChainRight); j++;
3186 XtSetArg(args[j], XtNresizable, True); j++;
3187 XtSetArg(args[j], XtNwidth, bw_width); j++; /*force wider than buttons*/
3188 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
3189 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
3190 XtSetArg(args[j], XtNautoFill, True); j++;
3191 XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
3193 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
3197 XtSetArg(args[j], XtNfromVert, edit); j++;
3198 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3199 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3200 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3201 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3203 XtCreateManagedWidget(_("ok"), commandWidgetClass, form, args, j);
3204 XtAddCallback(b_ok, XtNcallback, callback, (XtPointer) 0);
3207 XtSetArg(args[j], XtNfromVert, edit); j++;
3208 XtSetArg(args[j], XtNfromHoriz, b_ok); j++;
3209 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3210 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3211 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3212 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3214 XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
3215 XtAddCallback(b_cancel, XtNcallback, callback, (XtPointer) 0);
3218 XtSetArg(args[j], XtNfromVert, edit); j++;
3219 XtSetArg(args[j], XtNfromHoriz, b_cancel); j++;
3220 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3221 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3222 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3223 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3225 XtCreateManagedWidget(_("clear"), commandWidgetClass, form, args, j);
3226 XtAddCallback(b_clear, XtNcallback, callback, (XtPointer) 0);
3229 XtSetArg(args[j], XtNfromVert, edit); j++;
3230 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3231 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3232 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3233 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3235 XtCreateManagedWidget(_("close"), commandWidgetClass, form, args, j);
3236 XtAddCallback(b_close, XtNcallback, callback, (XtPointer) 0);
3239 XtSetArg(args[j], XtNfromVert, edit); j++;
3240 XtSetArg(args[j], XtNfromHoriz, b_close); j++;
3241 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3242 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3243 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3244 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3246 XtCreateManagedWidget(_("edit"), commandWidgetClass, form, args, j);
3247 XtAddCallback(b_edit, XtNcallback, callback, (XtPointer) 0);
3250 XtRealizeWidget(shell);
3252 if (commentX == -1) {
3255 Dimension pw_height;
3256 Dimension ew_height;
3259 XtSetArg(args[j], XtNheight, &ew_height); j++;
3260 XtGetValues(edit, args, j);
3263 XtSetArg(args[j], XtNheight, &pw_height); j++;
3264 XtGetValues(shell, args, j);
3265 commentH = pw_height + (lines - 1) * ew_height;
3266 commentW = bw_width - 16;
3268 // XSync(xDisplay, False);
3270 /* This code seems to tickle an X bug if it is executed too soon
3271 after xboard starts up. The coordinates get transformed as if
3272 the main window was positioned at (0, 0).
3274 // XtTranslateCoords(shellWidget,
3275 // (bw_width - commentW) / 2, 0 - commentH / 2,
3276 // &commentX, &commentY);
3278 // XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
3279 // RootWindowOfScreen(XtScreen(shellWidget)),
3280 // (bw_width - commentW) / 2, 0 - commentH / 2,
3281 // &xx, &yy, &junk);
3285 if (commentY < 0) commentY = 0; /*avoid positioning top offscreen*/
3288 if(wpComment.width > 0) {
3289 commentX = wpComment.x;
3290 commentY = wpComment.y;
3291 commentW = wpComment.width;
3292 commentH = wpComment.height;
3296 XtSetArg(args[j], XtNheight, commentH); j++;
3297 XtSetArg(args[j], XtNwidth, commentW); j++;
3298 XtSetArg(args[j], XtNx, commentX); j++;
3299 XtSetArg(args[j], XtNy, commentY); j++;
3300 XtSetValues(shell, args, j);
3301 XtSetKeyboardFocus(shell, edit);
3306 /* Used for analysis window and ICS input window */
3307 Widget MiscCreate(name, text, mutable, callback, lines)
3309 int /*Boolean*/ mutable;
3310 XtCallbackProc callback;
3314 Widget shell, layout, form, edit;
3316 Dimension bw_width, pw_height, ew_height, w, h;
3322 XtSetArg(args[j], XtNresizable, True); j++;
3325 // XtCreatePopupShell(name, topLevelShellWidgetClass,
3326 // shellWidget, args, j);
3329 // XtCreatePopupShell(name, transientShellWidgetClass,
3330 // shellWidget, args, j);
3333 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
3334 layoutArgs, XtNumber(layoutArgs));
3336 XtCreateManagedWidget("form", formWidgetClass, layout,
3337 formArgs, XtNumber(formArgs));
3341 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
3342 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
3344 XtSetArg(args[j], XtNstring, text); j++;
3345 XtSetArg(args[j], XtNtop, XtChainTop); j++;
3346 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3347 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3348 XtSetArg(args[j], XtNright, XtChainRight); j++;
3349 XtSetArg(args[j], XtNresizable, True); j++;
3350 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
3351 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
3352 XtSetArg(args[j], XtNautoFill, True); j++;
3353 XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
3355 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
3357 XtRealizeWidget(shell);
3360 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3361 XtGetValues(boardWidget, args, j);
3364 XtSetArg(args[j], XtNheight, &ew_height); j++;
3365 XtGetValues(edit, args, j);
3368 XtSetArg(args[j], XtNheight, &pw_height); j++;
3369 XtGetValues(shell, args, j);
3370 h = pw_height + (lines - 1) * ew_height;
3373 // XSync(xDisplay, False);
3375 /* This code seems to tickle an X bug if it is executed too soon
3376 after xboard starts up. The coordinates get transformed as if
3377 the main window was positioned at (0, 0).
3379 // XtTranslateCoords(shellWidget, (bw_width - w) / 2, 0 - h / 2, &x, &y);
3381 // XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
3382 // RootWindowOfScreen(XtScreen(shellWidget)),
3383 // (bw_width - w) / 2, 0 - h / 2, &xx, &yy, &junk);
3387 if (y < 0) y = 0; /*avoid positioning top offscreen*/
3390 XtSetArg(args[j], XtNheight, h); j++;
3391 XtSetArg(args[j], XtNwidth, w); j++;
3392 XtSetArg(args[j], XtNx, x); j++;
3393 XtSetArg(args[j], XtNy, y); j++;
3394 XtSetValues(shell, args, j);
3400 static int savedIndex; /* gross that this is global */
3402 void EditCommentPopUp(index, title, text)
3411 if (text == NULL) text = "";
3413 if (editShell == NULL) {
3415 CommentCreate(title, text, True, EditCommentCallback, 4);
3416 XtRealizeWidget(editShell);
3417 // CatchDeleteWindow(editShell, "EditCommentPopDown");
3419 edit = XtNameToWidget(editShell, "*form.text");
3421 XtSetArg(args[j], XtNstring, text); j++;
3422 XtSetValues(edit, args, j);
3424 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
3425 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
3426 XtSetValues(editShell, args, j);
3429 XtPopup(editShell, XtGrabNone);
3433 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
3434 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
3438 void EditCommentCallback(w, client_data, call_data)
3440 XtPointer client_data, call_data;
3448 XtSetArg(args[j], XtNlabel, &name); j++;
3449 XtGetValues(w, args, j);
3451 if (strcmp(name, _("ok")) == 0) {
3452 edit = XtNameToWidget(editShell, "*form.text");
3454 XtSetArg(args[j], XtNstring, &val); j++;
3455 XtGetValues(edit, args, j);
3456 ReplaceComment(savedIndex, val);
3457 EditCommentPopDown();
3458 } else if (strcmp(name, _("cancel")) == 0) {
3459 EditCommentPopDown();
3460 } else if (strcmp(name, _("clear")) == 0) {
3461 edit = XtNameToWidget(editShell, "*form.text");
3462 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
3463 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
3467 void EditCommentPopDown()
3472 if (!editUp) return;
3474 XtSetArg(args[j], XtNx, &commentX); j++;
3475 XtSetArg(args[j], XtNy, &commentY); j++;
3476 XtSetArg(args[j], XtNheight, &commentH); j++;
3477 XtSetArg(args[j], XtNwidth, &commentW); j++;
3478 XtGetValues(editShell, args, j);
3479 XtPopdown(editShell);
3482 XtSetArg(args[j], XtNleftBitmap, None); j++;
3483 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
3487 void ICSInputBoxPopUp()
3492 char *title = _("ICS Input");
3495 if (ICSInputShell == NULL) {
3496 ICSInputShell = MiscCreate(title, "", True, NULL, 1);
3497 tr = XtParseTranslationTable(ICSInputTranslations);
3498 edit = XtNameToWidget(ICSInputShell, "*form.text");
3499 XtOverrideTranslations(edit, tr);
3500 XtRealizeWidget(ICSInputShell);
3501 // CatchDeleteWindow(ICSInputShell, "ICSInputBoxPopDown");
3504 edit = XtNameToWidget(ICSInputShell, "*form.text");
3506 XtSetArg(args[j], XtNstring, ""); j++;
3507 XtSetValues(edit, args, j);
3509 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
3510 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
3511 XtSetValues(ICSInputShell, args, j);
3514 XtPopup(ICSInputShell, XtGrabNone);
3515 XtSetKeyboardFocus(ICSInputShell, edit);
3517 ICSInputBoxUp = True;
3519 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
3520 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
3524 void ICSInputSendText()
3531 edit = XtNameToWidget(ICSInputShell, "*form.text");
3533 XtSetArg(args[j], XtNstring, &val); j++;
3534 XtGetValues(edit, args, j);
3535 SendMultiLineToICS(val);
3536 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
3537 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
3540 void ICSInputBoxPopDown()
3545 if (!ICSInputBoxUp) return;
3547 XtPopdown(ICSInputShell);
3548 ICSInputBoxUp = False;
3550 XtSetArg(args[j], XtNleftBitmap, None); j++;
3551 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
3555 void CommentPopUp(title, text)
3562 if (commentShell == NULL) {
3564 CommentCreate(title, text, False, CommentCallback, 4);
3565 XtRealizeWidget(commentShell);
3566 // CatchDeleteWindow(commentShell, "CommentPopDown");
3568 edit = XtNameToWidget(commentShell, "*form.text");
3570 XtSetArg(args[j], XtNstring, text); j++;
3571 XtSetValues(edit, args, j);
3573 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
3574 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
3575 XtSetValues(commentShell, args, j);
3578 XtPopup(commentShell, XtGrabNone);
3579 // XSync(xDisplay, False);
3584 void CommentCallback(w, client_data, call_data)
3586 XtPointer client_data, call_data;
3593 XtSetArg(args[j], XtNlabel, &name); j++;
3594 XtGetValues(w, args, j);
3596 if (strcmp(name, _("close")) == 0) {
3598 } else if (strcmp(name, _("edit")) == 0) {
3605 void CommentPopDown()
3610 if (!commentUp) return;
3612 XtSetArg(args[j], XtNx, &commentX); j++;
3613 XtSetArg(args[j], XtNy, &commentY); j++;
3614 XtSetArg(args[j], XtNwidth, &commentW); j++;
3615 XtSetArg(args[j], XtNheight, &commentH); j++;
3616 XtGetValues(commentShell, args, j);
3617 XtPopdown(commentShell);
3618 // XSync(xDisplay, False);
3622 void PromotionPopUp()
3625 Widget dialog, layout;
3627 Dimension bw_width, pw_width;
3631 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3632 XtGetValues(boardWidget, args, j);
3635 XtSetArg(args[j], XtNresizable, True); j++;
3636 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
3638 // XtCreatePopupShell("Promotion", transientShellWidgetClass,
3639 // shellWidget, args, j);
3641 // XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
3642 // layoutArgs, XtNumber(layoutArgs));
3645 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
3646 XtSetArg(args[j], XtNborderWidth, 0); j++;
3647 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
3650 if(gameInfo.variant != VariantShogi) {
3651 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
3652 (XtPointer) dialog);
3653 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
3654 (XtPointer) dialog);
3655 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
3656 (XtPointer) dialog);
3657 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
3658 (XtPointer) dialog);
3659 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
3660 gameInfo.variant == VariantGiveaway) {
3661 XawDialogAddButton(dialog, _("King"), PromotionCallback,
3662 (XtPointer) dialog);
3664 if(gameInfo.variant == VariantCapablanca ||
3665 gameInfo.variant == VariantGothic ||
3666 gameInfo.variant == VariantCapaRandom) {
3667 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
3668 (XtPointer) dialog);
3669 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
3670 (XtPointer) dialog);
3672 } else // [HGM] shogi
3674 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
3675 (XtPointer) dialog);
3676 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
3677 (XtPointer) dialog);
3679 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
3680 (XtPointer) dialog);
3682 XtRealizeWidget(promotionShell);
3683 // CatchDeleteWindow(promotionShell, "PromotionPopDown");
3686 XtSetArg(args[j], XtNwidth, &pw_width); j++;
3687 XtGetValues(promotionShell, args, j);
3689 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3690 lineGap + squareSize/3 +
3691 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
3692 0 : 6*(squareSize + lineGap)), &x, &y);
3695 XtSetArg(args[j], XtNx, x); j++;
3696 XtSetArg(args[j], XtNy, y); j++;
3697 XtSetValues(promotionShell, args, j);
3699 XtPopup(promotionShell, XtGrabNone);
3704 void PromotionPopDown()
3706 if (!promotionUp) return;
3707 XtPopdown(promotionShell);
3708 XtDestroyWidget(promotionShell);
3709 promotionUp = False;
3712 void PromotionCallback(w, client_data, call_data)
3714 XtPointer client_data, call_data;
3720 XtSetArg(args[0], XtNlabel, &name);
3721 XtGetValues(w, args, 1);
3725 if (fromX == -1) return;
3727 if (strcmp(name, _("cancel")) == 0) {
3731 } else if (strcmp(name, _("Knight")) == 0) {
3733 } else if (strcmp(name, _("Promote")) == 0) {
3735 } else if (strcmp(name, _("Defer")) == 0) {
3738 promoChar = ToLower(name[0]);
3741 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
3743 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
3744 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3749 void ErrorCallback(w, client_data, call_data)
3751 XtPointer client_data, call_data;
3754 XtPopdown(w = XtParent(XtParent(XtParent(w))));
3756 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3762 if (!errorUp) return;
3766 gtk_widget_destroy(GTK_WIDGET(GUI_Error));
3768 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3773 void ErrorPopUp(title, label, modal)
3774 char *title, *label;
3777 GUI_Error = gtk_message_dialog_new(GTK_WINDOW(GUI_Window),
3778 GTK_DIALOG_DESTROY_WITH_PARENT,
3783 gtk_window_set_title(GTK_WINDOW(GUI_Error),(gchar *) title);
3786 gtk_dialog_run(GTK_DIALOG(GUI_Error));
3787 gtk_widget_destroy(GTK_WIDGET(GUI_Error));
3791 g_signal_connect_swapped (GUI_Error, "response",
3792 G_CALLBACK (ErrorPopDownProc),
3795 gtk_widget_show(GTK_WIDGET(GUI_Error));
3801 /* Disable all user input other than deleting the window */
3802 static int frozen = 0;
3806 /* Grab by a widget that doesn't accept input */
3807 // XtAddGrab(messageWidget, TRUE, FALSE);
3811 /* Undo a FreezeUI */
3814 if (!frozen) return;
3815 // XtRemoveGrab(messageWidget);
3819 char *ModeToWidgetName(mode)
3823 case BeginningOfGame:
3824 if (appData.icsActive)
3825 return "menuMode.ICS Client";
3826 else if (appData.noChessProgram ||
3827 *appData.cmailGameName != NULLCHAR)
3828 return "menuMode.Edit Game";
3830 return "menuMode.Machine Black";
3831 case MachinePlaysBlack:
3832 return "menuMode.Machine Black";
3833 case MachinePlaysWhite:
3834 return "menuMode.Machine White";
3836 return "menuMode.Analysis Mode";
3838 return "menuMode.Analyze File";
3839 case TwoMachinesPlay:
3840 return "menuMode.Two Machines";
3842 return "menuMode.Edit Game";
3843 case PlayFromGameFile:
3844 return "menuFile.Load Game";
3846 return "menuMode.Edit Position";
3848 return "menuMode.Training";
3849 case IcsPlayingWhite:
3850 case IcsPlayingBlack:
3854 return "menuMode.ICS Client";
3861 void ModeHighlight()
3863 static int oldPausing = FALSE;
3864 static GameMode oldmode = (GameMode) -1;
3867 // todo this toggling of the pause button doesn't seem to work?
3868 // e.g. select pause from buttonbar doesn't activate menumode.pause
3870 // if (!boardWidget || !XtIsRealized(boardWidget)) return;
3872 if (pausing != oldPausing) {
3873 oldPausing = pausing;
3874 gtk_button_set_relief(GTK_BUTTON (gtk_builder_get_object (builder, "menuMode.Pause")),pausing?GTK_RELIEF_NORMAL:GTK_RELIEF_NONE);
3875 /* toggle background color in showbuttonbar */
3876 if (appData.showButtonBar) {
3878 gtk_button_pressed(GTK_BUTTON (gtk_builder_get_object (builder, "buttonbar.Pause")));
3880 gtk_button_released(GTK_BUTTON (gtk_builder_get_object (builder, "buttonbar.Pause")));
3885 // probably not needed anymore
3886 // wname = ModeToWidgetName(oldmode);
3888 // gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, wname)),True);
3892 /* Maybe all the enables should be handled here, not just this one */
3893 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuMode.Training")),
3894 gameMode == Training || gameMode == PlayFromGameFile);
3899 * Button/menu procedures
3902 int LoadGamePopUp(f, gameNumber, title)
3907 cmailMsgLoaded = FALSE;
3909 if (gameNumber == 0)
3911 int error = GameListBuild(f);
3915 DisplayError(_("Cannot build game list"), error);
3917 else if (!ListEmpty(&gameList)
3918 && ((ListGame *) gameList.tailPred)->number > 1)
3920 /* we need an answer which game to load, so let's make it modal for a while*/
3921 gtk_window_set_modal(GTK_WINDOW(GUI_GameList) , TRUE);
3922 GameListPopUp(f, title);
3923 gtk_window_set_modal(GTK_WINDOW(GUI_GameList) , FALSE);
3932 return LoadGame(f, gameNumber, title, FALSE);
3935 void ReloadCmailMsgProc(w, event, prms, nprms)
3941 ReloadCmailMsgEvent(FALSE);
3944 void MailMoveProc(w, event, prms, nprms)
3953 /* this variable is shared between CopyPositionProc and SendPositionSelection */
3954 char *selected_fen_position=NULL;
3957 SendPositionSelection(Widget w, Atom *selection, Atom *target,
3958 Atom *type_return, XtPointer *value_return,
3959 unsigned long *length_return, int *format_return)
3961 char *selection_tmp;
3963 if (!selected_fen_position) return False; /* should never happen */
3964 // if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
3965 // /* note: since no XtSelectionDoneProc was registered, Xt will
3966 // * automatically call XtFree on the value returned. So have to
3967 // * make a copy of it allocated with XtMalloc */
3968 // selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
3969 // strcpy(selection_tmp, selected_fen_position);
3971 // *value_return=selection_tmp;
3972 // *length_return=strlen(selection_tmp);
3973 // *type_return=*target;
3974 // *format_return = 8; /* bits per byte */
3976 // } else if (*target == XA_TARGETS(xDisplay)) {
3977 // Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
3978 // targets_tmp[0] = XA_UTF8_STRING(xDisplay);
3979 // targets_tmp[1] = XA_STRING;
3980 // *value_return = targets_tmp;
3981 // *type_return = XA_ATOM;
3982 // *length_return = 2;
3983 // *format_return = 8 * sizeof(Atom);
3984 // if (*format_return > 32) {
3985 // *length_return *= *format_return / 32;
3986 // *format_return = 32;
3994 /* note: when called from menu all parameters are NULL, so no clue what the
3995 * Widget which was clicked on was, or what the click event was
3997 void CopyPositionProc(w, event, prms, nprms)
4004 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4005 * have a notion of a position that is selected but not copied.
4006 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4008 if(gameMode == EditPosition) EditPositionDone(TRUE);
4009 if (selected_fen_position) free(selected_fen_position);
4010 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
4011 if (!selected_fen_position) return;
4012 // XtOwnSelection(menuBarWidget, XA_PRIMARY,
4014 // SendPositionSelection,
4015 // NULL/* lose_ownership_proc */ ,
4016 // NULL/* transfer_done_proc */);
4017 // XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4019 // SendPositionSelection,
4020 // NULL/* lose_ownership_proc */ ,
4021 // NULL/* transfer_done_proc */);
4024 /* function called when the data to Paste is ready */
4026 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
4027 Atom *type, XtPointer value, unsigned long *len, int *format)
4030 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
4031 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
4032 EditPositionPasteFEN(fenstr);
4036 /* called when Paste Position button is pressed,
4037 * all parameters will be NULL */
4038 void PastePositionProc(w, event, prms, nprms)
4044 // XtGetSelectionValue(menuBarWidget,
4045 // appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4046 // /* (XtSelectionCallbackProc) */ PastePositionCB,
4047 // NULL, /* client_data passed to PastePositionCB */
4049 // /* better to use the time field from the event that triggered the
4050 // * call to this function, but that isn't trivial to get
4058 SendGameSelection(Widget w, Atom *selection, Atom *target,
4059 Atom *type_return, XtPointer *value_return,
4060 unsigned long *length_return, int *format_return)
4062 char *selection_tmp;
4064 // if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4065 // FILE* f = fopen(gameCopyFilename, "r");
4068 // if (f == NULL) return False;
4072 // selection_tmp = XtMalloc(len + 1);
4073 // count = fread(selection_tmp, 1, len, f);
4074 // if (len != count) {
4075 // XtFree(selection_tmp);
4078 // selection_tmp[len] = NULLCHAR;
4079 // *value_return = selection_tmp;
4080 // *length_return = len;
4081 // *type_return = *target;
4082 // *format_return = 8; /* bits per byte */
4084 // } else if (*target == XA_TARGETS(xDisplay)) {
4085 // Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4086 // targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4087 // targets_tmp[1] = XA_STRING;
4088 // *value_return = targets_tmp;
4089 // *type_return = XA_ATOM;
4090 // *length_return = 2;
4091 // *format_return = 8 * sizeof(Atom);
4092 // if (*format_return > 32) {
4093 // *length_return *= *format_return / 32;
4094 // *format_return = 32;
4102 /* note: when called from menu all parameters are NULL, so no clue what the
4103 * Widget which was clicked on was, or what the click event was
4105 void CopyGameProc(w, event, prms, nprms)
4113 ret = SaveGameToFile(gameCopyFilename, FALSE);
4117 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4118 * have a notion of a game that is selected but not copied.
4119 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4121 // XtOwnSelection(menuBarWidget, XA_PRIMARY,
4123 // SendGameSelection,
4124 // NULL/* lose_ownership_proc */ ,
4125 // NULL/* transfer_done_proc */);
4126 // XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4128 // SendGameSelection,
4129 // NULL/* lose_ownership_proc */ ,
4130 // NULL/* transfer_done_proc */);
4133 /* function called when the data to Paste is ready */
4135 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
4136 Atom *type, XtPointer value, unsigned long *len, int *format)
4139 if (value == NULL || *len == 0) {
4140 return; /* nothing had been selected to copy */
4142 f = fopen(gamePasteFilename, "w");
4144 DisplayError(_("Can't open temp file"), errno);
4147 fwrite(value, 1, *len, f);
4150 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
4153 /* called when Paste Game button is pressed,
4154 * all parameters will be NULL */
4155 void PasteGameProc(w, event, prms, nprms)
4161 // XtGetSelectionValue(menuBarWidget,
4162 // appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4163 // /* (XtSelectionCallbackProc) */ PasteGameCB,
4164 // NULL, /* client_data passed to PasteGameCB */
4166 // /* better to use the time field from the event that triggered the
4167 // * call to this function, but that isn't trivial to get
4174 void SaveOnExitProc(w, event, prms, nprms)
4182 saveSettingsOnExit = !saveSettingsOnExit;
4184 if (saveSettingsOnExit) {
4185 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
4187 XtSetArg(args[0], XtNleftBitmap, None);
4189 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
4193 void SaveSettingsProc(w, event, prms, nprms)
4199 SaveSettings(settingsFileName);
4205 SaveGameProc(NULL, NULL);
4210 void EditCommentProc(w, event, prms, nprms)
4217 EditCommentPopDown();
4223 void IcsInputBoxProc(w, event, prms, nprms)
4229 if (ICSInputBoxUp) {
4230 ICSInputBoxPopDown();
4237 void EnterKeyProc(w, event, prms, nprms)
4243 if (ICSInputBoxUp == True)
4248 void DebugProc(w, event, prms, nprms)
4254 appData.debugMode = !appData.debugMode;
4257 void AboutGameProc(w, event, prms, nprms)
4266 void NothingProc(w, event, prms, nprms)
4275 void Iconify(w, event, prms, nprms)
4283 // fromX = fromY = -1;
4284 // XtSetArg(args[0], XtNiconic, True);
4285 // XtSetValues(shellWidget, args, 1);
4288 void DisplayMessage(message, extMessage)
4289 gchar *message, *extMessage;
4296 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
4299 message = extMessage;
4302 gtk_label_set_text( GTK_LABEL(gtk_builder_get_object (builder, "Messages")),message);
4307 void DisplayTitle(text)
4310 gchar title[MSG_SIZ];
4312 if (text == NULL) text = "";
4314 if (appData.titleInWindow)
4319 if (*text != NULLCHAR)
4321 strcpy(title, text);
4323 else if (appData.icsActive)
4325 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
4327 else if (appData.cmailGameName[0] != NULLCHAR)
4329 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
4331 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
4333 else if (gameInfo.variant == VariantGothic)
4335 strcpy(title, GOTHIC);
4339 else if (gameInfo.variant == VariantFalcon)
4341 strcpy(title, FALCON);
4344 else if (appData.noChessProgram)
4346 strcpy(title, programName);
4350 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
4352 gtk_window_set_title(GTK_WINDOW(GUI_Window),title);
4358 void DisplayError(message, error)
4365 if (appData.debugMode || appData.matchMode) {
4366 fprintf(stderr, "%s: %s\n", programName, message);
4369 if (appData.debugMode || appData.matchMode) {
4370 fprintf(stderr, "%s: %s: %s\n",
4371 programName, message, strerror(error));
4373 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
4376 ErrorPopUp(_("Error"), message, FALSE);
4380 void DisplayMoveError(message)
4385 DrawPosition(FALSE, NULL);
4386 if (appData.debugMode || appData.matchMode) {
4387 fprintf(stderr, "%s: %s\n", programName, message);
4389 if (appData.popupMoveErrors) {
4390 ErrorPopUp(_("Error"), message, FALSE);
4392 DisplayMessage(message, "");
4397 void DisplayFatalError(message, error, status)
4403 errorExitStatus = status;
4405 fprintf(stderr, "%s: %s\n", programName, message);
4407 fprintf(stderr, "%s: %s: %s\n",
4408 programName, message, strerror(error));
4409 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
4412 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
4413 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
4419 void DisplayInformation(message)
4423 ErrorPopUp(_("Information"), message, TRUE);
4426 void DisplayNote(message)
4430 ErrorPopUp(_("Note"), message, FALSE);
4434 NullXErrorCheck(dpy, error_event)
4436 XErrorEvent *error_event;
4441 void DisplayIcsInteractionTitle(message)
4444 if (oldICSInteractionTitle == NULL) {
4445 /* Magic to find the old window title, adapted from vim */
4446 char *wina = getenv("WINDOWID");
4448 Window win = (Window) atoi(wina);
4449 Window root, parent, *children;
4450 unsigned int nchildren;
4451 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4453 // if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4454 // if (!XQueryTree(xDisplay, win, &root, &parent,
4455 // &children, &nchildren)) break;
4456 // if (children) XFree((void *)children);
4457 // if (parent == root || parent == 0) break;
4460 XSetErrorHandler(oldHandler);
4462 if (oldICSInteractionTitle == NULL) {
4463 oldICSInteractionTitle = "xterm";
4466 printf("\033]0;%s\007", message);
4470 char pendingReplyPrefix[MSG_SIZ];
4471 ProcRef pendingReplyPR;
4473 void AskQuestionProc(w, event, prms, nprms)
4480 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
4484 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
4487 void AskQuestionPopDown()
4489 if (!askQuestionUp) return;
4490 XtPopdown(askQuestionShell);
4491 XtDestroyWidget(askQuestionShell);
4492 askQuestionUp = False;
4495 void AskQuestionReplyAction(w, event, prms, nprms)
4505 reply = XawDialogGetValueString(w = XtParent(w));
4506 strcpy(buf, pendingReplyPrefix);
4507 if (*buf) strcat(buf, " ");
4510 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
4511 AskQuestionPopDown();
4513 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
4516 void AskQuestionCallback(w, client_data, call_data)
4518 XtPointer client_data, call_data;
4523 XtSetArg(args[0], XtNlabel, &name);
4524 XtGetValues(w, args, 1);
4526 if (strcmp(name, _("cancel")) == 0) {
4527 AskQuestionPopDown();
4529 AskQuestionReplyAction(w, NULL, NULL, NULL);
4533 void AskQuestion(title, question, replyPrefix, pr)
4534 char *title, *question, *replyPrefix;
4538 Widget popup, layout, dialog, edit;
4544 strcpy(pendingReplyPrefix, replyPrefix);
4545 pendingReplyPR = pr;
4548 XtSetArg(args[i], XtNresizable, True); i++;
4549 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
4550 // askQuestionShell = popup =
4551 // XtCreatePopupShell(title, transientShellWidgetClass,
4552 // shellWidget, args, i);
4555 // XtCreateManagedWidget(layoutName, formWidgetClass, popup,
4556 // layoutArgs, XtNumber(layoutArgs));
4559 XtSetArg(args[i], XtNlabel, question); i++;
4560 XtSetArg(args[i], XtNvalue, ""); i++;
4561 XtSetArg(args[i], XtNborderWidth, 0); i++;
4562 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
4565 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
4566 (XtPointer) dialog);
4567 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
4568 (XtPointer) dialog);
4570 XtRealizeWidget(popup);
4571 // CatchDeleteWindow(popup, "AskQuestionPopDown");
4573 // XQueryPointer(xDisplay, xBoardWindow, &root, &child,
4574 // &x, &y, &win_x, &win_y, &mask);
4576 // XtSetArg(args[0], XtNx, x - 10);
4577 // XtSetArg(args[1], XtNy, y - 30);
4578 // XtSetValues(popup, args, 2);
4580 // XtPopup(popup, XtGrabExclusive);
4581 // askQuestionUp = True;
4583 // edit = XtNameToWidget(dialog, "*value");
4584 // XtSetKeyboardFocus(popup, edit);
4592 if (*name == NULLCHAR) {
4594 } else if (strcmp(name, "$") == 0) {
4595 putc(BELLCHAR, stderr);
4598 snprintf(buf, sizeof(buf), "%s '%s' &", appData.soundProgram, name);
4606 PlaySound(appData.soundMove);
4612 PlaySound(appData.soundIcsWin);
4618 PlaySound(appData.soundIcsLoss);
4624 PlaySound(appData.soundIcsDraw);
4628 PlayIcsUnfinishedSound()
4630 PlaySound(appData.soundIcsUnfinished);
4636 PlaySound(appData.soundIcsAlarm);
4642 system("stty echo");
4648 system("stty -echo");
4652 Colorize(cc, continuation)
4657 int count, outCount, error;
4659 if (textColors[(int)cc].bg > 0) {
4660 if (textColors[(int)cc].fg > 0) {
4661 sprintf(buf, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
4662 textColors[(int)cc].fg, textColors[(int)cc].bg);
4664 sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
4665 textColors[(int)cc].bg);
4668 if (textColors[(int)cc].fg > 0) {
4669 sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
4670 textColors[(int)cc].fg);
4672 sprintf(buf, "\033[0;%dm", textColors[(int)cc].attr);
4675 count = strlen(buf);
4676 outCount = OutputToProcess(NoProc, buf, count, &error);
4677 if (outCount < count) {
4678 DisplayFatalError(_("Error writing to display"), error, 1);
4681 if (continuation) return;
4684 PlaySound(appData.soundShout);
4687 PlaySound(appData.soundSShout);
4690 PlaySound(appData.soundChannel1);
4693 PlaySound(appData.soundChannel);
4696 PlaySound(appData.soundKibitz);
4699 PlaySound(appData.soundTell);
4701 case ColorChallenge:
4702 PlaySound(appData.soundChallenge);
4705 PlaySound(appData.soundRequest);
4708 PlaySound(appData.soundSeek);
4719 return getpwuid(getuid())->pw_name;
4722 static char *ExpandPathName(path)
4725 static char static_buf[2000];
4726 char *d, *s, buf[2000];
4732 while (*s && isspace(*s))
4741 if (*(s+1) == '/') {
4742 strcpy(d, getpwuid(getuid())->pw_dir);
4747 *strchr(buf, '/') = 0;
4748 pwd = getpwnam(buf);
4751 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
4755 strcpy(d, pwd->pw_dir);
4756 strcat(d, strchr(s+1, '/'));
4767 static char host_name[MSG_SIZ];
4769 #if HAVE_GETHOSTNAME
4770 gethostname(host_name, MSG_SIZ);
4772 #else /* not HAVE_GETHOSTNAME */
4773 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
4774 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
4776 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4778 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4779 #endif /* not HAVE_GETHOSTNAME */
4782 guint delayedEventTimerTag = 0;
4783 DelayedEventCallback delayedEventCallback = 0;
4786 FireDelayedEvent(data)
4790 g_source_remove(delayedEventTimerTag);
4791 delayedEventTimerTag = 0;
4794 delayedEventCallback();
4800 ScheduleDelayedEvent(cb, millisec)
4801 DelayedEventCallback cb; guint millisec;
4803 if(delayedEventTimerTag && delayedEventCallback == cb)
4804 // [HGM] alive: replace, rather than add or flush identical event
4805 g_source_remove(delayedEventTimerTag);
4806 delayedEventCallback = cb;
4807 delayedEventTimerTag = g_timeout_add(millisec,(GSourceFunc) FireDelayedEvent, NULL);
4811 DelayedEventCallback
4814 if (delayedEventTimerTag)
4816 return delayedEventCallback;
4825 CancelDelayedEvent()
4827 if (delayedEventTimerTag)
4829 g_source_remove(delayedEventTimerTag);
4830 delayedEventTimerTag = 0;
4836 guint loadGameTimerTag = 0;
4838 int LoadGameTimerRunning()
4840 return loadGameTimerTag != 0;
4843 int StopLoadGameTimer()
4845 if (loadGameTimerTag != 0) {
4846 g_source_remove(loadGameTimerTag);
4847 loadGameTimerTag = 0;
4855 LoadGameTimerCallback(data)
4859 g_source_remove(loadGameTimerTag);
4860 loadGameTimerTag = 0;
4867 StartLoadGameTimer(millisec)
4871 g_timeout_add( millisec, (GSourceFunc) LoadGameTimerCallback, NULL);
4875 guint analysisClockTag = 0;
4878 AnalysisClockCallback(data)
4881 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
4882 || appData.icsEngineAnalyze)
4884 AnalysisPeriodicEvent(0);
4885 return 1; /* keep on going */
4887 return 0; /* stop timer */
4891 StartAnalysisClock()
4894 g_timeout_add( 2000,(GSourceFunc) AnalysisClockCallback, NULL);
4898 guint clockTimerTag = 0;
4900 int ClockTimerRunning()
4902 return clockTimerTag != 0;
4905 int StopClockTimer()
4907 if (clockTimerTag != 0)
4909 g_source_remove(clockTimerTag);
4920 ClockTimerCallback(data)
4924 g_source_remove(clockTimerTag);
4932 StartClockTimer(millisec)
4935 clockTimerTag = g_timeout_add(millisec,(GSourceFunc) ClockTimerCallback,NULL);
4940 DisplayTimerLabel(w, color, timer, highlight)
4949 if (appData.clockMode) {
4950 sprintf(buf, "%s: %s", color, TimeString(timer));
4952 sprintf(buf, "%s ", color);
4954 gtk_label_set_text(GTK_LABEL(w),buf);
4956 /* check for low time warning */
4957 // Pixel foregroundOrWarningColor = timerForegroundPixel;
4960 // appData.lowTimeWarning &&
4961 // (timer / 1000) < appData.icsAlarmTime)
4962 // foregroundOrWarningColor = lowTimeWarningColor;
4964 // if (appData.clockMode) {
4965 // sprintf(buf, "%s: %s", color, TimeString(timer));
4966 // XtSetArg(args[0], XtNlabel, buf);
4968 // sprintf(buf, "%s ", color);
4969 // XtSetArg(args[0], XtNlabel, buf);
4974 // XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
4975 // XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
4977 // XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
4978 // XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
4981 // XtSetValues(w, args, 3);
4986 DisplayWhiteClock(timeRemaining, highlight)
4990 if(appData.noGUI) return;
4992 DisplayTimerLabel(GUI_Whiteclock, _("White"), timeRemaining, highlight);
4993 if (highlight && WindowIcon == BlackIcon)
4995 WindowIcon = WhiteIcon;
4996 gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
5001 DisplayBlackClock(timeRemaining, highlight)
5005 if(appData.noGUI) return;
5007 DisplayTimerLabel(GUI_Blackclock, _("Black"), timeRemaining, highlight);
5008 if (highlight && WindowIcon == WhiteIcon)
5010 WindowIcon = BlackIcon;
5011 gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
5029 int StartChildProcess(cmdLine, dir, pr)
5036 int to_prog[2], from_prog[2];
5040 if (appData.debugMode) {
5041 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
5044 /* We do NOT feed the cmdLine to the shell; we just
5045 parse it into blank-separated arguments in the
5046 most simple-minded way possible.
5049 strcpy(buf, cmdLine);
5052 while(*p == ' ') p++;
5054 if(*p == '"' || *p == '\'')
5055 p = strchr(++argv[i-1], *p);
5056 else p = strchr(p, ' ');
5057 if (p == NULL) break;
5062 SetUpChildIO(to_prog, from_prog);
5064 if ((pid = fork()) == 0) {
5066 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
5067 close(to_prog[1]); // first close the unused pipe ends
5068 close(from_prog[0]);
5069 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
5070 dup2(from_prog[1], 1);
5071 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
5072 close(from_prog[1]); // and closing again loses one of the pipes!
5073 if(fileno(stderr) >= 2) // better safe than sorry...
5074 dup2(1, fileno(stderr)); /* force stderr to the pipe */
5076 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
5081 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
5083 execvp(argv[0], argv);
5085 /* If we get here, exec failed */
5090 /* Parent process */
5092 close(from_prog[1]);
5094 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5097 cp->fdFrom = from_prog[0];
5098 cp->fdTo = to_prog[1];
5103 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
5104 static RETSIGTYPE AlarmCallBack(int n)
5110 DestroyChildProcess(pr, signalType)
5114 ChildProc *cp = (ChildProc *) pr;
5116 if (cp->kind != CPReal) return;
5118 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
5119 signal(SIGALRM, AlarmCallBack);
5121 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
5122 kill(cp->pid, SIGKILL); // kill it forcefully
5123 wait((int *) 0); // and wait again
5127 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
5129 /* Process is exiting either because of the kill or because of
5130 a quit command sent by the backend; either way, wait for it to die.
5139 InterruptChildProcess(pr)
5142 ChildProc *cp = (ChildProc *) pr;
5144 if (cp->kind != CPReal) return;
5145 (void) kill(cp->pid, SIGINT); /* stop it thinking */
5148 int OpenTelnet(host, port, pr)
5153 char cmdLine[MSG_SIZ];
5155 if (port[0] == NULLCHAR) {
5156 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
5158 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
5160 return StartChildProcess(cmdLine, "", pr);
5163 int OpenTCP(host, port, pr)
5169 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
5170 #else /* !OMIT_SOCKETS */
5172 struct sockaddr_in sa;
5174 unsigned short uport;
5177 if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
5181 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
5182 sa.sin_family = AF_INET;
5183 sa.sin_addr.s_addr = INADDR_ANY;
5184 uport = (unsigned short) 0;
5185 sa.sin_port = htons(uport);
5186 if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
5190 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
5191 if (!(hp = gethostbyname(host))) {
5193 if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
5194 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
5195 hp->h_addrtype = AF_INET;
5197 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
5198 hp->h_addr_list[0] = (char *) malloc(4);
5199 hp->h_addr_list[0][0] = b0;
5200 hp->h_addr_list[0][1] = b1;
5201 hp->h_addr_list[0][2] = b2;
5202 hp->h_addr_list[0][3] = b3;
5207 sa.sin_family = hp->h_addrtype;
5208 uport = (unsigned short) atoi(port);
5209 sa.sin_port = htons(uport);
5210 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
5212 if (connect(s, (struct sockaddr *) &sa,
5213 sizeof(struct sockaddr_in)) < 0) {
5217 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5224 #endif /* !OMIT_SOCKETS */
5229 int OpenCommPort(name, pr)
5236 fd = open(name, 2, 0);
5237 if (fd < 0) return errno;
5239 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5249 int OpenLoopback(pr)
5255 SetUpChildIO(to, from);
5257 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5260 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
5267 int OpenRcmd(host, user, cmd, pr)
5268 char *host, *user, *cmd;
5271 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
5275 #define INPUT_SOURCE_BUF_SIZE 8192
5284 char buf[INPUT_SOURCE_BUF_SIZE];
5289 DoInputCallback(io,cond,data)
5294 /* read input from one of the input source (for example a chess program, ICS, etc).
5295 * and call a function that will handle the input
5298 int count; /* how many bytes did we read */
5302 /* All information (callback function, file descriptor, etc) is
5303 * saved in an InputSource structure
5305 InputSource *is = (InputSource *) data;
5309 count = read(is->fd, is->unused,
5310 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
5314 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
5317 is->unused += count;
5319 /* break input into lines and call the callback function on each
5322 while (p < is->unused)
5324 q = memchr(p, '\n', is->unused - p);
5325 if (q == NULL) break;
5327 (is->func)(is, is->closure, p, q - p, 0);
5330 /* remember not yet used part of the buffer */
5332 while (p < is->unused)
5340 /* read maximum length of input buffer and send the whole buffer
5341 * to the callback function
5343 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
5348 (is->func)(is, is->closure, is->buf, count, error);
5354 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
5361 GIOChannel *channel;
5362 ChildProc *cp = (ChildProc *) pr;
5364 is = (InputSource *) calloc(1, sizeof(InputSource));
5365 is->lineByLine = lineByLine;
5369 is->fd = fileno(stdin);
5371 is->kind = cp->kind;
5372 is->fd = cp->fdFrom;
5375 is->unused = is->buf;
5379 // is->xid = XtAppAddInput(appContext, is->fd,
5380 // (XtPointer) (XtInputReadMask),
5381 // (XtInputCallbackProc) DoInputCallback,
5385 /* TODO: will this work on windows?*/
5387 channel = g_io_channel_unix_new(is->fd);
5388 g_io_channel_set_close_on_unref (channel, TRUE);
5389 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
5390 is->closure = closure;
5391 return (InputSourceRef) is;
5395 RemoveInputSource(isr)
5398 InputSource *is = (InputSource *) isr;
5400 if (is->sid == 0) return;
5401 g_source_remove(is->sid);
5406 int OutputToProcess(pr, message, count, outError)
5412 static int line = 0;
5413 ChildProc *cp = (ChildProc *) pr;
5418 if (appData.noJoin || !appData.useInternalWrap)
5419 outCount = fwrite(message, 1, count, stdout);
5422 int width = get_term_width();
5423 int len = wrap(NULL, message, count, width, &line);
5424 char *msg = malloc(len);
5428 outCount = fwrite(message, 1, count, stdout);
5431 dbgchk = wrap(msg, message, count, width, &line);
5432 if (dbgchk != len && appData.debugMode)
5433 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
5434 outCount = fwrite(msg, 1, dbgchk, stdout);
5440 outCount = write(cp->fdTo, message, count);
5450 /* Output message to process, with "ms" milliseconds of delay
5451 between each character. This is needed when sending the logon
5452 script to ICC, which for some reason doesn't like the
5453 instantaneous send. */
5454 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
5461 ChildProc *cp = (ChildProc *) pr;
5466 r = write(cp->fdTo, message++, 1);
5479 /**** Animation code by Hugh Fisher, DCS, ANU.
5481 Known problem: if a window overlapping the board is
5482 moved away while a piece is being animated underneath,
5483 the newly exposed area won't be updated properly.
5484 I can live with this.
5486 Known problem: if you look carefully at the animation
5487 of pieces in mono mode, they are being drawn as solid
5488 shapes without interior detail while moving. Fixing
5489 this would be a major complication for minimal return.
5492 /* Masks for XPM pieces. Black and white pieces can have
5493 different shapes, but in the interest of retaining my
5494 sanity pieces must have the same outline on both light
5495 and dark squares, and all pieces must use the same
5496 background square colors/images. */
5498 static int xpmDone = 0;
5501 CreateAnimMasks (pieceDepth)
5508 unsigned long plane;
5511 /* just return for gtk at the moment */
5514 /* Need a bitmap just to get a GC with right depth */
5515 // buf = XCreatePixmap(xDisplay, xBoardWindow,
5517 values.foreground = 1;
5518 values.background = 0;
5519 /* Don't use XtGetGC, not read only */
5520 // maskGC = XCreateGC(xDisplay, buf,
5521 // GCForeground | GCBackground, &values);
5522 // XFreePixmap(xDisplay, buf);
5524 // buf = XCreatePixmap(xDisplay, xBoardWindow,
5525 // squareSize, squareSize, pieceDepth);
5526 // values.foreground = XBlackPixel(xDisplay, xScreen);
5527 // values.background = XWhitePixel(xDisplay, xScreen);
5528 // bufGC = XCreateGC(xDisplay, buf,
5529 // GCForeground | GCBackground, &values);
5531 for (piece = WhitePawn; piece <= BlackKing; piece++) {
5532 /* Begin with empty mask */
5533 // if(!xpmDone) // [HGM] pieces: keep using existing
5534 // xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
5535 // squareSize, squareSize, 1);
5536 // XSetFunction(xDisplay, maskGC, GXclear);
5537 // XFillRectangle(xDisplay, xpmMask[piece], maskGC,
5538 // 0, 0, squareSize, squareSize);
5540 /* Take a copy of the piece */
5545 // XSetFunction(xDisplay, bufGC, GXcopy);
5546 // XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
5548 // 0, 0, squareSize, squareSize, 0, 0);
5550 /* XOR the background (light) over the piece */
5551 // XSetFunction(xDisplay, bufGC, GXxor);
5553 // XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
5554 // 0, 0, squareSize, squareSize, 0, 0);
5556 // XSetForeground(xDisplay, bufGC, lightSquareColor);
5557 // XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
5560 /* We now have an inverted piece image with the background
5561 erased. Construct mask by just selecting all the non-zero
5562 pixels - no need to reconstruct the original image. */
5563 // XSetFunction(xDisplay, maskGC, GXor);
5565 /* Might be quicker to download an XImage and create bitmap
5566 data from it rather than this N copies per piece, but it
5567 only takes a fraction of a second and there is a much
5568 longer delay for loading the pieces. */
5569 // for (n = 0; n < pieceDepth; n ++) {
5570 // XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
5571 // 0, 0, squareSize, squareSize,
5573 // plane = plane << 1;
5577 // XFreePixmap(xDisplay, buf);
5578 // XFreeGC(xDisplay, bufGC);
5579 // XFreeGC(xDisplay, maskGC);
5583 InitAnimState (anim, info)
5585 XWindowAttributes * info;
5590 /* Each buffer is square size, same depth as window */
5591 // anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
5592 // squareSize, squareSize, info->depth);
5593 // anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
5594 // squareSize, squareSize, info->depth);
5596 // /* Create a plain GC for blitting */
5597 // mask = GCForeground | GCBackground | GCFunction |
5598 // GCPlaneMask | GCGraphicsExposures;
5599 // values.foreground = XBlackPixel(xDisplay, xScreen);
5600 // values.background = XWhitePixel(xDisplay, xScreen);
5601 // values.function = GXcopy;
5602 // values.plane_mask = AllPlanes;
5603 // values.graphics_exposures = False;
5604 // anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
5606 // /* Piece will be copied from an existing context at
5607 // the start of each new animation/drag. */
5608 // anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
5610 // /* Outline will be a read-only copy of an existing */
5611 // anim->outlineGC = None;
5617 static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
5618 XWindowAttributes info;
5620 /* for gtk at the moment just ... */
5623 if (xpmDone && gameInfo.variant == old) return;
5624 if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet
5625 // XGetWindowAttributes(xDisplay, xBoardWindow, &info);
5627 // InitAnimState(&game, &info);
5628 // InitAnimState(&player, &info);
5630 /* For XPM pieces, we need bitmaps to use as masks. */
5632 // CreateAnimMasks(info.depth);
5638 static Boolean frameWaiting;
5640 static RETSIGTYPE FrameAlarm (sig)
5643 frameWaiting = False;
5644 /* In case System-V style signals. Needed?? */
5645 signal(SIGALRM, FrameAlarm);
5652 struct itimerval delay;
5654 XSync(xDisplay, False);
5657 frameWaiting = True;
5658 signal(SIGALRM, FrameAlarm);
5659 delay.it_interval.tv_sec =
5660 delay.it_value.tv_sec = time / 1000;
5661 delay.it_interval.tv_usec =
5662 delay.it_value.tv_usec = (time % 1000) * 1000;
5663 setitimer(ITIMER_REAL, &delay, NULL);
5664 while (frameWaiting) pause();
5665 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
5666 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
5667 setitimer(ITIMER_REAL, &delay, NULL);
5677 // XSync(xDisplay, False);
5679 usleep(time * 1000);
5684 /* Convert board position to corner of screen rect and color */
5687 ScreenSquare(column, row, pt, color)
5688 int column; int row; XPoint * pt; int * color;
5691 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
5692 pt->y = lineGap + row * (squareSize + lineGap);
5694 pt->x = lineGap + column * (squareSize + lineGap);
5695 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
5697 *color = SquareColor(row, column);
5700 /* Convert window coords to square */
5703 BoardSquare(x, y, column, row)
5704 int x; int y; int * column; int * row;
5706 *column = EventToSquare(x, BOARD_WIDTH);
5707 if (flipView && *column >= 0)
5708 *column = BOARD_WIDTH - 1 - *column;
5709 *row = EventToSquare(y, BOARD_HEIGHT);
5710 if (!flipView && *row >= 0)
5711 *row = BOARD_HEIGHT - 1 - *row;
5716 #undef Max /* just in case */
5718 #define Max(a, b) ((a) > (b) ? (a) : (b))
5719 #define Min(a, b) ((a) < (b) ? (a) : (b))
5722 SetRect(rect, x, y, width, height)
5723 XRectangle * rect; int x; int y; int width; int height;
5727 rect->width = width;
5728 rect->height = height;
5731 /* Test if two frames overlap. If they do, return
5732 intersection rect within old and location of
5733 that rect within new. */
5736 Intersect(old, new, size, area, pt)
5737 XPoint * old; XPoint * new;
5738 int size; XRectangle * area; XPoint * pt;
5740 if (old->x > new->x + size || new->x > old->x + size ||
5741 old->y > new->y + size || new->y > old->y + size) {
5744 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
5745 size - abs(old->x - new->x), size - abs(old->y - new->y));
5746 pt->x = Max(old->x - new->x, 0);
5747 pt->y = Max(old->y - new->y, 0);
5752 /* For two overlapping frames, return the rect(s)
5753 in the old that do not intersect with the new. */
5756 CalcUpdateRects(old, new, size, update, nUpdates)
5757 XPoint * old; XPoint * new; int size;
5758 XRectangle update[]; int * nUpdates;
5762 /* If old = new (shouldn't happen) then nothing to draw */
5763 if (old->x == new->x && old->y == new->y) {
5767 /* Work out what bits overlap. Since we know the rects
5768 are the same size we don't need a full intersect calc. */
5770 /* Top or bottom edge? */
5771 if (new->y > old->y) {
5772 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
5774 } else if (old->y > new->y) {
5775 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
5776 size, old->y - new->y);
5779 /* Left or right edge - don't overlap any update calculated above. */
5780 if (new->x > old->x) {
5781 SetRect(&(update[count]), old->x, Max(new->y, old->y),
5782 new->x - old->x, size - abs(new->y - old->y));
5784 } else if (old->x > new->x) {
5785 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
5786 old->x - new->x, size - abs(new->y - old->y));
5793 /* Generate a series of frame coords from start->mid->finish.
5794 The movement rate doubles until the half way point is
5795 reached, then halves back down to the final destination,
5796 which gives a nice slow in/out effect. The algorithmn
5797 may seem to generate too many intermediates for short
5798 moves, but remember that the purpose is to attract the
5799 viewers attention to the piece about to be moved and
5800 then to where it ends up. Too few frames would be less
5804 Tween(start, mid, finish, factor, frames, nFrames)
5805 XPoint * start; XPoint * mid;
5806 XPoint * finish; int factor;
5807 XPoint frames[]; int * nFrames;
5809 int fraction, n, count;
5813 /* Slow in, stepping 1/16th, then 1/8th, ... */
5815 for (n = 0; n < factor; n++)
5817 for (n = 0; n < factor; n++) {
5818 frames[count].x = start->x + (mid->x - start->x) / fraction;
5819 frames[count].y = start->y + (mid->y - start->y) / fraction;
5821 fraction = fraction / 2;
5825 frames[count] = *mid;
5828 /* Slow out, stepping 1/2, then 1/4, ... */
5830 for (n = 0; n < factor; n++) {
5831 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
5832 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
5834 fraction = fraction * 2;
5839 /* Draw a piece on the screen without disturbing what's there */
5842 SelectGCMask(piece, clip, outline, mask)
5843 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
5847 /* Bitmap for piece being moved. */
5848 if (appData.monoMode) {
5849 *mask = *pieceToSolid(piece);
5850 } else if (useImages) {
5852 *mask = xpmMask[piece];
5854 *mask = ximMaskPm[piece];
5857 *mask = *pieceToSolid(piece);
5860 /* GC for piece being moved. Square color doesn't matter, but
5861 since it gets modified we make a copy of the original. */
5863 if (appData.monoMode)
5868 if (appData.monoMode)
5873 // XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
5875 /* Outline only used in mono mode and is not modified */
5877 *outline = bwPieceGC;
5879 *outline = wbPieceGC;
5883 OverlayPiece(piece, clip, outline, dest)
5884 ChessSquare piece; GC clip; GC outline; Drawable dest;
5889 /* Draw solid rectangle which will be clipped to shape of piece */
5890 // XFillRectangle(xDisplay, dest, clip,
5891 // 0, 0, squareSize, squareSize)
5893 if (appData.monoMode)
5894 /* Also draw outline in contrasting color for black
5895 on black / white on white cases */
5896 // XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
5897 // 0, 0, squareSize, squareSize, 0, 0, 1)
5900 /* Copy the piece */
5905 // XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
5907 // 0, 0, squareSize, squareSize,
5912 /* Animate the movement of a single piece */
5915 BeginAnimation(anim, piece, startColor, start)
5923 /* The old buffer is initialised with the start square (empty) */
5924 BlankSquare(0, 0, startColor, EmptySquare, anim->saveBuf);
5925 anim->prevFrame = *start;
5927 /* The piece will be drawn using its own bitmap as a matte */
5928 // SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
5929 // XSetClipMask(xDisplay, anim->pieceGC, mask);
5933 AnimationFrame(anim, frame, piece)
5938 XRectangle updates[4];
5943 /* Save what we are about to draw into the new buffer */
5944 // XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
5945 // frame->x, frame->y, squareSize, squareSize,
5948 /* Erase bits of the previous frame */
5949 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
5950 /* Where the new frame overlapped the previous,
5951 the contents in newBuf are wrong. */
5952 // XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
5953 // overlap.x, overlap.y,
5954 // overlap.width, overlap.height,
5956 /* Repaint the areas in the old that don't overlap new */
5957 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
5958 for (i = 0; i < count; i++)
5959 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
5960 // updates[i].x - anim->prevFrame.x,
5961 // updates[i].y - anim->prevFrame.y,
5962 // updates[i].width, updates[i].height,
5963 // updates[i].x, updates[i].y)
5966 /* Easy when no overlap */
5967 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
5968 // 0, 0, squareSize, squareSize,
5969 // anim->prevFrame.x, anim->prevFrame.y);
5972 /* Save this frame for next time round */
5973 // XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
5974 // 0, 0, squareSize, squareSize,
5976 anim->prevFrame = *frame;
5978 /* Draw piece over original screen contents, not current,
5979 and copy entire rect. Wipes out overlapping piece images. */
5980 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
5981 // XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
5982 // 0, 0, squareSize, squareSize,
5983 // frame->x, frame->y);
5987 EndAnimation (anim, finish)
5991 XRectangle updates[4];
5996 /* The main code will redraw the final square, so we
5997 only need to erase the bits that don't overlap. */
5998 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
5999 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
6000 for (i = 0; i < count; i++)
6001 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6002 // updates[i].x - anim->prevFrame.x,
6003 // updates[i].y - anim->prevFrame.y,
6004 // updates[i].width, updates[i].height,
6005 // updates[i].x, updates[i].y)
6008 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6009 // 0, 0, squareSize, squareSize,
6010 // anim->prevFrame.x, anim->prevFrame.y);
6015 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
6017 ChessSquare piece; int startColor;
6018 XPoint * start; XPoint * finish;
6019 XPoint frames[]; int nFrames;
6023 BeginAnimation(anim, piece, startColor, start);
6024 for (n = 0; n < nFrames; n++) {
6025 AnimationFrame(anim, &(frames[n]), piece);
6026 FrameDelay(appData.animSpeed);
6028 EndAnimation(anim, finish);
6031 /* Main control logic for deciding what to animate and how */
6034 AnimateMove(board, fromX, fromY, toX, toY)
6043 XPoint start, finish, mid;
6044 XPoint frames[kFactor * 2 + 1];
6045 int nFrames, startColor, endColor;
6047 /* Are we animating? */
6048 if (!appData.animate || appData.blindfold)
6051 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
6052 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
6053 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
6055 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
6056 piece = board[fromY][fromX];
6057 if (piece >= EmptySquare) return;
6062 hop = (piece == WhiteKnight || piece == BlackKnight);
6065 if (appData.debugMode) {
6066 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
6067 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
6068 piece, fromX, fromY, toX, toY); }
6070 ScreenSquare(fromX, fromY, &start, &startColor);
6071 ScreenSquare(toX, toY, &finish, &endColor);
6074 /* Knight: make diagonal movement then straight */
6075 if (abs(toY - fromY) < abs(toX - fromX)) {
6076 mid.x = start.x + (finish.x - start.x) / 2;
6080 mid.y = start.y + (finish.y - start.y) / 2;
6083 mid.x = start.x + (finish.x - start.x) / 2;
6084 mid.y = start.y + (finish.y - start.y) / 2;
6087 /* Don't use as many frames for very short moves */
6088 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
6089 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
6091 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
6092 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
6094 /* Be sure end square is redrawn */
6095 damage[toY][toX] = True;
6099 DragPieceBegin(x, y)
6102 int boardX, boardY, color;
6105 /* Are we animating? */
6106 if (!appData.animateDragging || appData.blindfold)
6109 /* Figure out which square we start in and the
6110 mouse position relative to top left corner. */
6111 BoardSquare(x, y, &boardX, &boardY);
6112 player.startBoardX = boardX;
6113 player.startBoardY = boardY;
6114 ScreenSquare(boardX, boardY, &corner, &color);
6115 player.startSquare = corner;
6116 player.startColor = color;
6117 /* As soon as we start dragging, the piece will jump slightly to
6118 be centered over the mouse pointer. */
6119 player.mouseDelta.x = squareSize/2;
6120 player.mouseDelta.y = squareSize/2;
6121 /* Initialise animation */
6122 player.dragPiece = PieceForSquare(boardX, boardY);
6124 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
6125 player.dragActive = True;
6126 BeginAnimation(&player, player.dragPiece, color, &corner);
6127 /* Mark this square as needing to be redrawn. Note that
6128 we don't remove the piece though, since logically (ie
6129 as seen by opponent) the move hasn't been made yet. */
6130 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
6131 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
6132 // XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
6133 // corner.x, corner.y, squareSize, squareSize,
6134 // 0, 0); // [HGM] zh: unstack in stead of grab
6135 damage[boardY][boardX] = True;
6137 player.dragActive = False;
6147 /* Are we animating? */
6148 if (!appData.animateDragging || appData.blindfold)
6152 if (! player.dragActive)
6154 /* Move piece, maintaining same relative position
6155 of mouse within square */
6156 corner.x = x - player.mouseDelta.x;
6157 corner.y = y - player.mouseDelta.y;
6158 AnimationFrame(&player, &corner, player.dragPiece);
6160 if (appData.highlightDragging) {
6162 BoardSquare(x, y, &boardX, &boardY);
6163 SetHighlights(fromX, fromY, boardX, boardY);
6172 int boardX, boardY, color;
6175 /* Are we animating? */
6176 if (!appData.animateDragging || appData.blindfold)
6180 if (! player.dragActive)
6182 /* Last frame in sequence is square piece is
6183 placed on, which may not match mouse exactly. */
6184 BoardSquare(x, y, &boardX, &boardY);
6185 ScreenSquare(boardX, boardY, &corner, &color);
6186 EndAnimation(&player, &corner);
6188 /* Be sure end square is redrawn */
6189 damage[boardY][boardX] = True;
6191 /* This prevents weird things happening with fast successive
6192 clicks which on my Sun at least can cause motion events
6193 without corresponding press/release. */
6194 player.dragActive = False;
6197 /* Handle expose event while piece being dragged */
6202 if (!player.dragActive || appData.blindfold)
6205 /* What we're doing: logically, the move hasn't been made yet,
6206 so the piece is still in it's original square. But visually
6207 it's being dragged around the board. So we erase the square
6208 that the piece is on and draw it at the last known drag point. */
6209 BlankSquare(player.startSquare.x, player.startSquare.y,
6210 player.startColor, EmptySquare, xBoardWindow);
6211 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
6212 damage[player.startBoardY][player.startBoardX] = TRUE;
6215 #include <sys/ioctl.h>
6216 int get_term_width()
6218 int fd, default_width;
6221 default_width = 79; // this is FICS default anyway...
6223 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
6225 if (!ioctl(fd, TIOCGSIZE, &win))
6226 default_width = win.ts_cols;
6227 #elif defined(TIOCGWINSZ)
6229 if (!ioctl(fd, TIOCGWINSZ, &win))
6230 default_width = win.ws_col;
6232 return default_width;
6235 void update_ics_width()
6237 static int old_width = 0;
6238 int new_width = get_term_width();
6240 if (old_width != new_width)
6241 ics_printf("set width %d\n", new_width);
6242 old_width = new_width;
6245 void NotifyFrontendLogin()