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 shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
357 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
358 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
359 menuBarWidget, editShell, errorShell, analysisShell,
360 ICSInputShell, fileNameShell, askQuestionShell;
362 Widget historyShell, evalGraphShell, gameListShell;
363 //XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
364 //XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
366 Font clockFontID, coordFontID, countFontID;
367 XFontStruct *clockFontStruct, *coordFontStruct, *countFontStruct;
368 XtAppContext appContext;
370 char *oldICSInteractionTitle;
374 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
376 Position commentX = -1, commentY = -1;
377 Dimension commentW, commentH;
378 typedef unsigned int BoardSize;
380 Boolean chessProgram;
382 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
383 int squareSize, smallLayout = 0, tinyLayout = 0,
384 marginW, marginH, // [HGM] for run-time resizing
385 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
386 ICSInputBoxUp = False, askQuestionUp = False,
387 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
388 editUp = False, errorUp = False, errorExitStatus = -1, lineGap;
389 Pixel timerForegroundPixel, timerBackgroundPixel;
390 Pixel buttonForegroundPixel, buttonBackgroundPixel;
391 char *chessDir, *programName, *programVersion,
392 *gameCopyFilename, *gamePasteFilename;
393 Boolean alwaysOnTop = False;
394 Boolean saveSettingsOnExit;
395 char *settingsFileName;
396 char *icsTextMenuString;
398 char *firstChessProgramNames;
399 char *secondChessProgramNames;
401 WindowPlacement wpMain;
402 WindowPlacement wpConsole;
403 WindowPlacement wpComment;
404 WindowPlacement wpMoveHistory;
405 WindowPlacement wpEvalGraph;
406 WindowPlacement wpEngineOutput;
407 WindowPlacement wpGameList;
408 WindowPlacement wpTags;
412 Pixmap pieceBitmap[2][(int)BlackPawn];
413 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
414 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
415 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
416 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
417 int useImages=0, useImageSqs;
418 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
419 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
420 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
421 XImage *ximLightSquare, *ximDarkSquare;
424 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
425 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
427 #define White(piece) ((int)(piece) < (int)BlackPawn)
429 /* Variables for doing smooth animation. This whole thing
430 would be much easier if the board was double-buffered,
431 but that would require a fairly major rewrite. */
436 GC blitGC, pieceGC, outlineGC;
437 XPoint startSquare, prevFrame, mouseDelta;
441 int startBoardX, startBoardY;
444 /* There can be two pieces being animated at once: a player
445 can begin dragging a piece before the remote opponent has moved. */
447 static AnimState game, player;
449 /* Bitmaps for use as masks when drawing XPM pieces.
450 Need one for each black and white piece. */
451 static Pixmap xpmMask[BlackKing + 1];
453 /* This magic number is the number of intermediate frames used
454 in each half of the animation. For short moves it's reduced
455 by 1. The total number of frames will be factor * 2 + 1. */
458 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
460 Enables icsEnables[] = {
461 { "menuFile.Mail Move", False },
462 { "menuFile.Reload CMail Message", False },
463 { "menuMode.Machine Black", False },
464 { "menuMode.Machine White", False },
465 { "menuMode.Analysis Mode", False },
466 { "menuMode.Analyze File", False },
467 { "menuMode.Two Machines", False },
469 { "menuHelp.Hint", False },
470 { "menuHelp.Book", False },
471 { "menuStep.Move Now", False },
472 { "menuOptions.Periodic Updates", False },
473 { "menuOptions.Hide Thinking", False },
474 { "menuOptions.Ponder Next Move", False },
479 Enables ncpEnables[] = {
480 { "menuFile.Mail Move", False },
481 { "menuFile.Reload CMail Message", False },
482 { "menuMode.Machine White", False },
483 { "menuMode.Machine Black", False },
484 { "menuMode.Analysis Mode", False },
485 { "menuMode.Analyze File", False },
486 { "menuMode.Two Machines", False },
487 { "menuMode.ICS Client", False },
488 { "menuMode.ICS Input Box", False },
490 { "menuStep.Revert", False },
491 { "menuStep.Move Now", False },
492 { "menuStep.Retract Move", False },
493 { "menuOptions.Auto Comment", False },
494 { "menuOptions.Auto Flag", False },
495 { "menuOptions.Auto Flip View", False },
496 { "menuOptions.Auto Observe", False },
497 { "menuOptions.Auto Raise Board", False },
498 { "menuOptions.Get Move List", False },
499 { "menuOptions.ICS Alarm", False },
500 { "menuOptions.Move Sound", False },
501 { "menuOptions.Quiet Play", False },
502 { "menuOptions.Hide Thinking", False },
503 { "menuOptions.Periodic Updates", False },
504 { "menuOptions.Ponder Next Move", False },
505 { "menuHelp.Hint", False },
506 { "menuHelp.Book", False },
510 Enables gnuEnables[] = {
511 { "menuMode.ICS Client", False },
512 { "menuMode.ICS Input Box", False },
513 { "menuAction.Accept", False },
514 { "menuAction.Decline", False },
515 { "menuAction.Rematch", False },
516 { "menuAction.Adjourn", False },
517 { "menuAction.Stop Examining", False },
518 { "menuAction.Stop Observing", False },
519 { "menuStep.Revert", False },
520 { "menuOptions.Auto Comment", False },
521 { "menuOptions.Auto Observe", False },
522 { "menuOptions.Auto Raise Board", False },
523 { "menuOptions.Get Move List", False },
524 { "menuOptions.Premove", False },
525 { "menuOptions.Quiet Play", False },
527 /* The next two options rely on SetCmailMode being called *after* */
528 /* SetGNUMode so that when GNU is being used to give hints these */
529 /* menu options are still available */
531 { "menuFile.Mail Move", False },
532 { "menuFile.Reload CMail Message", False },
536 Enables cmailEnables[] = {
538 { "menuAction.Call Flag", False },
539 { "menuAction.Draw", True },
540 { "menuAction.Adjourn", False },
541 { "menuAction.Abort", False },
542 { "menuAction.Stop Observing", False },
543 { "menuAction.Stop Examining", False },
544 { "menuFile.Mail Move", True },
545 { "menuFile.Reload CMail Message", True },
549 Enables trainingOnEnables[] = {
550 { "menuMode.Edit Comment", False },
551 { "menuMode.Pause", False },
552 { "menuStep.Forward", False },
553 { "menuStep.Backward", False },
554 { "menuStep.Forward to End", False },
555 { "menuStep.Back to Start", False },
556 { "menuStep.Move Now", False },
557 { "menuStep.Truncate Game", False },
561 Enables trainingOffEnables[] = {
562 { "menuMode.Edit Comment", True },
563 { "menuMode.Pause", True },
564 { "menuStep.Forward", True },
565 { "menuStep.Backward", True },
566 { "menuStep.Forward to End", True },
567 { "menuStep.Back to Start", True },
568 { "menuStep.Move Now", True },
569 { "menuStep.Truncate Game", True },
573 Enables machineThinkingEnables[] = {
574 { "menuFile.Load Game", False },
575 { "menuFile.Load Next Game", False },
576 { "menuFile.Load Previous Game", False },
577 { "menuFile.Reload Same Game", False },
578 { "menuFile.Paste Game", False },
579 { "menuFile.Load Position", False },
580 { "menuFile.Load Next Position", False },
581 { "menuFile.Load Previous Position", False },
582 { "menuFile.Reload Same Position", False },
583 { "menuFile.Paste Position", False },
584 { "menuMode.Machine White", False },
585 { "menuMode.Machine Black", False },
586 { "menuMode.Two Machines", False },
587 { "menuStep.Retract Move", False },
591 Enables userThinkingEnables[] = {
592 { "menuFile.Load Game", True },
593 { "menuFile.Load Next Game", True },
594 { "menuFile.Load Previous Game", True },
595 { "menuFile.Reload Same Game", True },
596 { "menuFile.Paste Game", True },
597 { "menuFile.Load Position", True },
598 { "menuFile.Load Next Position", True },
599 { "menuFile.Load Previous Position", True },
600 { "menuFile.Reload Same Position", True },
601 { "menuFile.Paste Position", True },
602 { "menuMode.Machine White", True },
603 { "menuMode.Machine Black", True },
604 { "menuMode.Two Machines", True },
605 { "menuStep.Retract Move", True },
611 MenuItem fileMenu[] = {
612 {N_("New Shuffle Game ..."), ShuffleMenuProc},
613 {N_("New Variant ..."), NewVariantProc}, // [HGM] variant: not functional yet
614 // {"----", NothingProc},
615 // {N_("Save Game"), SaveGameProc},
616 // {"----", NothingProc},
617 {N_("Copy Game"), CopyGameProc},
618 {N_("Paste Game"), PasteGameProc},
619 // {"----", NothingProc},
620 // {N_("Load Position"), LoadPositionProc},
621 // {N_("Load Next Position"), LoadNextPositionProc},
622 // {N_("Load Previous Position"), LoadPrevPositionProc},
623 // {N_("Reload Same Position"), ReloadPositionProc},
624 // {N_("Save Position"), SavePositionProc},
625 // {"----", NothingProc},
626 {N_("Copy Position"), CopyPositionProc},
627 {N_("Paste Position"), PastePositionProc},
628 // {"----", NothingProc},
629 {N_("Mail Move"), MailMoveProc},
630 {N_("Reload CMail Message"), ReloadCmailMsgProc},
631 // {"----", NothingProc},
635 MenuItem modeMenu[] = {
636 // {N_("Machine White"), MachineWhiteProc},
637 // {N_("Machine Black"), MachineBlackProc},
638 // {N_("Two Machines"), TwoMachinesProc},
639 // {N_("Analysis Mode"), AnalyzeModeProc},
640 // {N_("Analyze File"), AnalyzeFileProc },
641 // {N_("ICS Client"), IcsClientProc},
642 // {N_("Edit Game"), EditGameProc},
643 // {N_("Edit Position"), EditPositionProc},
644 // {N_("Training"), TrainingProc},
645 // {"----", NothingProc},
646 {N_("Show Engine Output"), EngineOutputProc},
647 {N_("Show Evaluation Graph"), EvalGraphProc},
648 {N_("Show Game List"), ShowGameListProc},
649 // {"Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
650 // {"----", NothingProc},
651 // {N_("Edit Tags"), EditTagsProc},
652 {N_("Edit Comment"), EditCommentProc},
653 {N_("ICS Input Box"), IcsInputBoxProc},
657 MenuItem optionsMenu[] = {
658 // {N_("Flip View"), FlipViewProc},
659 // {"----", NothingProc},
660 {N_("Adjudications ..."), EngineMenuProc},
661 {N_("General Settings ..."), UciMenuProc},
662 {N_("Engine #1 Settings ..."), FirstSettingsProc},
663 {N_("Engine #2 Settings ..."), SecondSettingsProc},
664 {N_("Time Control ..."), TimeControlProc},
665 {"----", NothingProc},
666 // {N_("Always Queen"), AlwaysQueenProc},
667 // {N_("Animate Dragging"), AnimateDraggingProc},
668 // {N_("Animate Moving"), AnimateMovingProc},
669 // {N_("Auto Comment"), AutocommProc},
670 // {N_("Auto Flag"), AutoflagProc},
671 // {N_("Auto Flip View"), AutoflipProc},
672 // {N_("Auto Observe"), AutobsProc},
673 // {N_("Auto Raise Board"), AutoraiseProc},
674 // {N_("Auto Save"), AutosaveProc},
675 // {N_("Blindfold"), BlindfoldProc},
676 // {N_("Flash Moves"), FlashMovesProc},
677 // {N_("Get Move List"), GetMoveListProc},
679 // {N_("Highlight Dragging"), HighlightDraggingProc},
681 // {N_("Highlight Last Move"), HighlightLastMoveProc},
682 // {N_("Move Sound"), MoveSoundProc},
683 // {N_("ICS Alarm"), IcsAlarmProc},
684 // {N_("Old Save Style"), OldSaveStyleProc},
685 // {N_("Periodic Updates"), PeriodicUpdatesProc},
686 // {N_("Ponder Next Move"), PonderNextMoveProc},
687 // {N_("Popup Exit Message"), PopupExitMessageProc},
688 // {N_("Popup Move Errors"), PopupMoveErrorsProc},
689 // {N_("Premove"), PremoveProc},
690 // {N_("Quiet Play"), QuietPlayProc},
691 // {N_("Hide Thinking"), HideThinkingProc},
692 // {N_("Test Legality"), TestLegalityProc},
693 // {N_("Show Coords"), ShowCoordsProc},
694 {"----", NothingProc},
695 {N_("Save Settings Now"), SaveSettingsProc},
696 {N_("Save Settings on Exit"), SaveOnExitProc},
701 {N_("File"), fileMenu},
702 {N_("Mode"), modeMenu},
703 {N_("Options"), optionsMenu},
707 #define PIECE_MENU_SIZE 18
708 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
709 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
710 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
711 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
712 N_("Empty square"), N_("Clear board") },
713 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
714 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
715 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
716 N_("Empty square"), N_("Clear board") }
718 /* must be in same order as PieceMenuStrings! */
719 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
720 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
721 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
722 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
723 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
724 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
725 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
726 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
727 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
730 #define DROP_MENU_SIZE 6
731 String dropMenuStrings[DROP_MENU_SIZE] = {
732 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
734 /* must be in same order as PieceMenuStrings! */
735 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
736 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
737 WhiteRook, WhiteQueen
745 DropMenuEnables dmEnables[] = {
754 { XtNborderWidth, 0 },
755 { XtNdefaultDistance, 0 },
759 { XtNborderWidth, 0 },
760 { XtNresizable, (XtArgVal) True },
764 { XtNborderWidth, 0 },
769 XtResource clientResources[] = {
770 { "flashCount", "flashCount", XtRInt, sizeof(int),
771 XtOffset(AppDataPtr, flashCount), XtRImmediate,
772 (XtPointer) FLASH_COUNT },
775 XrmOptionDescRec shellOptions[] = {
776 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
777 { "-flash", "flashCount", XrmoptionNoArg, "3" },
778 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
781 XtActionsRec boardActions[] = {
782 // { "HandleUserMove", HandleUserMove },
783 { "AnimateUserMove", AnimateUserMove },
784 // { "FileNameAction", FileNameAction },
785 { "HandlePV", HandlePV },
786 { "UnLoadPV", UnLoadPV },
787 { "AskQuestionProc", AskQuestionProc },
788 { "AskQuestionReplyAction", AskQuestionReplyAction },
789 { "PieceMenuPopup", PieceMenuPopup },
790 // { "WhiteClock", WhiteClock },
791 // { "BlackClock", BlackClock },
792 { "Iconify", Iconify },
793 { "LoadSelectedProc", LoadSelectedProc },
794 // { "LoadPositionProc", LoadPositionProc },
795 // { "LoadNextPositionProc", LoadNextPositionProc },
796 // { "LoadPrevPositionProc", LoadPrevPositionProc },
797 // { "ReloadPositionProc", ReloadPositionProc },
798 { "CopyPositionProc", CopyPositionProc },
799 { "PastePositionProc", PastePositionProc },
800 { "CopyGameProc", CopyGameProc },
801 { "PasteGameProc", PasteGameProc },
802 // { "SaveGameProc", SaveGameProc },
803 // { "SavePositionProc", SavePositionProc },
804 { "MailMoveProc", MailMoveProc },
805 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
806 // { "MachineWhiteProc", MachineWhiteProc },
807 // { "MachineBlackProc", MachineBlackProc },
808 // { "AnalysisModeProc", AnalyzeModeProc },
809 // { "AnalyzeFileProc", AnalyzeFileProc },
810 // { "TwoMachinesProc", TwoMachinesProc },
811 // { "IcsClientProc", IcsClientProc },
812 // { "EditGameProc", EditGameProc },
813 // { "EditPositionProc", EditPositionProc },
814 // { "TrainingProc", EditPositionProc },
815 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
816 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
817 { "ShowGameListProc", ShowGameListProc },
818 // { "ShowMoveListProc", HistoryShowProc},
819 // { "EditTagsProc", EditCommentProc },
820 { "EditCommentProc", EditCommentProc },
821 // { "IcsAlarmProc", IcsAlarmProc },
822 { "IcsInputBoxProc", IcsInputBoxProc },
823 // { "AcceptProc", AcceptProc },
824 // { "DeclineProc", DeclineProc },
825 // { "RematchProc", RematchProc },
826 // { "CallFlagProc", CallFlagProc },
827 // { "DrawProc", DrawProc },
828 // { "AdjournProc", AdjournProc },
829 // { "AbortProc", AbortProc },
830 // { "ResignProc", ResignProc },
831 // { "AdjuWhiteProc", AdjuWhiteProc },
832 // { "AdjuBlackProc", AdjuBlackProc },
833 // { "AdjuDrawProc", AdjuDrawProc },
834 { "EnterKeyProc", EnterKeyProc },
835 // { "StopObservingProc", StopObservingProc },
836 // { "StopExaminingProc", StopExaminingProc },
837 // { "BackwardProc", BackwardProc },
838 // { "ForwardProc", ForwardProc },
839 // { "ToStartProc", ToStartProc },
840 // { "ToEndProc", ToEndProc },
841 // { "RevertProc", RevertProc },
842 // { "TruncateGameProc", TruncateGameProc },
843 // { "MoveNowProc", MoveNowProc },
844 // { "RetractMoveProc", RetractMoveProc },
845 // { "AlwaysQueenProc", AlwaysQueenProc },
846 // { "AnimateDraggingProc", AnimateDraggingProc },
847 // { "AnimateMovingProc", AnimateMovingProc },
848 // { "AutoflagProc", AutoflagProc },
849 // { "AutoflipProc", AutoflipProc },
850 // { "AutobsProc", AutobsProc },
851 // { "AutoraiseProc", AutoraiseProc },
852 // { "AutosaveProc", AutosaveProc },
853 // { "BlindfoldProc", BlindfoldProc },
854 // { "FlashMovesProc", FlashMovesProc },
855 // { "FlipViewProc", FlipViewProc },
856 // { "GetMoveListProc", GetMoveListProc },
858 // { "HighlightDraggingProc", HighlightDraggingProc },
860 // { "HighlightLastMoveProc", HighlightLastMoveProc },
861 // { "IcsAlarmProc", IcsAlarmProc },
862 // { "MoveSoundProc", MoveSoundProc },
863 // { "OldSaveStyleProc", OldSaveStyleProc },
864 // { "PeriodicUpdatesProc", PeriodicUpdatesProc },
865 // { "PonderNextMoveProc", PonderNextMoveProc },
866 // { "PopupExitMessageProc", PopupExitMessageProc },
867 // { "PopupMoveErrorsProc", PopupMoveErrorsProc },
868 // { "PremoveProc", PremoveProc },
869 // { "QuietPlayProc", QuietPlayProc },
870 // { "ShowThinkingProc", ShowThinkingProc },
871 // { "HideThinkingProc", HideThinkingProc },
872 // { "TestLegalityProc", TestLegalityProc },
873 { "SaveSettingsProc", SaveSettingsProc },
874 { "SaveOnExitProc", SaveOnExitProc },
875 // { "InfoProc", InfoProc },
876 // { "ManProc", ManProc },
877 // { "HintProc", HintProc },
878 // { "BookProc", BookProc },
879 { "AboutGameProc", AboutGameProc },
880 { "DebugProc", DebugProc },
881 { "NothingProc", NothingProc },
882 { "CommentPopDown", (XtActionProc) CommentPopDown },
883 { "EditCommentPopDown", (XtActionProc) EditCommentPopDown },
884 { "TagsPopDown", (XtActionProc) TagsPopDown },
885 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
886 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
887 // { "FileNamePopDown", (XtActionProc) FileNamePopDown },
888 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
889 { "GameListPopDown", (XtActionProc) GameListPopDown },
890 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
891 // { "HistoryPopDown", (XtActionProc) HistoryPopDown },
892 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
893 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
894 { "ShufflePopDown", (XtActionProc) ShufflePopDown },
895 { "EnginePopDown", (XtActionProc) EnginePopDown },
896 { "UciPopDown", (XtActionProc) UciPopDown },
897 { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
898 { "NewVariantPopDown", (XtActionProc) NewVariantPopDown },
899 { "SettingsPopDown", (XtActionProc) SettingsPopDown },
900 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
903 //char globalTranslations[] =
904 // ":<Key>R: ResignProc() \n \
905 // :<Key>r: ResetProc() \n \
906 // :<Key>g: LoadGameProc() \n \
907 // :<Key>N: LoadNextGameProc() \n \
908 // :<Key>P: LoadPrevGameProc() \n \
909 // :<Key>Q: QuitProc() \n \
910 // :<Key>F: ToEndProc() \n \
911 // :<Key>f: ForwardProc() \n \
912 // :<Key>B: ToStartProc() \n \
913 // :<Key>b: BackwardProc() \n \
914 // :<Key>p: PauseProc() \n \
915 // :<Key>d: DrawProc() \n \
916 // :<Key>t: CallFlagProc() \n \
917 // :<Key>i: Iconify() \n \
918 // :<Key>c: Iconify() \n \
919 // :<Key>v: FlipViewProc() \n \
920 // <KeyDown>Control_L: BackwardProc() \n \
921 // <KeyUp>Control_L: ForwardProc() \n \
922 // <KeyDown>Control_R: BackwardProc() \n \
923 // <KeyUp>Control_R: ForwardProc() \n \
924 // Shift<Key>1: AskQuestionProc(\"Direct command\",\
925 // \"Send to chess program:\",,1) \n \
926 // Shift<Key>2: AskQuestionProc(\"Direct command\",\
927 // \"Send to second chess program:\",,2) \n";
929 //char boardTranslations[] =
930 // "<Btn1Down>: HandleUserMove() \n \
931 // <Btn1Up>: HandleUserMove() \n \
932 // <Btn1Motion>: AnimateUserMove() \n \
933 // <Btn3Motion>: HandlePV() \n \
934 // <Btn3Up>: UnLoadPV() \n \
935 // Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
936 // PieceMenuPopup(menuB) \n \
937 // Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
938 // PieceMenuPopup(menuW) \n \
939 // Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
940 // PieceMenuPopup(menuW) \n \
941 // Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
942 // PieceMenuPopup(menuB) \n";
944 //char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
945 //char blackTranslations[] = "<BtnDown>: BlackClock()\n";
947 char ICSInputTranslations[] =
948 "<Key>Return: EnterKeyProc() \n";
950 String xboardResources[] = {
951 // "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
952 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
953 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
957 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
958 "magenta", "cyan", "white" };
962 TextColors textColors[(int)NColorClasses];
964 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
966 parse_color(str, which)
970 char *p, buf[100], *d;
973 if (strlen(str) > 99) /* watch bounds on buf */
978 for (i=0; i<which; ++i) {
985 /* Could be looking at something like:
987 .. in which case we want to stop on a comma also */
988 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
992 return -1; /* Use default for empty field */
995 if (which == 2 || isdigit(*p))
998 while (*p && isalpha(*p))
1003 for (i=0; i<8; ++i) {
1004 if (!StrCaseCmp(buf, cnames[i]))
1005 return which? (i+40) : (i+30);
1007 if (!StrCaseCmp(buf, "default")) return -1;
1009 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1014 parse_cpair(cc, str)
1018 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1019 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1024 /* bg and attr are optional */
1025 textColors[(int)cc].bg = parse_color(str, 1);
1026 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1027 textColors[(int)cc].attr = 0;
1033 /* Arrange to catch delete-window events */
1034 Atom wm_delete_window;
1036 CatchDeleteWindow(Widget w, String procname)
1039 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1040 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1041 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1047 /* this should raise the board to the top */
1048 gtk_window_present(GTK_WINDOW(GUI_Window));
1052 //---------------------------------------------------------------------------------------------------------
1053 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1056 #define CW_USEDEFAULT (1<<31)
1057 #define ICS_TEXT_MENU_SIZE 90
1058 #define DEBUG_FILE "xboard.debug"
1059 #define SetCurrentDirectory chdir
1060 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1064 // these two must some day move to frontend.h, when they are implemented
1065 Boolean GameListIsUp();
1067 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1070 // front-end part of option handling
1072 // [HGM] This platform-dependent table provides the location for storing the color info
1073 extern char *crWhite, * crBlack;
1077 &appData.whitePieceColor,
1078 &appData.blackPieceColor,
1079 &appData.lightSquareColor,
1080 &appData.darkSquareColor,
1081 &appData.highlightSquareColor,
1082 &appData.premoveHighlightColor,
1083 &appData.lowTimeWarningColor,
1095 ParseFont(char *name, int number)
1096 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1098 case 0: // CLOCK_FONT
1099 appData.clockFont = strdup(name);
1101 case 1: // MESSAGE_FONT
1102 appData.font = strdup(name);
1104 case 2: // COORD_FONT
1105 appData.coordFont = strdup(name);
1114 { // only 2 fonts currently
1115 appData.clockFont = CLOCK_FONT_NAME;
1116 appData.coordFont = COORD_FONT_NAME;
1117 appData.font = DEFAULT_FONT_NAME;
1122 { // no-op, until we identify the code for this already in XBoard and move it here
1126 ParseColor(int n, char *name)
1127 { // in XBoard, just copy the color-name string
1128 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1132 ParseTextAttribs(ColorClass cc, char *s)
1134 (&appData.colorShout)[cc] = strdup(s);
1138 ParseBoardSize(void *addr, char *name)
1140 appData.boardSize = strdup(name);
1145 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1149 SetCommPortDefaults()
1150 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1153 // [HGM] args: these three cases taken out to stay in front-end
1155 SaveFontArg(FILE *f, ArgDescriptor *ad)
1158 switch((int)ad->argLoc) {
1159 case 0: // CLOCK_FONT
1160 name = appData.clockFont;
1162 case 1: // MESSAGE_FONT
1163 name = appData.font;
1165 case 2: // COORD_FONT
1166 name = appData.coordFont;
1171 // Do not save fonts for now, as the saved font would be board-size specific
1172 // and not suitable for a re-start at another board size
1173 // fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, name);
1178 { // nothing to do, as the sounds are at all times represented by their text-string names already
1182 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1183 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1184 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)ad->argLoc]);
1188 SaveColor(FILE *f, ArgDescriptor *ad)
1189 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1190 if(colorVariable[(int)ad->argLoc])
1191 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)ad->argLoc]);
1195 SaveBoardSize(FILE *f, char *name, void *addr)
1196 { // wrapper to shield back-end from BoardSize & sizeInfo
1197 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1201 ParseCommPortSettings(char *s)
1202 { // no such option in XBoard (yet)
1205 extern Widget engineOutputShell;
1206 extern Widget tagsShell, editTagsShell;
1208 GetActualPlacement(Widget wg, WindowPlacement *wp)
1218 XtSetArg(args[i], XtNx, &x); i++;
1219 XtSetArg(args[i], XtNy, &y); i++;
1220 XtSetArg(args[i], XtNwidth, &w); i++;
1221 XtSetArg(args[i], XtNheight, &h); i++;
1222 XtGetValues(wg, args, i);
1231 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1232 // In XBoard this will have to wait until awareness of window parameters is implemented
1233 GetActualPlacement(shellWidget, &wpMain);
1234 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1235 if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1236 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1237 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1238 if(commentShell) GetActualPlacement(commentShell, &wpComment);
1239 else GetActualPlacement(editShell, &wpComment);
1240 if(tagsShell) GetActualPlacement(tagsShell, &wpTags);
1241 else GetActualPlacement(editTagsShell, &wpTags);
1245 PrintCommPortSettings(FILE *f, char *name)
1246 { // This option does not exist in XBoard
1250 MySearchPath(char *installDir, char *name, char *fullname)
1251 { // just append installDir and name. Perhaps ExpandPath should be used here?
1252 name = ExpandPathName(name);
1253 if(name && name[0] == '/') strcpy(fullname, name); else {
1254 sprintf(fullname, "%s%c%s", installDir, '/', name);
1260 MyGetFullPathName(char *name, char *fullname)
1261 { // should use ExpandPath?
1262 name = ExpandPathName(name);
1263 strcpy(fullname, name);
1268 EnsureOnScreen(int *x, int *y, int minX, int minY)
1275 { // [HGM] args: allows testing if main window is realized from back-end
1276 return xBoardWindow != 0;
1280 PopUpStartupDialog()
1281 { // start menu not implemented in XBoard
1284 ConvertToLine(int argc, char **argv)
1286 static char line[128*1024], buf[1024];
1290 for(i=1; i<argc; i++) {
1291 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1292 && argv[i][0] != '{' )
1293 sprintf(buf, "{%s} ", argv[i]);
1294 else sprintf(buf, "%s ", argv[i]);
1297 line[strlen(line)-1] = NULLCHAR;
1301 //--------------------------------------------------------------------------------------------
1304 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1307 #define BoardSize int
1308 void InitDrawingSizes(BoardSize boardSize, int flags)
1309 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1310 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1312 XtGeometryResult gres;
1315 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1316 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1318 timerWidth = (boardWidth - sep) / 2;
1320 if (appData.titleInWindow)
1325 w = boardWidth - 2*bor;
1329 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1333 if(!formWidget) return;
1336 * Inhibit shell resizing.
1339 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1342 for(i=0; i<4; i++) {
1344 for(p=0; p<=(int)WhiteKing; p++)
1345 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1346 if(gameInfo.variant == VariantShogi) {
1347 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1348 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1349 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1350 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1351 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1354 if(gameInfo.variant == VariantGothic) {
1355 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1359 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1360 for(p=0; p<=(int)WhiteKing; p++)
1361 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1362 if(gameInfo.variant == VariantShogi) {
1363 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1364 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1365 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1366 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1367 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1370 if(gameInfo.variant == VariantGothic) {
1371 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1377 for(i=0; i<2; i++) {
1379 for(p=0; p<=(int)WhiteKing; p++)
1380 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1381 if(gameInfo.variant == VariantShogi) {
1382 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1383 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1384 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1385 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1386 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1389 if(gameInfo.variant == VariantGothic) {
1390 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1401 void EscapeExpand(char *p, char *q)
1402 { // [HGM] initstring: routine to shape up string arguments
1403 while(*p++ = *q++) if(p[-1] == '\\')
1405 case 'n': p[-1] = '\n'; break;
1406 case 'r': p[-1] = '\r'; break;
1407 case 't': p[-1] = '\t'; break;
1408 case '\\': p[-1] = '\\'; break;
1409 case 0: *p = 0; return;
1410 default: p[-1] = q[-1]; break;
1419 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1420 XSetWindowAttributes window_attributes;
1422 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1423 XrmValue vFrom, vTo;
1424 XtGeometryResult gres;
1427 int forceMono = False;
1429 srandom(time(0)); // [HGM] book: make random truly random
1431 setbuf(stdout, NULL);
1432 setbuf(stderr, NULL);
1435 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1436 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1440 programName = strrchr(argv[0], '/');
1441 if (programName == NULL)
1442 programName = argv[0];
1447 XtSetLanguageProc(NULL, NULL, NULL);
1448 bindtextdomain(PACKAGE, LOCALEDIR);
1449 textdomain(PACKAGE);
1453 XtAppInitialize(&appContext, "XBoard", shellOptions,
1454 XtNumber(shellOptions),
1455 &argc, argv, xboardResources, NULL, 0);
1458 gtk_init (&argc, &argv);
1460 /* parse glade file to build widgets */
1462 builder = gtk_builder_new ();
1463 gtk_builder_add_from_file (builder, "gtk-interface.xml", NULL);
1465 /* test if everything worked ok */
1467 GUI_Window = GTK_WIDGET (gtk_builder_get_object (builder, "MainWindow"));
1468 if(!GUI_Window) printf("Error: gtk_builder didn't work!\n");
1470 GUI_Aspect = GTK_WIDGET (gtk_builder_get_object (builder, "Aspectframe"));
1471 if(!GUI_Aspect) printf("Error: gtk_builder didn't work!\n");
1473 GUI_History = GTK_WIDGET (gtk_builder_get_object (builder, "MoveHistory"));
1474 if(!GUI_History) printf("Error: gtk_builder didn't work!\n");
1476 GUI_Menubar = GTK_WIDGET (gtk_builder_get_object (builder, "MenuBar"));
1477 if(!GUI_Menubar) printf("Error: gtk_builder didn't work!\n");
1478 GUI_Timer = GTK_WIDGET (gtk_builder_get_object (builder, "Timer"));
1479 if(!GUI_Timer) printf("Error: gtk_builder didn't work!\n");
1480 GUI_Buttonbar = GTK_WIDGET (gtk_builder_get_object (builder, "ButtonBar"));
1481 if(!GUI_Buttonbar) printf("Error: gtk_builder didn't work!\n");
1482 GUI_Board = GTK_WIDGET (gtk_builder_get_object (builder, "Board"));
1483 if(!GUI_Board) printf("Error: gtk_builder didn't work!\n");
1485 GUI_Whiteclock = GTK_WIDGET (gtk_builder_get_object (builder, "WhiteClock"));
1486 if(!GUI_Whiteclock) printf("Error: gtk_builder didn't work!\n");
1488 GUI_Blackclock = GTK_WIDGET (gtk_builder_get_object (builder, "BlackClock"));
1489 if(!GUI_Blackclock) printf("Error: gtk_builder didn't work!\n");
1491 LIST_MoveHistory = GTK_LIST_STORE (gtk_builder_get_object (builder, "MoveHistoryStore"));
1492 if(!LIST_MoveHistory) printf("Error: gtk_builder didn't work!\n");
1494 /* EditTags window */
1495 GUI_EditTags = GTK_WIDGET (gtk_builder_get_object (builder, "EditTags"));
1496 if(!GUI_EditTags) printf("Error: gtk_builder didn't work!\n");
1498 GUI_EditTagsTextArea = GTK_WIDGET (gtk_builder_get_object (builder, "EditTagsTextArea"));
1499 if(!GUI_EditTagsTextArea) printf("Error: gtk_builder didn't work!\n");
1502 gtk_builder_connect_signals (builder, NULL);
1504 // don't unref the builder, since we use it to get references to widgets
1505 // g_object_unref (G_OBJECT (builder));
1507 /* end parse glade file */
1509 appData.boardSize = "";
1510 InitAppData(ConvertToLine(argc, argv));
1514 fprintf(stderr, _("%s: unrecognized argument %s\n"),
1515 programName, argv[1]);
1517 fprintf(stderr, "Recognized options:\n");
1518 for(i = 0; i < XtNumber(shellOptions); i++)
1520 /* print first column */
1521 j = fprintf(stderr, " %s%s", shellOptions[i].option,
1522 (shellOptions[i].argKind == XrmoptionSepArg
1524 /* print second column and end line */
1525 if (++i < XtNumber(shellOptions))
1527 fprintf(stderr, "%*c%s%s\n", 40 - j, ' ',
1528 shellOptions[i].option,
1529 (shellOptions[i].argKind == XrmoptionSepArg
1534 fprintf(stderr, "\n");
1541 if (p == NULL) p = "/tmp";
1542 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1543 gameCopyFilename = (char*) malloc(i);
1544 gamePasteFilename = (char*) malloc(i);
1545 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1546 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1548 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1549 clientResources, XtNumber(clientResources),
1552 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1553 static char buf[MSG_SIZ];
1554 EscapeExpand(buf, appData.initString);
1555 appData.initString = strdup(buf);
1556 EscapeExpand(buf, appData.secondInitString);
1557 appData.secondInitString = strdup(buf);
1558 EscapeExpand(buf, appData.firstComputerString);
1559 appData.firstComputerString = strdup(buf);
1560 EscapeExpand(buf, appData.secondComputerString);
1561 appData.secondComputerString = strdup(buf);
1564 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1567 if (chdir(chessDir) != 0) {
1568 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1574 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1575 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1576 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1577 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1580 setbuf(debugFP, NULL);
1583 /* [HGM,HR] make sure board size is acceptable */
1584 if(appData.NrFiles > BOARD_FILES ||
1585 appData.NrRanks > BOARD_RANKS )
1586 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1589 /* This feature does not work; animation needs a rewrite */
1590 appData.highlightDragging = FALSE;
1594 xDisplay = XtDisplay(shellWidget);
1595 xScreen = DefaultScreen(xDisplay);
1596 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1598 gameInfo.variant = StringToVariant(appData.variant);
1599 InitPosition(FALSE);
1601 /* calc board size */
1602 if (isdigit(appData.boardSize[0]))
1604 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1605 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1606 &fontPxlSize, &smallLayout, &tinyLayout);
1609 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1610 programName, appData.boardSize);
1615 /* Find some defaults; use the nearest known size */
1616 SizeDefaults *szd, *nearest;
1617 int distance = 99999;
1618 nearest = szd = sizeDefaults;
1619 while (szd->name != NULL)
1621 if (abs(szd->squareSize - squareSize) < distance)
1624 distance = abs(szd->squareSize - squareSize);
1625 if (distance == 0) break;
1629 if (i < 2) lineGap = nearest->lineGap;
1630 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1631 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1632 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1633 if (i < 6) smallLayout = nearest->smallLayout;
1634 if (i < 7) tinyLayout = nearest->tinyLayout;
1639 SizeDefaults *szd = sizeDefaults;
1640 if (*appData.boardSize == NULLCHAR)
1642 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize
1643 || DisplayHeight(xDisplay, xScreen) < szd->minScreenSize)
1647 if (szd->name == NULL) szd--;
1648 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1652 while (szd->name != NULL
1653 && StrCaseCmp(szd->name, appData.boardSize) != 0)
1655 if (szd->name == NULL)
1657 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1658 programName, appData.boardSize);
1662 squareSize = szd->squareSize;
1663 lineGap = szd->lineGap;
1664 clockFontPxlSize = szd->clockFontPxlSize;
1665 coordFontPxlSize = szd->coordFontPxlSize;
1666 fontPxlSize = szd->fontPxlSize;
1667 smallLayout = szd->smallLayout;
1668 tinyLayout = szd->tinyLayout;
1670 /* end figuring out what size to use */
1672 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1673 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1676 * Determine what fonts to use.
1678 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1679 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1680 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1681 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1682 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1683 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1684 appData.font = FindFont(appData.font, fontPxlSize);
1685 countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
1686 countFontStruct = XQueryFont(xDisplay, countFontID);
1687 // appData.font = FindFont(appData.font, fontPxlSize);
1689 xdb = XtDatabase(xDisplay);
1690 XrmPutStringResource(&xdb, "*font", appData.font);
1693 * Detect if there are not enough colors available and adapt.
1695 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1696 appData.monoMode = True;
1699 if (!appData.monoMode) {
1700 vFrom.addr = (caddr_t) appData.lightSquareColor;
1701 vFrom.size = strlen(appData.lightSquareColor);
1702 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1703 if (vTo.addr == NULL) {
1704 appData.monoMode = True;
1707 lightSquareColor = *(Pixel *) vTo.addr;
1710 if (!appData.monoMode) {
1711 vFrom.addr = (caddr_t) appData.darkSquareColor;
1712 vFrom.size = strlen(appData.darkSquareColor);
1713 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1714 if (vTo.addr == NULL) {
1715 appData.monoMode = True;
1718 darkSquareColor = *(Pixel *) vTo.addr;
1721 if (!appData.monoMode) {
1722 vFrom.addr = (caddr_t) appData.whitePieceColor;
1723 vFrom.size = strlen(appData.whitePieceColor);
1724 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1725 if (vTo.addr == NULL) {
1726 appData.monoMode = True;
1729 whitePieceColor = *(Pixel *) vTo.addr;
1732 if (!appData.monoMode) {
1733 vFrom.addr = (caddr_t) appData.blackPieceColor;
1734 vFrom.size = strlen(appData.blackPieceColor);
1735 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1736 if (vTo.addr == NULL) {
1737 appData.monoMode = True;
1740 blackPieceColor = *(Pixel *) vTo.addr;
1744 if (!appData.monoMode) {
1745 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1746 vFrom.size = strlen(appData.highlightSquareColor);
1747 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1748 if (vTo.addr == NULL) {
1749 appData.monoMode = True;
1752 highlightSquareColor = *(Pixel *) vTo.addr;
1756 if (!appData.monoMode) {
1757 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1758 vFrom.size = strlen(appData.premoveHighlightColor);
1759 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1760 if (vTo.addr == NULL) {
1761 appData.monoMode = True;
1764 premoveHighlightColor = *(Pixel *) vTo.addr;
1769 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1772 if (appData.bitmapDirectory == NULL ||
1773 appData.bitmapDirectory[0] == NULLCHAR)
1774 appData.bitmapDirectory = DEF_BITMAP_DIR;
1777 if (appData.lowTimeWarning && !appData.monoMode) {
1778 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1779 vFrom.size = strlen(appData.lowTimeWarningColor);
1780 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1781 if (vTo.addr == NULL)
1782 appData.monoMode = True;
1784 lowTimeWarningColor = *(Pixel *) vTo.addr;
1787 if (appData.monoMode && appData.debugMode) {
1788 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1789 (unsigned long) XWhitePixel(xDisplay, xScreen),
1790 (unsigned long) XBlackPixel(xDisplay, xScreen));
1793 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1794 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1795 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1796 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1797 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1798 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1799 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1800 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1801 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1802 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1804 if (appData.colorize) {
1806 _("%s: can't parse color names; disabling colorization\n"),
1809 appData.colorize = FALSE;
1811 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
1812 textColors[ColorNone].attr = 0;
1814 // XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1820 layoutName = "tinyLayout";
1821 } else if (smallLayout) {
1822 layoutName = "smallLayout";
1824 layoutName = "normalLayout";
1827 if (appData.titleInWindow) {
1828 /* todo check what this appdata does */
1831 if (appData.showButtonBar) {
1832 /* TODO hide button bar if requested */
1836 if (appData.titleInWindow)
1841 if (appData.showButtonBar)
1848 if (appData.showButtonBar)
1858 /* set some checkboxes in the menu according to appData */
1860 if (appData.alwaysPromoteToQueen)
1861 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Always Queen")),TRUE);
1863 if (appData.animateDragging)
1864 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Animate Dragging")),TRUE);
1866 if (appData.animate)
1867 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Animate Moving")),TRUE);
1869 if (appData.autoComment)
1870 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Comment")),TRUE);
1872 if (appData.autoCallFlag)
1873 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Flag")),TRUE);
1875 if (appData.autoFlipView)
1876 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Flip View")),TRUE);
1878 if (appData.autoObserve)
1879 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Observe")),TRUE);
1881 if (appData.autoRaiseBoard)
1882 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Raise Board")),TRUE);
1884 if (appData.autoSaveGames)
1885 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Save")),TRUE);
1887 if (appData.saveGameFile[0] != NULLCHAR)
1889 /* Can't turn this off from menu */
1890 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Save")),TRUE);
1891 gtk_action_set_sensitive(GTK_ACTION (gtk_builder_get_object (builder, "menuOptions.Auto Save")),FALSE);
1894 if (appData.blindfold)
1895 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Blindfold")),TRUE);
1897 if (appData.flashCount > 0)
1898 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Flash Moves")),TRUE);
1900 if (appData.getMoveList)
1901 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Get Move List")),TRUE);
1904 if (appData.highlightDragging)
1905 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Highlight Dragging")),TRUE);
1908 if (appData.highlightLastMove)
1909 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Highlight Last Move")),TRUE);
1911 if (appData.icsAlarm)
1912 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.ICS Alarm")),TRUE);
1914 if (appData.ringBellAfterMoves)
1915 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Move Sound")),TRUE);
1917 if (appData.oldSaveStyle)
1918 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Old Save Style")),TRUE);
1920 if (appData.periodicUpdates)
1921 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Periodic Updates")),TRUE);
1923 if (appData.ponderNextMove)
1924 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Ponder Next Move")),TRUE);
1926 if (appData.popupExitMessage)
1927 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Popup Exit Message")),TRUE);
1929 if (appData.popupMoveErrors)
1930 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Popup Move Errors")),TRUE);
1932 if (appData.premove)
1933 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Premove")),TRUE);
1935 if (appData.quietPlay)
1936 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Quit Play")),TRUE);
1938 if (appData.showCoords)
1939 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Show Coords")),TRUE);
1941 if (appData.showThinking)
1942 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Hide Thinking")),TRUE);
1944 if (appData.testLegality)
1945 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Test Legality")),TRUE);
1948 // if (saveSettingsOnExit) {
1949 // XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
1954 /* end setting check boxes */
1956 /* load square colors */
1957 SVGLightSquare = load_pixbuf("svg/LightSquare.svg",squareSize);
1958 SVGDarkSquare = load_pixbuf("svg/DarkSquare.svg",squareSize);
1959 SVGNeutralSquare = load_pixbuf("svg/NeutralSquare.svg",squareSize);
1961 /* use two icons to indicate if it is white's or black's turn */
1962 WhiteIcon = load_pixbuf("svg/icon_white.svg",0);
1963 BlackIcon = load_pixbuf("svg/icon_black.svg",0);
1964 WindowIcon = WhiteIcon;
1965 gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
1968 /* realize window */
1969 gtk_widget_show (GUI_Window);
1971 /* recalc boardsize */
1976 if (appData.animate || appData.animateDragging)
1981 if (errorExitStatus == -1) {
1982 if (appData.icsActive) {
1983 /* We now wait until we see "login:" from the ICS before
1984 sending the logon script (problems with timestamp otherwise) */
1985 /*ICSInitScript();*/
1986 if (appData.icsInputBox) ICSInputBoxPopUp();
1990 signal(SIGWINCH, TermSizeSigHandler);
1992 signal(SIGINT, IntSigHandler);
1993 signal(SIGTERM, IntSigHandler);
1994 if (*appData.cmailGameName != NULLCHAR) {
1995 signal(SIGUSR1, CmailSigHandler);
1998 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2002 * Create a cursor for the board widget.
2003 * (This needs to be called after the window has been created to have access to board-window)
2006 BoardCursor = gdk_cursor_new(GDK_HAND2);
2007 gdk_window_set_cursor(GUI_Board->window, BoardCursor);
2008 gdk_cursor_destroy(BoardCursor);
2013 if (appData.debugMode) fclose(debugFP); // [DM] debug
2020 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2021 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2023 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2024 unlink(gameCopyFilename);
2025 unlink(gamePasteFilename);
2028 RETSIGTYPE TermSizeSigHandler(int sig)
2041 CmailSigHandler(sig)
2047 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2049 /* Activate call-back function CmailSigHandlerCallBack() */
2050 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2052 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2056 CmailSigHandlerCallBack(isr, closure, message, count, error)
2064 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2066 /**** end signal code ****/
2076 f = fopen(appData.icsLogon, "r");
2082 strcat(buf, appData.icsLogon);
2083 f = fopen(buf, "r");
2087 ProcessICSInitScript(f);
2094 EditCommentPopDown();
2104 if (!menuBarWidget) return;
2105 w = XtNameToWidget(menuBarWidget, "menuStep.Revert");
2107 DisplayError("menuStep.Revert", 0);
2109 XtSetSensitive(w, !grey);
2114 SetMenuEnables(enab)
2119 if (!builder) return;
2120 while (enab->name != NULL) {
2121 o = gtk_builder_get_object(builder, enab->name);
2122 if(GTK_IS_WIDGET(o))
2123 gtk_widget_set_sensitive(GTK_WIDGET (o),enab->value);
2126 if(GTK_IS_ACTION(o))
2127 gtk_action_set_sensitive(GTK_ACTION (o),enab->value);
2129 DisplayError(enab->name, 0);
2137 SetMenuEnables(icsEnables);
2140 if (appData.zippyPlay && !appData.noChessProgram) /* [DM] icsEngineAnalyze */
2141 {}; // XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2148 SetMenuEnables(ncpEnables);
2154 SetMenuEnables(gnuEnables);
2160 SetMenuEnables(cmailEnables);
2166 SetMenuEnables(trainingOnEnables);
2167 if (appData.showButtonBar) {
2168 // XtSetSensitive(buttonBarWidget, False);
2174 SetTrainingModeOff()
2176 SetMenuEnables(trainingOffEnables);
2177 if (appData.showButtonBar) {
2178 // XtSetSensitive(buttonBarWidget, True);
2183 SetUserThinkingEnables()
2185 if (appData.noChessProgram) return;
2186 SetMenuEnables(userThinkingEnables);
2190 SetMachineThinkingEnables()
2192 if (appData.noChessProgram) return;
2193 SetMenuEnables(machineThinkingEnables);
2195 case MachinePlaysBlack:
2196 case MachinePlaysWhite:
2197 case TwoMachinesPlay:
2198 // XtSetSensitive(XtNameToWidget(menuBarWidget,
2199 // ModeToWidgetName(gameMode)), True);
2206 #define Abs(n) ((n)<0 ? -(n) : (n))
2209 * Find a font that matches "pattern" that is as close as
2210 * possible to the targetPxlSize. Prefer fonts that are k
2211 * pixels smaller to fonts that are k pixels larger. The
2212 * pattern must be in the X Consortium standard format,
2213 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2214 * The return value should be freed with XtFree when no
2217 char *FindFont(pattern, targetPxlSize)
2221 char **fonts, *p, *best, *scalable, *scalableTail;
2222 int i, j, nfonts, minerr, err, pxlSize;
2225 char **missing_list;
2227 char *def_string, *base_fnt_lst, strInt[3];
2229 XFontStruct **fnt_list;
2231 base_fnt_lst = calloc(1, strlen(pattern) + 3);
2232 sprintf(strInt, "%d", targetPxlSize);
2233 p = strstr(pattern, "--");
2234 strncpy(base_fnt_lst, pattern, p - pattern + 2);
2235 strcat(base_fnt_lst, strInt);
2236 strcat(base_fnt_lst, strchr(p + 2, '-'));
2238 if ((fntSet = XCreateFontSet(xDisplay,
2242 &def_string)) == NULL) {
2244 fprintf(stderr, _("Unable to create font set.\n"));
2248 nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
2250 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2252 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2253 programName, pattern);
2261 for (i=0; i<nfonts; i++) {
2264 if (*p != '-') continue;
2266 if (*p == NULLCHAR) break;
2267 if (*p++ == '-') j++;
2269 if (j < 7) continue;
2272 scalable = fonts[i];
2275 err = pxlSize - targetPxlSize;
2276 if (Abs(err) < Abs(minerr) ||
2277 (minerr > 0 && err < 0 && -err == minerr)) {
2283 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2284 /* If the error is too big and there is a scalable font,
2285 use the scalable font. */
2286 int headlen = scalableTail - scalable;
2287 p = (char *) XtMalloc(strlen(scalable) + 10);
2288 while (isdigit(*scalableTail)) scalableTail++;
2289 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2291 p = (char *) XtMalloc(strlen(best) + 1);
2294 if (appData.debugMode) {
2295 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2296 pattern, targetPxlSize, p);
2299 if (missing_count > 0)
2300 XFreeStringList(missing_list);
2301 XFreeFontSet(xDisplay, fntSet);
2303 XFreeFontNames(fonts);
2310 /* 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*/
2312 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2313 | GCBackground | GCFunction | GCPlaneMask;
2314 XGCValues gc_values;
2317 gc_values.plane_mask = AllPlanes;
2318 gc_values.line_width = lineGap;
2319 gc_values.line_style = LineSolid;
2320 gc_values.function = GXcopy;
2322 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
2323 gc_values.background = XWhitePixel(xDisplay, xScreen);
2324 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
2325 XSetFont(xDisplay, coordGC, coordFontID);
2327 if (appData.monoMode) {
2328 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
2329 gc_values.background = XBlackPixel(xDisplay, xScreen);
2330 lightSquareGC = wbPieceGC
2331 = XtGetGC(shellWidget, value_mask, &gc_values);
2333 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
2334 gc_values.background = XWhitePixel(xDisplay, xScreen);
2335 darkSquareGC = bwPieceGC
2336 = XtGetGC(shellWidget, value_mask, &gc_values);
2338 if (DefaultDepth(xDisplay, xScreen) == 1) {
2339 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2340 gc_values.function = GXcopyInverted;
2341 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
2342 gc_values.function = GXcopy;
2343 if (XBlackPixel(xDisplay, xScreen) == 1) {
2344 bwPieceGC = darkSquareGC;
2345 wbPieceGC = copyInvertedGC;
2347 bwPieceGC = copyInvertedGC;
2348 wbPieceGC = lightSquareGC;
2352 gc_values.foreground = lightSquareColor;
2353 gc_values.background = darkSquareColor;
2354 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
2356 gc_values.foreground = darkSquareColor;
2357 gc_values.background = lightSquareColor;
2358 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
2360 gc_values.foreground = jailSquareColor;
2361 gc_values.background = jailSquareColor;
2362 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
2364 gc_values.foreground = whitePieceColor;
2365 gc_values.background = darkSquareColor;
2366 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2368 gc_values.foreground = whitePieceColor;
2369 gc_values.background = lightSquareColor;
2370 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2372 gc_values.foreground = whitePieceColor;
2373 gc_values.background = jailSquareColor;
2374 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2376 gc_values.foreground = blackPieceColor;
2377 gc_values.background = darkSquareColor;
2378 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2380 gc_values.foreground = blackPieceColor;
2381 gc_values.background = lightSquareColor;
2382 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2384 gc_values.foreground = blackPieceColor;
2385 gc_values.background = jailSquareColor;
2386 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2395 for(i=0;i<MAXPIECES;i++)
2399 g_free(SVGpieces[i]);
2406 SVGLightSquare = load_pixbuf("svg/LightSquare.svg",squareSize);
2407 SVGDarkSquare = load_pixbuf("svg/DarkSquare.svg",squareSize);
2408 SVGNeutralSquare = load_pixbuf("svg/NeutralSquare.svg",squareSize);
2411 /* get some defaults going */
2412 for(i=WhitePawn; i<DemotePiece+1; i++)
2413 SVGpieces[i] = load_pixbuf("svg/NeutralSquare.svg",squareSize);
2415 SVGpieces[WhitePawn] = load_pixbuf("svg/WhitePawn.svg",squareSize);
2416 SVGpieces[WhiteKnight] = load_pixbuf("svg/WhiteKnight.svg",squareSize);
2417 SVGpieces[WhiteBishop] = load_pixbuf("svg/WhiteBishop.svg",squareSize);
2418 SVGpieces[WhiteRook] = load_pixbuf("svg/WhiteRook.svg",squareSize);
2419 SVGpieces[WhiteQueen] = load_pixbuf("svg/WhiteQueen.svg",squareSize);
2420 SVGpieces[WhiteKing] = load_pixbuf("svg/WhiteKing.svg",squareSize);
2422 SVGpieces[BlackPawn] = load_pixbuf("svg/BlackPawn.svg",squareSize);
2423 SVGpieces[BlackKnight] = load_pixbuf("svg/BlackKnight.svg",squareSize);
2424 SVGpieces[BlackBishop] = load_pixbuf("svg/BlackBishop.svg",squareSize);
2425 SVGpieces[BlackRook] = load_pixbuf("svg/BlackRook.svg",squareSize);
2426 SVGpieces[BlackQueen] = load_pixbuf("svg/BlackQueen.svg",squareSize);
2427 SVGpieces[BlackKing] = load_pixbuf("svg/BlackKing.svg",squareSize);
2433 static void MenuBarSelect(w, addr, index)
2438 XtActionProc proc = (XtActionProc) addr;
2440 (proc)(NULL, NULL, NULL, NULL);
2443 void CreateMenuBarPopup(parent, name, mb)
2453 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2456 XtSetArg(args[j], XtNleftMargin, 20); j++;
2457 XtSetArg(args[j], XtNrightMargin, 20); j++;
2459 while (mi->string != NULL) {
2460 if (strcmp(mi->string, "----") == 0) {
2461 entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
2464 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
2465 entry = XtCreateManagedWidget(mi->string, smeBSBObjectClass,
2467 XtAddCallback(entry, XtNcallback,
2468 (XtCallbackProc) MenuBarSelect,
2469 (caddr_t) mi->proc);
2475 Widget CreateMenuBar(mb)
2479 Widget anchor, menuBar;
2481 char menuName[MSG_SIZ];
2484 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2485 XtSetArg(args[j], XtNvSpace, 0); j++;
2486 XtSetArg(args[j], XtNborderWidth, 0); j++;
2487 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
2488 formWidget, args, j);
2490 while (mb->name != NULL) {
2491 strcpy(menuName, "menu");
2492 strcat(menuName, mb->name);
2494 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
2497 shortName[0] = _(mb->name)[0];
2498 shortName[1] = NULLCHAR;
2499 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
2502 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
2505 XtSetArg(args[j], XtNborderWidth, 0); j++;
2506 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
2508 CreateMenuBarPopup(menuBar, menuName, mb);
2516 CreatePieceMenu(name, color)
2523 ChessSquare selection;
2525 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2526 boardWidget, args, 0);
2528 for (i = 0; i < PIECE_MENU_SIZE; i++) {
2529 String item = pieceMenuStrings[color][i];
2531 if (strcmp(item, "----") == 0) {
2532 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2535 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2536 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2538 selection = pieceMenuTranslation[color][i];
2539 XtAddCallback(entry, XtNcallback,
2540 (XtCallbackProc) PieceMenuSelect,
2541 (caddr_t) selection);
2542 if (selection == WhitePawn || selection == BlackPawn) {
2543 XtSetArg(args[0], XtNpopupOnEntry, entry);
2544 XtSetValues(menu, args, 1);
2557 ChessSquare selection;
2559 // whitePieceMenu = CreatePieceMenu("menuW", 0);
2560 // blackPieceMenu = CreatePieceMenu("menuB", 1);
2562 // XtRegisterGrabAction(PieceMenuPopup, True,
2563 // (unsigned)(ButtonPressMask|ButtonReleaseMask),
2564 // GrabModeAsync, GrabModeAsync);
2566 // XtSetArg(args[0], XtNlabel, _("Drop"));
2567 // dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
2568 // boardWidget, args, 1);
2569 // for (i = 0; i < DROP_MENU_SIZE; i++) {
2570 // String item = dropMenuStrings[i];
2572 // if (strcmp(item, "----") == 0) {
2573 // entry = XtCreateManagedWidget(item, smeLineObjectClass,
2574 // dropMenu, NULL, 0);
2576 // XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2577 // entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2578 // dropMenu, args, 1);
2579 // selection = dropMenuTranslation[i];
2580 // XtAddCallback(entry, XtNcallback,
2581 // (XtCallbackProc) DropMenuSelect,
2582 // (caddr_t) selection);
2587 void SetupDropMenu()
2595 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2596 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2597 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2598 dmEnables[i].piece);
2599 XtSetSensitive(entry, p != NULL || !appData.testLegality
2600 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2601 && !appData.icsActive));
2603 while (p && *p++ == dmEnables[i].piece) count++;
2604 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2606 XtSetArg(args[j], XtNlabel, label); j++;
2607 XtSetValues(entry, args, j);
2611 void PieceMenuPopup(w, event, params, num_params)
2615 Cardinal *num_params;
2619 if (event->type != ButtonRelease) UnLoadPV(); // [HGM] pv
2620 if (event->type != ButtonPress) return;
2621 if (errorUp) ErrorPopDown();
2625 whichMenu = params[0];
2628 if(!appData.icsEngineAnalyze) return;
2629 case IcsPlayingWhite:
2630 case IcsPlayingBlack:
2631 if(!appData.zippyPlay) goto noZip;
2634 case MachinePlaysWhite:
2635 case MachinePlaysBlack:
2636 case TwoMachinesPlay: // [HGM] pv: use for showing PV
2637 if (!appData.dropMenu) {
2638 LoadPV(event->xbutton.x, event->xbutton.y);
2641 if(gameMode == TwoMachinesPlay || gameMode == AnalyzeMode ||
2642 gameMode == AnalyzeFile || gameMode == IcsObserving) return;
2645 if (!appData.dropMenu || appData.testLegality &&
2646 gameInfo.variant != VariantBughouse &&
2647 gameInfo.variant != VariantCrazyhouse) return;
2649 whichMenu = "menuD";
2655 if (((pmFromX = EventToSquare(event->xbutton.x, BOARD_WIDTH)) < 0) ||
2656 ((pmFromY = EventToSquare(event->xbutton.y, BOARD_HEIGHT)) < 0)) {
2657 pmFromX = pmFromY = -1;
2661 pmFromX = BOARD_WIDTH - 1 - pmFromX;
2663 pmFromY = BOARD_HEIGHT - 1 - pmFromY;
2665 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
2668 static void PieceMenuSelect(w, piece, junk)
2673 if (pmFromX < 0 || pmFromY < 0) return;
2674 EditPositionMenuEvent(piece, pmFromX, pmFromY);
2677 static void DropMenuSelect(w, piece, junk)
2682 if (pmFromX < 0 || pmFromY < 0) return;
2683 DropMenuEvent(piece, pmFromX, pmFromY);
2687 * If the user selects on a border boundary, return -1; if off the board,
2688 * return -2. Otherwise map the event coordinate to the square.
2690 int EventToSquare(x, limit)
2698 if ((x % (squareSize + lineGap)) >= squareSize)
2700 x /= (squareSize + lineGap);
2706 static void do_flash_delay(msec)
2712 static void drawHighlight(file, rank, line_type)
2713 int file, rank, line_type;
2718 if (lineGap == 0 || appData.blindfold) return;
2722 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
2723 (squareSize + lineGap);
2724 y = lineGap/2 + rank * (squareSize + lineGap);
2728 x = lineGap/2 + file * (squareSize + lineGap);
2729 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
2730 (squareSize + lineGap);
2734 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2736 /* draw the highlight */
2737 cairo_move_to (cr, x, y);
2738 cairo_rel_line_to (cr, 0,squareSize+lineGap);
2739 cairo_rel_line_to (cr, squareSize+lineGap,0);
2740 cairo_rel_line_to (cr, 0,-squareSize-lineGap);
2741 cairo_close_path (cr);
2743 cairo_set_line_width (cr, lineGap);
2746 /* TODO: use appdata colors */
2747 case LINE_TYPE_HIGHLIGHT:
2748 cairo_set_source_rgba (cr, 1, 1, 0, 1.0);
2751 cairo_set_source_rgba (cr, 1, 0, 0, 1.0);
2753 case LINE_TYPE_NORMAL:
2755 cairo_set_source_rgba (cr, 0, 1, 0, 1.0);
2766 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
2767 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
2770 SetHighlights(fromX, fromY, toX, toY)
2771 int fromX, fromY, toX, toY;
2773 if (hi1X != fromX || hi1Y != fromY)
2775 if (hi1X >= 0 && hi1Y >= 0)
2777 drawHighlight(hi1X, hi1Y, LINE_TYPE_NORMAL);
2779 if (fromX >= 0 && fromY >= 0)
2781 drawHighlight(fromX, fromY, LINE_TYPE_HIGHLIGHT);
2784 if (hi2X != toX || hi2Y != toY)
2786 if (hi2X >= 0 && hi2Y >= 0)
2788 drawHighlight(hi2X, hi2Y, LINE_TYPE_NORMAL);
2790 if (toX >= 0 && toY >= 0)
2792 drawHighlight(toX, toY, LINE_TYPE_HIGHLIGHT);
2806 SetHighlights(-1, -1, -1, -1);
2811 SetPremoveHighlights(fromX, fromY, toX, toY)
2812 int fromX, fromY, toX, toY;
2814 if (pm1X != fromX || pm1Y != fromY)
2816 if (pm1X >= 0 && pm1Y >= 0)
2818 drawHighlight(pm1X, pm1Y, LINE_TYPE_NORMAL);
2820 if (fromX >= 0 && fromY >= 0)
2822 drawHighlight(fromX, fromY, LINE_TYPE_PRE);
2825 if (pm2X != toX || pm2Y != toY)
2827 if (pm2X >= 0 && pm2Y >= 0)
2829 drawHighlight(pm2X, pm2Y, LINE_TYPE_NORMAL);
2831 if (toX >= 0 && toY >= 0)
2833 drawHighlight(toX, toY, LINE_TYPE_PRE);
2846 ClearPremoveHighlights()
2848 SetPremoveHighlights(-1, -1, -1, -1);
2851 static void BlankSquare(x, y, color, piece, dest)
2864 pb = SVGLightSquare;
2866 case 2: /* neutral */
2868 pb = SVGNeutralSquare;
2871 gdk_draw_pixbuf(GDK_WINDOW(GUI_Board->window),NULL,pb,0,0,x,y,-1,-1, GDK_RGB_DITHER_NORMAL, 0, 0);
2875 static void DrawPiece(piece, square_color, x, y, dest)
2877 int square_color, x, y;
2880 /* redraw background, since piece might be transparent in some areas */
2881 BlankSquare(x,y,square_color,piece,dest);
2884 gdk_draw_pixbuf(GDK_WINDOW(GUI_Board->window),NULL,
2885 GDK_PIXBUF(SVGpieces[piece]),0,0,x,y,-1,-1,
2886 GDK_RGB_DITHER_NORMAL, 0, 0);
2890 /* [HR] determine square color depending on chess variant. */
2891 static int SquareColor(row, column)
2896 if (gameInfo.variant == VariantXiangqi) {
2897 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
2899 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
2901 } else if (row <= 4) {
2907 square_color = ((column + row) % 2) == 1;
2910 /* [hgm] holdings: next line makes all holdings squares light */
2911 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
2913 return square_color;
2916 void DrawSquare(row, column, piece, do_flash)
2917 int row, column, do_flash;
2920 int square_color, x, y;
2925 /* Calculate delay in milliseconds (2-delays per complete flash) */
2926 flash_delay = 500 / appData.flashRate;
2928 /* calculate x and y coordinates from row and column */
2931 x = lineGap + ((BOARD_WIDTH-1)-column) *
2932 (squareSize + lineGap);
2933 y = lineGap + row * (squareSize + lineGap);
2937 x = lineGap + column * (squareSize + lineGap);
2938 y = lineGap + ((BOARD_HEIGHT-1)-row) *
2939 (squareSize + lineGap);
2942 square_color = SquareColor(row, column);
2944 // [HGM] holdings: blank out area between board and holdings
2945 if ( column == BOARD_LEFT-1 || column == BOARD_RGHT
2946 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
2947 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) )
2949 BlankSquare(x, y, 2, EmptySquare, xBoardWindow);
2951 // [HGM] print piece counts next to holdings
2952 string[1] = NULLCHAR;
2955 cairo_text_extents_t extents;
2960 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2962 string[0] = '0' + piece;
2964 /* TODO this has to go into the font-selection */
2965 cairo_select_font_face (cr, "Sans",
2966 CAIRO_FONT_SLANT_NORMAL,
2967 CAIRO_FONT_WEIGHT_NORMAL);
2969 cairo_set_font_size (cr, 12.0);
2970 cairo_text_extents (cr, string, &extents);
2972 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) )
2974 xpos= x + squareSize - extents.width - 2;
2975 ypos= y + extents.y_bearing + 1;
2977 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1)
2980 ypos = y + extents.y_bearing + 1;
2983 /* TODO mono mode? */
2984 cairo_move_to (cr, xpos, ypos);
2985 cairo_text_path (cr, string);
2986 cairo_set_source_rgb (cr, 1.0, 1.0, 1);
2987 cairo_fill_preserve (cr);
2988 cairo_set_source_rgb (cr, 0, 0, 0);
2989 cairo_set_line_width (cr, 0.1);
2998 /* square on the board */
2999 if (piece == EmptySquare || appData.blindfold)
3001 BlankSquare(x, y, square_color, piece, xBoardWindow);
3005 if (do_flash && appData.flashCount > 0)
3007 for (i=0; i<appData.flashCount; ++i)
3010 DrawPiece(piece, square_color, x, y, xBoardWindow);
3011 do_flash_delay(flash_delay);
3013 BlankSquare(x, y, square_color, piece, xBoardWindow);
3014 do_flash_delay(flash_delay);
3017 DrawPiece(piece, square_color, x, y, xBoardWindow);
3021 /* show coordinates if necessary */
3022 if(appData.showCoords)
3024 cairo_text_extents_t extents;
3028 /* TODO this has to go into the font-selection */
3029 cairo_select_font_face (cr, "Sans",
3030 CAIRO_FONT_SLANT_NORMAL,
3031 CAIRO_FONT_WEIGHT_NORMAL);
3032 cairo_set_font_size (cr, 12.0);
3034 string[1] = NULLCHAR;
3037 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
3039 if (row == (flipView ? BOARD_HEIGHT-1 : 0) &&
3040 column >= BOARD_LEFT && column < BOARD_RGHT)
3042 string[0] = 'a' + column - BOARD_LEFT;
3043 cairo_text_extents (cr, string, &extents);
3045 xpos = x + squareSize - extents.width - 2;
3046 ypos = y + squareSize - extents.height - extents.y_bearing - 1;
3048 if (appData.monoMode)
3055 cairo_move_to (cr, xpos, ypos);
3056 cairo_text_path (cr, string);
3057 cairo_set_source_rgb (cr, 0.0, 0.0, 0);
3058 cairo_fill_preserve (cr);
3059 cairo_set_source_rgb (cr, 0, 1.0, 0);
3060 cairo_set_line_width (cr, 0.1);
3063 if ( column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT))
3066 string[0] = ONE + row;
3067 cairo_text_extents (cr, string, &extents);
3070 ypos = y + extents.height + 1;
3072 if (appData.monoMode)
3079 cairo_move_to (cr, xpos, ypos);
3080 cairo_text_path (cr, string);
3081 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
3082 cairo_fill_preserve (cr);
3083 cairo_set_source_rgb (cr, 0, 0, 1.0);
3084 cairo_set_line_width (cr, 0.1);
3096 /* Returns 1 if there are "too many" differences between b1 and b2
3097 (i.e. more than 1 move was made) */
3098 static int too_many_diffs(b1, b2)
3104 for (i=0; i<BOARD_HEIGHT; ++i) {
3105 for (j=0; j<BOARD_WIDTH; ++j) {
3106 if (b1[i][j] != b2[i][j]) {
3107 if (++c > 4) /* Castling causes 4 diffs */
3116 /* Matrix describing castling maneuvers */
3117 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
3118 static int castling_matrix[4][5] = {
3119 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
3120 { 0, 7, 4, 5, 6 }, /* 0-0, white */
3121 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
3122 { 7, 7, 4, 5, 6 } /* 0-0, black */
3125 /* Checks whether castling occurred. If it did, *rrow and *rcol
3126 are set to the destination (row,col) of the rook that moved.
3128 Returns 1 if castling occurred, 0 if not.
3130 Note: Only handles a max of 1 castling move, so be sure
3131 to call too_many_diffs() first.
3133 static int check_castle_draw(newb, oldb, rrow, rcol)
3140 /* For each type of castling... */
3141 for (i=0; i<4; ++i) {
3142 r = castling_matrix[i];
3144 /* Check the 4 squares involved in the castling move */
3146 for (j=1; j<=4; ++j) {
3147 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
3154 /* All 4 changed, so it must be a castling move */
3163 static int damage[BOARD_RANKS][BOARD_FILES];
3166 * event handler for redrawing the board
3168 void DrawPosition( repaint, board)
3169 /*Boolean*/int repaint;
3173 static int lastFlipView = 0;
3174 static int lastBoardValid = 0;
3175 static Board lastBoard;
3178 if (board == NULL) {
3179 if (!lastBoardValid) return;
3182 if (!lastBoardValid || lastFlipView != flipView) {
3183 // XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
3184 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flip View"),
3189 * It would be simpler to clear the window with XClearWindow()
3190 * but this causes a very distracting flicker.
3193 if (!repaint && lastBoardValid && lastFlipView == flipView)
3195 /* If too much changes (begin observing new game, etc.), don't
3197 do_flash = too_many_diffs(board, lastBoard) ? 0 : 1;
3199 /* Special check for castling so we don't flash both the king
3200 and the rook (just flash the king). */
3203 if (check_castle_draw(board, lastBoard, &rrow, &rcol))
3205 /* Draw rook with NO flashing. King will be drawn flashing later */
3206 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
3207 lastBoard[rrow][rcol] = board[rrow][rcol];
3211 /* First pass -- Draw (newly) empty squares and repair damage.
3212 This prevents you from having a piece show up twice while it
3213 is flashing on its new square */
3214 for (i = 0; i < BOARD_HEIGHT; i++)
3215 for (j = 0; j < BOARD_WIDTH; j++)
3216 if ((board[i][j] != lastBoard[i][j] && board[i][j] == EmptySquare)
3219 DrawSquare(i, j, board[i][j], 0);
3220 damage[i][j] = False;
3223 /* Second pass -- Draw piece(s) in new position and flash them */
3224 for (i = 0; i < BOARD_HEIGHT; i++)
3225 for (j = 0; j < BOARD_WIDTH; j++)
3226 if (board[i][j] != lastBoard[i][j])
3228 DrawSquare(i, j, board[i][j], do_flash);
3240 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
3242 cairo_set_line_width (cr, lineGap);
3244 /* TODO: use appdata colors */
3245 cairo_set_source_rgba (cr, 0, 1, 0, 1.0);
3249 for (i = 0; i < BOARD_HEIGHT + 1; i++)
3252 x2 = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3253 y1 = y2 = lineGap / 2 + (i * (squareSize + lineGap));
3255 cairo_move_to (cr, x1, y1);
3256 cairo_rel_line_to (cr, x2,0);
3260 for (j = 0; j < BOARD_WIDTH + 1; j++)
3263 y2 = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3264 x1 = x2 = lineGap / 2 + (j * (squareSize + lineGap));
3266 cairo_move_to (cr, x1, y1);
3267 cairo_rel_line_to (cr, 0, y2);
3276 for (i = 0; i < BOARD_HEIGHT; i++)
3277 for (j = 0; j < BOARD_WIDTH; j++)
3279 DrawSquare(i, j, board[i][j], 0);
3280 damage[i][j] = False;
3284 CopyBoard(lastBoard, board);
3286 lastFlipView = flipView;
3288 /* Draw highlights */
3289 if (pm1X >= 0 && pm1Y >= 0)
3291 drawHighlight(pm1X, pm1Y, LINE_TYPE_PRE);
3293 if (pm2X >= 0 && pm2Y >= 0)
3295 drawHighlight(pm2X, pm2Y, LINE_TYPE_PRE);
3297 if (hi1X >= 0 && hi1Y >= 0)
3299 drawHighlight(hi1X, hi1Y, LINE_TYPE_HIGHLIGHT);
3301 if (hi2X >= 0 && hi2Y >= 0)
3303 drawHighlight(hi2X, hi2Y, LINE_TYPE_HIGHLIGHT);
3306 /* If piece being dragged around board, must redraw that too */
3312 void AnimateUserMove (Widget w, XEvent * event,
3313 String * params, Cardinal * nParams)
3315 DragPieceMove(event->xmotion.x, event->xmotion.y);
3318 void HandlePV (Widget w, XEvent * event,
3319 String * params, Cardinal * nParams)
3320 { // [HGM] pv: walk PV
3321 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3324 Widget CommentCreate(name, text, mutable, callback, lines)
3326 int /*Boolean*/ mutable;
3327 XtCallbackProc callback;
3331 Widget shell, layout, form, edit, b_ok, b_cancel, b_clear, b_close, b_edit;
3336 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3337 XtGetValues(boardWidget, args, j);
3340 XtSetArg(args[j], XtNresizable, True); j++;
3343 XtCreatePopupShell(name, topLevelShellWidgetClass,
3344 shellWidget, args, j);
3347 XtCreatePopupShell(name, transientShellWidgetClass,
3348 shellWidget, args, j);
3351 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
3352 layoutArgs, XtNumber(layoutArgs));
3354 XtCreateManagedWidget("form", formWidgetClass, layout,
3355 formArgs, XtNumber(formArgs));
3359 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
3360 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
3362 XtSetArg(args[j], XtNstring, text); j++;
3363 XtSetArg(args[j], XtNtop, XtChainTop); j++;
3364 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3365 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3366 XtSetArg(args[j], XtNright, XtChainRight); j++;
3367 XtSetArg(args[j], XtNresizable, True); j++;
3368 XtSetArg(args[j], XtNwidth, bw_width); j++; /*force wider than buttons*/
3369 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
3370 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
3371 XtSetArg(args[j], XtNautoFill, True); j++;
3372 XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
3374 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
3378 XtSetArg(args[j], XtNfromVert, edit); j++;
3379 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3380 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3381 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3382 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3384 XtCreateManagedWidget(_("ok"), commandWidgetClass, form, args, j);
3385 XtAddCallback(b_ok, XtNcallback, callback, (XtPointer) 0);
3388 XtSetArg(args[j], XtNfromVert, edit); j++;
3389 XtSetArg(args[j], XtNfromHoriz, b_ok); j++;
3390 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3391 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3392 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3393 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3395 XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
3396 XtAddCallback(b_cancel, XtNcallback, callback, (XtPointer) 0);
3399 XtSetArg(args[j], XtNfromVert, edit); j++;
3400 XtSetArg(args[j], XtNfromHoriz, b_cancel); j++;
3401 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3402 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3403 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3404 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3406 XtCreateManagedWidget(_("clear"), commandWidgetClass, form, args, j);
3407 XtAddCallback(b_clear, XtNcallback, callback, (XtPointer) 0);
3410 XtSetArg(args[j], XtNfromVert, edit); j++;
3411 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3412 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3413 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3414 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3416 XtCreateManagedWidget(_("close"), commandWidgetClass, form, args, j);
3417 XtAddCallback(b_close, XtNcallback, callback, (XtPointer) 0);
3420 XtSetArg(args[j], XtNfromVert, edit); j++;
3421 XtSetArg(args[j], XtNfromHoriz, b_close); j++;
3422 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3423 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3424 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3425 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3427 XtCreateManagedWidget(_("edit"), commandWidgetClass, form, args, j);
3428 XtAddCallback(b_edit, XtNcallback, callback, (XtPointer) 0);
3431 XtRealizeWidget(shell);
3433 if (commentX == -1) {
3436 Dimension pw_height;
3437 Dimension ew_height;
3440 XtSetArg(args[j], XtNheight, &ew_height); j++;
3441 XtGetValues(edit, args, j);
3444 XtSetArg(args[j], XtNheight, &pw_height); j++;
3445 XtGetValues(shell, args, j);
3446 commentH = pw_height + (lines - 1) * ew_height;
3447 commentW = bw_width - 16;
3449 XSync(xDisplay, False);
3451 /* This code seems to tickle an X bug if it is executed too soon
3452 after xboard starts up. The coordinates get transformed as if
3453 the main window was positioned at (0, 0).
3455 XtTranslateCoords(shellWidget,
3456 (bw_width - commentW) / 2, 0 - commentH / 2,
3457 &commentX, &commentY);
3459 XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
3460 RootWindowOfScreen(XtScreen(shellWidget)),
3461 (bw_width - commentW) / 2, 0 - commentH / 2,
3466 if (commentY < 0) commentY = 0; /*avoid positioning top offscreen*/
3469 if(wpComment.width > 0) {
3470 commentX = wpComment.x;
3471 commentY = wpComment.y;
3472 commentW = wpComment.width;
3473 commentH = wpComment.height;
3477 XtSetArg(args[j], XtNheight, commentH); j++;
3478 XtSetArg(args[j], XtNwidth, commentW); j++;
3479 XtSetArg(args[j], XtNx, commentX); j++;
3480 XtSetArg(args[j], XtNy, commentY); j++;
3481 XtSetValues(shell, args, j);
3482 XtSetKeyboardFocus(shell, edit);
3487 /* Used for analysis window and ICS input window */
3488 Widget MiscCreate(name, text, mutable, callback, lines)
3490 int /*Boolean*/ mutable;
3491 XtCallbackProc callback;
3495 Widget shell, layout, form, edit;
3497 Dimension bw_width, pw_height, ew_height, w, h;
3503 XtSetArg(args[j], XtNresizable, True); j++;
3506 XtCreatePopupShell(name, topLevelShellWidgetClass,
3507 shellWidget, args, j);
3510 XtCreatePopupShell(name, transientShellWidgetClass,
3511 shellWidget, args, j);
3514 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
3515 layoutArgs, XtNumber(layoutArgs));
3517 XtCreateManagedWidget("form", formWidgetClass, layout,
3518 formArgs, XtNumber(formArgs));
3522 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
3523 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
3525 XtSetArg(args[j], XtNstring, text); j++;
3526 XtSetArg(args[j], XtNtop, XtChainTop); j++;
3527 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3528 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3529 XtSetArg(args[j], XtNright, XtChainRight); j++;
3530 XtSetArg(args[j], XtNresizable, True); j++;
3531 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
3532 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
3533 XtSetArg(args[j], XtNautoFill, True); j++;
3534 XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
3536 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
3538 XtRealizeWidget(shell);
3541 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3542 XtGetValues(boardWidget, args, j);
3545 XtSetArg(args[j], XtNheight, &ew_height); j++;
3546 XtGetValues(edit, args, j);
3549 XtSetArg(args[j], XtNheight, &pw_height); j++;
3550 XtGetValues(shell, args, j);
3551 h = pw_height + (lines - 1) * ew_height;
3554 XSync(xDisplay, False);
3556 /* This code seems to tickle an X bug if it is executed too soon
3557 after xboard starts up. The coordinates get transformed as if
3558 the main window was positioned at (0, 0).
3560 XtTranslateCoords(shellWidget, (bw_width - w) / 2, 0 - h / 2, &x, &y);
3562 XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
3563 RootWindowOfScreen(XtScreen(shellWidget)),
3564 (bw_width - w) / 2, 0 - h / 2, &xx, &yy, &junk);
3568 if (y < 0) y = 0; /*avoid positioning top offscreen*/
3571 XtSetArg(args[j], XtNheight, h); j++;
3572 XtSetArg(args[j], XtNwidth, w); j++;
3573 XtSetArg(args[j], XtNx, x); j++;
3574 XtSetArg(args[j], XtNy, y); j++;
3575 XtSetValues(shell, args, j);
3581 static int savedIndex; /* gross that this is global */
3583 void EditCommentPopUp(index, title, text)
3592 if (text == NULL) text = "";
3594 if (editShell == NULL) {
3596 CommentCreate(title, text, True, EditCommentCallback, 4);
3597 XtRealizeWidget(editShell);
3598 CatchDeleteWindow(editShell, "EditCommentPopDown");
3600 edit = XtNameToWidget(editShell, "*form.text");
3602 XtSetArg(args[j], XtNstring, text); j++;
3603 XtSetValues(edit, args, j);
3605 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
3606 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
3607 XtSetValues(editShell, args, j);
3610 XtPopup(editShell, XtGrabNone);
3614 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
3615 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
3619 void EditCommentCallback(w, client_data, call_data)
3621 XtPointer client_data, call_data;
3629 XtSetArg(args[j], XtNlabel, &name); j++;
3630 XtGetValues(w, args, j);
3632 if (strcmp(name, _("ok")) == 0) {
3633 edit = XtNameToWidget(editShell, "*form.text");
3635 XtSetArg(args[j], XtNstring, &val); j++;
3636 XtGetValues(edit, args, j);
3637 ReplaceComment(savedIndex, val);
3638 EditCommentPopDown();
3639 } else if (strcmp(name, _("cancel")) == 0) {
3640 EditCommentPopDown();
3641 } else if (strcmp(name, _("clear")) == 0) {
3642 edit = XtNameToWidget(editShell, "*form.text");
3643 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
3644 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
3648 void EditCommentPopDown()
3653 if (!editUp) return;
3655 XtSetArg(args[j], XtNx, &commentX); j++;
3656 XtSetArg(args[j], XtNy, &commentY); j++;
3657 XtSetArg(args[j], XtNheight, &commentH); j++;
3658 XtSetArg(args[j], XtNwidth, &commentW); j++;
3659 XtGetValues(editShell, args, j);
3660 XtPopdown(editShell);
3663 XtSetArg(args[j], XtNleftBitmap, None); j++;
3664 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
3668 void ICSInputBoxPopUp()
3673 char *title = _("ICS Input");
3676 if (ICSInputShell == NULL) {
3677 ICSInputShell = MiscCreate(title, "", True, NULL, 1);
3678 tr = XtParseTranslationTable(ICSInputTranslations);
3679 edit = XtNameToWidget(ICSInputShell, "*form.text");
3680 XtOverrideTranslations(edit, tr);
3681 XtRealizeWidget(ICSInputShell);
3682 CatchDeleteWindow(ICSInputShell, "ICSInputBoxPopDown");
3685 edit = XtNameToWidget(ICSInputShell, "*form.text");
3687 XtSetArg(args[j], XtNstring, ""); j++;
3688 XtSetValues(edit, args, j);
3690 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
3691 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
3692 XtSetValues(ICSInputShell, args, j);
3695 XtPopup(ICSInputShell, XtGrabNone);
3696 XtSetKeyboardFocus(ICSInputShell, edit);
3698 ICSInputBoxUp = True;
3700 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
3701 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
3705 void ICSInputSendText()
3712 edit = XtNameToWidget(ICSInputShell, "*form.text");
3714 XtSetArg(args[j], XtNstring, &val); j++;
3715 XtGetValues(edit, args, j);
3716 SendMultiLineToICS(val);
3717 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
3718 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
3721 void ICSInputBoxPopDown()
3726 if (!ICSInputBoxUp) return;
3728 XtPopdown(ICSInputShell);
3729 ICSInputBoxUp = False;
3731 XtSetArg(args[j], XtNleftBitmap, None); j++;
3732 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
3736 void CommentPopUp(title, text)
3743 if (commentShell == NULL) {
3745 CommentCreate(title, text, False, CommentCallback, 4);
3746 XtRealizeWidget(commentShell);
3747 CatchDeleteWindow(commentShell, "CommentPopDown");
3749 edit = XtNameToWidget(commentShell, "*form.text");
3751 XtSetArg(args[j], XtNstring, text); j++;
3752 XtSetValues(edit, args, j);
3754 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
3755 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
3756 XtSetValues(commentShell, args, j);
3759 XtPopup(commentShell, XtGrabNone);
3760 XSync(xDisplay, False);
3765 void CommentCallback(w, client_data, call_data)
3767 XtPointer client_data, call_data;
3774 XtSetArg(args[j], XtNlabel, &name); j++;
3775 XtGetValues(w, args, j);
3777 if (strcmp(name, _("close")) == 0) {
3779 } else if (strcmp(name, _("edit")) == 0) {
3786 void CommentPopDown()
3791 if (!commentUp) return;
3793 XtSetArg(args[j], XtNx, &commentX); j++;
3794 XtSetArg(args[j], XtNy, &commentY); j++;
3795 XtSetArg(args[j], XtNwidth, &commentW); j++;
3796 XtSetArg(args[j], XtNheight, &commentH); j++;
3797 XtGetValues(commentShell, args, j);
3798 XtPopdown(commentShell);
3799 XSync(xDisplay, False);
3803 void PromotionPopUp()
3806 Widget dialog, layout;
3808 Dimension bw_width, pw_width;
3812 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3813 XtGetValues(boardWidget, args, j);
3816 XtSetArg(args[j], XtNresizable, True); j++;
3817 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
3819 XtCreatePopupShell("Promotion", transientShellWidgetClass,
3820 shellWidget, args, j);
3822 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
3823 layoutArgs, XtNumber(layoutArgs));
3826 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
3827 XtSetArg(args[j], XtNborderWidth, 0); j++;
3828 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
3831 if(gameInfo.variant != VariantShogi) {
3832 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
3833 (XtPointer) dialog);
3834 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
3835 (XtPointer) dialog);
3836 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
3837 (XtPointer) dialog);
3838 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
3839 (XtPointer) dialog);
3840 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
3841 gameInfo.variant == VariantGiveaway) {
3842 XawDialogAddButton(dialog, _("King"), PromotionCallback,
3843 (XtPointer) dialog);
3845 if(gameInfo.variant == VariantCapablanca ||
3846 gameInfo.variant == VariantGothic ||
3847 gameInfo.variant == VariantCapaRandom) {
3848 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
3849 (XtPointer) dialog);
3850 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
3851 (XtPointer) dialog);
3853 } else // [HGM] shogi
3855 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
3856 (XtPointer) dialog);
3857 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
3858 (XtPointer) dialog);
3860 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
3861 (XtPointer) dialog);
3863 XtRealizeWidget(promotionShell);
3864 CatchDeleteWindow(promotionShell, "PromotionPopDown");
3867 XtSetArg(args[j], XtNwidth, &pw_width); j++;
3868 XtGetValues(promotionShell, args, j);
3870 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3871 lineGap + squareSize/3 +
3872 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
3873 0 : 6*(squareSize + lineGap)), &x, &y);
3876 XtSetArg(args[j], XtNx, x); j++;
3877 XtSetArg(args[j], XtNy, y); j++;
3878 XtSetValues(promotionShell, args, j);
3880 XtPopup(promotionShell, XtGrabNone);
3885 void PromotionPopDown()
3887 if (!promotionUp) return;
3888 XtPopdown(promotionShell);
3889 XtDestroyWidget(promotionShell);
3890 promotionUp = False;
3893 void PromotionCallback(w, client_data, call_data)
3895 XtPointer client_data, call_data;
3901 XtSetArg(args[0], XtNlabel, &name);
3902 XtGetValues(w, args, 1);
3906 if (fromX == -1) return;
3908 if (strcmp(name, _("cancel")) == 0) {
3912 } else if (strcmp(name, _("Knight")) == 0) {
3914 } else if (strcmp(name, _("Promote")) == 0) {
3916 } else if (strcmp(name, _("Defer")) == 0) {
3919 promoChar = ToLower(name[0]);
3922 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
3924 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
3925 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3930 void ErrorCallback(w, client_data, call_data)
3932 XtPointer client_data, call_data;
3935 XtPopdown(w = XtParent(XtParent(XtParent(w))));
3937 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3943 if (!errorUp) return;
3947 gtk_widget_destroy(GTK_WIDGET(GUI_Error));
3949 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3954 void ErrorPopUp(title, label, modal)
3955 char *title, *label;
3958 GUI_Error = gtk_message_dialog_new(GTK_WINDOW(GUI_Window),
3959 GTK_DIALOG_DESTROY_WITH_PARENT,
3964 gtk_window_set_title(GTK_WINDOW(GUI_Error),(gchar *) title);
3967 gtk_dialog_run(GTK_DIALOG(GUI_Error));
3968 gtk_widget_destroy(GTK_WIDGET(GUI_Error));
3972 g_signal_connect_swapped (GUI_Error, "response",
3973 G_CALLBACK (ErrorPopDownProc),
3976 gtk_widget_show(GTK_WIDGET(GUI_Error));
3982 /* Disable all user input other than deleting the window */
3983 static int frozen = 0;
3987 /* Grab by a widget that doesn't accept input */
3988 // XtAddGrab(messageWidget, TRUE, FALSE);
3992 /* Undo a FreezeUI */
3995 if (!frozen) return;
3996 // XtRemoveGrab(messageWidget);
4000 char *ModeToWidgetName(mode)
4004 case BeginningOfGame:
4005 if (appData.icsActive)
4006 return "menuMode.ICS Client";
4007 else if (appData.noChessProgram ||
4008 *appData.cmailGameName != NULLCHAR)
4009 return "menuMode.Edit Game";
4011 return "menuMode.Machine Black";
4012 case MachinePlaysBlack:
4013 return "menuMode.Machine Black";
4014 case MachinePlaysWhite:
4015 return "menuMode.Machine White";
4017 return "menuMode.Analysis Mode";
4019 return "menuMode.Analyze File";
4020 case TwoMachinesPlay:
4021 return "menuMode.Two Machines";
4023 return "menuMode.Edit Game";
4024 case PlayFromGameFile:
4025 return "menuFile.Load Game";
4027 return "menuMode.Edit Position";
4029 return "menuMode.Training";
4030 case IcsPlayingWhite:
4031 case IcsPlayingBlack:
4035 return "menuMode.ICS Client";
4042 void ModeHighlight()
4044 static int oldPausing = FALSE;
4045 static GameMode oldmode = (GameMode) -1;
4048 // todo this toggling of the pause button doesn't seem to work?
4049 // e.g. select pause from buttonbar doesn't activate menumode.pause
4051 // if (!boardWidget || !XtIsRealized(boardWidget)) return;
4053 if (pausing != oldPausing) {
4054 oldPausing = pausing;
4055 gtk_button_set_relief(GTK_BUTTON (gtk_builder_get_object (builder, "menuMode.Pause")),pausing?GTK_RELIEF_NORMAL:GTK_RELIEF_NONE);
4056 /* toggle background color in showbuttonbar */
4057 if (appData.showButtonBar) {
4059 gtk_button_pressed(GTK_BUTTON (gtk_builder_get_object (builder, "buttonbar.Pause")));
4061 gtk_button_released(GTK_BUTTON (gtk_builder_get_object (builder, "buttonbar.Pause")));
4066 wname = ModeToWidgetName(oldmode);
4068 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, wname)),True);
4072 /* Maybe all the enables should be handled here, not just this one */
4073 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuMode.Training")),
4074 gameMode == Training || gameMode == PlayFromGameFile);
4079 * Button/menu procedures
4082 int LoadGamePopUp(f, gameNumber, title)
4087 cmailMsgLoaded = FALSE;
4089 if (gameNumber == 0)
4091 int error = GameListBuild(f);
4095 DisplayError(_("Cannot build game list"), error);
4097 else if (!ListEmpty(&gameList)
4098 && ((ListGame *) gameList.tailPred)->number > 1)
4100 // TODO convert to GTK
4101 // GameListPopUp(f, title);
4109 return LoadGame(f, gameNumber, title, FALSE);
4112 void ReloadCmailMsgProc(w, event, prms, nprms)
4118 ReloadCmailMsgEvent(FALSE);
4121 void MailMoveProc(w, event, prms, nprms)
4130 /* this variable is shared between CopyPositionProc and SendPositionSelection */
4131 char *selected_fen_position=NULL;
4134 SendPositionSelection(Widget w, Atom *selection, Atom *target,
4135 Atom *type_return, XtPointer *value_return,
4136 unsigned long *length_return, int *format_return)
4138 char *selection_tmp;
4140 if (!selected_fen_position) return False; /* should never happen */
4141 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4142 /* note: since no XtSelectionDoneProc was registered, Xt will
4143 * automatically call XtFree on the value returned. So have to
4144 * make a copy of it allocated with XtMalloc */
4145 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
4146 strcpy(selection_tmp, selected_fen_position);
4148 *value_return=selection_tmp;
4149 *length_return=strlen(selection_tmp);
4150 *type_return=*target;
4151 *format_return = 8; /* bits per byte */
4153 } else if (*target == XA_TARGETS(xDisplay)) {
4154 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4155 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4156 targets_tmp[1] = XA_STRING;
4157 *value_return = targets_tmp;
4158 *type_return = XA_ATOM;
4160 *format_return = 8 * sizeof(Atom);
4161 if (*format_return > 32) {
4162 *length_return *= *format_return / 32;
4163 *format_return = 32;
4171 /* note: when called from menu all parameters are NULL, so no clue what the
4172 * Widget which was clicked on was, or what the click event was
4174 void CopyPositionProc(w, event, prms, nprms)
4181 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4182 * have a notion of a position that is selected but not copied.
4183 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4185 if(gameMode == EditPosition) EditPositionDone(TRUE);
4186 if (selected_fen_position) free(selected_fen_position);
4187 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
4188 if (!selected_fen_position) return;
4189 XtOwnSelection(menuBarWidget, XA_PRIMARY,
4191 SendPositionSelection,
4192 NULL/* lose_ownership_proc */ ,
4193 NULL/* transfer_done_proc */);
4194 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4196 SendPositionSelection,
4197 NULL/* lose_ownership_proc */ ,
4198 NULL/* transfer_done_proc */);
4201 /* function called when the data to Paste is ready */
4203 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
4204 Atom *type, XtPointer value, unsigned long *len, int *format)
4207 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
4208 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
4209 EditPositionPasteFEN(fenstr);
4213 /* called when Paste Position button is pressed,
4214 * all parameters will be NULL */
4215 void PastePositionProc(w, event, prms, nprms)
4221 XtGetSelectionValue(menuBarWidget,
4222 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4223 /* (XtSelectionCallbackProc) */ PastePositionCB,
4224 NULL, /* client_data passed to PastePositionCB */
4226 /* better to use the time field from the event that triggered the
4227 * call to this function, but that isn't trivial to get
4235 SendGameSelection(Widget w, Atom *selection, Atom *target,
4236 Atom *type_return, XtPointer *value_return,
4237 unsigned long *length_return, int *format_return)
4239 char *selection_tmp;
4241 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4242 FILE* f = fopen(gameCopyFilename, "r");
4245 if (f == NULL) return False;
4249 selection_tmp = XtMalloc(len + 1);
4250 count = fread(selection_tmp, 1, len, f);
4252 XtFree(selection_tmp);
4255 selection_tmp[len] = NULLCHAR;
4256 *value_return = selection_tmp;
4257 *length_return = len;
4258 *type_return = *target;
4259 *format_return = 8; /* bits per byte */
4261 } else if (*target == XA_TARGETS(xDisplay)) {
4262 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4263 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4264 targets_tmp[1] = XA_STRING;
4265 *value_return = targets_tmp;
4266 *type_return = XA_ATOM;
4268 *format_return = 8 * sizeof(Atom);
4269 if (*format_return > 32) {
4270 *length_return *= *format_return / 32;
4271 *format_return = 32;
4279 /* note: when called from menu all parameters are NULL, so no clue what the
4280 * Widget which was clicked on was, or what the click event was
4282 void CopyGameProc(w, event, prms, nprms)
4290 ret = SaveGameToFile(gameCopyFilename, FALSE);
4294 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4295 * have a notion of a game that is selected but not copied.
4296 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4298 XtOwnSelection(menuBarWidget, XA_PRIMARY,
4301 NULL/* lose_ownership_proc */ ,
4302 NULL/* transfer_done_proc */);
4303 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4306 NULL/* lose_ownership_proc */ ,
4307 NULL/* transfer_done_proc */);
4310 /* function called when the data to Paste is ready */
4312 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
4313 Atom *type, XtPointer value, unsigned long *len, int *format)
4316 if (value == NULL || *len == 0) {
4317 return; /* nothing had been selected to copy */
4319 f = fopen(gamePasteFilename, "w");
4321 DisplayError(_("Can't open temp file"), errno);
4324 fwrite(value, 1, *len, f);
4327 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
4330 /* called when Paste Game button is pressed,
4331 * all parameters will be NULL */
4332 void PasteGameProc(w, event, prms, nprms)
4338 XtGetSelectionValue(menuBarWidget,
4339 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4340 /* (XtSelectionCallbackProc) */ PasteGameCB,
4341 NULL, /* client_data passed to PasteGameCB */
4343 /* better to use the time field from the event that triggered the
4344 * call to this function, but that isn't trivial to get
4351 void SaveOnExitProc(w, event, prms, nprms)
4359 saveSettingsOnExit = !saveSettingsOnExit;
4361 if (saveSettingsOnExit) {
4362 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
4364 XtSetArg(args[0], XtNleftBitmap, None);
4366 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
4370 void SaveSettingsProc(w, event, prms, nprms)
4376 SaveSettings(settingsFileName);
4382 SaveGameProc(NULL, NULL);
4387 void EditCommentProc(w, event, prms, nprms)
4394 EditCommentPopDown();
4400 void IcsInputBoxProc(w, event, prms, nprms)
4406 if (ICSInputBoxUp) {
4407 ICSInputBoxPopDown();
4414 void EnterKeyProc(w, event, prms, nprms)
4420 if (ICSInputBoxUp == True)
4425 void DebugProc(w, event, prms, nprms)
4431 appData.debugMode = !appData.debugMode;
4434 void AboutGameProc(w, event, prms, nprms)
4443 void NothingProc(w, event, prms, nprms)
4452 void Iconify(w, event, prms, nprms)
4461 XtSetArg(args[0], XtNiconic, True);
4462 XtSetValues(shellWidget, args, 1);
4465 void DisplayMessage(message, extMessage)
4466 gchar *message, *extMessage;
4473 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
4476 message = extMessage;
4479 gtk_label_set_text( GTK_LABEL(gtk_builder_get_object (builder, "Messages")),message);
4484 void DisplayTitle(text)
4487 gchar title[MSG_SIZ];
4489 if (text == NULL) text = "";
4491 if (appData.titleInWindow)
4496 if (*text != NULLCHAR)
4498 strcpy(title, text);
4500 else if (appData.icsActive)
4502 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
4504 else if (appData.cmailGameName[0] != NULLCHAR)
4506 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
4508 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
4510 else if (gameInfo.variant == VariantGothic)
4512 strcpy(title, GOTHIC);
4516 else if (gameInfo.variant == VariantFalcon)
4518 strcpy(title, FALCON);
4521 else if (appData.noChessProgram)
4523 strcpy(title, programName);
4527 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
4529 gtk_window_set_title(GTK_WINDOW(GUI_Window),title);
4535 void DisplayError(message, error)
4542 if (appData.debugMode || appData.matchMode) {
4543 fprintf(stderr, "%s: %s\n", programName, message);
4546 if (appData.debugMode || appData.matchMode) {
4547 fprintf(stderr, "%s: %s: %s\n",
4548 programName, message, strerror(error));
4550 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
4553 ErrorPopUp(_("Error"), message, FALSE);
4557 void DisplayMoveError(message)
4562 DrawPosition(FALSE, NULL);
4563 if (appData.debugMode || appData.matchMode) {
4564 fprintf(stderr, "%s: %s\n", programName, message);
4566 if (appData.popupMoveErrors) {
4567 ErrorPopUp(_("Error"), message, FALSE);
4569 DisplayMessage(message, "");
4574 void DisplayFatalError(message, error, status)
4580 errorExitStatus = status;
4582 fprintf(stderr, "%s: %s\n", programName, message);
4584 fprintf(stderr, "%s: %s: %s\n",
4585 programName, message, strerror(error));
4586 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
4589 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
4590 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
4596 void DisplayInformation(message)
4600 ErrorPopUp(_("Information"), message, TRUE);
4603 void DisplayNote(message)
4607 ErrorPopUp(_("Note"), message, FALSE);
4611 NullXErrorCheck(dpy, error_event)
4613 XErrorEvent *error_event;
4618 void DisplayIcsInteractionTitle(message)
4621 if (oldICSInteractionTitle == NULL) {
4622 /* Magic to find the old window title, adapted from vim */
4623 char *wina = getenv("WINDOWID");
4625 Window win = (Window) atoi(wina);
4626 Window root, parent, *children;
4627 unsigned int nchildren;
4628 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4630 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4631 if (!XQueryTree(xDisplay, win, &root, &parent,
4632 &children, &nchildren)) break;
4633 if (children) XFree((void *)children);
4634 if (parent == root || parent == 0) break;
4637 XSetErrorHandler(oldHandler);
4639 if (oldICSInteractionTitle == NULL) {
4640 oldICSInteractionTitle = "xterm";
4643 printf("\033]0;%s\007", message);
4647 char pendingReplyPrefix[MSG_SIZ];
4648 ProcRef pendingReplyPR;
4650 void AskQuestionProc(w, event, prms, nprms)
4657 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
4661 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
4664 void AskQuestionPopDown()
4666 if (!askQuestionUp) return;
4667 XtPopdown(askQuestionShell);
4668 XtDestroyWidget(askQuestionShell);
4669 askQuestionUp = False;
4672 void AskQuestionReplyAction(w, event, prms, nprms)
4682 reply = XawDialogGetValueString(w = XtParent(w));
4683 strcpy(buf, pendingReplyPrefix);
4684 if (*buf) strcat(buf, " ");
4687 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
4688 AskQuestionPopDown();
4690 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
4693 void AskQuestionCallback(w, client_data, call_data)
4695 XtPointer client_data, call_data;
4700 XtSetArg(args[0], XtNlabel, &name);
4701 XtGetValues(w, args, 1);
4703 if (strcmp(name, _("cancel")) == 0) {
4704 AskQuestionPopDown();
4706 AskQuestionReplyAction(w, NULL, NULL, NULL);
4710 void AskQuestion(title, question, replyPrefix, pr)
4711 char *title, *question, *replyPrefix;
4715 Widget popup, layout, dialog, edit;
4721 strcpy(pendingReplyPrefix, replyPrefix);
4722 pendingReplyPR = pr;
4725 XtSetArg(args[i], XtNresizable, True); i++;
4726 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
4727 askQuestionShell = popup =
4728 XtCreatePopupShell(title, transientShellWidgetClass,
4729 shellWidget, args, i);
4732 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
4733 layoutArgs, XtNumber(layoutArgs));
4736 XtSetArg(args[i], XtNlabel, question); i++;
4737 XtSetArg(args[i], XtNvalue, ""); i++;
4738 XtSetArg(args[i], XtNborderWidth, 0); i++;
4739 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
4742 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
4743 (XtPointer) dialog);
4744 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
4745 (XtPointer) dialog);
4747 XtRealizeWidget(popup);
4748 CatchDeleteWindow(popup, "AskQuestionPopDown");
4750 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
4751 &x, &y, &win_x, &win_y, &mask);
4753 XtSetArg(args[0], XtNx, x - 10);
4754 XtSetArg(args[1], XtNy, y - 30);
4755 XtSetValues(popup, args, 2);
4757 XtPopup(popup, XtGrabExclusive);
4758 askQuestionUp = True;
4760 edit = XtNameToWidget(dialog, "*value");
4761 XtSetKeyboardFocus(popup, edit);
4769 if (*name == NULLCHAR) {
4771 } else if (strcmp(name, "$") == 0) {
4772 putc(BELLCHAR, stderr);
4775 snprintf(buf, sizeof(buf), "%s '%s' &", appData.soundProgram, name);
4783 PlaySound(appData.soundMove);
4789 PlaySound(appData.soundIcsWin);
4795 PlaySound(appData.soundIcsLoss);
4801 PlaySound(appData.soundIcsDraw);
4805 PlayIcsUnfinishedSound()
4807 PlaySound(appData.soundIcsUnfinished);
4813 PlaySound(appData.soundIcsAlarm);
4819 system("stty echo");
4825 system("stty -echo");
4829 Colorize(cc, continuation)
4834 int count, outCount, error;
4836 if (textColors[(int)cc].bg > 0) {
4837 if (textColors[(int)cc].fg > 0) {
4838 sprintf(buf, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
4839 textColors[(int)cc].fg, textColors[(int)cc].bg);
4841 sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
4842 textColors[(int)cc].bg);
4845 if (textColors[(int)cc].fg > 0) {
4846 sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
4847 textColors[(int)cc].fg);
4849 sprintf(buf, "\033[0;%dm", textColors[(int)cc].attr);
4852 count = strlen(buf);
4853 outCount = OutputToProcess(NoProc, buf, count, &error);
4854 if (outCount < count) {
4855 DisplayFatalError(_("Error writing to display"), error, 1);
4858 if (continuation) return;
4861 PlaySound(appData.soundShout);
4864 PlaySound(appData.soundSShout);
4867 PlaySound(appData.soundChannel1);
4870 PlaySound(appData.soundChannel);
4873 PlaySound(appData.soundKibitz);
4876 PlaySound(appData.soundTell);
4878 case ColorChallenge:
4879 PlaySound(appData.soundChallenge);
4882 PlaySound(appData.soundRequest);
4885 PlaySound(appData.soundSeek);
4896 return getpwuid(getuid())->pw_name;
4899 static char *ExpandPathName(path)
4902 static char static_buf[2000];
4903 char *d, *s, buf[2000];
4909 while (*s && isspace(*s))
4918 if (*(s+1) == '/') {
4919 strcpy(d, getpwuid(getuid())->pw_dir);
4924 *strchr(buf, '/') = 0;
4925 pwd = getpwnam(buf);
4928 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
4932 strcpy(d, pwd->pw_dir);
4933 strcat(d, strchr(s+1, '/'));
4944 static char host_name[MSG_SIZ];
4946 #if HAVE_GETHOSTNAME
4947 gethostname(host_name, MSG_SIZ);
4949 #else /* not HAVE_GETHOSTNAME */
4950 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
4951 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
4953 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4955 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4956 #endif /* not HAVE_GETHOSTNAME */
4959 guint delayedEventTimerTag = 0;
4960 DelayedEventCallback delayedEventCallback = 0;
4963 FireDelayedEvent(data)
4967 g_source_remove(delayedEventTimerTag);
4968 delayedEventTimerTag = 0;
4971 delayedEventCallback();
4977 ScheduleDelayedEvent(cb, millisec)
4978 DelayedEventCallback cb; guint millisec;
4980 if(delayedEventTimerTag && delayedEventCallback == cb)
4981 // [HGM] alive: replace, rather than add or flush identical event
4982 g_source_remove(delayedEventTimerTag);
4983 delayedEventCallback = cb;
4984 delayedEventTimerTag = g_timeout_add(millisec,(GSourceFunc) FireDelayedEvent, NULL);
4988 DelayedEventCallback
4991 if (delayedEventTimerTag)
4993 return delayedEventCallback;
5002 CancelDelayedEvent()
5004 if (delayedEventTimerTag)
5006 g_source_remove(delayedEventTimerTag);
5007 delayedEventTimerTag = 0;
5013 guint loadGameTimerTag = 0;
5015 int LoadGameTimerRunning()
5017 return loadGameTimerTag != 0;
5020 int StopLoadGameTimer()
5022 if (loadGameTimerTag != 0) {
5023 g_source_remove(loadGameTimerTag);
5024 loadGameTimerTag = 0;
5032 LoadGameTimerCallback(data)
5036 g_source_remove(loadGameTimerTag);
5037 loadGameTimerTag = 0;
5044 StartLoadGameTimer(millisec)
5048 g_timeout_add( millisec, (GSourceFunc) LoadGameTimerCallback, NULL);
5052 guint analysisClockTag = 0;
5055 AnalysisClockCallback(data)
5058 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
5059 || appData.icsEngineAnalyze)
5061 AnalysisPeriodicEvent(0);
5062 return 1; /* keep on going */
5064 return 0; /* stop timer */
5068 StartAnalysisClock()
5071 g_timeout_add( 2000,(GSourceFunc) AnalysisClockCallback, NULL);
5075 guint clockTimerTag = 0;
5077 int ClockTimerRunning()
5079 return clockTimerTag != 0;
5082 int StopClockTimer()
5084 if (clockTimerTag != 0)
5086 g_source_remove(clockTimerTag);
5097 ClockTimerCallback(data)
5101 g_source_remove(clockTimerTag);
5109 StartClockTimer(millisec)
5112 clockTimerTag = g_timeout_add(millisec,(GSourceFunc) ClockTimerCallback,NULL);
5117 DisplayTimerLabel(w, color, timer, highlight)
5126 if (appData.clockMode) {
5127 sprintf(buf, "%s: %s", color, TimeString(timer));
5129 sprintf(buf, "%s ", color);
5131 gtk_label_set_text(GTK_LABEL(w),buf);
5133 /* check for low time warning */
5134 // Pixel foregroundOrWarningColor = timerForegroundPixel;
5137 // appData.lowTimeWarning &&
5138 // (timer / 1000) < appData.icsAlarmTime)
5139 // foregroundOrWarningColor = lowTimeWarningColor;
5141 // if (appData.clockMode) {
5142 // sprintf(buf, "%s: %s", color, TimeString(timer));
5143 // XtSetArg(args[0], XtNlabel, buf);
5145 // sprintf(buf, "%s ", color);
5146 // XtSetArg(args[0], XtNlabel, buf);
5151 // XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
5152 // XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
5154 // XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
5155 // XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
5158 // XtSetValues(w, args, 3);
5163 DisplayWhiteClock(timeRemaining, highlight)
5167 if(appData.noGUI) return;
5169 DisplayTimerLabel(GUI_Whiteclock, _("White"), timeRemaining, highlight);
5170 if (highlight && WindowIcon == BlackIcon)
5172 WindowIcon = WhiteIcon;
5173 gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
5178 DisplayBlackClock(timeRemaining, highlight)
5182 if(appData.noGUI) return;
5184 DisplayTimerLabel(GUI_Blackclock, _("Black"), timeRemaining, highlight);
5185 if (highlight && WindowIcon == WhiteIcon)
5187 WindowIcon = BlackIcon;
5188 gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
5206 int StartChildProcess(cmdLine, dir, pr)
5213 int to_prog[2], from_prog[2];
5217 if (appData.debugMode) {
5218 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
5221 /* We do NOT feed the cmdLine to the shell; we just
5222 parse it into blank-separated arguments in the
5223 most simple-minded way possible.
5226 strcpy(buf, cmdLine);
5229 while(*p == ' ') p++;
5231 if(*p == '"' || *p == '\'')
5232 p = strchr(++argv[i-1], *p);
5233 else p = strchr(p, ' ');
5234 if (p == NULL) break;
5239 SetUpChildIO(to_prog, from_prog);
5241 if ((pid = fork()) == 0) {
5243 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
5244 close(to_prog[1]); // first close the unused pipe ends
5245 close(from_prog[0]);
5246 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
5247 dup2(from_prog[1], 1);
5248 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
5249 close(from_prog[1]); // and closing again loses one of the pipes!
5250 if(fileno(stderr) >= 2) // better safe than sorry...
5251 dup2(1, fileno(stderr)); /* force stderr to the pipe */
5253 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
5258 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
5260 execvp(argv[0], argv);
5262 /* If we get here, exec failed */
5267 /* Parent process */
5269 close(from_prog[1]);
5271 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5274 cp->fdFrom = from_prog[0];
5275 cp->fdTo = to_prog[1];
5280 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
5281 static RETSIGTYPE AlarmCallBack(int n)
5287 DestroyChildProcess(pr, signalType)
5291 ChildProc *cp = (ChildProc *) pr;
5293 if (cp->kind != CPReal) return;
5295 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
5296 signal(SIGALRM, AlarmCallBack);
5298 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
5299 kill(cp->pid, SIGKILL); // kill it forcefully
5300 wait((int *) 0); // and wait again
5304 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
5306 /* Process is exiting either because of the kill or because of
5307 a quit command sent by the backend; either way, wait for it to die.
5316 InterruptChildProcess(pr)
5319 ChildProc *cp = (ChildProc *) pr;
5321 if (cp->kind != CPReal) return;
5322 (void) kill(cp->pid, SIGINT); /* stop it thinking */
5325 int OpenTelnet(host, port, pr)
5330 char cmdLine[MSG_SIZ];
5332 if (port[0] == NULLCHAR) {
5333 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
5335 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
5337 return StartChildProcess(cmdLine, "", pr);
5340 int OpenTCP(host, port, pr)
5346 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
5347 #else /* !OMIT_SOCKETS */
5349 struct sockaddr_in sa;
5351 unsigned short uport;
5354 if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
5358 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
5359 sa.sin_family = AF_INET;
5360 sa.sin_addr.s_addr = INADDR_ANY;
5361 uport = (unsigned short) 0;
5362 sa.sin_port = htons(uport);
5363 if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
5367 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
5368 if (!(hp = gethostbyname(host))) {
5370 if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
5371 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
5372 hp->h_addrtype = AF_INET;
5374 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
5375 hp->h_addr_list[0] = (char *) malloc(4);
5376 hp->h_addr_list[0][0] = b0;
5377 hp->h_addr_list[0][1] = b1;
5378 hp->h_addr_list[0][2] = b2;
5379 hp->h_addr_list[0][3] = b3;
5384 sa.sin_family = hp->h_addrtype;
5385 uport = (unsigned short) atoi(port);
5386 sa.sin_port = htons(uport);
5387 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
5389 if (connect(s, (struct sockaddr *) &sa,
5390 sizeof(struct sockaddr_in)) < 0) {
5394 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5401 #endif /* !OMIT_SOCKETS */
5406 int OpenCommPort(name, pr)
5413 fd = open(name, 2, 0);
5414 if (fd < 0) return errno;
5416 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5426 int OpenLoopback(pr)
5432 SetUpChildIO(to, from);
5434 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5437 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
5444 int OpenRcmd(host, user, cmd, pr)
5445 char *host, *user, *cmd;
5448 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
5452 #define INPUT_SOURCE_BUF_SIZE 8192
5461 char buf[INPUT_SOURCE_BUF_SIZE];
5466 DoInputCallback(io,cond,data)
5471 /* read input from one of the input source (for example a chess program, ICS, etc).
5472 * and call a function that will handle the input
5475 int count; /* how many bytes did we read */
5479 /* All information (callback function, file descriptor, etc) is
5480 * saved in an InputSource structure
5482 InputSource *is = (InputSource *) data;
5486 count = read(is->fd, is->unused,
5487 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
5491 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
5494 is->unused += count;
5496 /* break input into lines and call the callback function on each
5499 while (p < is->unused)
5501 q = memchr(p, '\n', is->unused - p);
5502 if (q == NULL) break;
5504 (is->func)(is, is->closure, p, q - p, 0);
5507 /* remember not yet used part of the buffer */
5509 while (p < is->unused)
5517 /* read maximum length of input buffer and send the whole buffer
5518 * to the callback function
5520 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
5525 (is->func)(is, is->closure, is->buf, count, error);
5531 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
5538 GIOChannel *channel;
5539 ChildProc *cp = (ChildProc *) pr;
5541 is = (InputSource *) calloc(1, sizeof(InputSource));
5542 is->lineByLine = lineByLine;
5546 is->fd = fileno(stdin);
5548 is->kind = cp->kind;
5549 is->fd = cp->fdFrom;
5552 is->unused = is->buf;
5556 // is->xid = XtAppAddInput(appContext, is->fd,
5557 // (XtPointer) (XtInputReadMask),
5558 // (XtInputCallbackProc) DoInputCallback,
5562 /* TODO: will this work on windows?*/
5563 printf("DEBUG: fd=%d %d\n",is->fd,is);
5565 channel = g_io_channel_unix_new(is->fd);
5566 g_io_channel_set_close_on_unref (channel, TRUE);
5567 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
5568 is->closure = closure;
5569 return (InputSourceRef) is;
5573 RemoveInputSource(isr)
5576 InputSource *is = (InputSource *) isr;
5578 if (is->sid == 0) return;
5579 g_source_remove(is->sid);
5584 int OutputToProcess(pr, message, count, outError)
5590 static int line = 0;
5591 ChildProc *cp = (ChildProc *) pr;
5596 if (appData.noJoin || !appData.useInternalWrap)
5597 outCount = fwrite(message, 1, count, stdout);
5600 int width = get_term_width();
5601 int len = wrap(NULL, message, count, width, &line);
5602 char *msg = malloc(len);
5606 outCount = fwrite(message, 1, count, stdout);
5609 dbgchk = wrap(msg, message, count, width, &line);
5610 if (dbgchk != len && appData.debugMode)
5611 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
5612 outCount = fwrite(msg, 1, dbgchk, stdout);
5618 outCount = write(cp->fdTo, message, count);
5628 /* Output message to process, with "ms" milliseconds of delay
5629 between each character. This is needed when sending the logon
5630 script to ICC, which for some reason doesn't like the
5631 instantaneous send. */
5632 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
5639 ChildProc *cp = (ChildProc *) pr;
5644 r = write(cp->fdTo, message++, 1);
5657 /**** Animation code by Hugh Fisher, DCS, ANU.
5659 Known problem: if a window overlapping the board is
5660 moved away while a piece is being animated underneath,
5661 the newly exposed area won't be updated properly.
5662 I can live with this.
5664 Known problem: if you look carefully at the animation
5665 of pieces in mono mode, they are being drawn as solid
5666 shapes without interior detail while moving. Fixing
5667 this would be a major complication for minimal return.
5670 /* Masks for XPM pieces. Black and white pieces can have
5671 different shapes, but in the interest of retaining my
5672 sanity pieces must have the same outline on both light
5673 and dark squares, and all pieces must use the same
5674 background square colors/images. */
5676 static int xpmDone = 0;
5679 CreateAnimMasks (pieceDepth)
5686 unsigned long plane;
5689 /* just return for gtk at the moment */
5692 /* Need a bitmap just to get a GC with right depth */
5693 buf = XCreatePixmap(xDisplay, xBoardWindow,
5695 values.foreground = 1;
5696 values.background = 0;
5697 /* Don't use XtGetGC, not read only */
5698 maskGC = XCreateGC(xDisplay, buf,
5699 GCForeground | GCBackground, &values);
5700 XFreePixmap(xDisplay, buf);
5702 buf = XCreatePixmap(xDisplay, xBoardWindow,
5703 squareSize, squareSize, pieceDepth);
5704 values.foreground = XBlackPixel(xDisplay, xScreen);
5705 values.background = XWhitePixel(xDisplay, xScreen);
5706 bufGC = XCreateGC(xDisplay, buf,
5707 GCForeground | GCBackground, &values);
5709 for (piece = WhitePawn; piece <= BlackKing; piece++) {
5710 /* Begin with empty mask */
5711 if(!xpmDone) // [HGM] pieces: keep using existing
5712 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
5713 squareSize, squareSize, 1);
5714 XSetFunction(xDisplay, maskGC, GXclear);
5715 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
5716 0, 0, squareSize, squareSize);
5718 /* Take a copy of the piece */
5723 XSetFunction(xDisplay, bufGC, GXcopy);
5724 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
5726 0, 0, squareSize, squareSize, 0, 0);
5728 /* XOR the background (light) over the piece */
5729 XSetFunction(xDisplay, bufGC, GXxor);
5731 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
5732 0, 0, squareSize, squareSize, 0, 0);
5734 XSetForeground(xDisplay, bufGC, lightSquareColor);
5735 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
5738 /* We now have an inverted piece image with the background
5739 erased. Construct mask by just selecting all the non-zero
5740 pixels - no need to reconstruct the original image. */
5741 XSetFunction(xDisplay, maskGC, GXor);
5743 /* Might be quicker to download an XImage and create bitmap
5744 data from it rather than this N copies per piece, but it
5745 only takes a fraction of a second and there is a much
5746 longer delay for loading the pieces. */
5747 for (n = 0; n < pieceDepth; n ++) {
5748 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
5749 0, 0, squareSize, squareSize,
5755 XFreePixmap(xDisplay, buf);
5756 XFreeGC(xDisplay, bufGC);
5757 XFreeGC(xDisplay, maskGC);
5761 InitAnimState (anim, info)
5763 XWindowAttributes * info;
5768 /* Each buffer is square size, same depth as window */
5769 // anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
5770 // squareSize, squareSize, info->depth);
5771 // anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
5772 // squareSize, squareSize, info->depth);
5774 // /* Create a plain GC for blitting */
5775 // mask = GCForeground | GCBackground | GCFunction |
5776 // GCPlaneMask | GCGraphicsExposures;
5777 // values.foreground = XBlackPixel(xDisplay, xScreen);
5778 // values.background = XWhitePixel(xDisplay, xScreen);
5779 // values.function = GXcopy;
5780 // values.plane_mask = AllPlanes;
5781 // values.graphics_exposures = False;
5782 // anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
5784 // /* Piece will be copied from an existing context at
5785 // the start of each new animation/drag. */
5786 // anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
5788 // /* Outline will be a read-only copy of an existing */
5789 // anim->outlineGC = None;
5795 static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
5796 XWindowAttributes info;
5798 /* for gtk at the moment just ... */
5801 if (xpmDone && gameInfo.variant == old) return;
5802 if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet
5803 // XGetWindowAttributes(xDisplay, xBoardWindow, &info);
5805 // InitAnimState(&game, &info);
5806 // InitAnimState(&player, &info);
5808 /* For XPM pieces, we need bitmaps to use as masks. */
5810 // CreateAnimMasks(info.depth);
5816 static Boolean frameWaiting;
5818 static RETSIGTYPE FrameAlarm (sig)
5821 frameWaiting = False;
5822 /* In case System-V style signals. Needed?? */
5823 signal(SIGALRM, FrameAlarm);
5830 struct itimerval delay;
5832 XSync(xDisplay, False);
5835 frameWaiting = True;
5836 signal(SIGALRM, FrameAlarm);
5837 delay.it_interval.tv_sec =
5838 delay.it_value.tv_sec = time / 1000;
5839 delay.it_interval.tv_usec =
5840 delay.it_value.tv_usec = (time % 1000) * 1000;
5841 setitimer(ITIMER_REAL, &delay, NULL);
5842 while (frameWaiting) pause();
5843 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
5844 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
5845 setitimer(ITIMER_REAL, &delay, NULL);
5855 // XSync(xDisplay, False);
5857 usleep(time * 1000);
5862 /* Convert board position to corner of screen rect and color */
5865 ScreenSquare(column, row, pt, color)
5866 int column; int row; XPoint * pt; int * color;
5869 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
5870 pt->y = lineGap + row * (squareSize + lineGap);
5872 pt->x = lineGap + column * (squareSize + lineGap);
5873 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
5875 *color = SquareColor(row, column);
5878 /* Convert window coords to square */
5881 BoardSquare(x, y, column, row)
5882 int x; int y; int * column; int * row;
5884 *column = EventToSquare(x, BOARD_WIDTH);
5885 if (flipView && *column >= 0)
5886 *column = BOARD_WIDTH - 1 - *column;
5887 *row = EventToSquare(y, BOARD_HEIGHT);
5888 if (!flipView && *row >= 0)
5889 *row = BOARD_HEIGHT - 1 - *row;
5894 #undef Max /* just in case */
5896 #define Max(a, b) ((a) > (b) ? (a) : (b))
5897 #define Min(a, b) ((a) < (b) ? (a) : (b))
5900 SetRect(rect, x, y, width, height)
5901 XRectangle * rect; int x; int y; int width; int height;
5905 rect->width = width;
5906 rect->height = height;
5909 /* Test if two frames overlap. If they do, return
5910 intersection rect within old and location of
5911 that rect within new. */
5914 Intersect(old, new, size, area, pt)
5915 XPoint * old; XPoint * new;
5916 int size; XRectangle * area; XPoint * pt;
5918 if (old->x > new->x + size || new->x > old->x + size ||
5919 old->y > new->y + size || new->y > old->y + size) {
5922 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
5923 size - abs(old->x - new->x), size - abs(old->y - new->y));
5924 pt->x = Max(old->x - new->x, 0);
5925 pt->y = Max(old->y - new->y, 0);
5930 /* For two overlapping frames, return the rect(s)
5931 in the old that do not intersect with the new. */
5934 CalcUpdateRects(old, new, size, update, nUpdates)
5935 XPoint * old; XPoint * new; int size;
5936 XRectangle update[]; int * nUpdates;
5940 /* If old = new (shouldn't happen) then nothing to draw */
5941 if (old->x == new->x && old->y == new->y) {
5945 /* Work out what bits overlap. Since we know the rects
5946 are the same size we don't need a full intersect calc. */
5948 /* Top or bottom edge? */
5949 if (new->y > old->y) {
5950 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
5952 } else if (old->y > new->y) {
5953 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
5954 size, old->y - new->y);
5957 /* Left or right edge - don't overlap any update calculated above. */
5958 if (new->x > old->x) {
5959 SetRect(&(update[count]), old->x, Max(new->y, old->y),
5960 new->x - old->x, size - abs(new->y - old->y));
5962 } else if (old->x > new->x) {
5963 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
5964 old->x - new->x, size - abs(new->y - old->y));
5971 /* Generate a series of frame coords from start->mid->finish.
5972 The movement rate doubles until the half way point is
5973 reached, then halves back down to the final destination,
5974 which gives a nice slow in/out effect. The algorithmn
5975 may seem to generate too many intermediates for short
5976 moves, but remember that the purpose is to attract the
5977 viewers attention to the piece about to be moved and
5978 then to where it ends up. Too few frames would be less
5982 Tween(start, mid, finish, factor, frames, nFrames)
5983 XPoint * start; XPoint * mid;
5984 XPoint * finish; int factor;
5985 XPoint frames[]; int * nFrames;
5987 int fraction, n, count;
5991 /* Slow in, stepping 1/16th, then 1/8th, ... */
5993 for (n = 0; n < factor; n++)
5995 for (n = 0; n < factor; n++) {
5996 frames[count].x = start->x + (mid->x - start->x) / fraction;
5997 frames[count].y = start->y + (mid->y - start->y) / fraction;
5999 fraction = fraction / 2;
6003 frames[count] = *mid;
6006 /* Slow out, stepping 1/2, then 1/4, ... */
6008 for (n = 0; n < factor; n++) {
6009 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
6010 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
6012 fraction = fraction * 2;
6017 /* Draw a piece on the screen without disturbing what's there */
6020 SelectGCMask(piece, clip, outline, mask)
6021 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
6025 /* Bitmap for piece being moved. */
6026 if (appData.monoMode) {
6027 *mask = *pieceToSolid(piece);
6028 } else if (useImages) {
6030 *mask = xpmMask[piece];
6032 *mask = ximMaskPm[piece];
6035 *mask = *pieceToSolid(piece);
6038 /* GC for piece being moved. Square color doesn't matter, but
6039 since it gets modified we make a copy of the original. */
6041 if (appData.monoMode)
6046 if (appData.monoMode)
6051 // XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
6053 /* Outline only used in mono mode and is not modified */
6055 *outline = bwPieceGC;
6057 *outline = wbPieceGC;
6061 OverlayPiece(piece, clip, outline, dest)
6062 ChessSquare piece; GC clip; GC outline; Drawable dest;
6067 /* Draw solid rectangle which will be clipped to shape of piece */
6068 // XFillRectangle(xDisplay, dest, clip,
6069 // 0, 0, squareSize, squareSize)
6071 if (appData.monoMode)
6072 /* Also draw outline in contrasting color for black
6073 on black / white on white cases */
6074 // XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
6075 // 0, 0, squareSize, squareSize, 0, 0, 1)
6078 /* Copy the piece */
6083 // XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
6085 // 0, 0, squareSize, squareSize,
6090 /* Animate the movement of a single piece */
6093 BeginAnimation(anim, piece, startColor, start)
6101 /* The old buffer is initialised with the start square (empty) */
6102 BlankSquare(0, 0, startColor, EmptySquare, anim->saveBuf);
6103 anim->prevFrame = *start;
6105 /* The piece will be drawn using its own bitmap as a matte */
6106 // SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
6107 // XSetClipMask(xDisplay, anim->pieceGC, mask);
6111 AnimationFrame(anim, frame, piece)
6116 XRectangle updates[4];
6121 /* Save what we are about to draw into the new buffer */
6122 // XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
6123 // frame->x, frame->y, squareSize, squareSize,
6126 /* Erase bits of the previous frame */
6127 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
6128 /* Where the new frame overlapped the previous,
6129 the contents in newBuf are wrong. */
6130 // XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
6131 // overlap.x, overlap.y,
6132 // overlap.width, overlap.height,
6134 /* Repaint the areas in the old that don't overlap new */
6135 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
6136 for (i = 0; i < count; i++)
6137 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6138 // updates[i].x - anim->prevFrame.x,
6139 // updates[i].y - anim->prevFrame.y,
6140 // updates[i].width, updates[i].height,
6141 // updates[i].x, updates[i].y)
6144 /* Easy when no overlap */
6145 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6146 // 0, 0, squareSize, squareSize,
6147 // anim->prevFrame.x, anim->prevFrame.y);
6150 /* Save this frame for next time round */
6151 // XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
6152 // 0, 0, squareSize, squareSize,
6154 anim->prevFrame = *frame;
6156 /* Draw piece over original screen contents, not current,
6157 and copy entire rect. Wipes out overlapping piece images. */
6158 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
6159 // XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
6160 // 0, 0, squareSize, squareSize,
6161 // frame->x, frame->y);
6165 EndAnimation (anim, finish)
6169 XRectangle updates[4];
6174 /* The main code will redraw the final square, so we
6175 only need to erase the bits that don't overlap. */
6176 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
6177 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
6178 for (i = 0; i < count; i++)
6179 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6180 // updates[i].x - anim->prevFrame.x,
6181 // updates[i].y - anim->prevFrame.y,
6182 // updates[i].width, updates[i].height,
6183 // updates[i].x, updates[i].y)
6186 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6187 // 0, 0, squareSize, squareSize,
6188 // anim->prevFrame.x, anim->prevFrame.y);
6193 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
6195 ChessSquare piece; int startColor;
6196 XPoint * start; XPoint * finish;
6197 XPoint frames[]; int nFrames;
6201 BeginAnimation(anim, piece, startColor, start);
6202 for (n = 0; n < nFrames; n++) {
6203 AnimationFrame(anim, &(frames[n]), piece);
6204 FrameDelay(appData.animSpeed);
6206 EndAnimation(anim, finish);
6209 /* Main control logic for deciding what to animate and how */
6212 AnimateMove(board, fromX, fromY, toX, toY)
6221 XPoint start, finish, mid;
6222 XPoint frames[kFactor * 2 + 1];
6223 int nFrames, startColor, endColor;
6225 /* Are we animating? */
6226 if (!appData.animate || appData.blindfold)
6229 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
6230 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
6231 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
6233 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
6234 piece = board[fromY][fromX];
6235 if (piece >= EmptySquare) return;
6240 hop = (piece == WhiteKnight || piece == BlackKnight);
6243 if (appData.debugMode) {
6244 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
6245 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
6246 piece, fromX, fromY, toX, toY); }
6248 ScreenSquare(fromX, fromY, &start, &startColor);
6249 ScreenSquare(toX, toY, &finish, &endColor);
6252 /* Knight: make diagonal movement then straight */
6253 if (abs(toY - fromY) < abs(toX - fromX)) {
6254 mid.x = start.x + (finish.x - start.x) / 2;
6258 mid.y = start.y + (finish.y - start.y) / 2;
6261 mid.x = start.x + (finish.x - start.x) / 2;
6262 mid.y = start.y + (finish.y - start.y) / 2;
6265 /* Don't use as many frames for very short moves */
6266 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
6267 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
6269 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
6270 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
6272 /* Be sure end square is redrawn */
6273 damage[toY][toX] = True;
6277 DragPieceBegin(x, y)
6280 int boardX, boardY, color;
6283 /* Are we animating? */
6284 if (!appData.animateDragging || appData.blindfold)
6287 /* Figure out which square we start in and the
6288 mouse position relative to top left corner. */
6289 BoardSquare(x, y, &boardX, &boardY);
6290 player.startBoardX = boardX;
6291 player.startBoardY = boardY;
6292 ScreenSquare(boardX, boardY, &corner, &color);
6293 player.startSquare = corner;
6294 player.startColor = color;
6295 /* As soon as we start dragging, the piece will jump slightly to
6296 be centered over the mouse pointer. */
6297 player.mouseDelta.x = squareSize/2;
6298 player.mouseDelta.y = squareSize/2;
6299 /* Initialise animation */
6300 player.dragPiece = PieceForSquare(boardX, boardY);
6302 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
6303 player.dragActive = True;
6304 BeginAnimation(&player, player.dragPiece, color, &corner);
6305 /* Mark this square as needing to be redrawn. Note that
6306 we don't remove the piece though, since logically (ie
6307 as seen by opponent) the move hasn't been made yet. */
6308 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
6309 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
6310 // XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
6311 // corner.x, corner.y, squareSize, squareSize,
6312 // 0, 0); // [HGM] zh: unstack in stead of grab
6313 damage[boardY][boardX] = True;
6315 player.dragActive = False;
6325 /* Are we animating? */
6326 if (!appData.animateDragging || appData.blindfold)
6330 if (! player.dragActive)
6332 /* Move piece, maintaining same relative position
6333 of mouse within square */
6334 corner.x = x - player.mouseDelta.x;
6335 corner.y = y - player.mouseDelta.y;
6336 AnimationFrame(&player, &corner, player.dragPiece);
6338 if (appData.highlightDragging) {
6340 BoardSquare(x, y, &boardX, &boardY);
6341 SetHighlights(fromX, fromY, boardX, boardY);
6350 int boardX, boardY, color;
6353 /* Are we animating? */
6354 if (!appData.animateDragging || appData.blindfold)
6358 if (! player.dragActive)
6360 /* Last frame in sequence is square piece is
6361 placed on, which may not match mouse exactly. */
6362 BoardSquare(x, y, &boardX, &boardY);
6363 ScreenSquare(boardX, boardY, &corner, &color);
6364 EndAnimation(&player, &corner);
6366 /* Be sure end square is redrawn */
6367 damage[boardY][boardX] = True;
6369 /* This prevents weird things happening with fast successive
6370 clicks which on my Sun at least can cause motion events
6371 without corresponding press/release. */
6372 player.dragActive = False;
6375 /* Handle expose event while piece being dragged */
6380 if (!player.dragActive || appData.blindfold)
6383 /* What we're doing: logically, the move hasn't been made yet,
6384 so the piece is still in it's original square. But visually
6385 it's being dragged around the board. So we erase the square
6386 that the piece is on and draw it at the last known drag point. */
6387 BlankSquare(player.startSquare.x, player.startSquare.y,
6388 player.startColor, EmptySquare, xBoardWindow);
6389 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
6390 damage[player.startBoardY][player.startBoardX] = TRUE;
6393 #include <sys/ioctl.h>
6394 int get_term_width()
6396 int fd, default_width;
6399 default_width = 79; // this is FICS default anyway...
6401 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
6403 if (!ioctl(fd, TIOCGSIZE, &win))
6404 default_width = win.ts_cols;
6405 #elif defined(TIOCGWINSZ)
6407 if (!ioctl(fd, TIOCGWINSZ, &win))
6408 default_width = win.ws_col;
6410 return default_width;
6413 void update_ics_width()
6415 static int old_width = 0;
6416 int new_width = get_term_width();
6418 if (old_width != new_width)
6419 ics_printf("set width %d\n", new_width);
6420 old_width = new_width;
6423 void NotifyFrontendLogin()