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. */
58 #include <sys/types.h>
63 # if HAVE_SYS_SOCKET_H
64 # include <sys/socket.h>
65 # include <netinet/in.h>
67 # else /* not HAVE_SYS_SOCKET_H */
68 # if HAVE_LAN_SOCKET_H
69 # include <lan/socket.h>
71 # include <lan/netdb.h>
72 # else /* not HAVE_LAN_SOCKET_H */
73 # define OMIT_SOCKETS 1
74 # endif /* not HAVE_LAN_SOCKET_H */
75 # endif /* not HAVE_SYS_SOCKET_H */
76 #endif /* !OMIT_SOCKETS */
81 #else /* not STDC_HEADERS */
82 extern char *getenv();
85 # else /* not HAVE_STRING_H */
87 # endif /* not HAVE_STRING_H */
88 #endif /* not STDC_HEADERS */
91 # include <sys/fcntl.h>
92 #else /* not HAVE_SYS_FCNTL_H */
95 # endif /* HAVE_FCNTL_H */
96 #endif /* not HAVE_SYS_FCNTL_H */
98 #if HAVE_SYS_SYSTEMINFO_H
99 # include <sys/systeminfo.h>
100 #endif /* HAVE_SYS_SYSTEMINFO_H */
102 #if TIME_WITH_SYS_TIME
103 # include <sys/time.h>
107 # include <sys/time.h>
118 # include <sys/wait.h>
123 # define NAMLEN(dirent) strlen((dirent)->d_name)
124 # define HAVE_DIR_STRUCT
126 # define dirent direct
127 # define NAMLEN(dirent) (dirent)->d_namlen
129 # include <sys/ndir.h>
130 # define HAVE_DIR_STRUCT
133 # include <sys/dir.h>
134 # define HAVE_DIR_STRUCT
138 # define HAVE_DIR_STRUCT
142 #include <X11/Intrinsic.h>
143 #include <X11/StringDefs.h>
144 #include <X11/Shell.h>
145 #include <X11/cursorfont.h>
146 #include <X11/Xatom.h>
147 #include <X11/Xmu/Atoms.h>
149 #include <X11/Xaw3d/Dialog.h>
150 #include <X11/Xaw3d/Form.h>
151 #include <X11/Xaw3d/List.h>
152 #include <X11/Xaw3d/Label.h>
153 #include <X11/Xaw3d/SimpleMenu.h>
154 #include <X11/Xaw3d/SmeBSB.h>
155 #include <X11/Xaw3d/SmeLine.h>
156 #include <X11/Xaw3d/Box.h>
157 #include <X11/Xaw3d/MenuButton.h>
158 #include <X11/Xaw3d/Text.h>
159 #include <X11/Xaw3d/AsciiText.h>
161 #include <X11/Xaw/Dialog.h>
162 #include <X11/Xaw/Form.h>
163 #include <X11/Xaw/List.h>
164 #include <X11/Xaw/Label.h>
165 #include <X11/Xaw/SimpleMenu.h>
166 #include <X11/Xaw/SmeBSB.h>
167 #include <X11/Xaw/SmeLine.h>
168 #include <X11/Xaw/Box.h>
169 #include <X11/Xaw/MenuButton.h>
170 #include <X11/Xaw/Text.h>
171 #include <X11/Xaw/AsciiText.h>
174 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
179 #include "pixmaps/pixmaps.h"
180 #define IMAGE_EXT "xpm"
182 #define IMAGE_EXT "xim"
183 #include "bitmaps/bitmaps.h"
188 #include <gdk-pixbuf/gdk-pixbuf.h>
190 #include "bitmaps/icon_white.bm"
191 #include "bitmaps/icon_black.bm"
192 #include "bitmaps/checkmark.bm"
194 #include "frontend.h"
199 #include "xgamelist.h"
200 #include "xhistory.h"
201 #include "xedittags.h"
203 #include "callback.h"
204 #include "interface.h"
206 // must be moved to xengineoutput.h
208 void EngineOutputProc P((Widget w, XEvent *event,
209 String *prms, Cardinal *nprms));
210 void EvalGraphProc P((Widget w, XEvent *event,
211 String *prms, Cardinal *nprms));
218 #define usleep(t) _sleep2(((t)+500)/1000)
222 # define _(s) gettext (s)
223 # define N_(s) gettext_noop (s)
246 int main P((int argc, char **argv));
247 RETSIGTYPE CmailSigHandler P((int sig));
248 RETSIGTYPE IntSigHandler P((int sig));
249 RETSIGTYPE TermSizeSigHandler P((int sig));
250 void CreateGCs P((void));
251 void CreateXIMPieces P((void));
252 void CreateXPMPieces P((void));
253 void CreatePieces P((void));
254 void CreatePieceMenus P((void));
255 Widget CreateMenuBar P((Menu *mb));
256 char *FindFont P((char *pattern, int targetPxlSize));
257 void PieceMenuPopup P((Widget w, XEvent *event,
258 String *params, Cardinal *num_params));
259 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
260 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
261 int EventToSquare P((int x, int limit));
262 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
263 void AnimateUserMove P((Widget w, XEvent * event,
264 String * params, Cardinal * nParams));
265 void CommentPopUp P((char *title, char *label));
266 void CommentPopDown P((void));
267 void CommentCallback P((Widget w, XtPointer client_data,
268 XtPointer call_data));
269 void ICSInputBoxPopUp P((void));
270 void ICSInputBoxPopDown P((void));
271 void AskQuestionReplyAction P((Widget w, XEvent *event,
272 String *prms, Cardinal *nprms));
273 void AskQuestionProc P((Widget w, XEvent *event,
274 String *prms, Cardinal *nprms));
275 void AskQuestionPopDown P((void));
276 void PromotionPopDown P((void));
277 void PromotionCallback P((Widget w, XtPointer client_data,
278 XtPointer call_data));
279 void EditCommentPopDown P((void));
280 void EditCommentCallback P((Widget w, XtPointer client_data,
281 XtPointer call_data));
282 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
283 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
285 void PastePositionProc P((Widget w, XEvent *event, String *prms,
287 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
288 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
289 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
290 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
292 void EditCommentProc P((Widget w, XEvent *event,
293 String *prms, Cardinal *nprms));
294 void IcsInputBoxProc P((Widget w, XEvent *event,
295 String *prms, Cardinal *nprms));
296 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
297 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
298 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
299 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
300 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
301 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
302 void Iconify P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
303 void DisplayMove P((int moveNumber));
304 void DisplayTitle P((char *title));
305 void ICSInitScript P((void));
306 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
307 void ErrorPopUp P((char *title, char *text, int modal));
308 void ErrorPopDown P((void));
309 static char *ExpandPathName P((char *path));
310 static void CreateAnimVars P((void));
311 static void DragPieceMove P((int x, int y));
312 static void DrawDragPiece P((void));
313 char *ModeToWidgetName P((GameMode mode));
314 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
315 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
316 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
317 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
318 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
319 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
320 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
321 void ShufflePopDown P(());
322 void EnginePopDown P(());
323 void UciPopDown P(());
324 void TimeControlPopDown P(());
325 void NewVariantPopDown P(());
326 void SettingsPopDown P(());
327 void SetMenuEnables P((Enables *enab));
328 void update_ics_width P(());
329 int get_term_width P(());
331 * XBoard depends on Xt R4 or higher
333 int xtVersion = XtSpecificationRelease;
338 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
339 jailSquareColor, highlightSquareColor, premoveHighlightColor;
340 Pixel lowTimeWarningColor;
342 #define LINE_TYPE_NORMAL 0
343 #define LINE_TYPE_HIGHLIGHT 1
344 #define LINE_TYPE_PRE 2
347 GC lightSquareGC, darkSquareGC, jailSquareGC, wdPieceGC, wlPieceGC,
348 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC,
349 wjPieceGC, bjPieceGC;
350 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
351 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
352 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
353 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
354 menuBarWidget, editShell, errorShell, analysisShell,
355 ICSInputShell, fileNameShell, askQuestionShell;
357 Widget historyShell, evalGraphShell, gameListShell;
358 //XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
359 //XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
361 Font clockFontID, coordFontID, countFontID;
362 XFontStruct *clockFontStruct, *coordFontStruct, *countFontStruct;
363 XtAppContext appContext;
365 char *oldICSInteractionTitle;
369 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
371 Position commentX = -1, commentY = -1;
372 Dimension commentW, commentH;
373 typedef unsigned int BoardSize;
375 Boolean chessProgram;
377 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
378 int squareSize, smallLayout = 0, tinyLayout = 0,
379 marginW, marginH, // [HGM] for run-time resizing
380 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
381 ICSInputBoxUp = False, askQuestionUp = False,
382 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
383 editUp = False, errorUp = False, errorExitStatus = -1, lineGap;
384 Pixel timerForegroundPixel, timerBackgroundPixel;
385 Pixel buttonForegroundPixel, buttonBackgroundPixel;
386 char *chessDir, *programName, *programVersion,
387 *gameCopyFilename, *gamePasteFilename;
388 Boolean alwaysOnTop = False;
389 Boolean saveSettingsOnExit;
390 char *settingsFileName;
391 char *icsTextMenuString;
393 char *firstChessProgramNames;
394 char *secondChessProgramNames;
396 WindowPlacement wpMain;
397 WindowPlacement wpConsole;
398 WindowPlacement wpComment;
399 WindowPlacement wpMoveHistory;
400 WindowPlacement wpEvalGraph;
401 WindowPlacement wpEngineOutput;
402 WindowPlacement wpGameList;
403 WindowPlacement wpTags;
407 Pixmap pieceBitmap[2][(int)BlackPawn];
408 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
409 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
410 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
411 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
412 int useImages=0, useImageSqs;
413 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
414 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
415 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
416 XImage *ximLightSquare, *ximDarkSquare;
419 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
420 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
422 #define White(piece) ((int)(piece) < (int)BlackPawn)
424 /* Variables for doing smooth animation. This whole thing
425 would be much easier if the board was double-buffered,
426 but that would require a fairly major rewrite. */
431 GC blitGC, pieceGC, outlineGC;
432 XPoint startSquare, prevFrame, mouseDelta;
436 int startBoardX, startBoardY;
439 /* There can be two pieces being animated at once: a player
440 can begin dragging a piece before the remote opponent has moved. */
442 static AnimState game, player;
444 /* Bitmaps for use as masks when drawing XPM pieces.
445 Need one for each black and white piece. */
446 static Pixmap xpmMask[BlackKing + 1];
448 /* This magic number is the number of intermediate frames used
449 in each half of the animation. For short moves it's reduced
450 by 1. The total number of frames will be factor * 2 + 1. */
453 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
455 Enables icsEnables[] = {
456 { "menuFile.Mail Move", False },
457 { "menuFile.Reload CMail Message", False },
458 { "menuMode.Machine Black", False },
459 { "menuMode.Machine White", False },
460 { "menuMode.Analysis Mode", False },
461 { "menuMode.Analyze File", False },
462 { "menuMode.Two Machines", False },
464 { "menuHelp.Hint", False },
465 { "menuHelp.Book", False },
466 { "menuStep.Move Now", False },
467 { "menuOptions.Periodic Updates", False },
468 { "menuOptions.Hide Thinking", False },
469 { "menuOptions.Ponder Next Move", False },
474 Enables ncpEnables[] = {
475 { "menuFile.Mail Move", False },
476 { "menuFile.Reload CMail Message", False },
477 { "menuMode.Machine White", False },
478 { "menuMode.Machine Black", False },
479 { "menuMode.Analysis Mode", False },
480 { "menuMode.Analyze File", False },
481 { "menuMode.Two Machines", False },
482 { "menuMode.ICS Client", False },
483 { "menuMode.ICS Input Box", False },
485 { "menuStep.Revert", False },
486 { "menuStep.Move Now", False },
487 { "menuStep.Retract Move", False },
488 { "menuOptions.Auto Comment", False },
489 { "menuOptions.Auto Flag", False },
490 { "menuOptions.Auto Flip View", False },
491 { "menuOptions.Auto Observe", False },
492 { "menuOptions.Auto Raise Board", False },
493 { "menuOptions.Get Move List", False },
494 { "menuOptions.ICS Alarm", False },
495 { "menuOptions.Move Sound", False },
496 { "menuOptions.Quiet Play", False },
497 { "menuOptions.Hide Thinking", False },
498 { "menuOptions.Periodic Updates", False },
499 { "menuOptions.Ponder Next Move", False },
500 { "menuHelp.Hint", False },
501 { "menuHelp.Book", False },
505 Enables gnuEnables[] = {
506 { "menuMode.ICS Client", False },
507 { "menuMode.ICS Input Box", False },
508 { "menuAction.Accept", False },
509 { "menuAction.Decline", False },
510 { "menuAction.Rematch", False },
511 { "menuAction.Adjourn", False },
512 { "menuAction.Stop Examining", False },
513 { "menuAction.Stop Observing", False },
514 { "menuStep.Revert", False },
515 { "menuOptions.Auto Comment", False },
516 { "menuOptions.Auto Observe", False },
517 { "menuOptions.Auto Raise Board", False },
518 { "menuOptions.Get Move List", False },
519 { "menuOptions.Premove", False },
520 { "menuOptions.Quiet Play", False },
522 /* The next two options rely on SetCmailMode being called *after* */
523 /* SetGNUMode so that when GNU is being used to give hints these */
524 /* menu options are still available */
526 { "menuFile.Mail Move", False },
527 { "menuFile.Reload CMail Message", False },
531 Enables cmailEnables[] = {
533 { "menuAction.Call Flag", False },
534 { "menuAction.Draw", True },
535 { "menuAction.Adjourn", False },
536 { "menuAction.Abort", False },
537 { "menuAction.Stop Observing", False },
538 { "menuAction.Stop Examining", False },
539 { "menuFile.Mail Move", True },
540 { "menuFile.Reload CMail Message", True },
544 Enables trainingOnEnables[] = {
545 { "menuMode.Edit Comment", False },
546 { "menuMode.Pause", False },
547 { "menuStep.Forward", False },
548 { "menuStep.Backward", False },
549 { "menuStep.Forward to End", False },
550 { "menuStep.Back to Start", False },
551 { "menuStep.Move Now", False },
552 { "menuStep.Truncate Game", False },
556 Enables trainingOffEnables[] = {
557 { "menuMode.Edit Comment", True },
558 { "menuMode.Pause", True },
559 { "menuStep.Forward", True },
560 { "menuStep.Backward", True },
561 { "menuStep.Forward to End", True },
562 { "menuStep.Back to Start", True },
563 { "menuStep.Move Now", True },
564 { "menuStep.Truncate Game", True },
568 Enables machineThinkingEnables[] = {
569 { "menuFile.Load Game", False },
570 { "menuFile.Load Next Game", False },
571 { "menuFile.Load Previous Game", False },
572 { "menuFile.Reload Same Game", False },
573 { "menuFile.Paste Game", False },
574 { "menuFile.Load Position", False },
575 { "menuFile.Load Next Position", False },
576 { "menuFile.Load Previous Position", False },
577 { "menuFile.Reload Same Position", False },
578 { "menuFile.Paste Position", False },
579 { "menuMode.Machine White", False },
580 { "menuMode.Machine Black", False },
581 { "menuMode.Two Machines", False },
582 { "menuStep.Retract Move", False },
586 Enables userThinkingEnables[] = {
587 { "menuFile.Load Game", True },
588 { "menuFile.Load Next Game", True },
589 { "menuFile.Load Previous Game", True },
590 { "menuFile.Reload Same Game", True },
591 { "menuFile.Paste Game", True },
592 { "menuFile.Load Position", True },
593 { "menuFile.Load Next Position", True },
594 { "menuFile.Load Previous Position", True },
595 { "menuFile.Reload Same Position", True },
596 { "menuFile.Paste Position", True },
597 { "menuMode.Machine White", True },
598 { "menuMode.Machine Black", True },
599 { "menuMode.Two Machines", True },
600 { "menuStep.Retract Move", True },
606 MenuItem fileMenu[] = {
607 {N_("New Shuffle Game ..."), ShuffleMenuProc},
608 {N_("New Variant ..."), NewVariantProc}, // [HGM] variant: not functional yet
609 // {"----", NothingProc},
610 // {N_("Save Game"), SaveGameProc},
611 // {"----", NothingProc},
612 {N_("Copy Game"), CopyGameProc},
613 {N_("Paste Game"), PasteGameProc},
614 // {"----", NothingProc},
615 // {N_("Load Position"), LoadPositionProc},
616 // {N_("Load Next Position"), LoadNextPositionProc},
617 // {N_("Load Previous Position"), LoadPrevPositionProc},
618 // {N_("Reload Same Position"), ReloadPositionProc},
619 // {N_("Save Position"), SavePositionProc},
620 // {"----", NothingProc},
621 {N_("Copy Position"), CopyPositionProc},
622 {N_("Paste Position"), PastePositionProc},
623 // {"----", NothingProc},
624 {N_("Mail Move"), MailMoveProc},
625 {N_("Reload CMail Message"), ReloadCmailMsgProc},
626 // {"----", NothingProc},
630 MenuItem modeMenu[] = {
631 // {N_("Machine White"), MachineWhiteProc},
632 // {N_("Machine Black"), MachineBlackProc},
633 // {N_("Two Machines"), TwoMachinesProc},
634 // {N_("Analysis Mode"), AnalyzeModeProc},
635 // {N_("Analyze File"), AnalyzeFileProc },
636 // {N_("ICS Client"), IcsClientProc},
637 // {N_("Edit Game"), EditGameProc},
638 // {N_("Edit Position"), EditPositionProc},
639 // {N_("Training"), TrainingProc},
640 // {"----", NothingProc},
641 {N_("Show Engine Output"), EngineOutputProc},
642 {N_("Show Evaluation Graph"), EvalGraphProc},
643 {N_("Show Game List"), ShowGameListProc},
644 // {"Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
645 // {"----", NothingProc},
646 // {N_("Edit Tags"), EditTagsProc},
647 {N_("Edit Comment"), EditCommentProc},
648 {N_("ICS Input Box"), IcsInputBoxProc},
652 MenuItem optionsMenu[] = {
653 // {N_("Flip View"), FlipViewProc},
654 // {"----", NothingProc},
655 {N_("Adjudications ..."), EngineMenuProc},
656 {N_("General Settings ..."), UciMenuProc},
657 {N_("Engine #1 Settings ..."), FirstSettingsProc},
658 {N_("Engine #2 Settings ..."), SecondSettingsProc},
659 {N_("Time Control ..."), TimeControlProc},
660 {"----", NothingProc},
661 // {N_("Always Queen"), AlwaysQueenProc},
662 // {N_("Animate Dragging"), AnimateDraggingProc},
663 // {N_("Animate Moving"), AnimateMovingProc},
664 // {N_("Auto Comment"), AutocommProc},
665 // {N_("Auto Flag"), AutoflagProc},
666 // {N_("Auto Flip View"), AutoflipProc},
667 // {N_("Auto Observe"), AutobsProc},
668 // {N_("Auto Raise Board"), AutoraiseProc},
669 // {N_("Auto Save"), AutosaveProc},
670 // {N_("Blindfold"), BlindfoldProc},
671 // {N_("Flash Moves"), FlashMovesProc},
672 // {N_("Get Move List"), GetMoveListProc},
674 // {N_("Highlight Dragging"), HighlightDraggingProc},
676 // {N_("Highlight Last Move"), HighlightLastMoveProc},
677 // {N_("Move Sound"), MoveSoundProc},
678 // {N_("ICS Alarm"), IcsAlarmProc},
679 // {N_("Old Save Style"), OldSaveStyleProc},
680 // {N_("Periodic Updates"), PeriodicUpdatesProc},
681 // {N_("Ponder Next Move"), PonderNextMoveProc},
682 // {N_("Popup Exit Message"), PopupExitMessageProc},
683 // {N_("Popup Move Errors"), PopupMoveErrorsProc},
684 // {N_("Premove"), PremoveProc},
685 // {N_("Quiet Play"), QuietPlayProc},
686 // {N_("Hide Thinking"), HideThinkingProc},
687 // {N_("Test Legality"), TestLegalityProc},
688 // {N_("Show Coords"), ShowCoordsProc},
689 {"----", NothingProc},
690 {N_("Save Settings Now"), SaveSettingsProc},
691 {N_("Save Settings on Exit"), SaveOnExitProc},
696 {N_("File"), fileMenu},
697 {N_("Mode"), modeMenu},
698 {N_("Options"), optionsMenu},
702 #define PIECE_MENU_SIZE 18
703 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
704 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
705 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
706 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
707 N_("Empty square"), N_("Clear board") },
708 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
709 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
710 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
711 N_("Empty square"), N_("Clear board") }
713 /* must be in same order as PieceMenuStrings! */
714 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
715 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
716 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
717 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
718 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
719 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
720 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
721 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
722 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
725 #define DROP_MENU_SIZE 6
726 String dropMenuStrings[DROP_MENU_SIZE] = {
727 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
729 /* must be in same order as PieceMenuStrings! */
730 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
731 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
732 WhiteRook, WhiteQueen
740 DropMenuEnables dmEnables[] = {
749 { XtNborderWidth, 0 },
750 { XtNdefaultDistance, 0 },
754 { XtNborderWidth, 0 },
755 { XtNresizable, (XtArgVal) True },
759 { XtNborderWidth, 0 },
764 XtResource clientResources[] = {
765 { "flashCount", "flashCount", XtRInt, sizeof(int),
766 XtOffset(AppDataPtr, flashCount), XtRImmediate,
767 (XtPointer) FLASH_COUNT },
770 XrmOptionDescRec shellOptions[] = {
771 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
772 { "-flash", "flashCount", XrmoptionNoArg, "3" },
773 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
776 XtActionsRec boardActions[] = {
777 // { "HandleUserMove", HandleUserMove },
778 { "AnimateUserMove", AnimateUserMove },
779 // { "FileNameAction", FileNameAction },
780 { "AskQuestionProc", AskQuestionProc },
781 { "AskQuestionReplyAction", AskQuestionReplyAction },
782 { "PieceMenuPopup", PieceMenuPopup },
783 // { "WhiteClock", WhiteClock },
784 // { "BlackClock", BlackClock },
785 { "Iconify", Iconify },
786 { "LoadSelectedProc", LoadSelectedProc },
787 // { "LoadPositionProc", LoadPositionProc },
788 // { "LoadNextPositionProc", LoadNextPositionProc },
789 // { "LoadPrevPositionProc", LoadPrevPositionProc },
790 // { "ReloadPositionProc", ReloadPositionProc },
791 { "CopyPositionProc", CopyPositionProc },
792 { "PastePositionProc", PastePositionProc },
793 { "CopyGameProc", CopyGameProc },
794 { "PasteGameProc", PasteGameProc },
795 // { "SaveGameProc", SaveGameProc },
796 // { "SavePositionProc", SavePositionProc },
797 { "MailMoveProc", MailMoveProc },
798 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
799 // { "MachineWhiteProc", MachineWhiteProc },
800 // { "MachineBlackProc", MachineBlackProc },
801 // { "AnalysisModeProc", AnalyzeModeProc },
802 // { "AnalyzeFileProc", AnalyzeFileProc },
803 // { "TwoMachinesProc", TwoMachinesProc },
804 // { "IcsClientProc", IcsClientProc },
805 // { "EditGameProc", EditGameProc },
806 // { "EditPositionProc", EditPositionProc },
807 // { "TrainingProc", EditPositionProc },
808 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
809 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
810 { "ShowGameListProc", ShowGameListProc },
811 // { "ShowMoveListProc", HistoryShowProc},
812 // { "EditTagsProc", EditCommentProc },
813 { "EditCommentProc", EditCommentProc },
814 // { "IcsAlarmProc", IcsAlarmProc },
815 { "IcsInputBoxProc", IcsInputBoxProc },
816 // { "AcceptProc", AcceptProc },
817 // { "DeclineProc", DeclineProc },
818 // { "RematchProc", RematchProc },
819 // { "CallFlagProc", CallFlagProc },
820 // { "DrawProc", DrawProc },
821 // { "AdjournProc", AdjournProc },
822 // { "AbortProc", AbortProc },
823 // { "ResignProc", ResignProc },
824 // { "AdjuWhiteProc", AdjuWhiteProc },
825 // { "AdjuBlackProc", AdjuBlackProc },
826 // { "AdjuDrawProc", AdjuDrawProc },
827 { "EnterKeyProc", EnterKeyProc },
828 // { "StopObservingProc", StopObservingProc },
829 // { "StopExaminingProc", StopExaminingProc },
830 // { "BackwardProc", BackwardProc },
831 // { "ForwardProc", ForwardProc },
832 // { "ToStartProc", ToStartProc },
833 // { "ToEndProc", ToEndProc },
834 // { "RevertProc", RevertProc },
835 // { "TruncateGameProc", TruncateGameProc },
836 // { "MoveNowProc", MoveNowProc },
837 // { "RetractMoveProc", RetractMoveProc },
838 // { "AlwaysQueenProc", AlwaysQueenProc },
839 // { "AnimateDraggingProc", AnimateDraggingProc },
840 // { "AnimateMovingProc", AnimateMovingProc },
841 // { "AutoflagProc", AutoflagProc },
842 // { "AutoflipProc", AutoflipProc },
843 // { "AutobsProc", AutobsProc },
844 // { "AutoraiseProc", AutoraiseProc },
845 // { "AutosaveProc", AutosaveProc },
846 // { "BlindfoldProc", BlindfoldProc },
847 // { "FlashMovesProc", FlashMovesProc },
848 // { "FlipViewProc", FlipViewProc },
849 // { "GetMoveListProc", GetMoveListProc },
851 // { "HighlightDraggingProc", HighlightDraggingProc },
853 // { "HighlightLastMoveProc", HighlightLastMoveProc },
854 // { "IcsAlarmProc", IcsAlarmProc },
855 // { "MoveSoundProc", MoveSoundProc },
856 // { "OldSaveStyleProc", OldSaveStyleProc },
857 // { "PeriodicUpdatesProc", PeriodicUpdatesProc },
858 // { "PonderNextMoveProc", PonderNextMoveProc },
859 // { "PopupExitMessageProc", PopupExitMessageProc },
860 // { "PopupMoveErrorsProc", PopupMoveErrorsProc },
861 // { "PremoveProc", PremoveProc },
862 // { "QuietPlayProc", QuietPlayProc },
863 // { "ShowThinkingProc", ShowThinkingProc },
864 // { "HideThinkingProc", HideThinkingProc },
865 // { "TestLegalityProc", TestLegalityProc },
866 { "SaveSettingsProc", SaveSettingsProc },
867 { "SaveOnExitProc", SaveOnExitProc },
868 // { "InfoProc", InfoProc },
869 // { "ManProc", ManProc },
870 // { "HintProc", HintProc },
871 // { "BookProc", BookProc },
872 { "AboutGameProc", AboutGameProc },
873 { "DebugProc", DebugProc },
874 { "NothingProc", NothingProc },
875 { "CommentPopDown", (XtActionProc) CommentPopDown },
876 { "EditCommentPopDown", (XtActionProc) EditCommentPopDown },
877 { "TagsPopDown", (XtActionProc) TagsPopDown },
878 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
879 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
880 // { "FileNamePopDown", (XtActionProc) FileNamePopDown },
881 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
882 { "GameListPopDown", (XtActionProc) GameListPopDown },
883 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
884 // { "HistoryPopDown", (XtActionProc) HistoryPopDown },
885 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
886 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
887 { "ShufflePopDown", (XtActionProc) ShufflePopDown },
888 { "EnginePopDown", (XtActionProc) EnginePopDown },
889 { "UciPopDown", (XtActionProc) UciPopDown },
890 { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
891 { "NewVariantPopDown", (XtActionProc) NewVariantPopDown },
892 { "SettingsPopDown", (XtActionProc) SettingsPopDown },
896 char ICSInputTranslations[] =
897 "<Key>Return: EnterKeyProc() \n";
899 String xboardResources[] = {
900 // "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
901 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
902 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
906 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
907 "magenta", "cyan", "white" };
911 TextColors textColors[(int)NColorClasses];
913 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
915 parse_color(str, which)
919 char *p, buf[100], *d;
922 if (strlen(str) > 99) /* watch bounds on buf */
927 for (i=0; i<which; ++i) {
934 /* Could be looking at something like:
936 .. in which case we want to stop on a comma also */
937 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
941 return -1; /* Use default for empty field */
944 if (which == 2 || isdigit(*p))
947 while (*p && isalpha(*p))
952 for (i=0; i<8; ++i) {
953 if (!StrCaseCmp(buf, cnames[i]))
954 return which? (i+40) : (i+30);
956 if (!StrCaseCmp(buf, "default")) return -1;
958 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
967 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
968 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
973 /* bg and attr are optional */
974 textColors[(int)cc].bg = parse_color(str, 1);
975 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
976 textColors[(int)cc].attr = 0;
982 /* Arrange to catch delete-window events */
983 Atom wm_delete_window;
985 CatchDeleteWindow(Widget w, String procname)
988 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
989 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
990 XtAugmentTranslations(w, XtParseTranslationTable(buf));
996 /* this should raise the board to the top */
997 gtk_window_present(GTK_WINDOW(GUI_Window));
1001 //---------------------------------------------------------------------------------------------------------
1002 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1005 #define CW_USEDEFAULT (1<<31)
1006 #define ICS_TEXT_MENU_SIZE 90
1007 #define SetCurrentDirectory chdir
1008 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1012 // these two must some day move to frontend.h, when they are implemented
1013 Boolean MoveHistoryIsUp();
1014 Boolean GameListIsUp();
1016 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1019 // front-end part of option handling
1021 // [HGM] This platform-dependent table provides the location for storing the color info
1022 extern char *crWhite, * crBlack;
1026 &appData.whitePieceColor,
1027 &appData.blackPieceColor,
1028 &appData.lightSquareColor,
1029 &appData.darkSquareColor,
1030 &appData.highlightSquareColor,
1031 &appData.premoveHighlightColor,
1044 ParseFont(char *name, int number)
1045 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1047 case 0: // CLOCK_FONT
1048 appData.clockFont = strdup(name);
1050 case 1: // MESSAGE_FONT
1051 appData.font = strdup(name);
1053 case 2: // COORD_FONT
1054 appData.coordFont = strdup(name);
1063 { // only 2 fonts currently
1064 appData.clockFont = CLOCK_FONT_NAME;
1065 appData.coordFont = COORD_FONT_NAME;
1066 appData.font = DEFAULT_FONT_NAME;
1071 { // no-op, until we identify the code for this already in XBoard and move it here
1075 ParseColor(int n, char *name)
1076 { // in XBoard, just copy the color-name string
1077 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1081 ParseTextAttribs(ColorClass cc, char *s)
1083 (&appData.colorShout)[cc] = strdup(s);
1087 ParseBoardSize(void *addr, char *name)
1089 appData.boardSize = strdup(name);
1094 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1098 SetCommPortDefaults()
1099 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1102 // [HGM] args: these three cases taken out to stay in front-end
1104 SaveFontArg(FILE *f, ArgDescriptor *ad)
1107 switch((int)ad->argLoc) {
1108 case 0: // CLOCK_FONT
1109 name = appData.clockFont;
1111 case 1: // MESSAGE_FONT
1112 name = appData.font;
1114 case 2: // COORD_FONT
1115 name = appData.coordFont;
1120 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, name);
1125 { // nothing to do, as the sounds are at all times represented by their text-string names already
1129 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1130 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1131 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)ad->argLoc]);
1135 SaveColor(FILE *f, ArgDescriptor *ad)
1136 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1137 if(colorVariable[(int)ad->argLoc])
1138 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)ad->argLoc]);
1142 SaveBoardSize(FILE *f, char *name, void *addr)
1143 { // wrapper to shield back-end from BoardSize & sizeInfo
1144 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1148 ParseCommPortSettings(char *s)
1149 { // no such option in XBoard (yet)
1152 extern Widget engineOutputShell;
1153 extern Widget tagsShell, editTagsShell;
1155 GetActualPlacement(Widget wg, WindowPlacement *wp)
1165 XtSetArg(args[i], XtNx, &x); i++;
1166 XtSetArg(args[i], XtNy, &y); i++;
1167 XtSetArg(args[i], XtNwidth, &w); i++;
1168 XtSetArg(args[i], XtNheight, &h); i++;
1169 XtGetValues(wg, args, i);
1178 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1179 // In XBoard this will have to wait until awareness of window parameters is implemented
1180 GetActualPlacement(shellWidget, &wpMain);
1181 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1182 if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1183 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1184 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1185 if(commentShell) GetActualPlacement(commentShell, &wpComment);
1186 else GetActualPlacement(editShell, &wpComment);
1187 if(tagsShell) GetActualPlacement(tagsShell, &wpTags);
1188 else GetActualPlacement(editTagsShell, &wpTags);
1192 PrintCommPortSettings(FILE *f, char *name)
1193 { // This option does not exist in XBoard
1197 MySearchPath(char *installDir, char *name, char *fullname)
1198 { // just append installDir and name. Perhaps ExpandPath should be used here?
1199 name = ExpandPathName(name);
1200 if(name && name[0] == '/') strcpy(fullname, name); else {
1201 sprintf(fullname, "%s%c%s", installDir, '/', name);
1207 MyGetFullPathName(char *name, char *fullname)
1208 { // should use ExpandPath?
1209 name = ExpandPathName(name);
1210 strcpy(fullname, name);
1215 EnsureOnScreen(int *x, int *y, int minX, int minY)
1222 { // [HGM] args: allows testing if main window is realized from back-end
1223 return xBoardWindow != 0;
1227 PopUpStartupDialog()
1228 { // start menu not implemented in XBoard
1231 ConvertToLine(int argc, char **argv)
1233 static char line[128*1024], buf[1024];
1237 for(i=1; i<argc; i++) {
1238 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1239 && argv[i][0] != '{' )
1240 sprintf(buf, "{%s} ", argv[i]);
1241 else sprintf(buf, "%s ", argv[i]);
1244 line[strlen(line)-1] = NULLCHAR;
1248 //--------------------------------------------------------------------------------------------
1251 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1254 #define BoardSize int
1255 void InitDrawingSizes(BoardSize boardSize, int flags)
1256 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1257 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1259 XtGeometryResult gres;
1262 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1263 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1265 timerWidth = (boardWidth - sep) / 2;
1267 if (appData.titleInWindow)
1272 w = boardWidth - 2*bor;
1276 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1280 if(!formWidget) return;
1283 * Inhibit shell resizing.
1286 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1289 for(i=0; i<4; i++) {
1291 for(p=0; p<=(int)WhiteKing; p++)
1292 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1293 if(gameInfo.variant == VariantShogi) {
1294 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1295 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1296 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1297 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1298 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1301 if(gameInfo.variant == VariantGothic) {
1302 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1306 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1307 for(p=0; p<=(int)WhiteKing; p++)
1308 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1309 if(gameInfo.variant == VariantShogi) {
1310 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1311 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1312 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1313 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1314 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1317 if(gameInfo.variant == VariantGothic) {
1318 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1324 for(i=0; i<2; i++) {
1326 for(p=0; p<=(int)WhiteKing; p++)
1327 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1328 if(gameInfo.variant == VariantShogi) {
1329 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1330 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1331 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1332 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1333 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1336 if(gameInfo.variant == VariantGothic) {
1337 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1348 void EscapeExpand(char *p, char *q)
1349 { // [HGM] initstring: routine to shape up string arguments
1350 while(*p++ = *q++) if(p[-1] == '\\')
1352 case 'n': p[-1] = '\n'; break;
1353 case 'r': p[-1] = '\r'; break;
1354 case 't': p[-1] = '\t'; break;
1355 case '\\': p[-1] = '\\'; break;
1356 case 0: *p = 0; return;
1357 default: p[-1] = q[-1]; break;
1366 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1367 XSetWindowAttributes window_attributes;
1369 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1370 XrmValue vFrom, vTo;
1371 XtGeometryResult gres;
1374 int forceMono = False;
1376 //define INDIRECTION
1379 // [HGM] before anything else, expand any indirection files amongst options
1380 char *argvCopy[1000]; // 1000 seems enough
1381 char newArgs[10000]; // holds actual characters
1384 srandom(time(0)); // [HGM] book: make random truly random
1387 for(i=0; i<argc; i++) {
1388 if(j >= 1000-2) { printf(_("too many arguments\n")); exit(-1); }
1389 //fprintf(stderr, "arg %s\n", argv[i]);
1390 if(argv[i][0] != '@') argvCopy[j++] = argv[i]; else {
1392 FILE *f = fopen(argv[i]+1, "rb");
1393 if(f == NULL) { fprintf(stderr, _("ignore %s\n"), argv[i]); continue; } // do not expand non-existing
1394 argvCopy[j++] = newArgs + k; // get ready for first argument from file
1395 while((c = fgetc(f)) != EOF) { // each line of file inserts 1 argument in the list
1397 if(j >= 1000-2) { printf(_("too many arguments\n")); exit(-1); }
1398 newArgs[k++] = 0; // terminate current arg
1399 if(k >= 10000-1) { printf(_("too long arguments\n")); exit(-1); }
1400 argvCopy[j++] = newArgs + k; // get ready for next
1402 if(k >= 10000-1) { printf(_("too long arguments\n")); exit(-1); }
1416 setbuf(stdout, NULL);
1417 setbuf(stderr, NULL);
1420 programName = strrchr(argv[0], '/');
1421 if (programName == NULL)
1422 programName = argv[0];
1427 XtSetLanguageProc(NULL, NULL, NULL);
1428 bindtextdomain(PACKAGE, LOCALEDIR);
1429 textdomain(PACKAGE);
1433 XtAppInitialize(&appContext, "XBoard", shellOptions,
1434 XtNumber(shellOptions),
1435 &argc, argv, xboardResources, NULL, 0);
1438 gtk_init (&argc, &argv);
1440 /* parse glade file to build widgets */
1442 builder = gtk_builder_new ();
1443 gtk_builder_add_from_file (builder, "gtk-interface.xml", NULL);
1445 /* test if everything worked ok */
1447 GUI_Window = GTK_WIDGET (gtk_builder_get_object (builder, "MainWindow"));
1448 if(!GUI_Window) printf("Error: gtk_builder didn't work!\n");
1450 GUI_Aspect = GTK_WIDGET (gtk_builder_get_object (builder, "Aspectframe"));
1451 if(!GUI_Aspect) printf("Error: gtk_builder didn't work!\n");
1453 GUI_History = GTK_WIDGET (gtk_builder_get_object (builder, "MoveHistory"));
1454 if(!GUI_History) printf("Error: gtk_builder didn't work!\n");
1456 GUI_Menubar = GTK_WIDGET (gtk_builder_get_object (builder, "MenuBar"));
1457 if(!GUI_Menubar) printf("Error: gtk_builder didn't work!\n");
1458 GUI_Timer = GTK_WIDGET (gtk_builder_get_object (builder, "Timer"));
1459 if(!GUI_Timer) printf("Error: gtk_builder didn't work!\n");
1460 GUI_Buttonbar = GTK_WIDGET (gtk_builder_get_object (builder, "ButtonBar"));
1461 if(!GUI_Buttonbar) printf("Error: gtk_builder didn't work!\n");
1462 GUI_Board = GTK_WIDGET (gtk_builder_get_object (builder, "Board"));
1463 if(!GUI_Board) printf("Error: gtk_builder didn't work!\n");
1465 GUI_Whiteclock = GTK_WIDGET (gtk_builder_get_object (builder, "WhiteClock"));
1466 if(!GUI_Whiteclock) printf("Error: gtk_builder didn't work!\n");
1468 GUI_Blackclock = GTK_WIDGET (gtk_builder_get_object (builder, "BlackClock"));
1469 if(!GUI_Blackclock) printf("Error: gtk_builder didn't work!\n");
1471 LIST_MoveHistory = GTK_LIST_STORE (gtk_builder_get_object (builder, "MoveHistoryStore"));
1472 if(!LIST_MoveHistory) printf("Error: gtk_builder didn't work!\n");
1474 /* EditTags window */
1475 GUI_EditTags = GTK_WIDGET (gtk_builder_get_object (builder, "EditTags"));
1476 if(!GUI_EditTags) printf("Error: gtk_builder didn't work!\n");
1478 GUI_EditTagsTextArea = GTK_WIDGET (gtk_builder_get_object (builder, "EditTagsTextArea"));
1479 if(!GUI_EditTagsTextArea) printf("Error: gtk_builder didn't work!\n");
1482 gtk_builder_connect_signals (builder, NULL);
1484 // don't unref the builder, since we use it to get references to widgets
1485 // g_object_unref (G_OBJECT (builder));
1487 /* end parse glade file */
1489 appData.boardSize = "";
1490 InitAppData(ConvertToLine(argc, argv));
1494 fprintf(stderr, _("%s: unrecognized argument %s\n"),
1495 programName, argv[1]);
1497 fprintf(stderr, "Recognized options:\n");
1498 for(i = 0; i < XtNumber(shellOptions); i++)
1500 /* print first column */
1501 j = fprintf(stderr, " %s%s", shellOptions[i].option,
1502 (shellOptions[i].argKind == XrmoptionSepArg
1504 /* print second column and end line */
1505 if (++i < XtNumber(shellOptions))
1507 fprintf(stderr, "%*c%s%s\n", 40 - j, ' ',
1508 shellOptions[i].option,
1509 (shellOptions[i].argKind == XrmoptionSepArg
1514 fprintf(stderr, "\n");
1521 if (p == NULL) p = "/tmp";
1522 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1523 gameCopyFilename = (char*) malloc(i);
1524 gamePasteFilename = (char*) malloc(i);
1525 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1526 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1528 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1529 clientResources, XtNumber(clientResources),
1532 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1533 static char buf[MSG_SIZ];
1534 EscapeExpand(buf, appData.initString);
1535 appData.initString = strdup(buf);
1536 EscapeExpand(buf, appData.secondInitString);
1537 appData.secondInitString = strdup(buf);
1538 EscapeExpand(buf, appData.firstComputerString);
1539 appData.firstComputerString = strdup(buf);
1540 EscapeExpand(buf, appData.secondComputerString);
1541 appData.secondComputerString = strdup(buf);
1544 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1547 if (chdir(chessDir) != 0) {
1548 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1554 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1555 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1556 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1557 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1560 setbuf(debugFP, NULL);
1563 /* [HGM,HR] make sure board size is acceptable */
1564 if(appData.NrFiles > BOARD_FILES ||
1565 appData.NrRanks > BOARD_RANKS )
1566 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1569 /* This feature does not work; animation needs a rewrite */
1570 appData.highlightDragging = FALSE;
1574 xDisplay = XtDisplay(shellWidget);
1575 xScreen = DefaultScreen(xDisplay);
1576 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1578 gameInfo.variant = StringToVariant(appData.variant);
1579 InitPosition(FALSE);
1581 /* calc board size */
1582 if (isdigit(appData.boardSize[0]))
1584 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1585 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1586 &fontPxlSize, &smallLayout, &tinyLayout);
1589 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1590 programName, appData.boardSize);
1595 /* Find some defaults; use the nearest known size */
1596 SizeDefaults *szd, *nearest;
1597 int distance = 99999;
1598 nearest = szd = sizeDefaults;
1599 while (szd->name != NULL)
1601 if (abs(szd->squareSize - squareSize) < distance)
1604 distance = abs(szd->squareSize - squareSize);
1605 if (distance == 0) break;
1609 if (i < 2) lineGap = nearest->lineGap;
1610 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1611 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1612 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1613 if (i < 6) smallLayout = nearest->smallLayout;
1614 if (i < 7) tinyLayout = nearest->tinyLayout;
1619 SizeDefaults *szd = sizeDefaults;
1620 if (*appData.boardSize == NULLCHAR)
1622 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize
1623 || DisplayHeight(xDisplay, xScreen) < szd->minScreenSize)
1627 if (szd->name == NULL) szd--;
1628 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1632 while (szd->name != NULL
1633 && StrCaseCmp(szd->name, appData.boardSize) != 0)
1635 if (szd->name == NULL)
1637 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1638 programName, appData.boardSize);
1642 squareSize = szd->squareSize;
1643 lineGap = szd->lineGap;
1644 clockFontPxlSize = szd->clockFontPxlSize;
1645 coordFontPxlSize = szd->coordFontPxlSize;
1646 fontPxlSize = szd->fontPxlSize;
1647 smallLayout = szd->smallLayout;
1648 tinyLayout = szd->tinyLayout;
1650 /* end figuring out what size to use */
1652 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1653 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1656 * Determine what fonts to use.
1658 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1659 clockFontID = XLoadFont(xDisplay, appData.clockFont);
1660 clockFontStruct = XQueryFont(xDisplay, clockFontID);
1661 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1662 coordFontID = XLoadFont(xDisplay, appData.coordFont);
1663 coordFontStruct = XQueryFont(xDisplay, coordFontID);
1664 appData.font = FindFont(appData.font, fontPxlSize);
1665 countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
1666 countFontStruct = XQueryFont(xDisplay, countFontID);
1667 // appData.font = FindFont(appData.font, fontPxlSize);
1669 xdb = XtDatabase(xDisplay);
1670 XrmPutStringResource(&xdb, "*font", appData.font);
1673 * Detect if there are not enough colors available and adapt.
1675 if (DefaultDepth(xDisplay, xScreen) <= 2) {
1676 appData.monoMode = True;
1679 if (!appData.monoMode) {
1680 vFrom.addr = (caddr_t) appData.lightSquareColor;
1681 vFrom.size = strlen(appData.lightSquareColor);
1682 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1683 if (vTo.addr == NULL) {
1684 appData.monoMode = True;
1687 lightSquareColor = *(Pixel *) vTo.addr;
1690 if (!appData.monoMode) {
1691 vFrom.addr = (caddr_t) appData.darkSquareColor;
1692 vFrom.size = strlen(appData.darkSquareColor);
1693 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1694 if (vTo.addr == NULL) {
1695 appData.monoMode = True;
1698 darkSquareColor = *(Pixel *) vTo.addr;
1701 if (!appData.monoMode) {
1702 vFrom.addr = (caddr_t) appData.whitePieceColor;
1703 vFrom.size = strlen(appData.whitePieceColor);
1704 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1705 if (vTo.addr == NULL) {
1706 appData.monoMode = True;
1709 whitePieceColor = *(Pixel *) vTo.addr;
1712 if (!appData.monoMode) {
1713 vFrom.addr = (caddr_t) appData.blackPieceColor;
1714 vFrom.size = strlen(appData.blackPieceColor);
1715 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1716 if (vTo.addr == NULL) {
1717 appData.monoMode = True;
1720 blackPieceColor = *(Pixel *) vTo.addr;
1724 if (!appData.monoMode) {
1725 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1726 vFrom.size = strlen(appData.highlightSquareColor);
1727 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1728 if (vTo.addr == NULL) {
1729 appData.monoMode = True;
1732 highlightSquareColor = *(Pixel *) vTo.addr;
1736 if (!appData.monoMode) {
1737 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1738 vFrom.size = strlen(appData.premoveHighlightColor);
1739 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1740 if (vTo.addr == NULL) {
1741 appData.monoMode = True;
1744 premoveHighlightColor = *(Pixel *) vTo.addr;
1749 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1752 if (appData.bitmapDirectory == NULL ||
1753 appData.bitmapDirectory[0] == NULLCHAR)
1754 appData.bitmapDirectory = DEF_BITMAP_DIR;
1757 if (appData.lowTimeWarning && !appData.monoMode) {
1758 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1759 vFrom.size = strlen(appData.lowTimeWarningColor);
1760 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1761 if (vTo.addr == NULL)
1762 appData.monoMode = True;
1764 lowTimeWarningColor = *(Pixel *) vTo.addr;
1767 if (appData.monoMode && appData.debugMode) {
1768 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1769 (unsigned long) XWhitePixel(xDisplay, xScreen),
1770 (unsigned long) XBlackPixel(xDisplay, xScreen));
1773 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1774 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1775 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1776 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1777 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1778 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1779 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1780 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1781 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1782 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1784 if (appData.colorize) {
1786 _("%s: can't parse color names; disabling colorization\n"),
1789 appData.colorize = FALSE;
1791 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
1792 textColors[ColorNone].attr = 0;
1794 // XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1800 layoutName = "tinyLayout";
1801 } else if (smallLayout) {
1802 layoutName = "smallLayout";
1804 layoutName = "normalLayout";
1807 if (appData.titleInWindow) {
1808 /* todo check what this appdata does */
1811 if (appData.showButtonBar) {
1812 /* TODO hide button bar if requested */
1816 if (appData.titleInWindow)
1821 if (appData.showButtonBar)
1828 if (appData.showButtonBar)
1838 /* set some checkboxes in the menu according to appData */
1840 if (appData.alwaysPromoteToQueen)
1841 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Always Queen")),TRUE);
1843 if (appData.animateDragging)
1844 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Animate Dragging")),TRUE);
1846 if (appData.animate)
1847 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Animate Moving")),TRUE);
1849 if (appData.autoComment)
1850 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Comment")),TRUE);
1852 if (appData.autoCallFlag)
1853 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Flag")),TRUE);
1855 if (appData.autoFlipView)
1856 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Flip View")),TRUE);
1858 if (appData.autoObserve)
1859 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Observe")),TRUE);
1861 if (appData.autoRaiseBoard)
1862 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Raise Board")),TRUE);
1864 if (appData.autoSaveGames)
1865 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Save")),TRUE);
1867 if (appData.saveGameFile[0] != NULLCHAR)
1869 /* Can't turn this off from menu */
1870 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Save")),TRUE);
1871 gtk_action_set_sensitive(GTK_ACTION (gtk_builder_get_object (builder, "menuOptions.Auto Save")),FALSE);
1874 if (appData.blindfold)
1875 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Blindfold")),TRUE);
1877 if (appData.flashCount > 0)
1878 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Flash Moves")),TRUE);
1880 if (appData.getMoveList)
1881 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Get Move List")),TRUE);
1884 if (appData.highlightDragging)
1885 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Highlight Dragging")),TRUE);
1888 if (appData.highlightLastMove)
1889 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Highlight Last Move")),TRUE);
1891 if (appData.icsAlarm)
1892 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.ICS Alarm")),TRUE);
1894 if (appData.ringBellAfterMoves)
1895 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Move Sound")),TRUE);
1897 if (appData.oldSaveStyle)
1898 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Old Save Style")),TRUE);
1900 if (appData.periodicUpdates)
1901 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Periodic Updates")),TRUE);
1903 if (appData.ponderNextMove)
1904 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Ponder Next Move")),TRUE);
1906 if (appData.popupExitMessage)
1907 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Popup Exit Message")),TRUE);
1909 if (appData.popupMoveErrors)
1910 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Popup Move Errors")),TRUE);
1912 if (appData.premove)
1913 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Premove")),TRUE);
1915 if (appData.quietPlay)
1916 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Quit Play")),TRUE);
1918 if (appData.showCoords)
1919 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Show Coords")),TRUE);
1921 if (appData.showThinking)
1922 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Hide Thinking")),TRUE);
1924 if (appData.testLegality)
1925 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Test Legality")),TRUE);
1928 // if (saveSettingsOnExit) {
1929 // XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
1934 /* end setting check boxes */
1936 /* load square colors */
1937 SVGLightSquare = load_pixbuf("svg/LightSquare.svg",squareSize);
1938 SVGDarkSquare = load_pixbuf("svg/DarkSquare.svg",squareSize);
1939 SVGNeutralSquare = load_pixbuf("svg/NeutralSquare.svg",squareSize);
1941 /* use two icons to indicate if it is white's or black's turn */
1942 WhiteIcon = load_pixbuf("svg/icon_white.svg",0);
1943 BlackIcon = load_pixbuf("svg/icon_black.svg",0);
1944 WindowIcon = WhiteIcon;
1945 gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
1948 /* realize window */
1949 gtk_widget_show (GUI_Window);
1951 /* recalc boardsize */
1956 if (appData.animate || appData.animateDragging)
1961 if (errorExitStatus == -1) {
1962 if (appData.icsActive) {
1963 /* We now wait until we see "login:" from the ICS before
1964 sending the logon script (problems with timestamp otherwise) */
1965 /*ICSInitScript();*/
1966 if (appData.icsInputBox) ICSInputBoxPopUp();
1970 signal(SIGWINCH, TermSizeSigHandler);
1972 signal(SIGINT, IntSigHandler);
1973 signal(SIGTERM, IntSigHandler);
1974 if (*appData.cmailGameName != NULLCHAR) {
1975 signal(SIGUSR1, CmailSigHandler);
1978 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1982 * Create a cursor for the board widget.
1983 * (This needs to be called after the window has been created to have access to board-window)
1986 BoardCursor = gdk_cursor_new(GDK_HAND2);
1987 gdk_window_set_cursor(GUI_Board->window, BoardCursor);
1988 gdk_cursor_destroy(BoardCursor);
1993 if (appData.debugMode) fclose(debugFP); // [DM] debug
2000 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2001 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2003 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2004 unlink(gameCopyFilename);
2005 unlink(gamePasteFilename);
2008 RETSIGTYPE TermSizeSigHandler(int sig)
2021 CmailSigHandler(sig)
2027 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2029 /* Activate call-back function CmailSigHandlerCallBack() */
2030 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2032 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2036 CmailSigHandlerCallBack(isr, closure, message, count, error)
2044 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2046 /**** end signal code ****/
2056 f = fopen(appData.icsLogon, "r");
2062 strcat(buf, appData.icsLogon);
2063 f = fopen(buf, "r");
2067 ProcessICSInitScript(f);
2074 EditCommentPopDown();
2084 if (!menuBarWidget) return;
2085 w = XtNameToWidget(menuBarWidget, "menuStep.Revert");
2087 DisplayError("menuStep.Revert", 0);
2089 XtSetSensitive(w, !grey);
2094 SetMenuEnables(enab)
2099 if (!builder) return;
2100 while (enab->name != NULL) {
2101 o = gtk_builder_get_object(builder, enab->name);
2102 if(GTK_IS_WIDGET(o))
2103 gtk_widget_set_sensitive(GTK_WIDGET (o),enab->value);
2106 if(GTK_IS_ACTION(o))
2107 gtk_action_set_sensitive(GTK_ACTION (o),enab->value);
2109 DisplayError(enab->name, 0);
2117 SetMenuEnables(icsEnables);
2120 if (appData.zippyPlay && !appData.noChessProgram) /* [DM] icsEngineAnalyze */
2121 {}; // XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2128 SetMenuEnables(ncpEnables);
2134 SetMenuEnables(gnuEnables);
2140 SetMenuEnables(cmailEnables);
2146 SetMenuEnables(trainingOnEnables);
2147 if (appData.showButtonBar) {
2148 // XtSetSensitive(buttonBarWidget, False);
2154 SetTrainingModeOff()
2156 SetMenuEnables(trainingOffEnables);
2157 if (appData.showButtonBar) {
2158 // XtSetSensitive(buttonBarWidget, True);
2163 SetUserThinkingEnables()
2165 if (appData.noChessProgram) return;
2166 SetMenuEnables(userThinkingEnables);
2170 SetMachineThinkingEnables()
2172 if (appData.noChessProgram) return;
2173 SetMenuEnables(machineThinkingEnables);
2175 case MachinePlaysBlack:
2176 case MachinePlaysWhite:
2177 case TwoMachinesPlay:
2178 // XtSetSensitive(XtNameToWidget(menuBarWidget,
2179 // ModeToWidgetName(gameMode)), True);
2186 #define Abs(n) ((n)<0 ? -(n) : (n))
2189 * Find a font that matches "pattern" that is as close as
2190 * possible to the targetPxlSize. Prefer fonts that are k
2191 * pixels smaller to fonts that are k pixels larger. The
2192 * pattern must be in the X Consortium standard format,
2193 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2194 * The return value should be freed with XtFree when no
2197 char *FindFont(pattern, targetPxlSize)
2201 char **fonts, *p, *best, *scalable, *scalableTail;
2202 int i, j, nfonts, minerr, err, pxlSize;
2205 char **missing_list;
2207 char *def_string, *base_fnt_lst, strInt[3];
2209 XFontStruct **fnt_list;
2211 base_fnt_lst = calloc(1, strlen(pattern) + 3);
2212 sprintf(strInt, "%d", targetPxlSize);
2213 p = strstr(pattern, "--");
2214 strncpy(base_fnt_lst, pattern, p - pattern + 2);
2215 strcat(base_fnt_lst, strInt);
2216 strcat(base_fnt_lst, strchr(p + 2, '-'));
2218 if ((fntSet = XCreateFontSet(xDisplay,
2222 &def_string)) == NULL) {
2224 fprintf(stderr, _("Unable to create font set.\n"));
2228 nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
2230 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2232 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2233 programName, pattern);
2241 for (i=0; i<nfonts; i++) {
2244 if (*p != '-') continue;
2246 if (*p == NULLCHAR) break;
2247 if (*p++ == '-') j++;
2249 if (j < 7) continue;
2252 scalable = fonts[i];
2255 err = pxlSize - targetPxlSize;
2256 if (Abs(err) < Abs(minerr) ||
2257 (minerr > 0 && err < 0 && -err == minerr)) {
2263 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2264 /* If the error is too big and there is a scalable font,
2265 use the scalable font. */
2266 int headlen = scalableTail - scalable;
2267 p = (char *) XtMalloc(strlen(scalable) + 10);
2268 while (isdigit(*scalableTail)) scalableTail++;
2269 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2271 p = (char *) XtMalloc(strlen(best) + 1);
2274 if (appData.debugMode) {
2275 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
2276 pattern, targetPxlSize, p);
2279 if (missing_count > 0)
2280 XFreeStringList(missing_list);
2281 XFreeFontSet(xDisplay, fntSet);
2283 XFreeFontNames(fonts);
2290 /* 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*/
2292 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2293 | GCBackground | GCFunction | GCPlaneMask;
2294 XGCValues gc_values;
2297 gc_values.plane_mask = AllPlanes;
2298 gc_values.line_width = lineGap;
2299 gc_values.line_style = LineSolid;
2300 gc_values.function = GXcopy;
2302 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
2303 gc_values.background = XWhitePixel(xDisplay, xScreen);
2304 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
2305 XSetFont(xDisplay, coordGC, coordFontID);
2307 if (appData.monoMode) {
2308 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
2309 gc_values.background = XBlackPixel(xDisplay, xScreen);
2310 lightSquareGC = wbPieceGC
2311 = XtGetGC(shellWidget, value_mask, &gc_values);
2313 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
2314 gc_values.background = XWhitePixel(xDisplay, xScreen);
2315 darkSquareGC = bwPieceGC
2316 = XtGetGC(shellWidget, value_mask, &gc_values);
2318 if (DefaultDepth(xDisplay, xScreen) == 1) {
2319 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2320 gc_values.function = GXcopyInverted;
2321 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
2322 gc_values.function = GXcopy;
2323 if (XBlackPixel(xDisplay, xScreen) == 1) {
2324 bwPieceGC = darkSquareGC;
2325 wbPieceGC = copyInvertedGC;
2327 bwPieceGC = copyInvertedGC;
2328 wbPieceGC = lightSquareGC;
2332 gc_values.foreground = lightSquareColor;
2333 gc_values.background = darkSquareColor;
2334 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
2336 gc_values.foreground = darkSquareColor;
2337 gc_values.background = lightSquareColor;
2338 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
2340 gc_values.foreground = jailSquareColor;
2341 gc_values.background = jailSquareColor;
2342 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
2344 gc_values.foreground = whitePieceColor;
2345 gc_values.background = darkSquareColor;
2346 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2348 gc_values.foreground = whitePieceColor;
2349 gc_values.background = lightSquareColor;
2350 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2352 gc_values.foreground = whitePieceColor;
2353 gc_values.background = jailSquareColor;
2354 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2356 gc_values.foreground = blackPieceColor;
2357 gc_values.background = darkSquareColor;
2358 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2360 gc_values.foreground = blackPieceColor;
2361 gc_values.background = lightSquareColor;
2362 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2364 gc_values.foreground = blackPieceColor;
2365 gc_values.background = jailSquareColor;
2366 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2375 for(i=0;i<MAXPIECES;i++)
2379 g_free(SVGpieces[i]);
2386 SVGLightSquare = load_pixbuf("svg/LightSquare.svg",squareSize);
2387 SVGDarkSquare = load_pixbuf("svg/DarkSquare.svg",squareSize);
2388 SVGNeutralSquare = load_pixbuf("svg/NeutralSquare.svg",squareSize);
2391 /* get some defaults going */
2392 for(i=WhitePawn; i<DemotePiece+1; i++)
2393 SVGpieces[i] = load_pixbuf("svg/NeutralSquare.svg",squareSize);
2395 SVGpieces[WhitePawn] = load_pixbuf("svg/WhitePawn.svg",squareSize);
2396 SVGpieces[WhiteKnight] = load_pixbuf("svg/WhiteKnight.svg",squareSize);
2397 SVGpieces[WhiteBishop] = load_pixbuf("svg/WhiteBishop.svg",squareSize);
2398 SVGpieces[WhiteRook] = load_pixbuf("svg/WhiteRook.svg",squareSize);
2399 SVGpieces[WhiteQueen] = load_pixbuf("svg/WhiteQueen.svg",squareSize);
2400 SVGpieces[WhiteKing] = load_pixbuf("svg/WhiteKing.svg",squareSize);
2402 SVGpieces[BlackPawn] = load_pixbuf("svg/BlackPawn.svg",squareSize);
2403 SVGpieces[BlackKnight] = load_pixbuf("svg/BlackKnight.svg",squareSize);
2404 SVGpieces[BlackBishop] = load_pixbuf("svg/BlackBishop.svg",squareSize);
2405 SVGpieces[BlackRook] = load_pixbuf("svg/BlackRook.svg",squareSize);
2406 SVGpieces[BlackQueen] = load_pixbuf("svg/BlackQueen.svg",squareSize);
2407 SVGpieces[BlackKing] = load_pixbuf("svg/BlackKing.svg",squareSize);
2413 static void MenuBarSelect(w, addr, index)
2418 XtActionProc proc = (XtActionProc) addr;
2420 (proc)(NULL, NULL, NULL, NULL);
2423 void CreateMenuBarPopup(parent, name, mb)
2433 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2436 XtSetArg(args[j], XtNleftMargin, 20); j++;
2437 XtSetArg(args[j], XtNrightMargin, 20); j++;
2439 while (mi->string != NULL) {
2440 if (strcmp(mi->string, "----") == 0) {
2441 entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
2444 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
2445 entry = XtCreateManagedWidget(mi->string, smeBSBObjectClass,
2447 XtAddCallback(entry, XtNcallback,
2448 (XtCallbackProc) MenuBarSelect,
2449 (caddr_t) mi->proc);
2455 Widget CreateMenuBar(mb)
2459 Widget anchor, menuBar;
2461 char menuName[MSG_SIZ];
2464 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
2465 XtSetArg(args[j], XtNvSpace, 0); j++;
2466 XtSetArg(args[j], XtNborderWidth, 0); j++;
2467 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
2468 formWidget, args, j);
2470 while (mb->name != NULL) {
2471 strcpy(menuName, "menu");
2472 strcat(menuName, mb->name);
2474 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
2477 shortName[0] = _(mb->name)[0];
2478 shortName[1] = NULLCHAR;
2479 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
2482 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
2485 XtSetArg(args[j], XtNborderWidth, 0); j++;
2486 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
2488 CreateMenuBarPopup(menuBar, menuName, mb);
2496 CreatePieceMenu(name, color)
2503 ChessSquare selection;
2505 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2506 boardWidget, args, 0);
2508 for (i = 0; i < PIECE_MENU_SIZE; i++) {
2509 String item = pieceMenuStrings[color][i];
2511 if (strcmp(item, "----") == 0) {
2512 entry = XtCreateManagedWidget(item, smeLineObjectClass,
2515 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2516 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2518 selection = pieceMenuTranslation[color][i];
2519 XtAddCallback(entry, XtNcallback,
2520 (XtCallbackProc) PieceMenuSelect,
2521 (caddr_t) selection);
2522 if (selection == WhitePawn || selection == BlackPawn) {
2523 XtSetArg(args[0], XtNpopupOnEntry, entry);
2524 XtSetValues(menu, args, 1);
2537 ChessSquare selection;
2539 // whitePieceMenu = CreatePieceMenu("menuW", 0);
2540 // blackPieceMenu = CreatePieceMenu("menuB", 1);
2542 // XtRegisterGrabAction(PieceMenuPopup, True,
2543 // (unsigned)(ButtonPressMask|ButtonReleaseMask),
2544 // GrabModeAsync, GrabModeAsync);
2546 // XtSetArg(args[0], XtNlabel, _("Drop"));
2547 // dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
2548 // boardWidget, args, 1);
2549 // for (i = 0; i < DROP_MENU_SIZE; i++) {
2550 // String item = dropMenuStrings[i];
2552 // if (strcmp(item, "----") == 0) {
2553 // entry = XtCreateManagedWidget(item, smeLineObjectClass,
2554 // dropMenu, NULL, 0);
2556 // XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2557 // entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2558 // dropMenu, args, 1);
2559 // selection = dropMenuTranslation[i];
2560 // XtAddCallback(entry, XtNcallback,
2561 // (XtCallbackProc) DropMenuSelect,
2562 // (caddr_t) selection);
2567 void SetupDropMenu()
2575 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2576 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2577 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2578 dmEnables[i].piece);
2579 XtSetSensitive(entry, p != NULL || !appData.testLegality
2580 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2581 && !appData.icsActive));
2583 while (p && *p++ == dmEnables[i].piece) count++;
2584 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
2586 XtSetArg(args[j], XtNlabel, label); j++;
2587 XtSetValues(entry, args, j);
2591 void PieceMenuPopup(w, event, params, num_params)
2595 Cardinal *num_params;
2598 if (event->type != ButtonPress) return;
2599 if (errorUp) ErrorPopDown();
2603 whichMenu = params[0];
2605 case IcsPlayingWhite:
2606 case IcsPlayingBlack:
2608 case MachinePlaysWhite:
2609 case MachinePlaysBlack:
2610 if (appData.testLegality &&
2611 gameInfo.variant != VariantBughouse &&
2612 gameInfo.variant != VariantCrazyhouse) return;
2614 whichMenu = "menuD";
2620 if (((pmFromX = EventToSquare(event->xbutton.x, BOARD_WIDTH)) < 0) ||
2621 ((pmFromY = EventToSquare(event->xbutton.y, BOARD_HEIGHT)) < 0)) {
2622 pmFromX = pmFromY = -1;
2626 pmFromX = BOARD_WIDTH - 1 - pmFromX;
2628 pmFromY = BOARD_HEIGHT - 1 - pmFromY;
2630 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
2633 static void PieceMenuSelect(w, piece, junk)
2638 if (pmFromX < 0 || pmFromY < 0) return;
2639 EditPositionMenuEvent(piece, pmFromX, pmFromY);
2642 static void DropMenuSelect(w, piece, junk)
2647 if (pmFromX < 0 || pmFromY < 0) return;
2648 DropMenuEvent(piece, pmFromX, pmFromY);
2652 * If the user selects on a border boundary, return -1; if off the board,
2653 * return -2. Otherwise map the event coordinate to the square.
2655 int EventToSquare(x, limit)
2663 if ((x % (squareSize + lineGap)) >= squareSize)
2665 x /= (squareSize + lineGap);
2671 static void do_flash_delay(msec)
2677 static void drawHighlight(file, rank, line_type)
2678 int file, rank, line_type;
2683 if (lineGap == 0 || appData.blindfold) return;
2687 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
2688 (squareSize + lineGap);
2689 y = lineGap/2 + rank * (squareSize + lineGap);
2693 x = lineGap/2 + file * (squareSize + lineGap);
2694 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
2695 (squareSize + lineGap);
2699 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2701 /* draw the highlight */
2702 cairo_move_to (cr, x, y);
2703 cairo_rel_line_to (cr, 0,squareSize+lineGap);
2704 cairo_rel_line_to (cr, squareSize+lineGap,0);
2705 cairo_rel_line_to (cr, 0,-squareSize-lineGap);
2706 cairo_close_path (cr);
2708 cairo_set_line_width (cr, lineGap);
2711 /* TODO: use appdata colors */
2712 case LINE_TYPE_HIGHLIGHT:
2713 cairo_set_source_rgba (cr, 1, 1, 0, 1.0);
2716 cairo_set_source_rgba (cr, 1, 0, 0, 1.0);
2718 case LINE_TYPE_NORMAL:
2720 cairo_set_source_rgba (cr, 0, 1, 0, 1.0);
2731 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
2732 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
2735 SetHighlights(fromX, fromY, toX, toY)
2736 int fromX, fromY, toX, toY;
2738 if (hi1X != fromX || hi1Y != fromY)
2740 if (hi1X >= 0 && hi1Y >= 0)
2742 drawHighlight(hi1X, hi1Y, LINE_TYPE_NORMAL);
2744 if (fromX >= 0 && fromY >= 0)
2746 drawHighlight(fromX, fromY, LINE_TYPE_HIGHLIGHT);
2749 if (hi2X != toX || hi2Y != toY)
2751 if (hi2X >= 0 && hi2Y >= 0)
2753 drawHighlight(hi2X, hi2Y, LINE_TYPE_NORMAL);
2755 if (toX >= 0 && toY >= 0)
2757 drawHighlight(toX, toY, LINE_TYPE_HIGHLIGHT);
2771 SetHighlights(-1, -1, -1, -1);
2776 SetPremoveHighlights(fromX, fromY, toX, toY)
2777 int fromX, fromY, toX, toY;
2779 if (pm1X != fromX || pm1Y != fromY)
2781 if (pm1X >= 0 && pm1Y >= 0)
2783 drawHighlight(pm1X, pm1Y, LINE_TYPE_NORMAL);
2785 if (fromX >= 0 && fromY >= 0)
2787 drawHighlight(fromX, fromY, LINE_TYPE_PRE);
2790 if (pm2X != toX || pm2Y != toY)
2792 if (pm2X >= 0 && pm2Y >= 0)
2794 drawHighlight(pm2X, pm2Y, LINE_TYPE_NORMAL);
2796 if (toX >= 0 && toY >= 0)
2798 drawHighlight(toX, toY, LINE_TYPE_PRE);
2811 ClearPremoveHighlights()
2813 SetPremoveHighlights(-1, -1, -1, -1);
2816 static void BlankSquare(x, y, color, piece, dest)
2829 pb = SVGLightSquare;
2831 case 2: /* neutral */
2833 pb = SVGNeutralSquare;
2836 gdk_draw_pixbuf(GDK_WINDOW(GUI_Board->window),NULL,pb,0,0,x,y,-1,-1, GDK_RGB_DITHER_NORMAL, 0, 0);
2840 static void DrawPiece(piece, square_color, x, y, dest)
2842 int square_color, x, y;
2845 /* redraw background, since piece might be transparent in some areas */
2846 BlankSquare(x,y,square_color,piece,dest);
2849 gdk_draw_pixbuf(GDK_WINDOW(GUI_Board->window),NULL,
2850 GDK_PIXBUF(SVGpieces[piece]),0,0,x,y,-1,-1,
2851 GDK_RGB_DITHER_NORMAL, 0, 0);
2855 /* [HR] determine square color depending on chess variant. */
2856 static int SquareColor(row, column)
2861 if (gameInfo.variant == VariantXiangqi) {
2862 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
2864 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
2866 } else if (row <= 4) {
2872 square_color = ((column + row) % 2) == 1;
2875 /* [hgm] holdings: next line makes all holdings squares light */
2876 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
2878 return square_color;
2881 void DrawSquare(row, column, piece, do_flash)
2882 int row, column, do_flash;
2885 int square_color, x, y;
2890 /* Calculate delay in milliseconds (2-delays per complete flash) */
2891 flash_delay = 500 / appData.flashRate;
2893 /* calculate x and y coordinates from row and column */
2896 x = lineGap + ((BOARD_WIDTH-1)-column) *
2897 (squareSize + lineGap);
2898 y = lineGap + row * (squareSize + lineGap);
2902 x = lineGap + column * (squareSize + lineGap);
2903 y = lineGap + ((BOARD_HEIGHT-1)-row) *
2904 (squareSize + lineGap);
2907 square_color = SquareColor(row, column);
2909 // [HGM] holdings: blank out area between board and holdings
2910 if ( column == BOARD_LEFT-1 || column == BOARD_RGHT
2911 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
2912 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) )
2914 BlankSquare(x, y, 2, EmptySquare, xBoardWindow);
2916 // [HGM] print piece counts next to holdings
2917 string[1] = NULLCHAR;
2920 cairo_text_extents_t extents;
2925 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2927 string[0] = '0' + piece;
2929 /* TODO this has to go into the font-selection */
2930 cairo_select_font_face (cr, "Sans",
2931 CAIRO_FONT_SLANT_NORMAL,
2932 CAIRO_FONT_WEIGHT_NORMAL);
2934 cairo_set_font_size (cr, 12.0);
2935 cairo_text_extents (cr, string, &extents);
2937 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) )
2939 xpos= x + squareSize - extents.width - 2;
2940 ypos= y + extents.y_bearing + 1;
2942 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1)
2945 ypos = y + extents.y_bearing + 1;
2948 /* TODO mono mode? */
2949 cairo_move_to (cr, xpos, ypos);
2950 cairo_text_path (cr, string);
2951 cairo_set_source_rgb (cr, 1.0, 1.0, 1);
2952 cairo_fill_preserve (cr);
2953 cairo_set_source_rgb (cr, 0, 0, 0);
2954 cairo_set_line_width (cr, 0.1);
2963 /* square on the board */
2964 if (piece == EmptySquare || appData.blindfold)
2966 BlankSquare(x, y, square_color, piece, xBoardWindow);
2970 if (do_flash && appData.flashCount > 0)
2972 for (i=0; i<appData.flashCount; ++i)
2975 DrawPiece(piece, square_color, x, y, xBoardWindow);
2976 do_flash_delay(flash_delay);
2978 BlankSquare(x, y, square_color, piece, xBoardWindow);
2979 do_flash_delay(flash_delay);
2982 DrawPiece(piece, square_color, x, y, xBoardWindow);
2986 /* show coordinates if necessary */
2987 if(appData.showCoords)
2989 cairo_text_extents_t extents;
2993 /* TODO this has to go into the font-selection */
2994 cairo_select_font_face (cr, "Sans",
2995 CAIRO_FONT_SLANT_NORMAL,
2996 CAIRO_FONT_WEIGHT_NORMAL);
2997 cairo_set_font_size (cr, 12.0);
2999 string[1] = NULLCHAR;
3002 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
3004 if (row == (flipView ? BOARD_HEIGHT-1 : 0) &&
3005 column >= BOARD_LEFT && column < BOARD_RGHT)
3007 string[0] = 'a' + column - BOARD_LEFT;
3008 cairo_text_extents (cr, string, &extents);
3010 xpos = x + squareSize - extents.width - 2;
3011 ypos = y + squareSize - extents.height - extents.y_bearing - 1;
3013 if (appData.monoMode)
3020 cairo_move_to (cr, xpos, ypos);
3021 cairo_text_path (cr, string);
3022 cairo_set_source_rgb (cr, 0.0, 0.0, 0);
3023 cairo_fill_preserve (cr);
3024 cairo_set_source_rgb (cr, 0, 1.0, 0);
3025 cairo_set_line_width (cr, 0.1);
3028 if ( column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT))
3031 string[0] = ONE + row;
3032 cairo_text_extents (cr, string, &extents);
3035 ypos = y + extents.height + 1;
3037 if (appData.monoMode)
3044 cairo_move_to (cr, xpos, ypos);
3045 cairo_text_path (cr, string);
3046 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
3047 cairo_fill_preserve (cr);
3048 cairo_set_source_rgb (cr, 0, 0, 1.0);
3049 cairo_set_line_width (cr, 0.1);
3061 /* Returns 1 if there are "too many" differences between b1 and b2
3062 (i.e. more than 1 move was made) */
3063 static int too_many_diffs(b1, b2)
3069 for (i=0; i<BOARD_HEIGHT; ++i) {
3070 for (j=0; j<BOARD_WIDTH; ++j) {
3071 if (b1[i][j] != b2[i][j]) {
3072 if (++c > 4) /* Castling causes 4 diffs */
3081 /* Matrix describing castling maneuvers */
3082 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
3083 static int castling_matrix[4][5] = {
3084 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
3085 { 0, 7, 4, 5, 6 }, /* 0-0, white */
3086 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
3087 { 7, 7, 4, 5, 6 } /* 0-0, black */
3090 /* Checks whether castling occurred. If it did, *rrow and *rcol
3091 are set to the destination (row,col) of the rook that moved.
3093 Returns 1 if castling occurred, 0 if not.
3095 Note: Only handles a max of 1 castling move, so be sure
3096 to call too_many_diffs() first.
3098 static int check_castle_draw(newb, oldb, rrow, rcol)
3105 /* For each type of castling... */
3106 for (i=0; i<4; ++i) {
3107 r = castling_matrix[i];
3109 /* Check the 4 squares involved in the castling move */
3111 for (j=1; j<=4; ++j) {
3112 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
3119 /* All 4 changed, so it must be a castling move */
3128 static int damage[BOARD_RANKS][BOARD_FILES];
3131 * event handler for redrawing the board
3133 void DrawPosition( repaint, board)
3134 /*Boolean*/int repaint;
3138 static int lastFlipView = 0;
3139 static int lastBoardValid = 0;
3140 static Board lastBoard;
3143 if (board == NULL) {
3144 if (!lastBoardValid) return;
3147 if (!lastBoardValid || lastFlipView != flipView) {
3148 // XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
3149 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flip View"),
3154 * It would be simpler to clear the window with XClearWindow()
3155 * but this causes a very distracting flicker.
3158 if (!repaint && lastBoardValid && lastFlipView == flipView)
3160 /* If too much changes (begin observing new game, etc.), don't
3162 do_flash = too_many_diffs(board, lastBoard) ? 0 : 1;
3164 /* Special check for castling so we don't flash both the king
3165 and the rook (just flash the king). */
3168 if (check_castle_draw(board, lastBoard, &rrow, &rcol))
3170 /* Draw rook with NO flashing. King will be drawn flashing later */
3171 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
3172 lastBoard[rrow][rcol] = board[rrow][rcol];
3176 /* First pass -- Draw (newly) empty squares and repair damage.
3177 This prevents you from having a piece show up twice while it
3178 is flashing on its new square */
3179 for (i = 0; i < BOARD_HEIGHT; i++)
3180 for (j = 0; j < BOARD_WIDTH; j++)
3181 if ((board[i][j] != lastBoard[i][j] && board[i][j] == EmptySquare)
3184 DrawSquare(i, j, board[i][j], 0);
3185 damage[i][j] = False;
3188 /* Second pass -- Draw piece(s) in new position and flash them */
3189 for (i = 0; i < BOARD_HEIGHT; i++)
3190 for (j = 0; j < BOARD_WIDTH; j++)
3191 if (board[i][j] != lastBoard[i][j])
3193 DrawSquare(i, j, board[i][j], do_flash);
3205 cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
3207 cairo_set_line_width (cr, lineGap);
3209 /* TODO: use appdata colors */
3210 cairo_set_source_rgba (cr, 0, 1, 0, 1.0);
3214 for (i = 0; i < BOARD_HEIGHT + 1; i++)
3217 x2 = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3218 y1 = y2 = lineGap / 2 + (i * (squareSize + lineGap));
3220 cairo_move_to (cr, x1, y1);
3221 cairo_rel_line_to (cr, x2,0);
3225 for (j = 0; j < BOARD_WIDTH + 1; j++)
3228 y2 = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3229 x1 = x2 = lineGap / 2 + (j * (squareSize + lineGap));
3231 cairo_move_to (cr, x1, y1);
3232 cairo_rel_line_to (cr, 0, y2);
3241 for (i = 0; i < BOARD_HEIGHT; i++)
3242 for (j = 0; j < BOARD_WIDTH; j++)
3244 DrawSquare(i, j, board[i][j], 0);
3245 damage[i][j] = False;
3249 CopyBoard(lastBoard, board);
3251 lastFlipView = flipView;
3253 /* Draw highlights */
3254 if (pm1X >= 0 && pm1Y >= 0)
3256 drawHighlight(pm1X, pm1Y, LINE_TYPE_PRE);
3258 if (pm2X >= 0 && pm2Y >= 0)
3260 drawHighlight(pm2X, pm2Y, LINE_TYPE_PRE);
3262 if (hi1X >= 0 && hi1Y >= 0)
3264 drawHighlight(hi1X, hi1Y, LINE_TYPE_HIGHLIGHT);
3266 if (hi2X >= 0 && hi2Y >= 0)
3268 drawHighlight(hi2X, hi2Y, LINE_TYPE_HIGHLIGHT);
3271 /* If piece being dragged around board, must redraw that too */
3277 void AnimateUserMove (Widget w, XEvent * event,
3278 String * params, Cardinal * nParams)
3280 DragPieceMove(event->xmotion.x, event->xmotion.y);
3283 Widget CommentCreate(name, text, mutable, callback, lines)
3285 int /*Boolean*/ mutable;
3286 XtCallbackProc callback;
3290 Widget shell, layout, form, edit, b_ok, b_cancel, b_clear, b_close, b_edit;
3295 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3296 XtGetValues(boardWidget, args, j);
3299 XtSetArg(args[j], XtNresizable, True); j++;
3302 XtCreatePopupShell(name, topLevelShellWidgetClass,
3303 shellWidget, args, j);
3306 XtCreatePopupShell(name, transientShellWidgetClass,
3307 shellWidget, args, j);
3310 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
3311 layoutArgs, XtNumber(layoutArgs));
3313 XtCreateManagedWidget("form", formWidgetClass, layout,
3314 formArgs, XtNumber(formArgs));
3318 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
3319 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
3321 XtSetArg(args[j], XtNstring, text); j++;
3322 XtSetArg(args[j], XtNtop, XtChainTop); j++;
3323 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3324 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3325 XtSetArg(args[j], XtNright, XtChainRight); j++;
3326 XtSetArg(args[j], XtNresizable, True); j++;
3327 XtSetArg(args[j], XtNwidth, bw_width); j++; /*force wider than buttons*/
3328 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
3329 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
3330 XtSetArg(args[j], XtNautoFill, True); j++;
3331 XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
3333 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
3337 XtSetArg(args[j], XtNfromVert, edit); j++;
3338 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3339 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3340 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3341 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3343 XtCreateManagedWidget(_("ok"), commandWidgetClass, form, args, j);
3344 XtAddCallback(b_ok, XtNcallback, callback, (XtPointer) 0);
3347 XtSetArg(args[j], XtNfromVert, edit); j++;
3348 XtSetArg(args[j], XtNfromHoriz, b_ok); j++;
3349 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3350 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3351 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3352 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3354 XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
3355 XtAddCallback(b_cancel, XtNcallback, callback, (XtPointer) 0);
3358 XtSetArg(args[j], XtNfromVert, edit); j++;
3359 XtSetArg(args[j], XtNfromHoriz, b_cancel); j++;
3360 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3361 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3362 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3363 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3365 XtCreateManagedWidget(_("clear"), commandWidgetClass, form, args, j);
3366 XtAddCallback(b_clear, XtNcallback, callback, (XtPointer) 0);
3369 XtSetArg(args[j], XtNfromVert, edit); j++;
3370 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3371 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3372 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3373 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3375 XtCreateManagedWidget(_("close"), commandWidgetClass, form, args, j);
3376 XtAddCallback(b_close, XtNcallback, callback, (XtPointer) 0);
3379 XtSetArg(args[j], XtNfromVert, edit); j++;
3380 XtSetArg(args[j], XtNfromHoriz, b_close); j++;
3381 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3382 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3383 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3384 XtSetArg(args[j], XtNright, XtChainLeft); j++;
3386 XtCreateManagedWidget(_("edit"), commandWidgetClass, form, args, j);
3387 XtAddCallback(b_edit, XtNcallback, callback, (XtPointer) 0);
3390 XtRealizeWidget(shell);
3392 if (commentX == -1) {
3395 Dimension pw_height;
3396 Dimension ew_height;
3399 XtSetArg(args[j], XtNheight, &ew_height); j++;
3400 XtGetValues(edit, args, j);
3403 XtSetArg(args[j], XtNheight, &pw_height); j++;
3404 XtGetValues(shell, args, j);
3405 commentH = pw_height + (lines - 1) * ew_height;
3406 commentW = bw_width - 16;
3408 XSync(xDisplay, False);
3410 /* This code seems to tickle an X bug if it is executed too soon
3411 after xboard starts up. The coordinates get transformed as if
3412 the main window was positioned at (0, 0).
3414 XtTranslateCoords(shellWidget,
3415 (bw_width - commentW) / 2, 0 - commentH / 2,
3416 &commentX, &commentY);
3418 XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
3419 RootWindowOfScreen(XtScreen(shellWidget)),
3420 (bw_width - commentW) / 2, 0 - commentH / 2,
3425 if (commentY < 0) commentY = 0; /*avoid positioning top offscreen*/
3428 if(wpComment.width > 0) {
3429 commentX = wpComment.x;
3430 commentY = wpComment.y;
3431 commentW = wpComment.width;
3432 commentH = wpComment.height;
3436 XtSetArg(args[j], XtNheight, commentH); j++;
3437 XtSetArg(args[j], XtNwidth, commentW); j++;
3438 XtSetArg(args[j], XtNx, commentX); j++;
3439 XtSetArg(args[j], XtNy, commentY); j++;
3440 XtSetValues(shell, args, j);
3441 XtSetKeyboardFocus(shell, edit);
3446 /* Used for analysis window and ICS input window */
3447 Widget MiscCreate(name, text, mutable, callback, lines)
3449 int /*Boolean*/ mutable;
3450 XtCallbackProc callback;
3454 Widget shell, layout, form, edit;
3456 Dimension bw_width, pw_height, ew_height, w, h;
3462 XtSetArg(args[j], XtNresizable, True); j++;
3465 XtCreatePopupShell(name, topLevelShellWidgetClass,
3466 shellWidget, args, j);
3469 XtCreatePopupShell(name, transientShellWidgetClass,
3470 shellWidget, args, j);
3473 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
3474 layoutArgs, XtNumber(layoutArgs));
3476 XtCreateManagedWidget("form", formWidgetClass, layout,
3477 formArgs, XtNumber(formArgs));
3481 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
3482 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
3484 XtSetArg(args[j], XtNstring, text); j++;
3485 XtSetArg(args[j], XtNtop, XtChainTop); j++;
3486 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3487 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3488 XtSetArg(args[j], XtNright, XtChainRight); j++;
3489 XtSetArg(args[j], XtNresizable, True); j++;
3490 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
3491 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
3492 XtSetArg(args[j], XtNautoFill, True); j++;
3493 XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
3495 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
3497 XtRealizeWidget(shell);
3500 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3501 XtGetValues(boardWidget, args, j);
3504 XtSetArg(args[j], XtNheight, &ew_height); j++;
3505 XtGetValues(edit, args, j);
3508 XtSetArg(args[j], XtNheight, &pw_height); j++;
3509 XtGetValues(shell, args, j);
3510 h = pw_height + (lines - 1) * ew_height;
3513 XSync(xDisplay, False);
3515 /* This code seems to tickle an X bug if it is executed too soon
3516 after xboard starts up. The coordinates get transformed as if
3517 the main window was positioned at (0, 0).
3519 XtTranslateCoords(shellWidget, (bw_width - w) / 2, 0 - h / 2, &x, &y);
3521 XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
3522 RootWindowOfScreen(XtScreen(shellWidget)),
3523 (bw_width - w) / 2, 0 - h / 2, &xx, &yy, &junk);
3527 if (y < 0) y = 0; /*avoid positioning top offscreen*/
3530 XtSetArg(args[j], XtNheight, h); j++;
3531 XtSetArg(args[j], XtNwidth, w); j++;
3532 XtSetArg(args[j], XtNx, x); j++;
3533 XtSetArg(args[j], XtNy, y); j++;
3534 XtSetValues(shell, args, j);
3540 static int savedIndex; /* gross that this is global */
3542 void EditCommentPopUp(index, title, text)
3551 if (text == NULL) text = "";
3553 if (editShell == NULL) {
3555 CommentCreate(title, text, True, EditCommentCallback, 4);
3556 XtRealizeWidget(editShell);
3557 CatchDeleteWindow(editShell, "EditCommentPopDown");
3559 edit = XtNameToWidget(editShell, "*form.text");
3561 XtSetArg(args[j], XtNstring, text); j++;
3562 XtSetValues(edit, args, j);
3564 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
3565 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
3566 XtSetValues(editShell, args, j);
3569 XtPopup(editShell, XtGrabNone);
3573 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
3574 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
3578 void EditCommentCallback(w, client_data, call_data)
3580 XtPointer client_data, call_data;
3588 XtSetArg(args[j], XtNlabel, &name); j++;
3589 XtGetValues(w, args, j);
3591 if (strcmp(name, _("ok")) == 0) {
3592 edit = XtNameToWidget(editShell, "*form.text");
3594 XtSetArg(args[j], XtNstring, &val); j++;
3595 XtGetValues(edit, args, j);
3596 ReplaceComment(savedIndex, val);
3597 EditCommentPopDown();
3598 } else if (strcmp(name, _("cancel")) == 0) {
3599 EditCommentPopDown();
3600 } else if (strcmp(name, _("clear")) == 0) {
3601 edit = XtNameToWidget(editShell, "*form.text");
3602 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
3603 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
3607 void EditCommentPopDown()
3612 if (!editUp) return;
3614 XtSetArg(args[j], XtNx, &commentX); j++;
3615 XtSetArg(args[j], XtNy, &commentY); j++;
3616 XtSetArg(args[j], XtNheight, &commentH); j++;
3617 XtSetArg(args[j], XtNwidth, &commentW); j++;
3618 XtGetValues(editShell, args, j);
3619 XtPopdown(editShell);
3622 XtSetArg(args[j], XtNleftBitmap, None); j++;
3623 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
3627 void ICSInputBoxPopUp()
3632 char *title = _("ICS Input");
3635 if (ICSInputShell == NULL) {
3636 ICSInputShell = MiscCreate(title, "", True, NULL, 1);
3637 tr = XtParseTranslationTable(ICSInputTranslations);
3638 edit = XtNameToWidget(ICSInputShell, "*form.text");
3639 XtOverrideTranslations(edit, tr);
3640 XtRealizeWidget(ICSInputShell);
3641 CatchDeleteWindow(ICSInputShell, "ICSInputBoxPopDown");
3644 edit = XtNameToWidget(ICSInputShell, "*form.text");
3646 XtSetArg(args[j], XtNstring, ""); j++;
3647 XtSetValues(edit, args, j);
3649 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
3650 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
3651 XtSetValues(ICSInputShell, args, j);
3654 XtPopup(ICSInputShell, XtGrabNone);
3655 XtSetKeyboardFocus(ICSInputShell, edit);
3657 ICSInputBoxUp = True;
3659 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
3660 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
3664 void ICSInputSendText()
3671 edit = XtNameToWidget(ICSInputShell, "*form.text");
3673 XtSetArg(args[j], XtNstring, &val); j++;
3674 XtGetValues(edit, args, j);
3675 SendMultiLineToICS(val);
3676 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
3677 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
3680 void ICSInputBoxPopDown()
3685 if (!ICSInputBoxUp) return;
3687 XtPopdown(ICSInputShell);
3688 ICSInputBoxUp = False;
3690 XtSetArg(args[j], XtNleftBitmap, None); j++;
3691 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
3695 void CommentPopUp(title, text)
3702 if (commentShell == NULL) {
3704 CommentCreate(title, text, False, CommentCallback, 4);
3705 XtRealizeWidget(commentShell);
3706 CatchDeleteWindow(commentShell, "CommentPopDown");
3708 edit = XtNameToWidget(commentShell, "*form.text");
3710 XtSetArg(args[j], XtNstring, text); j++;
3711 XtSetValues(edit, args, j);
3713 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
3714 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
3715 XtSetValues(commentShell, args, j);
3718 XtPopup(commentShell, XtGrabNone);
3719 XSync(xDisplay, False);
3724 void CommentCallback(w, client_data, call_data)
3726 XtPointer client_data, call_data;
3733 XtSetArg(args[j], XtNlabel, &name); j++;
3734 XtGetValues(w, args, j);
3736 if (strcmp(name, _("close")) == 0) {
3738 } else if (strcmp(name, _("edit")) == 0) {
3745 void CommentPopDown()
3750 if (!commentUp) return;
3752 XtSetArg(args[j], XtNx, &commentX); j++;
3753 XtSetArg(args[j], XtNy, &commentY); j++;
3754 XtSetArg(args[j], XtNwidth, &commentW); j++;
3755 XtSetArg(args[j], XtNheight, &commentH); j++;
3756 XtGetValues(commentShell, args, j);
3757 XtPopdown(commentShell);
3758 XSync(xDisplay, False);
3762 void PromotionPopUp()
3765 Widget dialog, layout;
3767 Dimension bw_width, pw_width;
3771 XtSetArg(args[j], XtNwidth, &bw_width); j++;
3772 XtGetValues(boardWidget, args, j);
3775 XtSetArg(args[j], XtNresizable, True); j++;
3776 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
3778 XtCreatePopupShell("Promotion", transientShellWidgetClass,
3779 shellWidget, args, j);
3781 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
3782 layoutArgs, XtNumber(layoutArgs));
3785 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
3786 XtSetArg(args[j], XtNborderWidth, 0); j++;
3787 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
3790 if(gameInfo.variant != VariantShogi) {
3791 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
3792 (XtPointer) dialog);
3793 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
3794 (XtPointer) dialog);
3795 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
3796 (XtPointer) dialog);
3797 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
3798 (XtPointer) dialog);
3799 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
3800 gameInfo.variant == VariantGiveaway) {
3801 XawDialogAddButton(dialog, _("King"), PromotionCallback,
3802 (XtPointer) dialog);
3804 if(gameInfo.variant == VariantCapablanca ||
3805 gameInfo.variant == VariantGothic ||
3806 gameInfo.variant == VariantCapaRandom) {
3807 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
3808 (XtPointer) dialog);
3809 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
3810 (XtPointer) dialog);
3812 } else // [HGM] shogi
3814 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
3815 (XtPointer) dialog);
3816 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
3817 (XtPointer) dialog);
3819 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
3820 (XtPointer) dialog);
3822 XtRealizeWidget(promotionShell);
3823 CatchDeleteWindow(promotionShell, "PromotionPopDown");
3826 XtSetArg(args[j], XtNwidth, &pw_width); j++;
3827 XtGetValues(promotionShell, args, j);
3829 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3830 lineGap + squareSize/3 +
3831 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
3832 0 : 6*(squareSize + lineGap)), &x, &y);
3835 XtSetArg(args[j], XtNx, x); j++;
3836 XtSetArg(args[j], XtNy, y); j++;
3837 XtSetValues(promotionShell, args, j);
3839 XtPopup(promotionShell, XtGrabNone);
3844 void PromotionPopDown()
3846 if (!promotionUp) return;
3847 XtPopdown(promotionShell);
3848 XtDestroyWidget(promotionShell);
3849 promotionUp = False;
3852 void PromotionCallback(w, client_data, call_data)
3854 XtPointer client_data, call_data;
3860 XtSetArg(args[0], XtNlabel, &name);
3861 XtGetValues(w, args, 1);
3865 if (fromX == -1) return;
3867 if (strcmp(name, _("cancel")) == 0) {
3871 } else if (strcmp(name, _("Knight")) == 0) {
3873 } else if (strcmp(name, _("Promote")) == 0) {
3875 } else if (strcmp(name, _("Defer")) == 0) {
3878 promoChar = ToLower(name[0]);
3881 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
3883 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
3884 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3889 void ErrorCallback(w, client_data, call_data)
3891 XtPointer client_data, call_data;
3894 XtPopdown(w = XtParent(XtParent(XtParent(w))));
3896 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3902 if (!errorUp) return;
3906 gtk_widget_destroy(GTK_WIDGET(GUI_Error));
3908 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3913 void ErrorPopUp(title, label, modal)
3914 char *title, *label;
3917 GUI_Error = gtk_message_dialog_new(GTK_WINDOW(GUI_Window),
3918 GTK_DIALOG_DESTROY_WITH_PARENT,
3923 gtk_window_set_title(GTK_WINDOW(GUI_Error),(gchar *) title);
3926 gtk_dialog_run(GTK_DIALOG(GUI_Error));
3927 gtk_widget_destroy(GTK_WIDGET(GUI_Error));
3931 g_signal_connect_swapped (GUI_Error, "response",
3932 G_CALLBACK (ErrorPopDownProc),
3935 gtk_widget_show(GTK_WIDGET(GUI_Error));
3941 /* Disable all user input other than deleting the window */
3942 static int frozen = 0;
3946 /* Grab by a widget that doesn't accept input */
3947 // XtAddGrab(messageWidget, TRUE, FALSE);
3951 /* Undo a FreezeUI */
3954 if (!frozen) return;
3955 // XtRemoveGrab(messageWidget);
3959 char *ModeToWidgetName(mode)
3963 case BeginningOfGame:
3964 if (appData.icsActive)
3965 return "menuMode.ICS Client";
3966 else if (appData.noChessProgram ||
3967 *appData.cmailGameName != NULLCHAR)
3968 return "menuMode.Edit Game";
3970 return "menuMode.Machine Black";
3971 case MachinePlaysBlack:
3972 return "menuMode.Machine Black";
3973 case MachinePlaysWhite:
3974 return "menuMode.Machine White";
3976 return "menuMode.Analysis Mode";
3978 return "menuMode.Analyze File";
3979 case TwoMachinesPlay:
3980 return "menuMode.Two Machines";
3982 return "menuMode.Edit Game";
3983 case PlayFromGameFile:
3984 return "menuFile.Load Game";
3986 return "menuMode.Edit Position";
3988 return "menuMode.Training";
3989 case IcsPlayingWhite:
3990 case IcsPlayingBlack:
3994 return "menuMode.ICS Client";
4001 void ModeHighlight()
4003 static int oldPausing = FALSE;
4004 static GameMode oldmode = (GameMode) -1;
4007 // todo this toggling of the pause button doesn't seem to work?
4008 // e.g. select pause from buttonbar doesn't activate menumode.pause
4010 // if (!boardWidget || !XtIsRealized(boardWidget)) return;
4012 if (pausing != oldPausing) {
4013 oldPausing = pausing;
4014 gtk_button_set_relief(GTK_BUTTON (gtk_builder_get_object (builder, "menuMode.Pause")),pausing?GTK_RELIEF_NORMAL:GTK_RELIEF_NONE);
4015 /* toggle background color in showbuttonbar */
4016 if (appData.showButtonBar) {
4018 gtk_button_pressed(GTK_BUTTON (gtk_builder_get_object (builder, "buttonbar.Pause")));
4020 gtk_button_released(GTK_BUTTON (gtk_builder_get_object (builder, "buttonbar.Pause")));
4025 wname = ModeToWidgetName(oldmode);
4027 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, wname)),True);
4031 /* Maybe all the enables should be handled here, not just this one */
4032 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuMode.Training")),
4033 gameMode == Training || gameMode == PlayFromGameFile);
4038 * Button/menu procedures
4041 int LoadGamePopUp(f, gameNumber, title)
4046 cmailMsgLoaded = FALSE;
4048 if (gameNumber == 0)
4050 int error = GameListBuild(f);
4054 DisplayError(_("Cannot build game list"), error);
4056 else if (!ListEmpty(&gameList)
4057 && ((ListGame *) gameList.tailPred)->number > 1)
4059 // TODO convert to GTK
4060 // GameListPopUp(f, title);
4068 return LoadGame(f, gameNumber, title, FALSE);
4071 void ReloadCmailMsgProc(w, event, prms, nprms)
4077 ReloadCmailMsgEvent(FALSE);
4080 void MailMoveProc(w, event, prms, nprms)
4089 /* this variable is shared between CopyPositionProc and SendPositionSelection */
4090 static char *selected_fen_position=NULL;
4093 SendPositionSelection(Widget w, Atom *selection, Atom *target,
4094 Atom *type_return, XtPointer *value_return,
4095 unsigned long *length_return, int *format_return)
4097 char *selection_tmp;
4099 if (!selected_fen_position) return False; /* should never happen */
4100 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4101 /* note: since no XtSelectionDoneProc was registered, Xt will
4102 * automatically call XtFree on the value returned. So have to
4103 * make a copy of it allocated with XtMalloc */
4104 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
4105 strcpy(selection_tmp, selected_fen_position);
4107 *value_return=selection_tmp;
4108 *length_return=strlen(selection_tmp);
4109 *type_return=*target;
4110 *format_return = 8; /* bits per byte */
4112 } else if (*target == XA_TARGETS(xDisplay)) {
4113 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4114 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4115 targets_tmp[1] = XA_STRING;
4116 *value_return = targets_tmp;
4117 *type_return = XA_ATOM;
4119 *format_return = 8 * sizeof(Atom);
4120 if (*format_return > 32) {
4121 *length_return *= *format_return / 32;
4122 *format_return = 32;
4130 /* note: when called from menu all parameters are NULL, so no clue what the
4131 * Widget which was clicked on was, or what the click event was
4133 void CopyPositionProc(w, event, prms, nprms)
4140 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4141 * have a notion of a position that is selected but not copied.
4142 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4144 if(gameMode == EditPosition) EditPositionDone(TRUE);
4145 if (selected_fen_position) free(selected_fen_position);
4146 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
4147 if (!selected_fen_position) return;
4148 XtOwnSelection(menuBarWidget, XA_PRIMARY,
4150 SendPositionSelection,
4151 NULL/* lose_ownership_proc */ ,
4152 NULL/* transfer_done_proc */);
4153 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4155 SendPositionSelection,
4156 NULL/* lose_ownership_proc */ ,
4157 NULL/* transfer_done_proc */);
4160 /* function called when the data to Paste is ready */
4162 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
4163 Atom *type, XtPointer value, unsigned long *len, int *format)
4166 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
4167 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
4168 EditPositionPasteFEN(fenstr);
4172 /* called when Paste Position button is pressed,
4173 * all parameters will be NULL */
4174 void PastePositionProc(w, event, prms, nprms)
4180 XtGetSelectionValue(menuBarWidget,
4181 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4182 /* (XtSelectionCallbackProc) */ PastePositionCB,
4183 NULL, /* client_data passed to PastePositionCB */
4185 /* better to use the time field from the event that triggered the
4186 * call to this function, but that isn't trivial to get
4194 SendGameSelection(Widget w, Atom *selection, Atom *target,
4195 Atom *type_return, XtPointer *value_return,
4196 unsigned long *length_return, int *format_return)
4198 char *selection_tmp;
4200 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4201 FILE* f = fopen(gameCopyFilename, "r");
4204 if (f == NULL) return False;
4208 selection_tmp = XtMalloc(len + 1);
4209 count = fread(selection_tmp, 1, len, f);
4211 XtFree(selection_tmp);
4214 selection_tmp[len] = NULLCHAR;
4215 *value_return = selection_tmp;
4216 *length_return = len;
4217 *type_return = *target;
4218 *format_return = 8; /* bits per byte */
4220 } else if (*target == XA_TARGETS(xDisplay)) {
4221 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4222 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4223 targets_tmp[1] = XA_STRING;
4224 *value_return = targets_tmp;
4225 *type_return = XA_ATOM;
4227 *format_return = 8 * sizeof(Atom);
4228 if (*format_return > 32) {
4229 *length_return *= *format_return / 32;
4230 *format_return = 32;
4238 /* note: when called from menu all parameters are NULL, so no clue what the
4239 * Widget which was clicked on was, or what the click event was
4241 void CopyGameProc(w, event, prms, nprms)
4249 ret = SaveGameToFile(gameCopyFilename, FALSE);
4253 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4254 * have a notion of a game that is selected but not copied.
4255 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4257 XtOwnSelection(menuBarWidget, XA_PRIMARY,
4260 NULL/* lose_ownership_proc */ ,
4261 NULL/* transfer_done_proc */);
4262 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4265 NULL/* lose_ownership_proc */ ,
4266 NULL/* transfer_done_proc */);
4269 /* function called when the data to Paste is ready */
4271 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
4272 Atom *type, XtPointer value, unsigned long *len, int *format)
4275 if (value == NULL || *len == 0) {
4276 return; /* nothing had been selected to copy */
4278 f = fopen(gamePasteFilename, "w");
4280 DisplayError(_("Can't open temp file"), errno);
4283 fwrite(value, 1, *len, f);
4286 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
4289 /* called when Paste Game button is pressed,
4290 * all parameters will be NULL */
4291 void PasteGameProc(w, event, prms, nprms)
4297 XtGetSelectionValue(menuBarWidget,
4298 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4299 /* (XtSelectionCallbackProc) */ PasteGameCB,
4300 NULL, /* client_data passed to PasteGameCB */
4302 /* better to use the time field from the event that triggered the
4303 * call to this function, but that isn't trivial to get
4310 void SaveOnExitProc(w, event, prms, nprms)
4318 saveSettingsOnExit = !saveSettingsOnExit;
4320 if (saveSettingsOnExit) {
4321 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
4323 XtSetArg(args[0], XtNleftBitmap, None);
4325 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
4329 void SaveSettingsProc(w, event, prms, nprms)
4335 SaveSettings(settingsFileName);
4341 SaveGameProc(NULL, NULL);
4346 void EditCommentProc(w, event, prms, nprms)
4353 EditCommentPopDown();
4359 void IcsInputBoxProc(w, event, prms, nprms)
4365 if (ICSInputBoxUp) {
4366 ICSInputBoxPopDown();
4373 void EnterKeyProc(w, event, prms, nprms)
4379 if (ICSInputBoxUp == True)
4384 void DebugProc(w, event, prms, nprms)
4390 appData.debugMode = !appData.debugMode;
4393 void AboutGameProc(w, event, prms, nprms)
4402 void NothingProc(w, event, prms, nprms)
4411 void Iconify(w, event, prms, nprms)
4420 XtSetArg(args[0], XtNiconic, True);
4421 XtSetValues(shellWidget, args, 1);
4424 void DisplayMessage(message, extMessage)
4425 gchar *message, *extMessage;
4432 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
4435 message = extMessage;
4438 gtk_label_set_text( GTK_LABEL(gtk_builder_get_object (builder, "Messages")),message);
4443 void DisplayTitle(text)
4446 gchar title[MSG_SIZ];
4448 if (text == NULL) text = "";
4450 if (appData.titleInWindow)
4455 if (*text != NULLCHAR)
4457 strcpy(title, text);
4459 else if (appData.icsActive)
4461 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
4463 else if (appData.cmailGameName[0] != NULLCHAR)
4465 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
4467 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
4469 else if (gameInfo.variant == VariantGothic)
4471 strcpy(title, GOTHIC);
4475 else if (gameInfo.variant == VariantFalcon)
4477 strcpy(title, FALCON);
4480 else if (appData.noChessProgram)
4482 strcpy(title, programName);
4486 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
4488 gtk_window_set_title(GTK_WINDOW(GUI_Window),title);
4494 void DisplayError(message, error)
4501 if (appData.debugMode || appData.matchMode) {
4502 fprintf(stderr, "%s: %s\n", programName, message);
4505 if (appData.debugMode || appData.matchMode) {
4506 fprintf(stderr, "%s: %s: %s\n",
4507 programName, message, strerror(error));
4509 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
4512 ErrorPopUp(_("Error"), message, FALSE);
4516 void DisplayMoveError(message)
4521 DrawPosition(FALSE, NULL);
4522 if (appData.debugMode || appData.matchMode) {
4523 fprintf(stderr, "%s: %s\n", programName, message);
4525 if (appData.popupMoveErrors) {
4526 ErrorPopUp(_("Error"), message, FALSE);
4528 DisplayMessage(message, "");
4533 void DisplayFatalError(message, error, status)
4539 errorExitStatus = status;
4541 fprintf(stderr, "%s: %s\n", programName, message);
4543 fprintf(stderr, "%s: %s: %s\n",
4544 programName, message, strerror(error));
4545 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
4548 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
4549 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
4555 void DisplayInformation(message)
4559 ErrorPopUp(_("Information"), message, TRUE);
4562 void DisplayNote(message)
4566 ErrorPopUp(_("Note"), message, FALSE);
4570 NullXErrorCheck(dpy, error_event)
4572 XErrorEvent *error_event;
4577 void DisplayIcsInteractionTitle(message)
4580 if (oldICSInteractionTitle == NULL) {
4581 /* Magic to find the old window title, adapted from vim */
4582 char *wina = getenv("WINDOWID");
4584 Window win = (Window) atoi(wina);
4585 Window root, parent, *children;
4586 unsigned int nchildren;
4587 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4589 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4590 if (!XQueryTree(xDisplay, win, &root, &parent,
4591 &children, &nchildren)) break;
4592 if (children) XFree((void *)children);
4593 if (parent == root || parent == 0) break;
4596 XSetErrorHandler(oldHandler);
4598 if (oldICSInteractionTitle == NULL) {
4599 oldICSInteractionTitle = "xterm";
4602 printf("\033]0;%s\007", message);
4606 char pendingReplyPrefix[MSG_SIZ];
4607 ProcRef pendingReplyPR;
4609 void AskQuestionProc(w, event, prms, nprms)
4616 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
4620 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
4623 void AskQuestionPopDown()
4625 if (!askQuestionUp) return;
4626 XtPopdown(askQuestionShell);
4627 XtDestroyWidget(askQuestionShell);
4628 askQuestionUp = False;
4631 void AskQuestionReplyAction(w, event, prms, nprms)
4641 reply = XawDialogGetValueString(w = XtParent(w));
4642 strcpy(buf, pendingReplyPrefix);
4643 if (*buf) strcat(buf, " ");
4646 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
4647 AskQuestionPopDown();
4649 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
4652 void AskQuestionCallback(w, client_data, call_data)
4654 XtPointer client_data, call_data;
4659 XtSetArg(args[0], XtNlabel, &name);
4660 XtGetValues(w, args, 1);
4662 if (strcmp(name, _("cancel")) == 0) {
4663 AskQuestionPopDown();
4665 AskQuestionReplyAction(w, NULL, NULL, NULL);
4669 void AskQuestion(title, question, replyPrefix, pr)
4670 char *title, *question, *replyPrefix;
4674 Widget popup, layout, dialog, edit;
4680 strcpy(pendingReplyPrefix, replyPrefix);
4681 pendingReplyPR = pr;
4684 XtSetArg(args[i], XtNresizable, True); i++;
4685 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
4686 askQuestionShell = popup =
4687 XtCreatePopupShell(title, transientShellWidgetClass,
4688 shellWidget, args, i);
4691 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
4692 layoutArgs, XtNumber(layoutArgs));
4695 XtSetArg(args[i], XtNlabel, question); i++;
4696 XtSetArg(args[i], XtNvalue, ""); i++;
4697 XtSetArg(args[i], XtNborderWidth, 0); i++;
4698 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
4701 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
4702 (XtPointer) dialog);
4703 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
4704 (XtPointer) dialog);
4706 XtRealizeWidget(popup);
4707 CatchDeleteWindow(popup, "AskQuestionPopDown");
4709 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
4710 &x, &y, &win_x, &win_y, &mask);
4712 XtSetArg(args[0], XtNx, x - 10);
4713 XtSetArg(args[1], XtNy, y - 30);
4714 XtSetValues(popup, args, 2);
4716 XtPopup(popup, XtGrabExclusive);
4717 askQuestionUp = True;
4719 edit = XtNameToWidget(dialog, "*value");
4720 XtSetKeyboardFocus(popup, edit);
4728 if (*name == NULLCHAR) {
4730 } else if (strcmp(name, "$") == 0) {
4731 putc(BELLCHAR, stderr);
4734 snprintf(buf, sizeof(buf), "%s '%s' &", appData.soundProgram, name);
4742 PlaySound(appData.soundMove);
4748 PlaySound(appData.soundIcsWin);
4754 PlaySound(appData.soundIcsLoss);
4760 PlaySound(appData.soundIcsDraw);
4764 PlayIcsUnfinishedSound()
4766 PlaySound(appData.soundIcsUnfinished);
4772 PlaySound(appData.soundIcsAlarm);
4778 system("stty echo");
4784 system("stty -echo");
4788 Colorize(cc, continuation)
4793 int count, outCount, error;
4795 if (textColors[(int)cc].bg > 0) {
4796 if (textColors[(int)cc].fg > 0) {
4797 sprintf(buf, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
4798 textColors[(int)cc].fg, textColors[(int)cc].bg);
4800 sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
4801 textColors[(int)cc].bg);
4804 if (textColors[(int)cc].fg > 0) {
4805 sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
4806 textColors[(int)cc].fg);
4808 sprintf(buf, "\033[0;%dm", textColors[(int)cc].attr);
4811 count = strlen(buf);
4812 outCount = OutputToProcess(NoProc, buf, count, &error);
4813 if (outCount < count) {
4814 DisplayFatalError(_("Error writing to display"), error, 1);
4817 if (continuation) return;
4820 PlaySound(appData.soundShout);
4823 PlaySound(appData.soundSShout);
4826 PlaySound(appData.soundChannel1);
4829 PlaySound(appData.soundChannel);
4832 PlaySound(appData.soundKibitz);
4835 PlaySound(appData.soundTell);
4837 case ColorChallenge:
4838 PlaySound(appData.soundChallenge);
4841 PlaySound(appData.soundRequest);
4844 PlaySound(appData.soundSeek);
4855 return getpwuid(getuid())->pw_name;
4858 static char *ExpandPathName(path)
4861 static char static_buf[2000];
4862 char *d, *s, buf[2000];
4868 while (*s && isspace(*s))
4877 if (*(s+1) == '/') {
4878 strcpy(d, getpwuid(getuid())->pw_dir);
4883 *strchr(buf, '/') = 0;
4884 pwd = getpwnam(buf);
4887 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
4891 strcpy(d, pwd->pw_dir);
4892 strcat(d, strchr(s+1, '/'));
4903 static char host_name[MSG_SIZ];
4905 #if HAVE_GETHOSTNAME
4906 gethostname(host_name, MSG_SIZ);
4908 #else /* not HAVE_GETHOSTNAME */
4909 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
4910 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
4912 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4914 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4915 #endif /* not HAVE_GETHOSTNAME */
4918 guint delayedEventTimerTag = 0;
4919 DelayedEventCallback delayedEventCallback = 0;
4922 FireDelayedEvent(data)
4926 g_source_remove(delayedEventTimerTag);
4927 delayedEventTimerTag = 0;
4930 delayedEventCallback();
4936 ScheduleDelayedEvent(cb, millisec)
4937 DelayedEventCallback cb; guint millisec;
4939 if(delayedEventTimerTag && delayedEventCallback == cb)
4940 // [HGM] alive: replace, rather than add or flush identical event
4941 g_source_remove(delayedEventTimerTag);
4942 delayedEventCallback = cb;
4943 delayedEventTimerTag = g_timeout_add(millisec,(GSourceFunc) FireDelayedEvent, NULL);
4947 DelayedEventCallback
4950 if (delayedEventTimerTag)
4952 return delayedEventCallback;
4961 CancelDelayedEvent()
4963 if (delayedEventTimerTag)
4965 g_source_remove(delayedEventTimerTag);
4966 delayedEventTimerTag = 0;
4972 guint loadGameTimerTag = 0;
4974 int LoadGameTimerRunning()
4976 return loadGameTimerTag != 0;
4979 int StopLoadGameTimer()
4981 if (loadGameTimerTag != 0) {
4982 g_source_remove(loadGameTimerTag);
4983 loadGameTimerTag = 0;
4991 LoadGameTimerCallback(data)
4995 g_source_remove(loadGameTimerTag);
4996 loadGameTimerTag = 0;
5003 StartLoadGameTimer(millisec)
5007 g_timeout_add( millisec, (GSourceFunc) LoadGameTimerCallback, NULL);
5011 guint analysisClockTag = 0;
5014 AnalysisClockCallback(data)
5017 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
5018 || appData.icsEngineAnalyze)
5020 AnalysisPeriodicEvent(0);
5021 return 1; /* keep on going */
5023 return 0; /* stop timer */
5027 StartAnalysisClock()
5030 g_timeout_add( 2000,(GSourceFunc) AnalysisClockCallback, NULL);
5034 guint clockTimerTag = 0;
5036 int ClockTimerRunning()
5038 return clockTimerTag != 0;
5041 int StopClockTimer()
5043 if (clockTimerTag != 0)
5045 g_source_remove(clockTimerTag);
5056 ClockTimerCallback(data)
5060 g_source_remove(clockTimerTag);
5068 StartClockTimer(millisec)
5071 clockTimerTag = g_timeout_add(millisec,(GSourceFunc) ClockTimerCallback,NULL);
5076 DisplayTimerLabel(w, color, timer, highlight)
5085 if (appData.clockMode) {
5086 sprintf(buf, "%s: %s", color, TimeString(timer));
5088 sprintf(buf, "%s ", color);
5090 gtk_label_set_text(GTK_LABEL(w),buf);
5092 /* check for low time warning */
5093 // Pixel foregroundOrWarningColor = timerForegroundPixel;
5096 // appData.lowTimeWarning &&
5097 // (timer / 1000) < appData.icsAlarmTime)
5098 // foregroundOrWarningColor = lowTimeWarningColor;
5100 // if (appData.clockMode) {
5101 // sprintf(buf, "%s: %s", color, TimeString(timer));
5102 // XtSetArg(args[0], XtNlabel, buf);
5104 // sprintf(buf, "%s ", color);
5105 // XtSetArg(args[0], XtNlabel, buf);
5110 // XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
5111 // XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
5113 // XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
5114 // XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
5117 // XtSetValues(w, args, 3);
5122 DisplayWhiteClock(timeRemaining, highlight)
5126 if(appData.noGUI) return;
5128 DisplayTimerLabel(GUI_Whiteclock, _("White"), timeRemaining, highlight);
5129 if (highlight && WindowIcon == BlackIcon)
5131 WindowIcon = WhiteIcon;
5132 gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
5137 DisplayBlackClock(timeRemaining, highlight)
5141 if(appData.noGUI) return;
5143 DisplayTimerLabel(GUI_Blackclock, _("Black"), timeRemaining, highlight);
5144 if (highlight && WindowIcon == WhiteIcon)
5146 WindowIcon = BlackIcon;
5147 gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
5165 int StartChildProcess(cmdLine, dir, pr)
5172 int to_prog[2], from_prog[2];
5176 if (appData.debugMode) {
5177 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
5180 /* We do NOT feed the cmdLine to the shell; we just
5181 parse it into blank-separated arguments in the
5182 most simple-minded way possible.
5185 strcpy(buf, cmdLine);
5188 while(*p == ' ') p++;
5190 if(*p == '"' || *p == '\'')
5191 p = strchr(++argv[i-1], *p);
5192 else p = strchr(p, ' ');
5193 if (p == NULL) break;
5198 SetUpChildIO(to_prog, from_prog);
5200 if ((pid = fork()) == 0) {
5202 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
5203 close(to_prog[1]); // first close the unused pipe ends
5204 close(from_prog[0]);
5205 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
5206 dup2(from_prog[1], 1);
5207 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
5208 close(from_prog[1]); // and closing again loses one of the pipes!
5209 if(fileno(stderr) >= 2) // better safe than sorry...
5210 dup2(1, fileno(stderr)); /* force stderr to the pipe */
5212 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
5217 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
5219 execvp(argv[0], argv);
5221 /* If we get here, exec failed */
5226 /* Parent process */
5228 close(from_prog[1]);
5230 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5233 cp->fdFrom = from_prog[0];
5234 cp->fdTo = to_prog[1];
5239 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
5240 static RETSIGTYPE AlarmCallBack(int n)
5246 DestroyChildProcess(pr, signalType)
5250 ChildProc *cp = (ChildProc *) pr;
5252 if (cp->kind != CPReal) return;
5254 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
5255 signal(SIGALRM, AlarmCallBack);
5257 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
5258 kill(cp->pid, SIGKILL); // kill it forcefully
5259 wait((int *) 0); // and wait again
5263 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
5265 /* Process is exiting either because of the kill or because of
5266 a quit command sent by the backend; either way, wait for it to die.
5275 InterruptChildProcess(pr)
5278 ChildProc *cp = (ChildProc *) pr;
5280 if (cp->kind != CPReal) return;
5281 (void) kill(cp->pid, SIGINT); /* stop it thinking */
5284 int OpenTelnet(host, port, pr)
5289 char cmdLine[MSG_SIZ];
5291 if (port[0] == NULLCHAR) {
5292 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
5294 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
5296 return StartChildProcess(cmdLine, "", pr);
5299 int OpenTCP(host, port, pr)
5305 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
5306 #else /* !OMIT_SOCKETS */
5308 struct sockaddr_in sa;
5310 unsigned short uport;
5313 if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
5317 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
5318 sa.sin_family = AF_INET;
5319 sa.sin_addr.s_addr = INADDR_ANY;
5320 uport = (unsigned short) 0;
5321 sa.sin_port = htons(uport);
5322 if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
5326 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
5327 if (!(hp = gethostbyname(host))) {
5329 if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
5330 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
5331 hp->h_addrtype = AF_INET;
5333 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
5334 hp->h_addr_list[0] = (char *) malloc(4);
5335 hp->h_addr_list[0][0] = b0;
5336 hp->h_addr_list[0][1] = b1;
5337 hp->h_addr_list[0][2] = b2;
5338 hp->h_addr_list[0][3] = b3;
5343 sa.sin_family = hp->h_addrtype;
5344 uport = (unsigned short) atoi(port);
5345 sa.sin_port = htons(uport);
5346 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
5348 if (connect(s, (struct sockaddr *) &sa,
5349 sizeof(struct sockaddr_in)) < 0) {
5353 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5360 #endif /* !OMIT_SOCKETS */
5365 int OpenCommPort(name, pr)
5372 fd = open(name, 2, 0);
5373 if (fd < 0) return errno;
5375 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5385 int OpenLoopback(pr)
5391 SetUpChildIO(to, from);
5393 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5396 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
5403 int OpenRcmd(host, user, cmd, pr)
5404 char *host, *user, *cmd;
5407 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
5411 #define INPUT_SOURCE_BUF_SIZE 8192
5420 char buf[INPUT_SOURCE_BUF_SIZE];
5425 DoInputCallback(io,cond,data)
5430 /* read input from one of the input source (for example a chess program, ICS, etc).
5431 * and call a function that will handle the input
5434 int count; /* how many bytes did we read */
5438 /* All information (callback function, file descriptor, etc) is
5439 * saved in an InputSource structure
5441 InputSource *is = (InputSource *) data;
5445 count = read(is->fd, is->unused,
5446 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
5450 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
5453 is->unused += count;
5455 /* break input into lines and call the callback function on each
5458 while (p < is->unused)
5460 q = memchr(p, '\n', is->unused - p);
5461 if (q == NULL) break;
5463 (is->func)(is, is->closure, p, q - p, 0);
5466 /* remember not yet used part of the buffer */
5468 while (p < is->unused)
5476 /* read maximum length of input buffer and send the whole buffer
5477 * to the callback function
5479 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
5484 (is->func)(is, is->closure, is->buf, count, error);
5490 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
5497 GIOChannel *channel;
5498 ChildProc *cp = (ChildProc *) pr;
5500 is = (InputSource *) calloc(1, sizeof(InputSource));
5501 is->lineByLine = lineByLine;
5505 is->fd = fileno(stdin);
5507 is->kind = cp->kind;
5508 is->fd = cp->fdFrom;
5511 is->unused = is->buf;
5515 // is->xid = XtAppAddInput(appContext, is->fd,
5516 // (XtPointer) (XtInputReadMask),
5517 // (XtInputCallbackProc) DoInputCallback,
5521 /* TODO: will this work on windows?*/
5522 printf("DEBUG: fd=%d %d\n",is->fd,is);
5524 channel = g_io_channel_unix_new(is->fd);
5525 g_io_channel_set_close_on_unref (channel, TRUE);
5526 is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
5527 is->closure = closure;
5528 return (InputSourceRef) is;
5532 RemoveInputSource(isr)
5535 InputSource *is = (InputSource *) isr;
5537 if (is->sid == 0) return;
5538 g_source_remove(is->sid);
5543 int OutputToProcess(pr, message, count, outError)
5549 static int line = 0;
5550 ChildProc *cp = (ChildProc *) pr;
5555 if (appData.noJoin || !appData.useInternalWrap)
5556 outCount = fwrite(message, 1, count, stdout);
5559 int width = get_term_width();
5560 int len = wrap(NULL, message, count, width, &line);
5561 char *msg = malloc(len);
5565 outCount = fwrite(message, 1, count, stdout);
5568 dbgchk = wrap(msg, message, count, width, &line);
5569 if (dbgchk != len && appData.debugMode)
5570 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
5571 outCount = fwrite(msg, 1, dbgchk, stdout);
5577 outCount = write(cp->fdTo, message, count);
5587 /* Output message to process, with "ms" milliseconds of delay
5588 between each character. This is needed when sending the logon
5589 script to ICC, which for some reason doesn't like the
5590 instantaneous send. */
5591 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
5598 ChildProc *cp = (ChildProc *) pr;
5603 r = write(cp->fdTo, message++, 1);
5616 /**** Animation code by Hugh Fisher, DCS, ANU.
5618 Known problem: if a window overlapping the board is
5619 moved away while a piece is being animated underneath,
5620 the newly exposed area won't be updated properly.
5621 I can live with this.
5623 Known problem: if you look carefully at the animation
5624 of pieces in mono mode, they are being drawn as solid
5625 shapes without interior detail while moving. Fixing
5626 this would be a major complication for minimal return.
5629 /* Masks for XPM pieces. Black and white pieces can have
5630 different shapes, but in the interest of retaining my
5631 sanity pieces must have the same outline on both light
5632 and dark squares, and all pieces must use the same
5633 background square colors/images. */
5635 static int xpmDone = 0;
5638 CreateAnimMasks (pieceDepth)
5645 unsigned long plane;
5648 /* just return for gtk at the moment */
5651 /* Need a bitmap just to get a GC with right depth */
5652 buf = XCreatePixmap(xDisplay, xBoardWindow,
5654 values.foreground = 1;
5655 values.background = 0;
5656 /* Don't use XtGetGC, not read only */
5657 maskGC = XCreateGC(xDisplay, buf,
5658 GCForeground | GCBackground, &values);
5659 XFreePixmap(xDisplay, buf);
5661 buf = XCreatePixmap(xDisplay, xBoardWindow,
5662 squareSize, squareSize, pieceDepth);
5663 values.foreground = XBlackPixel(xDisplay, xScreen);
5664 values.background = XWhitePixel(xDisplay, xScreen);
5665 bufGC = XCreateGC(xDisplay, buf,
5666 GCForeground | GCBackground, &values);
5668 for (piece = WhitePawn; piece <= BlackKing; piece++) {
5669 /* Begin with empty mask */
5670 if(!xpmDone) // [HGM] pieces: keep using existing
5671 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
5672 squareSize, squareSize, 1);
5673 XSetFunction(xDisplay, maskGC, GXclear);
5674 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
5675 0, 0, squareSize, squareSize);
5677 /* Take a copy of the piece */
5682 XSetFunction(xDisplay, bufGC, GXcopy);
5683 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
5685 0, 0, squareSize, squareSize, 0, 0);
5687 /* XOR the background (light) over the piece */
5688 XSetFunction(xDisplay, bufGC, GXxor);
5690 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
5691 0, 0, squareSize, squareSize, 0, 0);
5693 XSetForeground(xDisplay, bufGC, lightSquareColor);
5694 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
5697 /* We now have an inverted piece image with the background
5698 erased. Construct mask by just selecting all the non-zero
5699 pixels - no need to reconstruct the original image. */
5700 XSetFunction(xDisplay, maskGC, GXor);
5702 /* Might be quicker to download an XImage and create bitmap
5703 data from it rather than this N copies per piece, but it
5704 only takes a fraction of a second and there is a much
5705 longer delay for loading the pieces. */
5706 for (n = 0; n < pieceDepth; n ++) {
5707 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
5708 0, 0, squareSize, squareSize,
5714 XFreePixmap(xDisplay, buf);
5715 XFreeGC(xDisplay, bufGC);
5716 XFreeGC(xDisplay, maskGC);
5720 InitAnimState (anim, info)
5722 XWindowAttributes * info;
5727 /* Each buffer is square size, same depth as window */
5728 // anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
5729 // squareSize, squareSize, info->depth);
5730 // anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
5731 // squareSize, squareSize, info->depth);
5733 // /* Create a plain GC for blitting */
5734 // mask = GCForeground | GCBackground | GCFunction |
5735 // GCPlaneMask | GCGraphicsExposures;
5736 // values.foreground = XBlackPixel(xDisplay, xScreen);
5737 // values.background = XWhitePixel(xDisplay, xScreen);
5738 // values.function = GXcopy;
5739 // values.plane_mask = AllPlanes;
5740 // values.graphics_exposures = False;
5741 // anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
5743 // /* Piece will be copied from an existing context at
5744 // the start of each new animation/drag. */
5745 // anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
5747 // /* Outline will be a read-only copy of an existing */
5748 // anim->outlineGC = None;
5754 static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
5755 XWindowAttributes info;
5757 /* for gtk at the moment just ... */
5760 if (xpmDone && gameInfo.variant == old) return;
5761 if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet
5762 // XGetWindowAttributes(xDisplay, xBoardWindow, &info);
5764 // InitAnimState(&game, &info);
5765 // InitAnimState(&player, &info);
5767 /* For XPM pieces, we need bitmaps to use as masks. */
5769 // CreateAnimMasks(info.depth);
5775 static Boolean frameWaiting;
5777 static RETSIGTYPE FrameAlarm (sig)
5780 frameWaiting = False;
5781 /* In case System-V style signals. Needed?? */
5782 signal(SIGALRM, FrameAlarm);
5789 struct itimerval delay;
5791 XSync(xDisplay, False);
5794 frameWaiting = True;
5795 signal(SIGALRM, FrameAlarm);
5796 delay.it_interval.tv_sec =
5797 delay.it_value.tv_sec = time / 1000;
5798 delay.it_interval.tv_usec =
5799 delay.it_value.tv_usec = (time % 1000) * 1000;
5800 setitimer(ITIMER_REAL, &delay, NULL);
5801 while (frameWaiting) pause();
5802 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
5803 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
5804 setitimer(ITIMER_REAL, &delay, NULL);
5814 // XSync(xDisplay, False);
5816 usleep(time * 1000);
5821 /* Convert board position to corner of screen rect and color */
5824 ScreenSquare(column, row, pt, color)
5825 int column; int row; XPoint * pt; int * color;
5828 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
5829 pt->y = lineGap + row * (squareSize + lineGap);
5831 pt->x = lineGap + column * (squareSize + lineGap);
5832 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
5834 *color = SquareColor(row, column);
5837 /* Convert window coords to square */
5840 BoardSquare(x, y, column, row)
5841 int x; int y; int * column; int * row;
5843 *column = EventToSquare(x, BOARD_WIDTH);
5844 if (flipView && *column >= 0)
5845 *column = BOARD_WIDTH - 1 - *column;
5846 *row = EventToSquare(y, BOARD_HEIGHT);
5847 if (!flipView && *row >= 0)
5848 *row = BOARD_HEIGHT - 1 - *row;
5853 #undef Max /* just in case */
5855 #define Max(a, b) ((a) > (b) ? (a) : (b))
5856 #define Min(a, b) ((a) < (b) ? (a) : (b))
5859 SetRect(rect, x, y, width, height)
5860 XRectangle * rect; int x; int y; int width; int height;
5864 rect->width = width;
5865 rect->height = height;
5868 /* Test if two frames overlap. If they do, return
5869 intersection rect within old and location of
5870 that rect within new. */
5873 Intersect(old, new, size, area, pt)
5874 XPoint * old; XPoint * new;
5875 int size; XRectangle * area; XPoint * pt;
5877 if (old->x > new->x + size || new->x > old->x + size ||
5878 old->y > new->y + size || new->y > old->y + size) {
5881 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
5882 size - abs(old->x - new->x), size - abs(old->y - new->y));
5883 pt->x = Max(old->x - new->x, 0);
5884 pt->y = Max(old->y - new->y, 0);
5889 /* For two overlapping frames, return the rect(s)
5890 in the old that do not intersect with the new. */
5893 CalcUpdateRects(old, new, size, update, nUpdates)
5894 XPoint * old; XPoint * new; int size;
5895 XRectangle update[]; int * nUpdates;
5899 /* If old = new (shouldn't happen) then nothing to draw */
5900 if (old->x == new->x && old->y == new->y) {
5904 /* Work out what bits overlap. Since we know the rects
5905 are the same size we don't need a full intersect calc. */
5907 /* Top or bottom edge? */
5908 if (new->y > old->y) {
5909 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
5911 } else if (old->y > new->y) {
5912 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
5913 size, old->y - new->y);
5916 /* Left or right edge - don't overlap any update calculated above. */
5917 if (new->x > old->x) {
5918 SetRect(&(update[count]), old->x, Max(new->y, old->y),
5919 new->x - old->x, size - abs(new->y - old->y));
5921 } else if (old->x > new->x) {
5922 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
5923 old->x - new->x, size - abs(new->y - old->y));
5930 /* Generate a series of frame coords from start->mid->finish.
5931 The movement rate doubles until the half way point is
5932 reached, then halves back down to the final destination,
5933 which gives a nice slow in/out effect. The algorithmn
5934 may seem to generate too many intermediates for short
5935 moves, but remember that the purpose is to attract the
5936 viewers attention to the piece about to be moved and
5937 then to where it ends up. Too few frames would be less
5941 Tween(start, mid, finish, factor, frames, nFrames)
5942 XPoint * start; XPoint * mid;
5943 XPoint * finish; int factor;
5944 XPoint frames[]; int * nFrames;
5946 int fraction, n, count;
5950 /* Slow in, stepping 1/16th, then 1/8th, ... */
5952 for (n = 0; n < factor; n++)
5954 for (n = 0; n < factor; n++) {
5955 frames[count].x = start->x + (mid->x - start->x) / fraction;
5956 frames[count].y = start->y + (mid->y - start->y) / fraction;
5958 fraction = fraction / 2;
5962 frames[count] = *mid;
5965 /* Slow out, stepping 1/2, then 1/4, ... */
5967 for (n = 0; n < factor; n++) {
5968 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
5969 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
5971 fraction = fraction * 2;
5976 /* Draw a piece on the screen without disturbing what's there */
5979 SelectGCMask(piece, clip, outline, mask)
5980 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
5984 /* Bitmap for piece being moved. */
5985 if (appData.monoMode) {
5986 *mask = *pieceToSolid(piece);
5987 } else if (useImages) {
5989 *mask = xpmMask[piece];
5991 *mask = ximMaskPm[piece];
5994 *mask = *pieceToSolid(piece);
5997 /* GC for piece being moved. Square color doesn't matter, but
5998 since it gets modified we make a copy of the original. */
6000 if (appData.monoMode)
6005 if (appData.monoMode)
6010 // XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
6012 /* Outline only used in mono mode and is not modified */
6014 *outline = bwPieceGC;
6016 *outline = wbPieceGC;
6020 OverlayPiece(piece, clip, outline, dest)
6021 ChessSquare piece; GC clip; GC outline; Drawable dest;
6026 /* Draw solid rectangle which will be clipped to shape of piece */
6027 // XFillRectangle(xDisplay, dest, clip,
6028 // 0, 0, squareSize, squareSize)
6030 if (appData.monoMode)
6031 /* Also draw outline in contrasting color for black
6032 on black / white on white cases */
6033 // XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
6034 // 0, 0, squareSize, squareSize, 0, 0, 1)
6037 /* Copy the piece */
6042 // XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
6044 // 0, 0, squareSize, squareSize,
6049 /* Animate the movement of a single piece */
6052 BeginAnimation(anim, piece, startColor, start)
6060 /* The old buffer is initialised with the start square (empty) */
6061 BlankSquare(0, 0, startColor, EmptySquare, anim->saveBuf);
6062 anim->prevFrame = *start;
6064 /* The piece will be drawn using its own bitmap as a matte */
6065 // SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
6066 // XSetClipMask(xDisplay, anim->pieceGC, mask);
6070 AnimationFrame(anim, frame, piece)
6075 XRectangle updates[4];
6080 /* Save what we are about to draw into the new buffer */
6081 // XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
6082 // frame->x, frame->y, squareSize, squareSize,
6085 /* Erase bits of the previous frame */
6086 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
6087 /* Where the new frame overlapped the previous,
6088 the contents in newBuf are wrong. */
6089 // XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
6090 // overlap.x, overlap.y,
6091 // overlap.width, overlap.height,
6093 /* Repaint the areas in the old that don't overlap new */
6094 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
6095 for (i = 0; i < count; i++)
6096 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6097 // updates[i].x - anim->prevFrame.x,
6098 // updates[i].y - anim->prevFrame.y,
6099 // updates[i].width, updates[i].height,
6100 // updates[i].x, updates[i].y)
6103 /* Easy when no overlap */
6104 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6105 // 0, 0, squareSize, squareSize,
6106 // anim->prevFrame.x, anim->prevFrame.y);
6109 /* Save this frame for next time round */
6110 // XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
6111 // 0, 0, squareSize, squareSize,
6113 anim->prevFrame = *frame;
6115 /* Draw piece over original screen contents, not current,
6116 and copy entire rect. Wipes out overlapping piece images. */
6117 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
6118 // XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
6119 // 0, 0, squareSize, squareSize,
6120 // frame->x, frame->y);
6124 EndAnimation (anim, finish)
6128 XRectangle updates[4];
6133 /* The main code will redraw the final square, so we
6134 only need to erase the bits that don't overlap. */
6135 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
6136 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
6137 for (i = 0; i < count; i++)
6138 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6139 // updates[i].x - anim->prevFrame.x,
6140 // updates[i].y - anim->prevFrame.y,
6141 // updates[i].width, updates[i].height,
6142 // updates[i].x, updates[i].y)
6145 // XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6146 // 0, 0, squareSize, squareSize,
6147 // anim->prevFrame.x, anim->prevFrame.y);
6152 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
6154 ChessSquare piece; int startColor;
6155 XPoint * start; XPoint * finish;
6156 XPoint frames[]; int nFrames;
6160 BeginAnimation(anim, piece, startColor, start);
6161 for (n = 0; n < nFrames; n++) {
6162 AnimationFrame(anim, &(frames[n]), piece);
6163 FrameDelay(appData.animSpeed);
6165 EndAnimation(anim, finish);
6168 /* Main control logic for deciding what to animate and how */
6171 AnimateMove(board, fromX, fromY, toX, toY)
6180 XPoint start, finish, mid;
6181 XPoint frames[kFactor * 2 + 1];
6182 int nFrames, startColor, endColor;
6184 /* Are we animating? */
6185 if (!appData.animate || appData.blindfold)
6188 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
6189 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
6190 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
6192 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
6193 piece = board[fromY][fromX];
6194 if (piece >= EmptySquare) return;
6199 hop = (piece == WhiteKnight || piece == BlackKnight);
6202 if (appData.debugMode) {
6203 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
6204 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
6205 piece, fromX, fromY, toX, toY); }
6207 ScreenSquare(fromX, fromY, &start, &startColor);
6208 ScreenSquare(toX, toY, &finish, &endColor);
6211 /* Knight: make diagonal movement then straight */
6212 if (abs(toY - fromY) < abs(toX - fromX)) {
6213 mid.x = start.x + (finish.x - start.x) / 2;
6217 mid.y = start.y + (finish.y - start.y) / 2;
6220 mid.x = start.x + (finish.x - start.x) / 2;
6221 mid.y = start.y + (finish.y - start.y) / 2;
6224 /* Don't use as many frames for very short moves */
6225 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
6226 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
6228 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
6229 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
6231 /* Be sure end square is redrawn */
6232 damage[toY][toX] = True;
6236 DragPieceBegin(x, y)
6239 int boardX, boardY, color;
6242 /* Are we animating? */
6243 if (!appData.animateDragging || appData.blindfold)
6246 /* Figure out which square we start in and the
6247 mouse position relative to top left corner. */
6248 BoardSquare(x, y, &boardX, &boardY);
6249 player.startBoardX = boardX;
6250 player.startBoardY = boardY;
6251 ScreenSquare(boardX, boardY, &corner, &color);
6252 player.startSquare = corner;
6253 player.startColor = color;
6254 /* As soon as we start dragging, the piece will jump slightly to
6255 be centered over the mouse pointer. */
6256 player.mouseDelta.x = squareSize/2;
6257 player.mouseDelta.y = squareSize/2;
6258 /* Initialise animation */
6259 player.dragPiece = PieceForSquare(boardX, boardY);
6261 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
6262 player.dragActive = True;
6263 BeginAnimation(&player, player.dragPiece, color, &corner);
6264 /* Mark this square as needing to be redrawn. Note that
6265 we don't remove the piece though, since logically (ie
6266 as seen by opponent) the move hasn't been made yet. */
6267 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
6268 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
6269 // XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
6270 // corner.x, corner.y, squareSize, squareSize,
6271 // 0, 0); // [HGM] zh: unstack in stead of grab
6272 damage[boardY][boardX] = True;
6274 player.dragActive = False;
6284 /* Are we animating? */
6285 if (!appData.animateDragging || appData.blindfold)
6289 if (! player.dragActive)
6291 /* Move piece, maintaining same relative position
6292 of mouse within square */
6293 corner.x = x - player.mouseDelta.x;
6294 corner.y = y - player.mouseDelta.y;
6295 AnimationFrame(&player, &corner, player.dragPiece);
6297 if (appData.highlightDragging) {
6299 BoardSquare(x, y, &boardX, &boardY);
6300 SetHighlights(fromX, fromY, boardX, boardY);
6309 int boardX, boardY, color;
6312 /* Are we animating? */
6313 if (!appData.animateDragging || appData.blindfold)
6317 if (! player.dragActive)
6319 /* Last frame in sequence is square piece is
6320 placed on, which may not match mouse exactly. */
6321 BoardSquare(x, y, &boardX, &boardY);
6322 ScreenSquare(boardX, boardY, &corner, &color);
6323 EndAnimation(&player, &corner);
6325 /* Be sure end square is redrawn */
6326 damage[boardY][boardX] = True;
6328 /* This prevents weird things happening with fast successive
6329 clicks which on my Sun at least can cause motion events
6330 without corresponding press/release. */
6331 player.dragActive = False;
6334 /* Handle expose event while piece being dragged */
6339 if (!player.dragActive || appData.blindfold)
6342 /* What we're doing: logically, the move hasn't been made yet,
6343 so the piece is still in it's original square. But visually
6344 it's being dragged around the board. So we erase the square
6345 that the piece is on and draw it at the last known drag point. */
6346 BlankSquare(player.startSquare.x, player.startSquare.y,
6347 player.startColor, EmptySquare, xBoardWindow);
6348 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
6349 damage[player.startBoardY][player.startBoardX] = TRUE;
6352 #include <sys/ioctl.h>
6353 int get_term_width()
6355 int fd, default_width;
6358 default_width = 79; // this is FICS default anyway...
6360 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
6362 if (!ioctl(fd, TIOCGSIZE, &win))
6363 default_width = win.ts_cols;
6364 #elif defined(TIOCGWINSZ)
6366 if (!ioctl(fd, TIOCGWINSZ, &win))
6367 default_width = win.ws_col;
6369 return default_width;
6372 void update_ics_width()
6374 static int old_width = 0;
6375 int new_width = get_term_width();
6377 if (old_width != new_width)
6378 ics_printf("set width %d\n", new_width);
6379 old_width = new_width;
6382 void NotifyFrontendLogin()