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, 2010 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
66 # if HAVE_SYS_SOCKET_H
67 # include <sys/socket.h>
68 # include <netinet/in.h>
70 # else /* not HAVE_SYS_SOCKET_H */
71 # if HAVE_LAN_SOCKET_H
72 # include <lan/socket.h>
74 # include <lan/netdb.h>
75 # else /* not HAVE_LAN_SOCKET_H */
76 # define OMIT_SOCKETS 1
77 # endif /* not HAVE_LAN_SOCKET_H */
78 # endif /* not HAVE_SYS_SOCKET_H */
79 #endif /* !OMIT_SOCKETS */
84 #else /* not STDC_HEADERS */
85 extern char *getenv();
88 # else /* not HAVE_STRING_H */
90 # endif /* not HAVE_STRING_H */
91 #endif /* not STDC_HEADERS */
94 # include <sys/fcntl.h>
95 #else /* not HAVE_SYS_FCNTL_H */
98 # endif /* HAVE_FCNTL_H */
99 #endif /* not HAVE_SYS_FCNTL_H */
101 #if HAVE_SYS_SYSTEMINFO_H
102 # include <sys/systeminfo.h>
103 #endif /* HAVE_SYS_SYSTEMINFO_H */
105 #if TIME_WITH_SYS_TIME
106 # include <sys/time.h>
110 # include <sys/time.h>
121 # include <sys/wait.h>
126 # define NAMLEN(dirent) strlen((dirent)->d_name)
127 # define HAVE_DIR_STRUCT
129 # define dirent direct
130 # define NAMLEN(dirent) (dirent)->d_namlen
132 # include <sys/ndir.h>
133 # define HAVE_DIR_STRUCT
136 # include <sys/dir.h>
137 # define HAVE_DIR_STRUCT
141 # define HAVE_DIR_STRUCT
145 #include <X11/Intrinsic.h>
146 #include <X11/StringDefs.h>
147 #include <X11/Shell.h>
148 #include <X11/cursorfont.h>
149 #include <X11/Xatom.h>
150 #include <X11/Xmu/Atoms.h>
152 #include <X11/Xaw3d/Dialog.h>
153 #include <X11/Xaw3d/Form.h>
154 #include <X11/Xaw3d/List.h>
155 #include <X11/Xaw3d/Label.h>
156 #include <X11/Xaw3d/SimpleMenu.h>
157 #include <X11/Xaw3d/SmeBSB.h>
158 #include <X11/Xaw3d/SmeLine.h>
159 #include <X11/Xaw3d/Box.h>
160 #include <X11/Xaw3d/MenuButton.h>
161 #include <X11/Xaw3d/Text.h>
162 #include <X11/Xaw3d/AsciiText.h>
164 #include <X11/Xaw/Dialog.h>
165 #include <X11/Xaw/Form.h>
166 #include <X11/Xaw/List.h>
167 #include <X11/Xaw/Label.h>
168 #include <X11/Xaw/SimpleMenu.h>
169 #include <X11/Xaw/SmeBSB.h>
170 #include <X11/Xaw/SmeLine.h>
171 #include <X11/Xaw/Box.h>
172 #include <X11/Xaw/MenuButton.h>
173 #include <X11/Xaw/Text.h>
174 #include <X11/Xaw/AsciiText.h>
177 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
182 #include "pixmaps/pixmaps.h"
183 #define IMAGE_EXT "xpm"
185 #define IMAGE_EXT "xim"
186 #include "bitmaps/bitmaps.h"
189 #include "bitmaps/icon_white.bm"
190 #include "bitmaps/icon_black.bm"
191 #include "bitmaps/checkmark.bm"
193 #include "frontend.h"
195 #include "backendz.h"
199 #include "xgamelist.h"
200 #include "xhistory.h"
201 #include "xedittags.h"
204 // must be moved to xengineoutput.h
206 void EngineOutputProc P((Widget w, XEvent *event,
207 String *prms, Cardinal *nprms));
208 void EvalGraphProc P((Widget w, XEvent *event,
209 String *prms, Cardinal *nprms));
216 #define usleep(t) _sleep2(((t)+500)/1000)
220 # define _(s) gettext (s)
221 # define N_(s) gettext_noop (s)
239 int main P((int argc, char **argv));
240 FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
241 char *init_path, char *mode, int (*show_entry)(), char **name_return));
242 RETSIGTYPE CmailSigHandler P((int sig));
243 RETSIGTYPE IntSigHandler P((int sig));
244 RETSIGTYPE TermSizeSigHandler P((int sig));
245 void CreateGCs P((void));
246 void CreateXIMPieces P((void));
247 void CreateXPMPieces P((void));
248 void CreateXPMBoard P((char *s, int n));
249 void CreatePieces P((void));
250 void CreatePieceMenus P((void));
251 Widget CreateMenuBar P((Menu *mb));
252 Widget CreateButtonBar P ((MenuItem *mi));
253 char *FindFont P((char *pattern, int targetPxlSize));
254 void PieceMenuPopup P((Widget w, XEvent *event,
255 String *params, Cardinal *num_params));
256 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
257 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
258 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
259 u_int wreq, u_int hreq));
260 void CreateGrid P((void));
261 int EventToSquare P((int x, int limit));
262 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
263 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
264 void HandleUserMove P((Widget w, XEvent *event,
265 String *prms, Cardinal *nprms));
266 void AnimateUserMove P((Widget w, XEvent * event,
267 String * params, Cardinal * nParams));
268 void HandlePV P((Widget w, XEvent * event,
269 String * params, Cardinal * nParams));
270 void SelectPV P((Widget w, XEvent * event,
271 String * params, Cardinal * nParams));
272 void StopPV P((Widget w, XEvent * event,
273 String * params, Cardinal * nParams));
274 void WhiteClock P((Widget w, XEvent *event,
275 String *prms, Cardinal *nprms));
276 void BlackClock P((Widget w, XEvent *event,
277 String *prms, Cardinal *nprms));
278 void DrawPositionProc P((Widget w, XEvent *event,
279 String *prms, Cardinal *nprms));
280 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
282 void CommentClick P((Widget w, XEvent * event,
283 String * params, Cardinal * nParams));
284 void CommentPopUp P((char *title, char *label));
285 void CommentPopDown P((void));
286 void CommentCallback P((Widget w, XtPointer client_data,
287 XtPointer call_data));
288 void ICSInputBoxPopUp P((void));
289 void ICSInputBoxPopDown P((void));
290 void FileNamePopUp P((char *label, char *def,
291 FileProc proc, char *openMode));
292 void FileNamePopDown P((void));
293 void FileNameCallback P((Widget w, XtPointer client_data,
294 XtPointer call_data));
295 void FileNameAction P((Widget w, XEvent *event,
296 String *prms, Cardinal *nprms));
297 void AskQuestionReplyAction P((Widget w, XEvent *event,
298 String *prms, Cardinal *nprms));
299 void AskQuestionProc P((Widget w, XEvent *event,
300 String *prms, Cardinal *nprms));
301 void AskQuestionPopDown P((void));
302 void PromotionPopDown P((void));
303 void PromotionCallback P((Widget w, XtPointer client_data,
304 XtPointer call_data));
305 void EditCommentPopDown P((void));
306 void EditCommentCallback P((Widget w, XtPointer client_data,
307 XtPointer call_data));
308 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
309 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
310 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
311 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
313 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
315 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
317 void LoadPositionProc P((Widget w, XEvent *event,
318 String *prms, Cardinal *nprms));
319 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
321 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
323 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
325 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
327 void PastePositionProc P((Widget w, XEvent *event, String *prms,
329 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
330 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
331 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
332 void SavePositionProc P((Widget w, XEvent *event,
333 String *prms, Cardinal *nprms));
334 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
335 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
337 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
338 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
339 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
341 void MachineWhiteProc P((Widget w, XEvent *event,
342 String *prms, Cardinal *nprms));
343 void AnalyzeModeProc P((Widget w, XEvent *event,
344 String *prms, Cardinal *nprms));
345 void AnalyzeFileProc P((Widget w, XEvent *event,
346 String *prms, Cardinal *nprms));
347 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
349 void IcsClientProc P((Widget w, XEvent *event, String *prms,
351 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
352 void EditPositionProc P((Widget w, XEvent *event,
353 String *prms, Cardinal *nprms));
354 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
355 void EditCommentProc P((Widget w, XEvent *event,
356 String *prms, Cardinal *nprms));
357 void IcsInputBoxProc P((Widget w, XEvent *event,
358 String *prms, Cardinal *nprms));
359 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
360 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
361 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
362 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
363 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
364 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
365 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
366 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
367 void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
368 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
369 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
370 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
371 void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
372 void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
373 void StopObservingProc P((Widget w, XEvent *event, String *prms,
375 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
377 void UploadProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
378 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
379 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
380 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
381 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
382 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
383 void AnnotateProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
384 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
386 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
388 void MoveNowProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
389 void AlwaysQueenProc P((Widget w, XEvent *event, String *prms,
391 void AnimateDraggingProc P((Widget w, XEvent *event, String *prms,
393 void AnimateMovingProc P((Widget w, XEvent *event, String *prms,
395 void AutocommProc P((Widget w, XEvent *event, String *prms,
397 void AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
398 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
399 void AutobsProc P((Widget w, XEvent *event, String *prms,
401 void AutoraiseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
402 void AutosaveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
403 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
404 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
406 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
407 void GetMoveListProc P((Widget w, XEvent *event, String *prms,
409 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
411 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
413 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
414 void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
415 void OldSaveStyleProc P((Widget w, XEvent *event, String *prms,
417 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
419 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
421 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
423 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
425 void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
426 void QuietPlayProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
427 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
429 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
431 void HideThinkingProc P((Widget w, XEvent *event, String *prms,
433 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
435 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
436 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
437 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
438 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
439 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
440 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
441 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
442 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
443 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
444 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
445 void Iconify P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
446 void DisplayMove P((int moveNumber));
447 void DisplayTitle P((char *title));
448 void ICSInitScript P((void));
449 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
450 void ErrorPopUp P((char *title, char *text, int modal));
451 void ErrorPopDown P((void));
452 static char *ExpandPathName P((char *path));
453 static void CreateAnimVars P((void));
454 static void DragPieceMove P((int x, int y));
455 static void DrawDragPiece P((void));
456 char *ModeToWidgetName P((GameMode mode));
457 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
458 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
459 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
460 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
461 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
462 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
463 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
464 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
465 void GameListOptionsPopDown P(());
466 void ShufflePopDown P(());
467 void EnginePopDown P(());
468 void UciPopDown P(());
469 void TimeControlPopDown P(());
470 void NewVariantPopDown P(());
471 void SettingsPopDown P(());
472 void update_ics_width P(());
473 int get_term_width P(());
474 int CopyMemoProc P(());
475 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
476 Boolean IsDrawArrowEnabled P(());
479 * XBoard depends on Xt R4 or higher
481 int xtVersion = XtSpecificationRelease;
486 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
487 jailSquareColor, highlightSquareColor, premoveHighlightColor;
488 Pixel lowTimeWarningColor;
489 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
490 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
491 wjPieceGC, bjPieceGC, prelineGC, countGC;
492 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
493 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
494 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
495 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
496 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
497 ICSInputShell, fileNameShell, askQuestionShell;
498 Widget historyShell, evalGraphShell, gameListShell;
499 int hOffset; // [HGM] dual
500 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
501 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
502 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
503 Font clockFontID, coordFontID, countFontID;
504 XFontStruct *clockFontStruct, *coordFontStruct, *countFontStruct;
505 XtAppContext appContext;
507 char *oldICSInteractionTitle;
511 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
513 Position commentX = -1, commentY = -1;
514 Dimension commentW, commentH;
515 typedef unsigned int BoardSize;
517 Boolean chessProgram;
519 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
520 int squareSize, smallLayout = 0, tinyLayout = 0,
521 marginW, marginH, // [HGM] for run-time resizing
522 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
523 ICSInputBoxUp = False, askQuestionUp = False,
524 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
525 editUp = False, errorUp = False, errorExitStatus = -1, lineGap;
526 Pixel timerForegroundPixel, timerBackgroundPixel;
527 Pixel buttonForegroundPixel, buttonBackgroundPixel;
528 char *chessDir, *programName, *programVersion,
529 *gameCopyFilename, *gamePasteFilename;
530 Boolean alwaysOnTop = False;
531 Boolean saveSettingsOnExit;
532 char *settingsFileName;
533 char *icsTextMenuString;
535 char *firstChessProgramNames;
536 char *secondChessProgramNames;
538 WindowPlacement wpMain;
539 WindowPlacement wpConsole;
540 WindowPlacement wpComment;
541 WindowPlacement wpMoveHistory;
542 WindowPlacement wpEvalGraph;
543 WindowPlacement wpEngineOutput;
544 WindowPlacement wpGameList;
545 WindowPlacement wpTags;
549 Pixmap pieceBitmap[2][(int)BlackPawn];
550 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
551 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
552 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
553 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
554 Pixmap xpmBoardBitmap[2];
555 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
556 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
557 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
558 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
559 XImage *ximLightSquare, *ximDarkSquare;
562 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
563 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
565 #define White(piece) ((int)(piece) < (int)BlackPawn)
567 /* Variables for doing smooth animation. This whole thing
568 would be much easier if the board was double-buffered,
569 but that would require a fairly major rewrite. */
574 GC blitGC, pieceGC, outlineGC;
575 XPoint startSquare, prevFrame, mouseDelta;
579 int startBoardX, startBoardY;
582 /* There can be two pieces being animated at once: a player
583 can begin dragging a piece before the remote opponent has moved. */
585 static AnimState game, player;
587 /* Bitmaps for use as masks when drawing XPM pieces.
588 Need one for each black and white piece. */
589 static Pixmap xpmMask[BlackKing + 1];
591 /* This magic number is the number of intermediate frames used
592 in each half of the animation. For short moves it's reduced
593 by 1. The total number of frames will be factor * 2 + 1. */
596 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
598 MenuItem fileMenu[] = {
599 {N_("New Game Ctrl+N"), "New Game", ResetProc},
600 {N_("New Shuffle Game ..."), "New Shuffle Game", ShuffleMenuProc},
601 {N_("New Variant ... Alt+Shift+V"), "New Variant", NewVariantProc}, // [HGM] variant: not functional yet
602 {"----", NULL, NothingProc},
603 {N_("Load Game Ctrl+O"), "Load Game", LoadGameProc},
604 {N_("Load Position Ctrl+Shift+O"), "Load Position", LoadPositionProc},
605 // {N_("Load Next Game"), "Load Next Game", LoadNextGameProc},
606 // {N_("Load Previous Game"), "Load Previous Game", LoadPrevGameProc},
607 // {N_("Reload Same Game"), "Reload Same Game", ReloadGameProc},
608 {"----", NULL, NothingProc},
609 // {N_("Load Next Position"), "Load Next Position", LoadNextPositionProc},
610 // {N_("Load Previous Position"), "Load Previous Position", LoadPrevPositionProc},
611 // {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
612 {N_("Save Game Ctrl+S"), "Save Game", SaveGameProc},
613 {N_("Save Position Ctrl+Shift+S"), "Save Position", SavePositionProc},
614 {"----", NULL, NothingProc},
615 {N_("Mail Move"), "Mail Move", MailMoveProc},
616 {N_("Reload CMail Message"), "Reload CMail Message", ReloadCmailMsgProc},
617 {"----", NULL, NothingProc},
618 {N_("Quit Ctr+Q"), "Exit", QuitProc},
622 MenuItem editMenu[] = {
623 {N_("Copy Game Ctrl+C"), "Copy Game", CopyGameProc},
624 {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
625 {"----", NULL, NothingProc},
626 {N_("Paste Game Ctrl+V"), "Paste Game", PasteGameProc},
627 {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
628 {"----", NULL, NothingProc},
629 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
630 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
631 {N_("Edit Tags"), "Edit Tags", EditTagsProc},
632 {N_("Edit Comment"), "Edit Comment", EditCommentProc},
633 {"----", NULL, NothingProc},
634 {N_("Revert Home"), "Revert", RevertProc},
635 {N_("Annotate"), "Annotate", AnnotateProc},
636 {N_("Truncate Game End"), "Truncate Game", TruncateGameProc},
637 {"----", NULL, NothingProc},
638 {N_("Backward Alt+Left"), "Backward", BackwardProc},
639 {N_("Forward Alt+Right"), "Forward", ForwardProc},
640 {N_("Back to Start Alt+Home"), "Back to Start", ToStartProc},
641 {N_("Forward to End Alt+End"), "Forward to End", ToEndProc},
645 MenuItem viewMenu[] = {
646 {N_("Flip View F2"), "Flip View", FlipViewProc},
647 {"----", NULL, NothingProc},
648 {N_("Engine Output Alt+Shift+O"), "Show Engine Output", EngineOutputProc},
649 {N_("Move History Alt+Shift+H"), "Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
650 {N_("Evaluation Graph Alt+Shift+E"), "Show Evaluation Graph", EvalGraphProc},
651 {N_("Game List Alt+Shift+G"), "Show Game List", ShowGameListProc},
652 {"----", NULL, NothingProc},
653 {N_("Tags"), "Show Tags", EditTagsProc},
654 {N_("Comments"), "Show Comments", EditCommentProc},
655 {N_("ICS Input Box"), "ICS Input Box", IcsInputBoxProc},
659 MenuItem modeMenu[] = {
660 {N_("Machine White Ctrl+W"), "Machine White", MachineWhiteProc},
661 {N_("Machine Black Ctrl+B"), "Machine Black", MachineBlackProc},
662 {N_("Two Machines Ctrl+T"), "Two Machines", TwoMachinesProc},
663 {N_("Analysis Mode Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
664 {N_("Analyze File Ctrl+F"), "Analyze File", AnalyzeFileProc },
665 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
666 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
667 {N_("Training"), "Training", TrainingProc},
668 {N_("ICS Client"), "ICS Client", IcsClientProc},
669 {"----", NULL, NothingProc},
670 {N_("Pause Pause"), "Pause", PauseProc},
674 MenuItem actionMenu[] = {
675 {N_("Accept F3"), "Accept", AcceptProc},
676 {N_("Decline F4"), "Decline", DeclineProc},
677 {N_("Rematch F12"), "Rematch", RematchProc},
678 {"----", NULL, NothingProc},
679 {N_("Call Flag F5"), "Call Flag", CallFlagProc},
680 {N_("Draw F6"), "Draw", DrawProc},
681 {N_("Adjourn F7"), "Adjourn", AdjournProc},
682 {N_("Abort F8"),"Abort", AbortProc},
683 {N_("Resign F9"), "Resign", ResignProc},
684 {"----", NULL, NothingProc},
685 {N_("Stop Observing F10"), "Stop Observing", StopObservingProc},
686 {N_("Stop Examining F11"), "Stop Examining", StopExaminingProc},
687 {N_("Upload to Examine"), "Upload to Examine", UploadProc},
688 {"----", NULL, NothingProc},
689 {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
690 {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
691 {N_("Adjudicate Draw"), "Adjudicate Draw", AdjuDrawProc},
695 MenuItem engineMenu[] = {
696 {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
697 {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
698 {"----", NULL, NothingProc},
699 {N_("Hint"), "Hint", HintProc},
700 {N_("Book"), "Book", BookProc},
701 {"----", NULL, NothingProc},
702 {N_("Move Now Ctrl+M"), "Move Now", MoveNowProc},
703 {N_("Retract Move Ctrl+X"), "Retract Move", RetractMoveProc},
707 MenuItem optionsMenu[] = {
708 {N_("Time Control ... Alt+Shift+T"), "Time Control", TimeControlProc},
709 {N_("Common Engine ... Alt+Shift+U"), "Common Engine", UciMenuProc},
710 {N_("Adjudications ... Alt+Shift+J"), "Adjudications", EngineMenuProc},
711 {N_("Game List ..."), "Game List", GameListOptionsPopUp},
712 {"----", NULL, NothingProc},
713 {N_("Always Queen Ctrl+Shift+Q"), "Always Queen", AlwaysQueenProc},
714 {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
715 {N_("Animate Moving Ctrl+Shift+A"), "Animate Moving", AnimateMovingProc},
716 {N_("Auto Comment"), "Auto Comment", AutocommProc},
717 {N_("Auto Flag Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
718 {N_("Auto Flip View"), "Auto Flip View", AutoflipProc},
719 {N_("Auto Observe"), "Auto Observe", AutobsProc},
720 {N_("Auto Raise Board"), "Auto Raise Board", AutoraiseProc},
721 {N_("Auto Save"), "Auto Save", AutosaveProc},
722 {N_("Blindfold"), "Blindfold", BlindfoldProc},
723 {N_("Flash Moves"), "Flash Moves", FlashMovesProc},
724 {N_("Get Move List"), "Get Move List", GetMoveListProc},
726 {N_("Highlight Dragging"), "Highlight Dragging", HighlightDraggingProc},
728 {N_("Highlight Last Move"), "Highlight Last Move", HighlightLastMoveProc},
729 {N_("Move Sound"), "Move Sound", MoveSoundProc},
730 {N_("ICS Alarm"), "ICS Alarm", IcsAlarmProc},
731 {N_("Old Save Style"), "Old Save Style", OldSaveStyleProc},
732 {N_("Periodic Updates"), "Periodic Updates", PeriodicUpdatesProc},
733 {N_("Ponder Next Move Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
734 {N_("Popup Exit Message"), "Popup Exit Message", PopupExitMessageProc},
735 {N_("Popup Move Errors"), "Popup Move Errors", PopupMoveErrorsProc},
736 {N_("Premove"), "Premove", PremoveProc},
737 {N_("Quiet Play"), "Quiet Play", QuietPlayProc},
738 {N_("Show Coords"), "Show Coords", ShowCoordsProc},
739 {N_("Hide Thinking Ctrl+Shift+H"), "Hide Thinking", HideThinkingProc},
740 {N_("Test Legality Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
741 {"----", NULL, NothingProc},
742 {N_("Save Settings Now"), "Save Settings Now", SaveSettingsProc},
743 {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
747 MenuItem helpMenu[] = {
748 {N_("Info XBoard"), "Info XBoard", InfoProc},
749 {N_("Man XBoard F1"), "Man XBoard", ManProc},
750 {"----", NULL, NothingProc},
751 {N_("About XBoard"), "About XBoard", AboutProc},
756 {N_("File"), "File", fileMenu},
757 {N_("Edit"), "Edit", editMenu},
758 {N_("View"), "View", viewMenu},
759 {N_("Mode"), "Mode", modeMenu},
760 {N_("Action"), "Action", actionMenu},
761 {N_("Engine"), "Engine", engineMenu},
762 {N_("Options"), "Options", optionsMenu},
763 {N_("Help"), "Help", helpMenu},
767 #define PAUSE_BUTTON "P"
768 MenuItem buttonBar[] = {
769 {"<<", "<<", ToStartProc},
770 {"<", "<", BackwardProc},
771 {PAUSE_BUTTON, PAUSE_BUTTON, PauseProc},
772 {">", ">", ForwardProc},
773 {">>", ">>", ToEndProc},
777 #define PIECE_MENU_SIZE 18
778 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
779 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
780 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
781 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
782 N_("Empty square"), N_("Clear board") },
783 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
784 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
785 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
786 N_("Empty square"), N_("Clear board") }
788 /* must be in same order as PieceMenuStrings! */
789 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
790 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
791 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
792 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
793 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
794 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
795 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
796 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
797 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
800 #define DROP_MENU_SIZE 6
801 String dropMenuStrings[DROP_MENU_SIZE] = {
802 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
804 /* must be in same order as PieceMenuStrings! */
805 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
806 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
807 WhiteRook, WhiteQueen
815 DropMenuEnables dmEnables[] = {
833 { XtNborderWidth, 0 },
834 { XtNdefaultDistance, 0 },
838 { XtNborderWidth, 0 },
839 { XtNresizable, (XtArgVal) True },
843 { XtNborderWidth, 0 },
849 { XtNjustify, (XtArgVal) XtJustifyRight },
850 { XtNlabel, (XtArgVal) "..." },
851 { XtNresizable, (XtArgVal) True },
852 { XtNresize, (XtArgVal) False }
855 Arg messageArgs[] = {
856 { XtNjustify, (XtArgVal) XtJustifyLeft },
857 { XtNlabel, (XtArgVal) "..." },
858 { XtNresizable, (XtArgVal) True },
859 { XtNresize, (XtArgVal) False }
863 { XtNborderWidth, 0 },
864 { XtNjustify, (XtArgVal) XtJustifyLeft }
867 XtResource clientResources[] = {
868 { "flashCount", "flashCount", XtRInt, sizeof(int),
869 XtOffset(AppDataPtr, flashCount), XtRImmediate,
870 (XtPointer) FLASH_COUNT },
873 XrmOptionDescRec shellOptions[] = {
874 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
875 { "-flash", "flashCount", XrmoptionNoArg, "3" },
876 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
879 XtActionsRec boardActions[] = {
880 { "DrawPosition", DrawPositionProc },
881 { "HandleUserMove", HandleUserMove },
882 { "AnimateUserMove", AnimateUserMove },
883 { "HandlePV", HandlePV },
884 { "SelectPV", SelectPV },
885 { "StopPV", StopPV },
886 { "FileNameAction", FileNameAction },
887 { "AskQuestionProc", AskQuestionProc },
888 { "AskQuestionReplyAction", AskQuestionReplyAction },
889 { "PieceMenuPopup", PieceMenuPopup },
890 { "WhiteClock", WhiteClock },
891 { "BlackClock", BlackClock },
892 { "Iconify", Iconify },
893 { "ResetProc", ResetProc },
894 { "NewVariantProc", NewVariantProc },
895 { "LoadGameProc", LoadGameProc },
896 { "LoadNextGameProc", LoadNextGameProc },
897 { "LoadPrevGameProc", LoadPrevGameProc },
898 { "LoadSelectedProc", LoadSelectedProc },
899 { "SetFilterProc", SetFilterProc },
900 { "ReloadGameProc", ReloadGameProc },
901 { "LoadPositionProc", LoadPositionProc },
902 { "LoadNextPositionProc", LoadNextPositionProc },
903 { "LoadPrevPositionProc", LoadPrevPositionProc },
904 { "ReloadPositionProc", ReloadPositionProc },
905 { "CopyPositionProc", CopyPositionProc },
906 { "PastePositionProc", PastePositionProc },
907 { "CopyGameProc", CopyGameProc },
908 { "PasteGameProc", PasteGameProc },
909 { "SaveGameProc", SaveGameProc },
910 { "SavePositionProc", SavePositionProc },
911 { "MailMoveProc", MailMoveProc },
912 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
913 { "QuitProc", QuitProc },
914 { "MachineWhiteProc", MachineWhiteProc },
915 { "MachineBlackProc", MachineBlackProc },
916 { "AnalysisModeProc", AnalyzeModeProc },
917 { "AnalyzeFileProc", AnalyzeFileProc },
918 { "TwoMachinesProc", TwoMachinesProc },
919 { "IcsClientProc", IcsClientProc },
920 { "EditGameProc", EditGameProc },
921 { "EditPositionProc", EditPositionProc },
922 { "TrainingProc", EditPositionProc },
923 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
924 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
925 { "ShowGameListProc", ShowGameListProc },
926 { "ShowMoveListProc", HistoryShowProc},
927 { "EditTagsProc", EditCommentProc },
928 { "EditCommentProc", EditCommentProc },
929 { "IcsAlarmProc", IcsAlarmProc },
930 { "IcsInputBoxProc", IcsInputBoxProc },
931 { "PauseProc", PauseProc },
932 { "AcceptProc", AcceptProc },
933 { "DeclineProc", DeclineProc },
934 { "RematchProc", RematchProc },
935 { "CallFlagProc", CallFlagProc },
936 { "DrawProc", DrawProc },
937 { "AdjournProc", AdjournProc },
938 { "AbortProc", AbortProc },
939 { "ResignProc", ResignProc },
940 { "AdjuWhiteProc", AdjuWhiteProc },
941 { "AdjuBlackProc", AdjuBlackProc },
942 { "AdjuDrawProc", AdjuDrawProc },
943 { "EnterKeyProc", EnterKeyProc },
944 { "UpKeyProc", UpKeyProc },
945 { "DownKeyProc", DownKeyProc },
946 { "StopObservingProc", StopObservingProc },
947 { "StopExaminingProc", StopExaminingProc },
948 { "UploadProc", UploadProc },
949 { "BackwardProc", BackwardProc },
950 { "ForwardProc", ForwardProc },
951 { "ToStartProc", ToStartProc },
952 { "ToEndProc", ToEndProc },
953 { "RevertProc", RevertProc },
954 { "AnnotateProc", AnnotateProc },
955 { "TruncateGameProc", TruncateGameProc },
956 { "MoveNowProc", MoveNowProc },
957 { "RetractMoveProc", RetractMoveProc },
958 { "EngineMenuProc", (XtActionProc) EngineMenuProc },
959 { "UciMenuProc", (XtActionProc) UciMenuProc },
960 { "TimeControlProc", (XtActionProc) TimeControlProc },
961 { "AlwaysQueenProc", AlwaysQueenProc },
962 { "AnimateDraggingProc", AnimateDraggingProc },
963 { "AnimateMovingProc", AnimateMovingProc },
964 { "AutoflagProc", AutoflagProc },
965 { "AutoflipProc", AutoflipProc },
966 { "AutobsProc", AutobsProc },
967 { "AutoraiseProc", AutoraiseProc },
968 { "AutosaveProc", AutosaveProc },
969 { "BlindfoldProc", BlindfoldProc },
970 { "FlashMovesProc", FlashMovesProc },
971 { "FlipViewProc", FlipViewProc },
972 { "GetMoveListProc", GetMoveListProc },
974 { "HighlightDraggingProc", HighlightDraggingProc },
976 { "HighlightLastMoveProc", HighlightLastMoveProc },
977 { "IcsAlarmProc", IcsAlarmProc },
978 { "MoveSoundProc", MoveSoundProc },
979 { "OldSaveStyleProc", OldSaveStyleProc },
980 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
981 { "PonderNextMoveProc", PonderNextMoveProc },
982 { "PopupExitMessageProc", PopupExitMessageProc },
983 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
984 { "PremoveProc", PremoveProc },
985 { "QuietPlayProc", QuietPlayProc },
986 { "ShowCoordsProc", ShowCoordsProc },
987 { "ShowThinkingProc", ShowThinkingProc },
988 { "HideThinkingProc", HideThinkingProc },
989 { "TestLegalityProc", TestLegalityProc },
990 { "SaveSettingsProc", SaveSettingsProc },
991 { "SaveOnExitProc", SaveOnExitProc },
992 { "InfoProc", InfoProc },
993 { "ManProc", ManProc },
994 { "HintProc", HintProc },
995 { "BookProc", BookProc },
996 { "AboutGameProc", AboutGameProc },
997 { "AboutProc", AboutProc },
998 { "DebugProc", DebugProc },
999 { "NothingProc", NothingProc },
1000 { "CommentClick", (XtActionProc) CommentClick },
1001 { "CommentPopDown", (XtActionProc) CommentPopDown },
1002 { "EditCommentPopDown", (XtActionProc) EditCommentPopDown },
1003 { "TagsPopDown", (XtActionProc) TagsPopDown },
1004 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1005 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1006 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1007 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1008 { "GameListPopDown", (XtActionProc) GameListPopDown },
1009 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1010 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1011 { "HistoryPopDown", (XtActionProc) HistoryPopDown },
1012 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1013 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1014 { "ShufflePopDown", (XtActionProc) ShufflePopDown },
1015 { "EnginePopDown", (XtActionProc) EnginePopDown },
1016 { "UciPopDown", (XtActionProc) UciPopDown },
1017 { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
1018 { "NewVariantPopDown", (XtActionProc) NewVariantPopDown },
1019 { "SettingsPopDown", (XtActionProc) SettingsPopDown },
1020 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1023 char globalTranslations[] =
1024 ":<Key>F9: ResignProc() \n \
1025 :Ctrl<Key>n: ResetProc() \n \
1026 :Meta<Key>V: NewVariantProc() \n \
1027 :Ctrl<Key>o: LoadGameProc() \n \
1028 :Meta<Key>Next: LoadNextGameProc() \n \
1029 :Meta<Key>Prior: LoadPrevGameProc() \n \
1030 :Ctrl<Key>s: SaveGameProc() \n \
1031 :Ctrl<Key>c: CopyGameProc() \n \
1032 :Ctrl<Key>v: PasteGameProc() \n \
1033 :Ctrl<Key>O: LoadPositionProc() \n \
1034 :Shift Meta<Key>Next: LoadNextPositionProc() \n \
1035 :Shift Meta<Key>Prior: LoadPrevPositionProc() \n \
1036 :Ctrl<Key>S: SavePositionProc() \n \
1037 :Ctrl<Key>C: CopyPositionProc() \n \
1038 :Ctrl<Key>V: PastePositionProc() \n \
1039 :Ctrl<Key>q: QuitProc() \n \
1040 :Ctrl<Key>w: MachineWhiteProc() \n \
1041 :Ctrl<Key>b: MachineBlackProc() \n \
1042 :Ctrl<Key>t: TwoMachinesProc() \n \
1043 :Ctrl<Key>a: AnalysisModeProc() \n \
1044 :Ctrl<Key>f: AnalyzeFileProc() \n \
1045 :Ctrl<Key>e: EditGameProc() \n \
1046 :Ctrl<Key>E: EditPositionProc() \n \
1047 :Meta<Key>O: EngineOutputProc() \n \
1048 :Meta<Key>E: EvalGraphProc() \n \
1049 :Meta<Key>G: ShowGameListProc() \n \
1050 :Meta<Key>H: ShowMoveListProc() \n \
1051 :<Key>Pause: PauseProc() \n \
1052 :<Key>F3: AcceptProc() \n \
1053 :<Key>F4: DeclineProc() \n \
1054 :<Key>F12: RematchProc() \n \
1055 :<Key>F5: CallFlagProc() \n \
1056 :<Key>F6: DrawProc() \n \
1057 :<Key>F7: AdjournProc() \n \
1058 :<Key>F8: AbortProc() \n \
1059 :<Key>F10: StopObservingProc() \n \
1060 :<Key>F11: StopExaminingProc() \n \
1061 :Meta Ctrl<Key>F12: DebugProc() \n \
1062 :Meta<Key>End: ToEndProc() \n \
1063 :Meta<Key>Right: ForwardProc() \n \
1064 :Meta<Key>Home: ToStartProc() \n \
1065 :Meta<Key>Left: BackwardProc() \n \
1066 :<Key>Home: RevertProc() \n \
1067 :<Key>End: TruncateGameProc() \n \
1068 :Ctrl<Key>m: MoveNowProc() \n \
1069 :Ctrl<Key>x: RetractMoveProc() \n \
1070 :Meta<Key>J: EngineMenuProc() \n \
1071 :Meta<Key>U: UciMenuProc() \n \
1072 :Meta<Key>T: TimeControlProc() \n \
1073 :Ctrl<Key>Q: AlwaysQueenProc() \n \
1074 :Ctrl<Key>F: AutoflagProc() \n \
1075 :Ctrl<Key>A: AnimateMovingProc() \n \
1076 :Ctrl<Key>P: PonderNextMoveProc() \n \
1077 :Ctrl<Key>L: TestLegalityProc() \n \
1078 :Ctrl<Key>H: HideThinkingProc() \n \
1079 :<Key>-: Iconify() \n \
1080 :<Key>F1: ManProc() \n \
1081 :<Key>F2: FlipViewProc() \n \
1082 <KeyDown>.: BackwardProc() \n \
1083 <KeyUp>.: ForwardProc() \n \
1084 Shift<Key>1: AskQuestionProc(\"Direct command\",\
1085 \"Send to chess program:\",,1) \n \
1086 Shift<Key>2: AskQuestionProc(\"Direct command\",\
1087 \"Send to second chess program:\",,2) \n";
1089 char boardTranslations[] =
1090 "<Btn1Down>: HandleUserMove(0) \n \
1091 Shift<Btn1Up>: HandleUserMove(1) \n \
1092 <Btn1Up>: HandleUserMove(0) \n \
1093 <Btn1Motion>: AnimateUserMove() \n \
1094 <Btn3Motion>: HandlePV() \n \
1095 <Btn3Up>: PieceMenuPopup(menuB) \n \
1096 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1097 PieceMenuPopup(menuB) \n \
1098 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1099 PieceMenuPopup(menuW) \n \
1100 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1101 PieceMenuPopup(menuW) \n \
1102 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1103 PieceMenuPopup(menuB) \n";
1105 char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
1106 char blackTranslations[] = "<BtnDown>: BlackClock()\n";
1108 char ICSInputTranslations[] =
1109 "<Key>Up: UpKeyProc() \n "
1110 "<Key>Down: DownKeyProc() \n "
1111 "<Key>Return: EnterKeyProc() \n";
1113 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1114 // as the widget is destroyed before the up-click can call extend-end
1115 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1117 String xboardResources[] = {
1118 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1119 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1120 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1125 /* Max possible square size */
1126 #define MAXSQSIZE 256
1128 static int xpm_avail[MAXSQSIZE];
1130 #ifdef HAVE_DIR_STRUCT
1132 /* Extract piece size from filename */
1134 xpm_getsize(name, len, ext)
1145 if ((p=strchr(name, '.')) == NULL ||
1146 StrCaseCmp(p+1, ext) != 0)
1152 while (*p && isdigit(*p))
1159 /* Setup xpm_avail */
1161 xpm_getavail(dirname, ext)
1169 for (i=0; i<MAXSQSIZE; ++i)
1172 if (appData.debugMode)
1173 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1175 dir = opendir(dirname);
1178 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1179 programName, dirname);
1183 while ((ent=readdir(dir)) != NULL) {
1184 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1185 if (i > 0 && i < MAXSQSIZE)
1195 xpm_print_avail(fp, ext)
1201 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1202 for (i=1; i<MAXSQSIZE; ++i) {
1208 /* Return XPM piecesize closest to size */
1210 xpm_closest_to(dirname, size, ext)
1216 int sm_diff = MAXSQSIZE;
1220 xpm_getavail(dirname, ext);
1222 if (appData.debugMode)
1223 xpm_print_avail(stderr, ext);
1225 for (i=1; i<MAXSQSIZE; ++i) {
1228 diff = (diff<0) ? -diff : diff;
1229 if (diff < sm_diff) {
1237 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1243 #else /* !HAVE_DIR_STRUCT */
1244 /* If we are on a system without a DIR struct, we can't
1245 read the directory, so we can't collect a list of
1246 filenames, etc., so we can't do any size-fitting. */
1248 xpm_closest_to(dirname, size, ext)
1253 fprintf(stderr, _("\
1254 Warning: No DIR structure found on this system --\n\
1255 Unable to autosize for XPM/XIM pieces.\n\
1256 Please report this error to frankm@hiwaay.net.\n\
1257 Include system type & operating system in message.\n"));
1260 #endif /* HAVE_DIR_STRUCT */
1262 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1263 "magenta", "cyan", "white" };
1267 TextColors textColors[(int)NColorClasses];
1269 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1271 parse_color(str, which)
1275 char *p, buf[100], *d;
1278 if (strlen(str) > 99) /* watch bounds on buf */
1283 for (i=0; i<which; ++i) {
1290 /* Could be looking at something like:
1292 .. in which case we want to stop on a comma also */
1293 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1297 return -1; /* Use default for empty field */
1300 if (which == 2 || isdigit(*p))
1303 while (*p && isalpha(*p))
1308 for (i=0; i<8; ++i) {
1309 if (!StrCaseCmp(buf, cnames[i]))
1310 return which? (i+40) : (i+30);
1312 if (!StrCaseCmp(buf, "default")) return -1;
1314 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1319 parse_cpair(cc, str)
1323 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1324 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1329 /* bg and attr are optional */
1330 textColors[(int)cc].bg = parse_color(str, 1);
1331 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1332 textColors[(int)cc].attr = 0;
1338 /* Arrange to catch delete-window events */
1339 Atom wm_delete_window;
1341 CatchDeleteWindow(Widget w, String procname)
1344 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1345 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1346 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1353 XtSetArg(args[0], XtNiconic, False);
1354 XtSetValues(shellWidget, args, 1);
1356 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1359 //---------------------------------------------------------------------------------------------------------
1360 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1363 #define CW_USEDEFAULT (1<<31)
1364 #define ICS_TEXT_MENU_SIZE 90
1365 #define DEBUG_FILE "xboard.debug"
1366 #define SetCurrentDirectory chdir
1367 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1371 // these two must some day move to frontend.h, when they are implemented
1372 Boolean GameListIsUp();
1374 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1377 // front-end part of option handling
1379 // [HGM] This platform-dependent table provides the location for storing the color info
1380 extern char *crWhite, * crBlack;
1384 &appData.whitePieceColor,
1385 &appData.blackPieceColor,
1386 &appData.lightSquareColor,
1387 &appData.darkSquareColor,
1388 &appData.highlightSquareColor,
1389 &appData.premoveHighlightColor,
1390 &appData.lowTimeWarningColor,
1401 // [HGM] font: keep a font for each square size, even non-stndard ones
1402 #define NUM_SIZES 18
1403 #define MAX_SIZE 130
1404 Boolean fontSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1405 char *fontTable[NUM_FONTS][MAX_SIZE];
1408 ParseFont(char *name, int number)
1409 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1411 if(sscanf(name, "size%d:", &size)) {
1412 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1413 // defer processing it until we know if it matches our board size
1414 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1415 fontTable[number][size] = strdup(strchr(name, ':')+1);
1416 fontValid[number][size] = True;
1421 case 0: // CLOCK_FONT
1422 appData.clockFont = strdup(name);
1424 case 1: // MESSAGE_FONT
1425 appData.font = strdup(name);
1427 case 2: // COORD_FONT
1428 appData.coordFont = strdup(name);
1433 fontSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1438 { // only 2 fonts currently
1439 appData.clockFont = CLOCK_FONT_NAME;
1440 appData.coordFont = COORD_FONT_NAME;
1441 appData.font = DEFAULT_FONT_NAME;
1446 { // no-op, until we identify the code for this already in XBoard and move it here
1450 ParseColor(int n, char *name)
1451 { // in XBoard, just copy the color-name string
1452 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1456 ParseTextAttribs(ColorClass cc, char *s)
1458 (&appData.colorShout)[cc] = strdup(s);
1462 ParseBoardSize(void *addr, char *name)
1464 appData.boardSize = strdup(name);
1469 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1473 SetCommPortDefaults()
1474 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1477 // [HGM] args: these three cases taken out to stay in front-end
1479 SaveFontArg(FILE *f, ArgDescriptor *ad)
1482 int i, n = (int)ad->argLoc;
1484 case 0: // CLOCK_FONT
1485 name = appData.clockFont;
1487 case 1: // MESSAGE_FONT
1488 name = appData.font;
1490 case 2: // COORD_FONT
1491 name = appData.coordFont;
1496 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1497 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1498 fontTable[n][squareSize] = strdup(name);
1499 fontValid[n][squareSize] = True;
1502 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1503 fprintf(f, OPTCHAR "%s" SEPCHAR "size%d:%s\n", ad->argName, i, fontTable[n][i]);
1508 { // nothing to do, as the sounds are at all times represented by their text-string names already
1512 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1513 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1514 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)ad->argLoc]);
1518 SaveColor(FILE *f, ArgDescriptor *ad)
1519 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1520 if(colorVariable[(int)ad->argLoc])
1521 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)ad->argLoc]);
1525 SaveBoardSize(FILE *f, char *name, void *addr)
1526 { // wrapper to shield back-end from BoardSize & sizeInfo
1527 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1531 ParseCommPortSettings(char *s)
1532 { // no such option in XBoard (yet)
1535 extern Widget engineOutputShell;
1536 extern Widget tagsShell, editTagsShell;
1538 GetActualPlacement(Widget wg, WindowPlacement *wp)
1548 XtSetArg(args[i], XtNx, &x); i++;
1549 XtSetArg(args[i], XtNy, &y); i++;
1550 XtSetArg(args[i], XtNwidth, &w); i++;
1551 XtSetArg(args[i], XtNheight, &h); i++;
1552 XtGetValues(wg, args, i);
1561 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1562 // In XBoard this will have to wait until awareness of window parameters is implemented
1563 GetActualPlacement(shellWidget, &wpMain);
1564 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1565 if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1566 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1567 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1568 if(commentShell) GetActualPlacement(commentShell, &wpComment);
1569 else GetActualPlacement(editShell, &wpComment);
1570 if(tagsShell) GetActualPlacement(tagsShell, &wpTags);
1571 else GetActualPlacement(editTagsShell, &wpTags);
1575 PrintCommPortSettings(FILE *f, char *name)
1576 { // This option does not exist in XBoard
1580 MySearchPath(char *installDir, char *name, char *fullname)
1581 { // just append installDir and name. Perhaps ExpandPath should be used here?
1582 name = ExpandPathName(name);
1583 if(name && name[0] == '/')
1584 safeStrCpy(fullname, name, MSG_SIZ );
1586 sprintf(fullname, "%s%c%s", installDir, '/', name);
1592 MyGetFullPathName(char *name, char *fullname)
1593 { // should use ExpandPath?
1594 name = ExpandPathName(name);
1595 safeStrCpy(fullname, name, MSG_SIZ );
1600 EnsureOnScreen(int *x, int *y, int minX, int minY)
1607 { // [HGM] args: allows testing if main window is realized from back-end
1608 return xBoardWindow != 0;
1612 PopUpStartupDialog()
1613 { // start menu not implemented in XBoard
1617 ConvertToLine(int argc, char **argv)
1619 static char line[128*1024], buf[1024];
1623 for(i=1; i<argc; i++)
1625 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1626 && argv[i][0] != '{' )
1627 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1629 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1630 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1633 line[strlen(line)-1] = NULLCHAR;
1637 //--------------------------------------------------------------------------------------------
1639 extern Boolean twoBoards, partnerUp;
1642 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1644 #define BoardSize int
1645 void InitDrawingSizes(BoardSize boardSize, int flags)
1646 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1647 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1649 XtGeometryResult gres;
1652 if(!formWidget) return;
1655 * Enable shell resizing.
1657 shellArgs[0].value = (XtArgVal) &w;
1658 shellArgs[1].value = (XtArgVal) &h;
1659 XtGetValues(shellWidget, shellArgs, 2);
1661 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1662 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1663 XtSetValues(shellWidget, &shellArgs[2], 4);
1665 XtSetArg(args[0], XtNdefaultDistance, &sep);
1666 XtGetValues(formWidget, args, 1);
1668 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1669 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1671 hOffset = boardWidth + 10;
1672 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1673 secondSegments[i] = gridSegments[i];
1674 secondSegments[i].x1 += hOffset;
1675 secondSegments[i].x2 += hOffset;
1678 XtSetArg(args[0], XtNwidth, boardWidth);
1679 XtSetArg(args[1], XtNheight, boardHeight);
1680 XtSetValues(boardWidget, args, 2);
1682 timerWidth = (boardWidth - sep) / 2;
1683 XtSetArg(args[0], XtNwidth, timerWidth);
1684 XtSetValues(whiteTimerWidget, args, 1);
1685 XtSetValues(blackTimerWidget, args, 1);
1687 XawFormDoLayout(formWidget, False);
1689 if (appData.titleInWindow) {
1691 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1692 XtSetArg(args[i], XtNheight, &h); i++;
1693 XtGetValues(titleWidget, args, i);
1695 w = boardWidth - 2*bor;
1697 XtSetArg(args[0], XtNwidth, &w);
1698 XtGetValues(menuBarWidget, args, 1);
1699 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1702 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1703 if (gres != XtGeometryYes && appData.debugMode) {
1705 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1706 programName, gres, w, h, wr, hr);
1710 XawFormDoLayout(formWidget, True);
1713 * Inhibit shell resizing.
1715 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1716 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1717 shellArgs[4].value = shellArgs[2].value = w;
1718 shellArgs[5].value = shellArgs[3].value = h;
1719 XtSetValues(shellWidget, &shellArgs[0], 6);
1721 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1724 for(i=0; i<4; i++) {
1726 for(p=0; p<=(int)WhiteKing; p++)
1727 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1728 if(gameInfo.variant == VariantShogi) {
1729 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1730 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1731 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1732 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1733 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1736 if(gameInfo.variant == VariantGothic) {
1737 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1740 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1741 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1742 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1745 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1746 for(p=0; p<=(int)WhiteKing; p++)
1747 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1748 if(gameInfo.variant == VariantShogi) {
1749 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1750 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1751 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1752 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1753 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1756 if(gameInfo.variant == VariantGothic) {
1757 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1760 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1761 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1762 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1767 for(i=0; i<2; i++) {
1769 for(p=0; p<=(int)WhiteKing; p++)
1770 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1771 if(gameInfo.variant == VariantShogi) {
1772 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1773 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1774 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1775 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1776 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1779 if(gameInfo.variant == VariantGothic) {
1780 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1783 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1784 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1785 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1800 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1801 XSetWindowAttributes window_attributes;
1803 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1804 XrmValue vFrom, vTo;
1805 XtGeometryResult gres;
1808 int forceMono = False;
1810 srandom(time(0)); // [HGM] book: make random truly random
1812 setbuf(stdout, NULL);
1813 setbuf(stderr, NULL);
1816 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1817 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1821 programName = strrchr(argv[0], '/');
1822 if (programName == NULL)
1823 programName = argv[0];
1828 XtSetLanguageProc(NULL, NULL, NULL);
1829 bindtextdomain(PACKAGE, LOCALEDIR);
1830 textdomain(PACKAGE);
1834 XtAppInitialize(&appContext, "XBoard", shellOptions,
1835 XtNumber(shellOptions),
1836 &argc, argv, xboardResources, NULL, 0);
1837 appData.boardSize = "";
1838 InitAppData(ConvertToLine(argc, argv));
1840 if (p == NULL) p = "/tmp";
1841 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1842 gameCopyFilename = (char*) malloc(i);
1843 gamePasteFilename = (char*) malloc(i);
1844 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1845 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1847 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1848 clientResources, XtNumber(clientResources),
1851 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1852 static char buf[MSG_SIZ];
1853 EscapeExpand(buf, appData.initString);
1854 appData.initString = strdup(buf);
1855 EscapeExpand(buf, appData.secondInitString);
1856 appData.secondInitString = strdup(buf);
1857 EscapeExpand(buf, appData.firstComputerString);
1858 appData.firstComputerString = strdup(buf);
1859 EscapeExpand(buf, appData.secondComputerString);
1860 appData.secondComputerString = strdup(buf);
1863 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1866 if (chdir(chessDir) != 0) {
1867 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1873 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1874 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1875 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1876 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1879 setbuf(debugFP, NULL);
1882 /* [HGM,HR] make sure board size is acceptable */
1883 if(appData.NrFiles > BOARD_FILES ||
1884 appData.NrRanks > BOARD_RANKS )
1885 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1888 /* This feature does not work; animation needs a rewrite */
1889 appData.highlightDragging = FALSE;
1893 xDisplay = XtDisplay(shellWidget);
1894 xScreen = DefaultScreen(xDisplay);
1895 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1897 gameInfo.variant = StringToVariant(appData.variant);
1898 InitPosition(FALSE);
1901 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1903 if (isdigit(appData.boardSize[0])) {
1904 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1905 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1906 &fontPxlSize, &smallLayout, &tinyLayout);
1908 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1909 programName, appData.boardSize);
1913 /* Find some defaults; use the nearest known size */
1914 SizeDefaults *szd, *nearest;
1915 int distance = 99999;
1916 nearest = szd = sizeDefaults;
1917 while (szd->name != NULL) {
1918 if (abs(szd->squareSize - squareSize) < distance) {
1920 distance = abs(szd->squareSize - squareSize);
1921 if (distance == 0) break;
1925 if (i < 2) lineGap = nearest->lineGap;
1926 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1927 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1928 if (i < 5) fontPxlSize = nearest->fontPxlSize;
1929 if (i < 6) smallLayout = nearest->smallLayout;
1930 if (i < 7) tinyLayout = nearest->tinyLayout;
1933 SizeDefaults *szd = sizeDefaults;
1934 if (*appData.boardSize == NULLCHAR) {
1935 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1936 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1939 if (szd->name == NULL) szd--;
1940 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1942 while (szd->name != NULL &&
1943 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1944 if (szd->name == NULL) {
1945 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1946 programName, appData.boardSize);
1950 squareSize = szd->squareSize;
1951 lineGap = szd->lineGap;
1952 clockFontPxlSize = szd->clockFontPxlSize;
1953 coordFontPxlSize = szd->coordFontPxlSize;
1954 fontPxlSize = szd->fontPxlSize;
1955 smallLayout = szd->smallLayout;
1956 tinyLayout = szd->tinyLayout;
1957 // [HGM] font: use defaults from settings file if available and not overruled
1959 if(!fontSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1960 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1961 if(!fontSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1962 appData.font = fontTable[MESSAGE_FONT][squareSize];
1963 if(!fontSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1964 appData.coordFont = fontTable[COORD_FONT][squareSize];
1966 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1967 if (strlen(appData.pixmapDirectory) > 0) {
1968 p = ExpandPathName(appData.pixmapDirectory);
1970 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1971 appData.pixmapDirectory);
1974 if (appData.debugMode) {
1975 fprintf(stderr, _("\
1976 XBoard square size (hint): %d\n\
1977 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1979 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1980 if (appData.debugMode) {
1981 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1984 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1986 /* [HR] height treated separately (hacked) */
1987 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1988 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1989 if (appData.showJail == 1) {
1990 /* Jail on top and bottom */
1991 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1992 XtSetArg(boardArgs[2], XtNheight,
1993 boardHeight + 2*(lineGap + squareSize));
1994 } else if (appData.showJail == 2) {
1996 XtSetArg(boardArgs[1], XtNwidth,
1997 boardWidth + 2*(lineGap + squareSize));
1998 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2001 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2002 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2006 * Determine what fonts to use.
2008 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2009 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2010 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2011 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2012 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2013 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2014 appData.font = FindFont(appData.font, fontPxlSize);
2015 countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
2016 countFontStruct = XQueryFont(xDisplay, countFontID);
2017 // appData.font = FindFont(appData.font, fontPxlSize);
2019 xdb = XtDatabase(xDisplay);
2020 XrmPutStringResource(&xdb, "*font", appData.font);
2023 * Detect if there are not enough colors available and adapt.
2025 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2026 appData.monoMode = True;
2029 if (!appData.monoMode) {
2030 vFrom.addr = (caddr_t) appData.lightSquareColor;
2031 vFrom.size = strlen(appData.lightSquareColor);
2032 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2033 if (vTo.addr == NULL) {
2034 appData.monoMode = True;
2037 lightSquareColor = *(Pixel *) vTo.addr;
2040 if (!appData.monoMode) {
2041 vFrom.addr = (caddr_t) appData.darkSquareColor;
2042 vFrom.size = strlen(appData.darkSquareColor);
2043 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2044 if (vTo.addr == NULL) {
2045 appData.monoMode = True;
2048 darkSquareColor = *(Pixel *) vTo.addr;
2051 if (!appData.monoMode) {
2052 vFrom.addr = (caddr_t) appData.whitePieceColor;
2053 vFrom.size = strlen(appData.whitePieceColor);
2054 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2055 if (vTo.addr == NULL) {
2056 appData.monoMode = True;
2059 whitePieceColor = *(Pixel *) vTo.addr;
2062 if (!appData.monoMode) {
2063 vFrom.addr = (caddr_t) appData.blackPieceColor;
2064 vFrom.size = strlen(appData.blackPieceColor);
2065 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2066 if (vTo.addr == NULL) {
2067 appData.monoMode = True;
2070 blackPieceColor = *(Pixel *) vTo.addr;
2074 if (!appData.monoMode) {
2075 vFrom.addr = (caddr_t) appData.highlightSquareColor;
2076 vFrom.size = strlen(appData.highlightSquareColor);
2077 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2078 if (vTo.addr == NULL) {
2079 appData.monoMode = True;
2082 highlightSquareColor = *(Pixel *) vTo.addr;
2086 if (!appData.monoMode) {
2087 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
2088 vFrom.size = strlen(appData.premoveHighlightColor);
2089 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2090 if (vTo.addr == NULL) {
2091 appData.monoMode = True;
2094 premoveHighlightColor = *(Pixel *) vTo.addr;
2099 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2102 if (appData.bitmapDirectory == NULL ||
2103 appData.bitmapDirectory[0] == NULLCHAR)
2104 appData.bitmapDirectory = DEF_BITMAP_DIR;
2107 if (appData.lowTimeWarning && !appData.monoMode) {
2108 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2109 vFrom.size = strlen(appData.lowTimeWarningColor);
2110 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2111 if (vTo.addr == NULL)
2112 appData.monoMode = True;
2114 lowTimeWarningColor = *(Pixel *) vTo.addr;
2117 if (appData.monoMode && appData.debugMode) {
2118 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2119 (unsigned long) XWhitePixel(xDisplay, xScreen),
2120 (unsigned long) XBlackPixel(xDisplay, xScreen));
2123 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
2124 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
2125 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
2126 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
2127 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
2128 parse_cpair(ColorTell, appData.colorTell) < 0 ||
2129 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
2130 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
2131 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
2132 parse_cpair(ColorNormal, appData.colorNormal) < 0)
2134 if (appData.colorize) {
2136 _("%s: can't parse color names; disabling colorization\n"),
2139 appData.colorize = FALSE;
2141 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2142 textColors[ColorNone].attr = 0;
2144 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2150 layoutName = "tinyLayout";
2151 } else if (smallLayout) {
2152 layoutName = "smallLayout";
2154 layoutName = "normalLayout";
2156 /* Outer layoutWidget is there only to provide a name for use in
2157 resources that depend on the layout style */
2159 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2160 layoutArgs, XtNumber(layoutArgs));
2162 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2163 formArgs, XtNumber(formArgs));
2164 XtSetArg(args[0], XtNdefaultDistance, &sep);
2165 XtGetValues(formWidget, args, 1);
2168 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2169 XtSetArg(args[0], XtNtop, XtChainTop);
2170 XtSetArg(args[1], XtNbottom, XtChainTop);
2171 XtSetArg(args[2], XtNright, XtChainLeft);
2172 XtSetValues(menuBarWidget, args, 3);
2174 widgetList[j++] = whiteTimerWidget =
2175 XtCreateWidget("whiteTime", labelWidgetClass,
2176 formWidget, timerArgs, XtNumber(timerArgs));
2177 XtSetArg(args[0], XtNfont, clockFontStruct);
2178 XtSetArg(args[1], XtNtop, XtChainTop);
2179 XtSetArg(args[2], XtNbottom, XtChainTop);
2180 XtSetValues(whiteTimerWidget, args, 3);
2182 widgetList[j++] = blackTimerWidget =
2183 XtCreateWidget("blackTime", labelWidgetClass,
2184 formWidget, timerArgs, XtNumber(timerArgs));
2185 XtSetArg(args[0], XtNfont, clockFontStruct);
2186 XtSetArg(args[1], XtNtop, XtChainTop);
2187 XtSetArg(args[2], XtNbottom, XtChainTop);
2188 XtSetValues(blackTimerWidget, args, 3);
2190 if (appData.titleInWindow) {
2191 widgetList[j++] = titleWidget =
2192 XtCreateWidget("title", labelWidgetClass, formWidget,
2193 titleArgs, XtNumber(titleArgs));
2194 XtSetArg(args[0], XtNtop, XtChainTop);
2195 XtSetArg(args[1], XtNbottom, XtChainTop);
2196 XtSetValues(titleWidget, args, 2);
2199 if (appData.showButtonBar) {
2200 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2201 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2202 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2203 XtSetArg(args[2], XtNtop, XtChainTop);
2204 XtSetArg(args[3], XtNbottom, XtChainTop);
2205 XtSetValues(buttonBarWidget, args, 4);
2208 widgetList[j++] = messageWidget =
2209 XtCreateWidget("message", labelWidgetClass, formWidget,
2210 messageArgs, XtNumber(messageArgs));
2211 XtSetArg(args[0], XtNtop, XtChainTop);
2212 XtSetArg(args[1], XtNbottom, XtChainTop);
2213 XtSetValues(messageWidget, args, 2);
2215 widgetList[j++] = boardWidget =
2216 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2217 XtNumber(boardArgs));
2219 XtManageChildren(widgetList, j);
2221 timerWidth = (boardWidth - sep) / 2;
2222 XtSetArg(args[0], XtNwidth, timerWidth);
2223 XtSetValues(whiteTimerWidget, args, 1);
2224 XtSetValues(blackTimerWidget, args, 1);
2226 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2227 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2228 XtGetValues(whiteTimerWidget, args, 2);
2230 if (appData.showButtonBar) {
2231 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2232 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2233 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2237 * formWidget uses these constraints but they are stored
2241 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2242 XtSetValues(menuBarWidget, args, i);
2243 if (appData.titleInWindow) {
2246 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2247 XtSetValues(whiteTimerWidget, args, i);
2249 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2250 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2251 XtSetValues(blackTimerWidget, args, i);
2253 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2254 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2255 XtSetValues(titleWidget, args, i);
2257 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2258 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2259 XtSetValues(messageWidget, args, i);
2260 if (appData.showButtonBar) {
2262 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2263 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2264 XtSetValues(buttonBarWidget, args, i);
2268 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2269 XtSetValues(whiteTimerWidget, args, i);
2271 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2272 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2273 XtSetValues(blackTimerWidget, args, i);
2275 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2276 XtSetValues(titleWidget, args, i);
2278 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2279 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2280 XtSetValues(messageWidget, args, i);
2281 if (appData.showButtonBar) {
2283 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2284 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2285 XtSetValues(buttonBarWidget, args, i);
2290 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2291 XtSetValues(whiteTimerWidget, args, i);
2293 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2294 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2295 XtSetValues(blackTimerWidget, args, i);
2297 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2298 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2299 XtSetValues(messageWidget, args, i);
2300 if (appData.showButtonBar) {
2302 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2303 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2304 XtSetValues(buttonBarWidget, args, i);
2308 XtSetArg(args[0], XtNfromVert, messageWidget);
2309 XtSetArg(args[1], XtNtop, XtChainTop);
2310 XtSetArg(args[2], XtNbottom, XtChainBottom);
2311 XtSetArg(args[3], XtNleft, XtChainLeft);
2312 XtSetArg(args[4], XtNright, XtChainRight);
2313 XtSetValues(boardWidget, args, 5);
2315 XtRealizeWidget(shellWidget);
2318 XtSetArg(args[0], XtNx, wpMain.x);
2319 XtSetArg(args[1], XtNy, wpMain.y);
2320 XtSetValues(shellWidget, args, 2);
2324 * Correct the width of the message and title widgets.
2325 * It is not known why some systems need the extra fudge term.
2326 * The value "2" is probably larger than needed.
2328 XawFormDoLayout(formWidget, False);
2330 #define WIDTH_FUDGE 2
2332 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2333 XtSetArg(args[i], XtNheight, &h); i++;
2334 XtGetValues(messageWidget, args, i);
2335 if (appData.showButtonBar) {
2337 XtSetArg(args[i], XtNwidth, &w); i++;
2338 XtGetValues(buttonBarWidget, args, i);
2339 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2341 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2344 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2345 if (gres != XtGeometryYes && appData.debugMode) {
2346 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2347 programName, gres, w, h, wr, hr);
2350 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2351 /* The size used for the child widget in layout lags one resize behind
2352 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2354 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2355 if (gres != XtGeometryYes && appData.debugMode) {
2356 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2357 programName, gres, w, h, wr, hr);
2360 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2361 XtSetArg(args[1], XtNright, XtChainRight);
2362 XtSetValues(messageWidget, args, 2);
2364 if (appData.titleInWindow) {
2366 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2367 XtSetArg(args[i], XtNheight, &h); i++;
2368 XtGetValues(titleWidget, args, i);
2370 w = boardWidth - 2*bor;
2372 XtSetArg(args[0], XtNwidth, &w);
2373 XtGetValues(menuBarWidget, args, 1);
2374 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2377 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2378 if (gres != XtGeometryYes && appData.debugMode) {
2380 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2381 programName, gres, w, h, wr, hr);
2384 XawFormDoLayout(formWidget, True);
2386 xBoardWindow = XtWindow(boardWidget);
2388 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2389 // not need to go into InitDrawingSizes().
2393 * Create X checkmark bitmap and initialize option menu checks.
2395 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2396 checkmark_bits, checkmark_width, checkmark_height);
2397 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2398 if (appData.alwaysPromoteToQueen) {
2399 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2402 if (appData.animateDragging) {
2403 XtSetValues(XtNameToWidget(menuBarWidget,
2404 "menuOptions.Animate Dragging"),
2407 if (appData.animate) {
2408 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2411 if (appData.autoComment) {
2412 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Comment"),
2415 if (appData.autoCallFlag) {
2416 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2419 if (appData.autoFlipView) {
2420 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2423 if (appData.autoObserve) {
2424 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Observe"),
2427 if (appData.autoRaiseBoard) {
2428 XtSetValues(XtNameToWidget(menuBarWidget,
2429 "menuOptions.Auto Raise Board"), args, 1);
2431 if (appData.autoSaveGames) {
2432 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2435 if (appData.saveGameFile[0] != NULLCHAR) {
2436 /* Can't turn this off from menu */
2437 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2439 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2443 if (appData.blindfold) {
2444 XtSetValues(XtNameToWidget(menuBarWidget,
2445 "menuOptions.Blindfold"), args, 1);
2447 if (appData.flashCount > 0) {
2448 XtSetValues(XtNameToWidget(menuBarWidget,
2449 "menuOptions.Flash Moves"),
2452 if (appData.getMoveList) {
2453 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Get Move List"),
2457 if (appData.highlightDragging) {
2458 XtSetValues(XtNameToWidget(menuBarWidget,
2459 "menuOptions.Highlight Dragging"),
2463 if (appData.highlightLastMove) {
2464 XtSetValues(XtNameToWidget(menuBarWidget,
2465 "menuOptions.Highlight Last Move"),
2468 if (appData.icsAlarm) {
2469 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2472 if (appData.ringBellAfterMoves) {
2473 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2476 if (appData.oldSaveStyle) {
2477 XtSetValues(XtNameToWidget(menuBarWidget,
2478 "menuOptions.Old Save Style"), args, 1);
2480 if (appData.periodicUpdates) {
2481 XtSetValues(XtNameToWidget(menuBarWidget,
2482 "menuOptions.Periodic Updates"), args, 1);
2484 if (appData.ponderNextMove) {
2485 XtSetValues(XtNameToWidget(menuBarWidget,
2486 "menuOptions.Ponder Next Move"), args, 1);
2488 if (appData.popupExitMessage) {
2489 XtSetValues(XtNameToWidget(menuBarWidget,
2490 "menuOptions.Popup Exit Message"), args, 1);
2492 if (appData.popupMoveErrors) {
2493 XtSetValues(XtNameToWidget(menuBarWidget,
2494 "menuOptions.Popup Move Errors"), args, 1);
2496 if (appData.premove) {
2497 XtSetValues(XtNameToWidget(menuBarWidget,
2498 "menuOptions.Premove"), args, 1);
2500 if (appData.quietPlay) {
2501 XtSetValues(XtNameToWidget(menuBarWidget,
2502 "menuOptions.Quiet Play"), args, 1);
2504 if (appData.showCoords) {
2505 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2508 if (appData.hideThinkingFromHuman) {
2509 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2512 if (appData.testLegality) {
2513 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2516 if (saveSettingsOnExit) {
2517 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2524 ReadBitmap(&wIconPixmap, "icon_white.bm",
2525 icon_white_bits, icon_white_width, icon_white_height);
2526 ReadBitmap(&bIconPixmap, "icon_black.bm",
2527 icon_black_bits, icon_black_width, icon_black_height);
2528 iconPixmap = wIconPixmap;
2530 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2531 XtSetValues(shellWidget, args, i);
2534 * Create a cursor for the board widget.
2536 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2537 XChangeWindowAttributes(xDisplay, xBoardWindow,
2538 CWCursor, &window_attributes);
2541 * Inhibit shell resizing.
2543 shellArgs[0].value = (XtArgVal) &w;
2544 shellArgs[1].value = (XtArgVal) &h;
2545 XtGetValues(shellWidget, shellArgs, 2);
2546 shellArgs[4].value = shellArgs[2].value = w;
2547 shellArgs[5].value = shellArgs[3].value = h;
2548 XtSetValues(shellWidget, &shellArgs[2], 4);
2549 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2550 marginH = h - boardHeight;
2552 CatchDeleteWindow(shellWidget, "QuitProc");
2557 if (appData.bitmapDirectory[0] != NULLCHAR) {
2561 CreateXPMBoard(appData.liteBackTextureFile, 1);
2562 CreateXPMBoard(appData.darkBackTextureFile, 0);
2566 /* Create regular pieces */
2567 if (!useImages) CreatePieces();
2572 if (appData.animate || appData.animateDragging)
2575 XtAugmentTranslations(formWidget,
2576 XtParseTranslationTable(globalTranslations));
2577 XtAugmentTranslations(boardWidget,
2578 XtParseTranslationTable(boardTranslations));
2579 XtAugmentTranslations(whiteTimerWidget,
2580 XtParseTranslationTable(whiteTranslations));
2581 XtAugmentTranslations(blackTimerWidget,
2582 XtParseTranslationTable(blackTranslations));
2584 /* Why is the following needed on some versions of X instead
2585 * of a translation? */
2586 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2587 (XtEventHandler) EventProc, NULL);
2590 /* [AS] Restore layout */
2591 if( wpMoveHistory.visible ) {
2595 if( wpEvalGraph.visible )
2600 if( wpEngineOutput.visible ) {
2601 EngineOutputPopUp();
2606 if (errorExitStatus == -1) {
2607 if (appData.icsActive) {
2608 /* We now wait until we see "login:" from the ICS before
2609 sending the logon script (problems with timestamp otherwise) */
2610 /*ICSInitScript();*/
2611 if (appData.icsInputBox) ICSInputBoxPopUp();
2615 signal(SIGWINCH, TermSizeSigHandler);
2617 signal(SIGINT, IntSigHandler);
2618 signal(SIGTERM, IntSigHandler);
2619 if (*appData.cmailGameName != NULLCHAR) {
2620 signal(SIGUSR1, CmailSigHandler);
2623 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2625 XtSetKeyboardFocus(shellWidget, formWidget);
2627 XtAppMainLoop(appContext);
2628 if (appData.debugMode) fclose(debugFP); // [DM] debug
2635 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2636 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2638 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2639 unlink(gameCopyFilename);
2640 unlink(gamePasteFilename);
2643 RETSIGTYPE TermSizeSigHandler(int sig)
2656 CmailSigHandler(sig)
2662 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2664 /* Activate call-back function CmailSigHandlerCallBack() */
2665 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2667 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2671 CmailSigHandlerCallBack(isr, closure, message, count, error)
2679 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2681 /**** end signal code ****/
2687 /* try to open the icsLogon script, either in the location given
2688 * or in the users HOME directory
2695 f = fopen(appData.icsLogon, "r");
2698 homedir = getenv("HOME");
2699 if (homedir != NULL)
2701 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2702 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2703 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2704 f = fopen(buf, "r");
2709 ProcessICSInitScript(f);
2711 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2720 EditCommentPopDown();
2735 if (!menuBarWidget) return;
2736 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2738 DisplayError("menuEdit.Revert", 0);
2740 XtSetSensitive(w, !grey);
2742 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2744 DisplayError("menuEdit.Annotate", 0);
2746 XtSetSensitive(w, !grey);
2751 SetMenuEnables(enab)
2755 if (!menuBarWidget) return;
2756 while (enab->name != NULL) {
2757 w = XtNameToWidget(menuBarWidget, enab->name);
2759 DisplayError(enab->name, 0);
2761 XtSetSensitive(w, enab->value);
2767 Enables icsEnables[] = {
2768 { "menuFile.Mail Move", False },
2769 { "menuFile.Reload CMail Message", False },
2770 { "menuMode.Machine Black", False },
2771 { "menuMode.Machine White", False },
2772 { "menuMode.Analysis Mode", False },
2773 { "menuMode.Analyze File", False },
2774 { "menuMode.Two Machines", False },
2776 { "menuEngine.Hint", False },
2777 { "menuEngine.Book", False },
2778 { "menuEngine.Move Now", False },
2779 { "menuOptions.Periodic Updates", False },
2780 { "menuOptions.Hide Thinking", False },
2781 { "menuOptions.Ponder Next Move", False },
2782 { "menuEngine.Engine #1 Settings", False },
2784 { "menuEngine.Engine #2 Settings", False },
2785 { "menuEdit.Annotate", False },
2789 Enables ncpEnables[] = {
2790 { "menuFile.Mail Move", False },
2791 { "menuFile.Reload CMail Message", False },
2792 { "menuMode.Machine White", False },
2793 { "menuMode.Machine Black", False },
2794 { "menuMode.Analysis Mode", False },
2795 { "menuMode.Analyze File", False },
2796 { "menuMode.Two Machines", False },
2797 { "menuMode.ICS Client", False },
2798 { "menuView.ICS Input Box", False },
2799 { "Action", False },
2800 { "menuEdit.Revert", False },
2801 { "menuEdit.Annotate", False },
2802 { "menuEngine.Engine #1 Settings", False },
2803 { "menuEngine.Engine #2 Settings", False },
2804 { "menuEngine.Move Now", False },
2805 { "menuEngine.Retract Move", False },
2806 { "menuOptions.Auto Comment", False },
2807 { "menuOptions.Auto Flag", False },
2808 { "menuOptions.Auto Flip View", False },
2809 { "menuOptions.Auto Observe", False },
2810 { "menuOptions.Auto Raise Board", False },
2811 { "menuOptions.Get Move List", False },
2812 { "menuOptions.ICS Alarm", False },
2813 { "menuOptions.Move Sound", False },
2814 { "menuOptions.Quiet Play", False },
2815 { "menuOptions.Hide Thinking", False },
2816 { "menuOptions.Periodic Updates", False },
2817 { "menuOptions.Ponder Next Move", False },
2818 { "menuEngine.Hint", False },
2819 { "menuEngine.Book", False },
2823 Enables gnuEnables[] = {
2824 { "menuMode.ICS Client", False },
2825 { "menuView.ICS Input Box", False },
2826 { "menuAction.Accept", False },
2827 { "menuAction.Decline", False },
2828 { "menuAction.Rematch", False },
2829 { "menuAction.Adjourn", False },
2830 { "menuAction.Stop Examining", False },
2831 { "menuAction.Stop Observing", False },
2832 { "menuAction.Upload to Examine", False },
2833 { "menuEdit.Revert", False },
2834 { "menuEdit.Annotate", False },
2835 { "menuOptions.Auto Comment", False },
2836 { "menuOptions.Auto Observe", False },
2837 { "menuOptions.Auto Raise Board", False },
2838 { "menuOptions.Get Move List", False },
2839 { "menuOptions.Premove", False },
2840 { "menuOptions.Quiet Play", False },
2842 /* The next two options rely on SetCmailMode being called *after* */
2843 /* SetGNUMode so that when GNU is being used to give hints these */
2844 /* menu options are still available */
2846 { "menuFile.Mail Move", False },
2847 { "menuFile.Reload CMail Message", False },
2851 Enables cmailEnables[] = {
2853 { "menuAction.Call Flag", False },
2854 { "menuAction.Draw", True },
2855 { "menuAction.Adjourn", False },
2856 { "menuAction.Abort", False },
2857 { "menuAction.Stop Observing", False },
2858 { "menuAction.Stop Examining", False },
2859 { "menuFile.Mail Move", True },
2860 { "menuFile.Reload CMail Message", True },
2864 Enables trainingOnEnables[] = {
2865 { "menuMode.Edit Comment", False },
2866 { "menuMode.Pause", False },
2867 { "menuEdit.Forward", False },
2868 { "menuEdit.Backward", False },
2869 { "menuEdit.Forward to End", False },
2870 { "menuEdit.Back to Start", False },
2871 { "menuEngine.Move Now", False },
2872 { "menuEdit.Truncate Game", False },
2876 Enables trainingOffEnables[] = {
2877 { "menuMode.Edit Comment", True },
2878 { "menuMode.Pause", True },
2879 { "menuEdit.Forward", True },
2880 { "menuEdit.Backward", True },
2881 { "menuEdit.Forward to End", True },
2882 { "menuEdit.Back to Start", True },
2883 { "menuEngine.Move Now", True },
2884 { "menuEdit.Truncate Game", True },
2888 Enables machineThinkingEnables[] = {
2889 { "menuFile.Load Game", False },
2890 // { "menuFile.Load Next Game", False },
2891 // { "menuFile.Load Previous Game", False },
2892 // { "menuFile.Reload Same Game", False },
2893 { "menuEdit.Paste Game", False },
2894 { "menuFile.Load Position", False },
2895 // { "menuFile.Load Next Position", False },
2896 // { "menuFile.Load Previous Position", False },
2897 // { "menuFile.Reload Same Position", False },
2898 { "menuEdit.Paste Position", False },
2899 { "menuMode.Machine White", False },
2900 { "menuMode.Machine Black", False },
2901 { "menuMode.Two Machines", False },
2902 { "menuEngine.Retract Move", False },
2906 Enables userThinkingEnables[] = {
2907 { "menuFile.Load Game", True },
2908 // { "menuFile.Load Next Game", True },
2909 // { "menuFile.Load Previous Game", True },
2910 // { "menuFile.Reload Same Game", True },
2911 { "menuEdit.Paste Game", True },
2912 { "menuFile.Load Position", True },
2913 // { "menuFile.Load Next Position", True },
2914 // { "menuFile.Load Previous Position", True },
2915 // { "menuFile.Reload Same Position", True },
2916 { "menuEdit.Paste Position", True },
2917 { "menuMode.Machine White", True },
2918 { "menuMode.Machine Black", True },
2919 { "menuMode.Two Machines", True },
2920 { "menuEngine.Retract Move", True },
2926 SetMenuEnables(icsEnables);
2929 if (appData.zippyPlay && !appData.noChessProgram) /* [DM] icsEngineAnalyze */
2930 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2937 SetMenuEnables(ncpEnables);
2943 SetMenuEnables(gnuEnables);
2949 SetMenuEnables(cmailEnables);
2955 SetMenuEnables(trainingOnEnables);
2956 if (appData.showButtonBar) {
2957 XtSetSensitive(buttonBarWidget, False);
2963 SetTrainingModeOff()
2965 SetMenuEnables(trainingOffEnables);
2966 if (appData.showButtonBar) {
2967 XtSetSensitive(buttonBarWidget, True);
2972 SetUserThinkingEnables()
2974 if (appData.noChessProgram) return;
2975 SetMenuEnables(userThinkingEnables);
2979 SetMachineThinkingEnables()
2981 if (appData.noChessProgram) return;
2982 SetMenuEnables(machineThinkingEnables);
2984 case MachinePlaysBlack:
2985 case MachinePlaysWhite:
2986 case TwoMachinesPlay:
2987 XtSetSensitive(XtNameToWidget(menuBarWidget,
2988 ModeToWidgetName(gameMode)), True);
2995 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
2996 #define HISTORY_SIZE 64
2997 static char *history[HISTORY_SIZE];
2998 int histIn = 0, histP = 0;
3001 SaveInHistory(char *cmd)
3003 if (history[histIn] != NULL) {
3004 free(history[histIn]);
3005 history[histIn] = NULL;
3007 if (*cmd == NULLCHAR) return;
3008 history[histIn] = StrSave(cmd);
3009 histIn = (histIn + 1) % HISTORY_SIZE;
3010 if (history[histIn] != NULL) {
3011 free(history[histIn]);
3012 history[histIn] = NULL;
3018 PrevInHistory(char *cmd)
3021 if (histP == histIn) {
3022 if (history[histIn] != NULL) free(history[histIn]);
3023 history[histIn] = StrSave(cmd);
3025 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3026 if (newhp == histIn || history[newhp] == NULL) return NULL;
3028 return history[histP];
3034 if (histP == histIn) return NULL;
3035 histP = (histP + 1) % HISTORY_SIZE;
3036 return history[histP];
3038 // end of borrowed code
3040 #define Abs(n) ((n)<0 ? -(n) : (n))
3043 * Find a font that matches "pattern" that is as close as
3044 * possible to the targetPxlSize. Prefer fonts that are k
3045 * pixels smaller to fonts that are k pixels larger. The
3046 * pattern must be in the X Consortium standard format,
3047 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3048 * The return value should be freed with XtFree when no
3052 FindFont(pattern, targetPxlSize)
3056 char **fonts, *p, *best, *scalable, *scalableTail;
3057 int i, j, nfonts, minerr, err, pxlSize;
3060 char **missing_list;
3062 char *def_string, *base_fnt_lst, strInt[3];
3064 XFontStruct **fnt_list;
3066 base_fnt_lst = calloc(1, strlen(pattern) + 3);
3067 snprintf(strInt, sizeof(strInt)/sizeof(strInt[0]), "%d", targetPxlSize);
3068 p = strstr(pattern, "--");
3069 strncpy(base_fnt_lst, pattern, p - pattern + 2);
3070 strcat(base_fnt_lst, strInt);
3071 strcat(base_fnt_lst, strchr(p + 2, '-'));
3073 if ((fntSet = XCreateFontSet(xDisplay,
3077 &def_string)) == NULL) {
3079 fprintf(stderr, _("Unable to create font set.\n"));
3083 nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
3085 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3087 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3088 programName, pattern);
3096 for (i=0; i<nfonts; i++) {
3099 if (*p != '-') continue;
3101 if (*p == NULLCHAR) break;
3102 if (*p++ == '-') j++;
3104 if (j < 7) continue;
3107 scalable = fonts[i];
3110 err = pxlSize - targetPxlSize;
3111 if (Abs(err) < Abs(minerr) ||
3112 (minerr > 0 && err < 0 && -err == minerr)) {
3118 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3119 /* If the error is too big and there is a scalable font,
3120 use the scalable font. */
3121 int headlen = scalableTail - scalable;
3122 p = (char *) XtMalloc(strlen(scalable) + 10);
3123 while (isdigit(*scalableTail)) scalableTail++;
3124 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3126 p = (char *) XtMalloc(strlen(best) + 2);
3127 safeStrCpy(p, best, strlen(best)+1 );
3129 if (appData.debugMode) {
3130 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3131 pattern, targetPxlSize, p);
3134 if (missing_count > 0)
3135 XFreeStringList(missing_list);
3136 XFreeFontSet(xDisplay, fntSet);
3138 XFreeFontNames(fonts);
3145 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3146 | GCBackground | GCFunction | GCPlaneMask;
3147 XGCValues gc_values;
3150 gc_values.plane_mask = AllPlanes;
3151 gc_values.line_width = lineGap;
3152 gc_values.line_style = LineSolid;
3153 gc_values.function = GXcopy;
3155 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3156 gc_values.background = XBlackPixel(xDisplay, xScreen);
3157 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3159 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3160 gc_values.background = XWhitePixel(xDisplay, xScreen);
3161 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3162 XSetFont(xDisplay, coordGC, coordFontID);
3164 // [HGM] make font for holdings counts (white on black0
3165 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3166 gc_values.background = XBlackPixel(xDisplay, xScreen);
3167 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3168 XSetFont(xDisplay, countGC, countFontID);
3170 if (appData.monoMode) {
3171 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3172 gc_values.background = XWhitePixel(xDisplay, xScreen);
3173 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3175 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3176 gc_values.background = XBlackPixel(xDisplay, xScreen);
3177 lightSquareGC = wbPieceGC
3178 = XtGetGC(shellWidget, value_mask, &gc_values);
3180 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3181 gc_values.background = XWhitePixel(xDisplay, xScreen);
3182 darkSquareGC = bwPieceGC
3183 = XtGetGC(shellWidget, value_mask, &gc_values);
3185 if (DefaultDepth(xDisplay, xScreen) == 1) {
3186 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3187 gc_values.function = GXcopyInverted;
3188 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3189 gc_values.function = GXcopy;
3190 if (XBlackPixel(xDisplay, xScreen) == 1) {
3191 bwPieceGC = darkSquareGC;
3192 wbPieceGC = copyInvertedGC;
3194 bwPieceGC = copyInvertedGC;
3195 wbPieceGC = lightSquareGC;
3199 gc_values.foreground = highlightSquareColor;
3200 gc_values.background = highlightSquareColor;
3201 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3203 gc_values.foreground = premoveHighlightColor;
3204 gc_values.background = premoveHighlightColor;
3205 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3207 gc_values.foreground = lightSquareColor;
3208 gc_values.background = darkSquareColor;
3209 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3211 gc_values.foreground = darkSquareColor;
3212 gc_values.background = lightSquareColor;
3213 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3215 gc_values.foreground = jailSquareColor;
3216 gc_values.background = jailSquareColor;
3217 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3219 gc_values.foreground = whitePieceColor;
3220 gc_values.background = darkSquareColor;
3221 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3223 gc_values.foreground = whitePieceColor;
3224 gc_values.background = lightSquareColor;
3225 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3227 gc_values.foreground = whitePieceColor;
3228 gc_values.background = jailSquareColor;
3229 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3231 gc_values.foreground = blackPieceColor;
3232 gc_values.background = darkSquareColor;
3233 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3235 gc_values.foreground = blackPieceColor;
3236 gc_values.background = lightSquareColor;
3237 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3239 gc_values.foreground = blackPieceColor;
3240 gc_values.background = jailSquareColor;
3241 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3245 void loadXIM(xim, xmask, filename, dest, mask)
3258 fp = fopen(filename, "rb");
3260 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3267 for (y=0; y<h; ++y) {
3268 for (x=0; x<h; ++x) {
3273 XPutPixel(xim, x, y, blackPieceColor);
3275 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3278 XPutPixel(xim, x, y, darkSquareColor);
3280 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3283 XPutPixel(xim, x, y, whitePieceColor);
3285 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3288 XPutPixel(xim, x, y, lightSquareColor);
3290 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3296 /* create Pixmap of piece */
3297 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3299 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3302 /* create Pixmap of clipmask
3303 Note: We assume the white/black pieces have the same
3304 outline, so we make only 6 masks. This is okay
3305 since the XPM clipmask routines do the same. */
3307 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3309 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3312 /* now create the 1-bit version */
3313 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3316 values.foreground = 1;
3317 values.background = 0;
3319 /* Don't use XtGetGC, not read only */
3320 maskGC = XCreateGC(xDisplay, *mask,
3321 GCForeground | GCBackground, &values);
3322 XCopyPlane(xDisplay, temp, *mask, maskGC,
3323 0, 0, squareSize, squareSize, 0, 0, 1);
3324 XFreePixmap(xDisplay, temp);
3329 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3331 void CreateXIMPieces()
3336 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3341 /* The XSynchronize calls were copied from CreatePieces.
3342 Not sure if needed, but can't hurt */
3343 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3346 /* temp needed by loadXIM() */
3347 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3348 0, 0, ss, ss, AllPlanes, XYPixmap);
3350 if (strlen(appData.pixmapDirectory) == 0) {
3354 if (appData.monoMode) {
3355 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3359 fprintf(stderr, _("\nLoading XIMs...\n"));
3361 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3362 fprintf(stderr, "%d", piece+1);
3363 for (kind=0; kind<4; kind++) {
3364 fprintf(stderr, ".");
3365 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3366 ExpandPathName(appData.pixmapDirectory),
3367 piece <= (int) WhiteKing ? "" : "w",
3368 pieceBitmapNames[piece],
3370 ximPieceBitmap[kind][piece] =
3371 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3372 0, 0, ss, ss, AllPlanes, XYPixmap);
3373 if (appData.debugMode)
3374 fprintf(stderr, _("(File:%s:) "), buf);
3375 loadXIM(ximPieceBitmap[kind][piece],
3377 &(xpmPieceBitmap2[kind][piece]),
3378 &(ximMaskPm2[piece]));
3379 if(piece <= (int)WhiteKing)
3380 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3382 fprintf(stderr," ");
3384 /* Load light and dark squares */
3385 /* If the LSQ and DSQ pieces don't exist, we will
3386 draw them with solid squares. */
3387 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3388 if (access(buf, 0) != 0) {
3392 fprintf(stderr, _("light square "));
3394 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3395 0, 0, ss, ss, AllPlanes, XYPixmap);
3396 if (appData.debugMode)
3397 fprintf(stderr, _("(File:%s:) "), buf);
3399 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3400 fprintf(stderr, _("dark square "));
3401 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3402 ExpandPathName(appData.pixmapDirectory), ss);
3403 if (appData.debugMode)
3404 fprintf(stderr, _("(File:%s:) "), buf);
3406 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3407 0, 0, ss, ss, AllPlanes, XYPixmap);
3408 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3409 xpmJailSquare = xpmLightSquare;
3411 fprintf(stderr, _("Done.\n"));
3413 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3417 void CreateXPMBoard(char *s, int kind)
3421 if(s == NULL || *s == 0 || *s == '*') return;
3422 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3423 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3427 void CreateXPMPieces()
3431 u_int ss = squareSize;
3433 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3434 XpmColorSymbol symbols[4];
3436 /* The XSynchronize calls were copied from CreatePieces.
3437 Not sure if needed, but can't hurt */
3438 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3440 /* Setup translations so piece colors match square colors */
3441 symbols[0].name = "light_piece";
3442 symbols[0].value = appData.whitePieceColor;
3443 symbols[1].name = "dark_piece";
3444 symbols[1].value = appData.blackPieceColor;
3445 symbols[2].name = "light_square";
3446 symbols[2].value = appData.lightSquareColor;
3447 symbols[3].name = "dark_square";
3448 symbols[3].value = appData.darkSquareColor;
3450 attr.valuemask = XpmColorSymbols;
3451 attr.colorsymbols = symbols;
3452 attr.numsymbols = 4;
3454 if (appData.monoMode) {
3455 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3459 if (strlen(appData.pixmapDirectory) == 0) {
3460 XpmPieces* pieces = builtInXpms;
3463 while (pieces->size != squareSize && pieces->size) pieces++;
3464 if (!pieces->size) {
3465 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3468 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3469 for (kind=0; kind<4; kind++) {
3471 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3472 pieces->xpm[piece][kind],
3473 &(xpmPieceBitmap2[kind][piece]),
3474 NULL, &attr)) != 0) {
3475 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3479 if(piece <= (int) WhiteKing)
3480 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3484 xpmJailSquare = xpmLightSquare;
3488 fprintf(stderr, _("\nLoading XPMs...\n"));
3491 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3492 fprintf(stderr, "%d ", piece+1);
3493 for (kind=0; kind<4; kind++) {
3494 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3495 ExpandPathName(appData.pixmapDirectory),
3496 piece > (int) WhiteKing ? "w" : "",
3497 pieceBitmapNames[piece],
3499 if (appData.debugMode) {
3500 fprintf(stderr, _("(File:%s:) "), buf);
3502 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3503 &(xpmPieceBitmap2[kind][piece]),
3504 NULL, &attr)) != 0) {
3505 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3506 // [HGM] missing: read of unorthodox piece failed; substitute King.
3507 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3508 ExpandPathName(appData.pixmapDirectory),
3510 if (appData.debugMode) {
3511 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3513 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3514 &(xpmPieceBitmap2[kind][piece]),
3518 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3523 if(piece <= (int) WhiteKing)
3524 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3527 /* Load light and dark squares */
3528 /* If the LSQ and DSQ pieces don't exist, we will
3529 draw them with solid squares. */
3530 fprintf(stderr, _("light square "));
3531 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3532 if (access(buf, 0) != 0) {
3536 if (appData.debugMode)
3537 fprintf(stderr, _("(File:%s:) "), buf);
3539 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3540 &xpmLightSquare, NULL, &attr)) != 0) {
3541 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3544 fprintf(stderr, _("dark square "));
3545 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3546 ExpandPathName(appData.pixmapDirectory), ss);
3547 if (appData.debugMode) {
3548 fprintf(stderr, _("(File:%s:) "), buf);
3550 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3551 &xpmDarkSquare, NULL, &attr)) != 0) {
3552 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3556 xpmJailSquare = xpmLightSquare;
3557 fprintf(stderr, _("Done.\n"));
3559 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3562 #endif /* HAVE_LIBXPM */
3565 /* No built-in bitmaps */
3570 u_int ss = squareSize;
3572 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3575 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3576 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3577 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3578 pieceBitmapNames[piece],
3579 ss, kind == SOLID ? 's' : 'o');
3580 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3581 if(piece <= (int)WhiteKing)
3582 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3586 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3590 /* With built-in bitmaps */
3593 BuiltInBits* bib = builtInBits;
3596 u_int ss = squareSize;
3598 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3601 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3603 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3604 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3605 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3606 pieceBitmapNames[piece],
3607 ss, kind == SOLID ? 's' : 'o');
3608 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3609 bib->bits[kind][piece], ss, ss);
3610 if(piece <= (int)WhiteKing)
3611 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3615 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3620 void ReadBitmap(pm, name, bits, wreq, hreq)
3623 unsigned char bits[];
3629 char msg[MSG_SIZ], fullname[MSG_SIZ];
3631 if (*appData.bitmapDirectory != NULLCHAR) {
3632 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3633 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3634 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3635 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3636 &w, &h, pm, &x_hot, &y_hot);
3637 fprintf(stderr, "load %s\n", name);
3638 if (errcode != BitmapSuccess) {
3640 case BitmapOpenFailed:
3641 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3643 case BitmapFileInvalid:
3644 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3646 case BitmapNoMemory:
3647 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3651 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3655 fprintf(stderr, _("%s: %s...using built-in\n"),
3657 } else if (w != wreq || h != hreq) {
3659 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3660 programName, fullname, w, h, wreq, hreq);
3666 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3675 if (lineGap == 0) return;
3677 /* [HR] Split this into 2 loops for non-square boards. */
3679 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3680 gridSegments[i].x1 = 0;
3681 gridSegments[i].x2 =
3682 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3683 gridSegments[i].y1 = gridSegments[i].y2
3684 = lineGap / 2 + (i * (squareSize + lineGap));
3687 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3688 gridSegments[j + i].y1 = 0;
3689 gridSegments[j + i].y2 =
3690 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3691 gridSegments[j + i].x1 = gridSegments[j + i].x2
3692 = lineGap / 2 + (j * (squareSize + lineGap));
3696 static void MenuBarSelect(w, addr, index)
3701 XtActionProc proc = (XtActionProc) addr;
3703 (proc)(NULL, NULL, NULL, NULL);
3706 void CreateMenuBarPopup(parent, name, mb)
3716 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3719 XtSetArg(args[j], XtNleftMargin, 20); j++;
3720 XtSetArg(args[j], XtNrightMargin, 20); j++;
3722 while (mi->string != NULL) {
3723 if (strcmp(mi->string, "----") == 0) {
3724 entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
3727 XtSetArg(args[j], XtNlabel, XtNewString(mi->string));
3728 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3730 XtAddCallback(entry, XtNcallback,
3731 (XtCallbackProc) MenuBarSelect,
3732 (caddr_t) mi->proc);
3738 Widget CreateMenuBar(mb)
3742 Widget anchor, menuBar;
3744 char menuName[MSG_SIZ];
3747 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3748 XtSetArg(args[j], XtNvSpace, 0); j++;
3749 XtSetArg(args[j], XtNborderWidth, 0); j++;
3750 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3751 formWidget, args, j);
3753 while (mb->name != NULL) {
3754 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3755 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3757 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3760 shortName[0] = mb->name[0];
3761 shortName[1] = NULLCHAR;
3762 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3765 XtSetArg(args[j], XtNlabel, XtNewString(mb->name)); j++;
3768 XtSetArg(args[j], XtNborderWidth, 0); j++;
3769 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3771 CreateMenuBarPopup(menuBar, menuName, mb);
3777 Widget CreateButtonBar(mi)
3781 Widget button, buttonBar;
3785 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3787 XtSetArg(args[j], XtNhSpace, 0); j++;
3789 XtSetArg(args[j], XtNborderWidth, 0); j++;
3790 XtSetArg(args[j], XtNvSpace, 0); j++;
3791 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3792 formWidget, args, j);
3794 while (mi->string != NULL) {
3797 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3798 XtSetArg(args[j], XtNborderWidth, 0); j++;
3800 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3801 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3802 buttonBar, args, j);
3803 XtAddCallback(button, XtNcallback,
3804 (XtCallbackProc) MenuBarSelect,
3805 (caddr_t) mi->proc);
3812 CreatePieceMenu(name, color)
3819 ChessSquare selection;
3821 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3822 boardWidget, args, 0);
3824 for (i = 0; i < PIECE_MENU_SIZE; i++) {
3825 String item = pieceMenuStrings[color][i];
3827 if (strcmp(item, "----") == 0) {
3828 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3831 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3832 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3834 selection = pieceMenuTranslation[color][i];
3835 XtAddCallback(entry, XtNcallback,
3836 (XtCallbackProc) PieceMenuSelect,
3837 (caddr_t) selection);
3838 if (selection == WhitePawn || selection == BlackPawn) {
3839 XtSetArg(args[0], XtNpopupOnEntry, entry);
3840 XtSetValues(menu, args, 1);
3853 ChessSquare selection;
3855 whitePieceMenu = CreatePieceMenu("menuW", 0);
3856 blackPieceMenu = CreatePieceMenu("menuB", 1);
3858 XtRegisterGrabAction(PieceMenuPopup, True,
3859 (unsigned)(ButtonPressMask|ButtonReleaseMask),
3860 GrabModeAsync, GrabModeAsync);
3862 XtSetArg(args[0], XtNlabel, _("Drop"));
3863 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3864 boardWidget, args, 1);
3865 for (i = 0; i < DROP_MENU_SIZE; i++) {
3866 String item = dropMenuStrings[i];
3868 if (strcmp(item, "----") == 0) {
3869 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3872 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3873 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3875 selection = dropMenuTranslation[i];
3876 XtAddCallback(entry, XtNcallback,
3877 (XtCallbackProc) DropMenuSelect,
3878 (caddr_t) selection);
3883 void SetupDropMenu()
3891 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3892 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3893 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3894 dmEnables[i].piece);
3895 XtSetSensitive(entry, p != NULL || !appData.testLegality
3896 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3897 && !appData.icsActive));
3899 while (p && *p++ == dmEnables[i].piece) count++;
3900 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3902 XtSetArg(args[j], XtNlabel, label); j++;
3903 XtSetValues(entry, args, j);
3907 void PieceMenuPopup(w, event, params, num_params)
3911 Cardinal *num_params;
3913 String whichMenu; int menuNr;
3914 if (event->type == ButtonRelease)
3915 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3916 else if (event->type == ButtonPress)
3917 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3919 case 0: whichMenu = params[0]; break;
3920 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3922 case -1: if (errorUp) ErrorPopDown();
3925 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3928 static void PieceMenuSelect(w, piece, junk)
3933 if (pmFromX < 0 || pmFromY < 0) return;
3934 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3937 static void DropMenuSelect(w, piece, junk)
3942 if (pmFromX < 0 || pmFromY < 0) return;
3943 DropMenuEvent(piece, pmFromX, pmFromY);
3946 void WhiteClock(w, event, prms, nprms)
3952 if (gameMode == EditPosition || gameMode == IcsExamining) {
3953 SetWhiteToPlayEvent();
3954 } else if (gameMode == IcsPlayingBlack || gameMode == MachinePlaysWhite) {
3959 void BlackClock(w, event, prms, nprms)
3965 if (gameMode == EditPosition || gameMode == IcsExamining) {
3966 SetBlackToPlayEvent();
3967 } else if (gameMode == IcsPlayingWhite || gameMode == MachinePlaysBlack) {
3974 * If the user selects on a border boundary, return -1; if off the board,
3975 * return -2. Otherwise map the event coordinate to the square.
3977 int EventToSquare(x, limit)
3985 if ((x % (squareSize + lineGap)) >= squareSize)
3987 x /= (squareSize + lineGap);
3993 static void do_flash_delay(msec)
3999 static void drawHighlight(file, rank, gc)
4005 if (lineGap == 0) return;
4008 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4009 (squareSize + lineGap);
4010 y = lineGap/2 + rank * (squareSize + lineGap);
4012 x = lineGap/2 + file * (squareSize + lineGap);
4013 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4014 (squareSize + lineGap);
4017 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4018 squareSize+lineGap, squareSize+lineGap);
4021 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4022 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4025 SetHighlights(fromX, fromY, toX, toY)
4026 int fromX, fromY, toX, toY;
4028 if (hi1X != fromX || hi1Y != fromY) {
4029 if (hi1X >= 0 && hi1Y >= 0) {
4030 drawHighlight(hi1X, hi1Y, lineGC);
4032 } // [HGM] first erase both, then draw new!
4033 if (hi2X != toX || hi2Y != toY) {
4034 if (hi2X >= 0 && hi2Y >= 0) {
4035 drawHighlight(hi2X, hi2Y, lineGC);
4038 if (hi1X != fromX || hi1Y != fromY) {
4039 if (fromX >= 0 && fromY >= 0) {
4040 drawHighlight(fromX, fromY, highlineGC);
4043 if (hi2X != toX || hi2Y != toY) {
4044 if (toX >= 0 && toY >= 0) {
4045 drawHighlight(toX, toY, highlineGC);
4057 SetHighlights(-1, -1, -1, -1);
4062 SetPremoveHighlights(fromX, fromY, toX, toY)
4063 int fromX, fromY, toX, toY;
4065 if (pm1X != fromX || pm1Y != fromY) {
4066 if (pm1X >= 0 && pm1Y >= 0) {
4067 drawHighlight(pm1X, pm1Y, lineGC);
4069 if (fromX >= 0 && fromY >= 0) {
4070 drawHighlight(fromX, fromY, prelineGC);
4073 if (pm2X != toX || pm2Y != toY) {
4074 if (pm2X >= 0 && pm2Y >= 0) {
4075 drawHighlight(pm2X, pm2Y, lineGC);
4077 if (toX >= 0 && toY >= 0) {
4078 drawHighlight(toX, toY, prelineGC);
4088 ClearPremoveHighlights()
4090 SetPremoveHighlights(-1, -1, -1, -1);
4093 static int CutOutSquare(x, y, x0, y0, kind)
4094 int x, y, *x0, *y0, kind;
4096 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4097 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4099 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4100 if(textureW[kind] < W*squareSize)
4101 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4103 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4104 if(textureH[kind] < H*squareSize)
4105 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4107 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4111 static void BlankSquare(x, y, color, piece, dest, fac)
4112 int x, y, color, fac;
4115 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4117 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4118 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4119 squareSize, squareSize, x*fac, y*fac);
4121 if (useImages && useImageSqs) {
4125 pm = xpmLightSquare;
4130 case 2: /* neutral */
4135 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4136 squareSize, squareSize, x*fac, y*fac);
4146 case 2: /* neutral */
4151 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4156 I split out the routines to draw a piece so that I could
4157 make a generic flash routine.
4159 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4161 int square_color, x, y;
4164 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4165 switch (square_color) {
4167 case 2: /* neutral */
4169 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4170 ? *pieceToOutline(piece)
4171 : *pieceToSolid(piece),
4172 dest, bwPieceGC, 0, 0,
4173 squareSize, squareSize, x, y);
4176 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4177 ? *pieceToSolid(piece)
4178 : *pieceToOutline(piece),
4179 dest, wbPieceGC, 0, 0,
4180 squareSize, squareSize, x, y);
4185 static void monoDrawPiece(piece, square_color, x, y, dest)
4187 int square_color, x, y;
4190 switch (square_color) {
4192 case 2: /* neutral */
4194 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4195 ? *pieceToOutline(piece)
4196 : *pieceToSolid(piece),
4197 dest, bwPieceGC, 0, 0,
4198 squareSize, squareSize, x, y, 1);
4201 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4202 ? *pieceToSolid(piece)
4203 : *pieceToOutline(piece),
4204 dest, wbPieceGC, 0, 0,
4205 squareSize, squareSize, x, y, 1);
4210 static void colorDrawPiece(piece, square_color, x, y, dest)
4212 int square_color, x, y;
4215 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4216 switch (square_color) {
4218 XCopyPlane(xDisplay, *pieceToSolid(piece),
4219 dest, (int) piece < (int) BlackPawn
4220 ? wlPieceGC : blPieceGC, 0, 0,
4221 squareSize, squareSize, x, y, 1);
4224 XCopyPlane(xDisplay, *pieceToSolid(piece),
4225 dest, (int) piece < (int) BlackPawn
4226 ? wdPieceGC : bdPieceGC, 0, 0,
4227 squareSize, squareSize, x, y, 1);
4229 case 2: /* neutral */
4231 XCopyPlane(xDisplay, *pieceToSolid(piece),
4232 dest, (int) piece < (int) BlackPawn
4233 ? wjPieceGC : bjPieceGC, 0, 0,
4234 squareSize, squareSize, x, y, 1);
4239 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4241 int square_color, x, y;
4244 int kind, p = piece;
4246 switch (square_color) {
4248 case 2: /* neutral */
4250 if ((int)piece < (int) BlackPawn) {
4258 if ((int)piece < (int) BlackPawn) {
4266 if(appData.upsideDown && flipView) kind ^= 2; // swap white and black pieces
4267 if(useTexture & square_color+1) {
4268 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4269 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4270 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4271 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4272 XSetClipMask(xDisplay, wlPieceGC, None);
4273 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4275 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4276 dest, wlPieceGC, 0, 0,
4277 squareSize, squareSize, x, y);
4280 typedef void (*DrawFunc)();
4282 DrawFunc ChooseDrawFunc()
4284 if (appData.monoMode) {
4285 if (DefaultDepth(xDisplay, xScreen) == 1) {
4286 return monoDrawPiece_1bit;
4288 return monoDrawPiece;
4292 return colorDrawPieceImage;
4294 return colorDrawPiece;
4298 /* [HR] determine square color depending on chess variant. */
4299 static int SquareColor(row, column)
4304 if (gameInfo.variant == VariantXiangqi) {
4305 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4307 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4309 } else if (row <= 4) {
4315 square_color = ((column + row) % 2) == 1;
4318 /* [hgm] holdings: next line makes all holdings squares light */
4319 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4321 return square_color;
4324 void DrawSquare(row, column, piece, do_flash)
4325 int row, column, do_flash;
4328 int square_color, x, y, direction, font_ascent, font_descent;
4331 XCharStruct overall;
4335 /* Calculate delay in milliseconds (2-delays per complete flash) */
4336 flash_delay = 500 / appData.flashRate;
4339 x = lineGap + ((BOARD_WIDTH-1)-column) *
4340 (squareSize + lineGap);
4341 y = lineGap + row * (squareSize + lineGap);
4343 x = lineGap + column * (squareSize + lineGap);
4344 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4345 (squareSize + lineGap);
4348 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4350 square_color = SquareColor(row, column);
4352 if ( // [HGM] holdings: blank out area between board and holdings
4353 column == BOARD_LEFT-1 || column == BOARD_RGHT
4354 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4355 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4356 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4358 // [HGM] print piece counts next to holdings
4359 string[1] = NULLCHAR;
4360 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4361 string[0] = '0' + piece;
4362 XTextExtents(countFontStruct, string, 1, &direction,
4363 &font_ascent, &font_descent, &overall);
4364 if (appData.monoMode) {
4365 XDrawImageString(xDisplay, xBoardWindow, countGC,
4366 x + squareSize - overall.width - 2,
4367 y + font_ascent + 1, string, 1);
4369 XDrawString(xDisplay, xBoardWindow, countGC,
4370 x + squareSize - overall.width - 2,
4371 y + font_ascent + 1, string, 1);
4374 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4375 string[0] = '0' + piece;
4376 XTextExtents(countFontStruct, string, 1, &direction,
4377 &font_ascent, &font_descent, &overall);
4378 if (appData.monoMode) {
4379 XDrawImageString(xDisplay, xBoardWindow, countGC,
4380 x + 2, y + font_ascent + 1, string, 1);
4382 XDrawString(xDisplay, xBoardWindow, countGC,
4383 x + 2, y + font_ascent + 1, string, 1);
4387 if (piece == EmptySquare || appData.blindfold) {
4388 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4390 drawfunc = ChooseDrawFunc();
4391 if (do_flash && appData.flashCount > 0) {
4392 for (i=0; i<appData.flashCount; ++i) {
4394 drawfunc(piece, square_color, x, y, xBoardWindow);
4395 XSync(xDisplay, False);
4396 do_flash_delay(flash_delay);
4398 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4399 XSync(xDisplay, False);
4400 do_flash_delay(flash_delay);
4403 drawfunc(piece, square_color, x, y, xBoardWindow);
4407 string[1] = NULLCHAR;
4408 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4409 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4410 string[0] = 'a' + column - BOARD_LEFT;
4411 XTextExtents(coordFontStruct, string, 1, &direction,
4412 &font_ascent, &font_descent, &overall);
4413 if (appData.monoMode) {
4414 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4415 x + squareSize - overall.width - 2,
4416 y + squareSize - font_descent - 1, string, 1);
4418 XDrawString(xDisplay, xBoardWindow, coordGC,
4419 x + squareSize - overall.width - 2,
4420 y + squareSize - font_descent - 1, string, 1);
4423 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4424 string[0] = ONE + row;
4425 XTextExtents(coordFontStruct, string, 1, &direction,
4426 &font_ascent, &font_descent, &overall);
4427 if (appData.monoMode) {
4428 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4429 x + 2, y + font_ascent + 1, string, 1);
4431 XDrawString(xDisplay, xBoardWindow, coordGC,
4432 x + 2, y + font_ascent + 1, string, 1);
4435 if(!partnerUp && marker[row][column]) {
4436 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4437 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4442 /* Why is this needed on some versions of X? */
4443 void EventProc(widget, unused, event)
4448 if (!XtIsRealized(widget))
4451 switch (event->type) {
4453 if (event->xexpose.count > 0) return; /* no clipping is done */
4454 XDrawPosition(widget, True, NULL);
4455 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4456 flipView = !flipView; partnerUp = !partnerUp;
4457 XDrawPosition(widget, True, NULL);
4458 flipView = !flipView; partnerUp = !partnerUp;
4462 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4469 void DrawPosition(fullRedraw, board)
4470 /*Boolean*/int fullRedraw;
4473 XDrawPosition(boardWidget, fullRedraw, board);
4476 /* Returns 1 if there are "too many" differences between b1 and b2
4477 (i.e. more than 1 move was made) */
4478 static int too_many_diffs(b1, b2)
4484 for (i=0; i<BOARD_HEIGHT; ++i) {
4485 for (j=0; j<BOARD_WIDTH; ++j) {
4486 if (b1[i][j] != b2[i][j]) {
4487 if (++c > 4) /* Castling causes 4 diffs */
4496 /* Matrix describing castling maneuvers */
4497 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4498 static int castling_matrix[4][5] = {
4499 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4500 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4501 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4502 { 7, 7, 4, 5, 6 } /* 0-0, black */
4505 /* Checks whether castling occurred. If it did, *rrow and *rcol
4506 are set to the destination (row,col) of the rook that moved.
4508 Returns 1 if castling occurred, 0 if not.
4510 Note: Only handles a max of 1 castling move, so be sure
4511 to call too_many_diffs() first.
4513 static int check_castle_draw(newb, oldb, rrow, rcol)
4520 /* For each type of castling... */
4521 for (i=0; i<4; ++i) {
4522 r = castling_matrix[i];
4524 /* Check the 4 squares involved in the castling move */
4526 for (j=1; j<=4; ++j) {
4527 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4534 /* All 4 changed, so it must be a castling move */
4543 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4544 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4546 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4549 void DrawSeekBackground( int left, int top, int right, int bottom )
4551 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4554 void DrawSeekText(char *buf, int x, int y)
4556 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4559 void DrawSeekDot(int x, int y, int colorNr)
4561 int square = colorNr & 0x80;
4564 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4566 XFillRectangle(xDisplay, xBoardWindow, color,
4567 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4569 XFillArc(xDisplay, xBoardWindow, color,
4570 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4573 static int damage[2][BOARD_RANKS][BOARD_FILES];
4576 * event handler for redrawing the board
4578 void XDrawPosition(w, repaint, board)
4580 /*Boolean*/int repaint;
4584 static int lastFlipView = 0;
4585 static int lastBoardValid[2] = {0, 0};
4586 static Board lastBoard[2];
4589 int nr = twoBoards*partnerUp;
4591 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4593 if (board == NULL) {
4594 if (!lastBoardValid[nr]) return;
4595 board = lastBoard[nr];
4597 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4598 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4599 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4604 * It would be simpler to clear the window with XClearWindow()
4605 * but this causes a very distracting flicker.
4608 if ( lineGap && IsDrawArrowEnabled()) repaint = True;
4609 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4611 /* If too much changes (begin observing new game, etc.), don't
4613 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4615 /* Special check for castling so we don't flash both the king
4616 and the rook (just flash the king). */
4618 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4619 /* Draw rook with NO flashing. King will be drawn flashing later */
4620 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4621 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4625 /* First pass -- Draw (newly) empty squares and repair damage.
4626 This prevents you from having a piece show up twice while it
4627 is flashing on its new square */
4628 for (i = 0; i < BOARD_HEIGHT; i++)
4629 for (j = 0; j < BOARD_WIDTH; j++)
4630 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4631 || damage[nr][i][j]) {
4632 DrawSquare(i, j, board[i][j], 0);
4633 damage[nr][i][j] = False;
4636 /* Second pass -- Draw piece(s) in new position and flash them */
4637 for (i = 0; i < BOARD_HEIGHT; i++)
4638 for (j = 0; j < BOARD_WIDTH; j++)
4639 if (board[i][j] != lastBoard[nr][i][j]) {
4640 DrawSquare(i, j, board[i][j], do_flash);
4644 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4645 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4646 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4648 for (i = 0; i < BOARD_HEIGHT; i++)
4649 for (j = 0; j < BOARD_WIDTH; j++) {
4650 DrawSquare(i, j, board[i][j], 0);
4651 damage[nr][i][j] = False;
4655 CopyBoard(lastBoard[nr], board);
4656 lastBoardValid[nr] = 1;
4657 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4658 lastFlipView = flipView;
4660 /* Draw highlights */
4661 if (pm1X >= 0 && pm1Y >= 0) {
4662 drawHighlight(pm1X, pm1Y, prelineGC);
4664 if (pm2X >= 0 && pm2Y >= 0) {
4665 drawHighlight(pm2X, pm2Y, prelineGC);
4667 if (hi1X >= 0 && hi1Y >= 0) {
4668 drawHighlight(hi1X, hi1Y, highlineGC);
4670 if (hi2X >= 0 && hi2Y >= 0) {
4671 drawHighlight(hi2X, hi2Y, highlineGC);
4673 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4675 /* If piece being dragged around board, must redraw that too */
4678 XSync(xDisplay, False);
4683 * event handler for redrawing the board
4685 void DrawPositionProc(w, event, prms, nprms)
4691 XDrawPosition(w, True, NULL);
4696 * event handler for parsing user moves
4698 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4699 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4700 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4701 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4702 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4703 // and at the end FinishMove() to perform the move after optional promotion popups.
4704 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4705 void HandleUserMove(w, event, prms, nprms)
4711 if (w != boardWidget || errorExitStatus != -1) return;
4712 if(nprms) shiftKey = !strcmp(prms[0], "1");
4715 if (event->type == ButtonPress) {
4716 XtPopdown(promotionShell);
4717 XtDestroyWidget(promotionShell);
4718 promotionUp = False;
4726 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4727 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4728 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4731 void AnimateUserMove (Widget w, XEvent * event,
4732 String * params, Cardinal * nParams)
4734 DragPieceMove(event->xmotion.x, event->xmotion.y);
4737 void HandlePV (Widget w, XEvent * event,
4738 String * params, Cardinal * nParams)
4739 { // [HGM] pv: walk PV
4740 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4743 Widget CommentCreate(name, text, mutable, callback, lines)
4745 int /*Boolean*/ mutable;
4746 XtCallbackProc callback;
4750 Widget shell, layout, form, edit, b_ok, b_cancel, b_clear, b_close, b_edit;
4755 XtSetArg(args[j], XtNwidth, &bw_width); j++;
4756 XtGetValues(boardWidget, args, j);
4759 XtSetArg(args[j], XtNresizable, True); j++;
4762 XtCreatePopupShell(name, topLevelShellWidgetClass,
4763 shellWidget, args, j);
4766 XtCreatePopupShell(name, transientShellWidgetClass,
4767 shellWidget, args, j);
4770 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
4771 layoutArgs, XtNumber(layoutArgs));
4773 XtCreateManagedWidget("form", formWidgetClass, layout,
4774 formArgs, XtNumber(formArgs));
4778 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
4779 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
4781 XtSetArg(args[j], XtNstring, text); j++;
4782 XtSetArg(args[j], XtNtop, XtChainTop); j++;
4783 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4784 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4785 XtSetArg(args[j], XtNright, XtChainRight); j++;
4786 XtSetArg(args[j], XtNresizable, True); j++;
4787 XtSetArg(args[j], XtNwidth, bw_width); j++; /*force wider than buttons*/
4788 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
4789 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
4790 XtSetArg(args[j], XtNautoFill, True); j++;
4791 XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
4793 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
4794 XtOverrideTranslations(edit, XtParseTranslationTable(commentTranslations));
4798 XtSetArg(args[j], XtNfromVert, edit); j++;
4799 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4800 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4801 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4802 XtSetArg(args[j], XtNright, XtChainLeft); j++;
4804 XtCreateManagedWidget(_("ok"), commandWidgetClass, form, args, j);
4805 XtAddCallback(b_ok, XtNcallback, callback, (XtPointer) 0);
4808 XtSetArg(args[j], XtNfromVert, edit); j++;
4809 XtSetArg(args[j], XtNfromHoriz, b_ok); j++;
4810 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4811 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4812 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4813 XtSetArg(args[j], XtNright, XtChainLeft); j++;
4815 XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
4816 XtAddCallback(b_cancel, XtNcallback, callback, (XtPointer) 0);
4819 XtSetArg(args[j], XtNfromVert, edit); j++;
4820 XtSetArg(args[j], XtNfromHoriz, b_cancel); j++;
4821 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4822 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4823 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4824 XtSetArg(args[j], XtNright, XtChainLeft); j++;
4826 XtCreateManagedWidget(_("clear"), commandWidgetClass, form, args, j);
4827 XtAddCallback(b_clear, XtNcallback, callback, (XtPointer) 0);
4830 XtSetArg(args[j], XtNfromVert, edit); j++;
4831 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4832 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4833 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4834 XtSetArg(args[j], XtNright, XtChainLeft); j++;
4836 XtCreateManagedWidget(_("close"), commandWidgetClass, form, args, j);
4837 XtAddCallback(b_close, XtNcallback, callback, (XtPointer) 0);
4840 XtSetArg(args[j], XtNfromVert, edit); j++;
4841 XtSetArg(args[j], XtNfromHoriz, b_close); j++;
4842 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4843 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4844 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4845 XtSetArg(args[j], XtNright, XtChainLeft); j++;
4847 XtCreateManagedWidget(_("edit"), commandWidgetClass, form, args, j);
4848 XtAddCallback(b_edit, XtNcallback, callback, (XtPointer) 0);
4851 XtRealizeWidget(shell);
4853 if (commentX == -1) {
4856 Dimension pw_height;
4857 Dimension ew_height;
4860 XtSetArg(args[j], XtNheight, &ew_height); j++;
4861 XtGetValues(edit, args, j);
4864 XtSetArg(args[j], XtNheight, &pw_height); j++;
4865 XtGetValues(shell, args, j);
4866 commentH = pw_height + (lines - 1) * ew_height;
4867 commentW = bw_width - 16;
4869 XSync(xDisplay, False);
4871 /* This code seems to tickle an X bug if it is executed too soon
4872 after xboard starts up. The coordinates get transformed as if
4873 the main window was positioned at (0, 0).
4875 XtTranslateCoords(shellWidget,
4876 (bw_width - commentW) / 2, 0 - commentH / 2,
4877 &commentX, &commentY);
4879 XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
4880 RootWindowOfScreen(XtScreen(shellWidget)),
4881 (bw_width - commentW) / 2, 0 - commentH / 2,
4886 if (commentY < 0) commentY = 0; /*avoid positioning top offscreen*/
4889 if(wpComment.width > 0) {
4890 commentX = wpComment.x;
4891 commentY = wpComment.y;
4892 commentW = wpComment.width;
4893 commentH = wpComment.height;
4897 XtSetArg(args[j], XtNheight, commentH); j++;
4898 XtSetArg(args[j], XtNwidth, commentW); j++;
4899 XtSetArg(args[j], XtNx, commentX); j++;
4900 XtSetArg(args[j], XtNy, commentY); j++;
4901 XtSetValues(shell, args, j);
4902 XtSetKeyboardFocus(shell, edit);
4907 /* Used for analysis window and ICS input window */
4908 Widget MiscCreate(name, text, mutable, callback, lines)
4910 int /*Boolean*/ mutable;
4911 XtCallbackProc callback;
4915 Widget shell, layout, form, edit;
4917 Dimension bw_width, pw_height, ew_height, w, h;
4923 XtSetArg(args[j], XtNresizable, True); j++;
4926 XtCreatePopupShell(name, topLevelShellWidgetClass,
4927 shellWidget, args, j);
4930 XtCreatePopupShell(name, transientShellWidgetClass,
4931 shellWidget, args, j);
4934 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
4935 layoutArgs, XtNumber(layoutArgs));
4937 XtCreateManagedWidget("form", formWidgetClass, layout,
4938 formArgs, XtNumber(formArgs));
4942 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
4943 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
4945 XtSetArg(args[j], XtNstring, text); j++;
4946 XtSetArg(args[j], XtNtop, XtChainTop); j++;
4947 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4948 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4949 XtSetArg(args[j], XtNright, XtChainRight); j++;
4950 XtSetArg(args[j], XtNresizable, True); j++;
4951 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
4952 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
4953 XtSetArg(args[j], XtNautoFill, True); j++;
4954 XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
4956 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
4958 XtRealizeWidget(shell);
4961 XtSetArg(args[j], XtNwidth, &bw_width); j++;
4962 XtGetValues(boardWidget, args, j);
4965 XtSetArg(args[j], XtNheight, &ew_height); j++;
4966 XtGetValues(edit, args, j);
4969 XtSetArg(args[j], XtNheight, &pw_height); j++;
4970 XtGetValues(shell, args, j);
4971 h = pw_height + (lines - 1) * ew_height;
4974 XSync(xDisplay, False);
4976 /* This code seems to tickle an X bug if it is executed too soon
4977 after xboard starts up. The coordinates get transformed as if
4978 the main window was positioned at (0, 0).
4980 XtTranslateCoords(shellWidget, (bw_width - w) / 2, 0 - h / 2, &x, &y);
4982 XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
4983 RootWindowOfScreen(XtScreen(shellWidget)),
4984 (bw_width - w) / 2, 0 - h / 2, &xx, &yy, &junk);
4988 if (y < 0) y = 0; /*avoid positioning top offscreen*/
4991 XtSetArg(args[j], XtNheight, h); j++;
4992 XtSetArg(args[j], XtNwidth, w); j++;
4993 XtSetArg(args[j], XtNx, x); j++;
4994 XtSetArg(args[j], XtNy, y); j++;
4995 XtSetValues(shell, args, j);
5001 static int savedIndex; /* gross that this is global */
5003 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
5006 XawTextPosition index, dummy;
5009 XawTextGetSelectionPos(w, &index, &dummy);
5010 XtSetArg(arg, XtNstring, &val);
5011 XtGetValues(w, &arg, 1);
5012 ReplaceComment(savedIndex, val);
5013 if(savedIndex != currentMove) ToNrEvent(savedIndex);
5014 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
5017 void EditCommentPopUp(index, title, text)
5026 if (text == NULL) text = "";
5028 if (editShell == NULL) {
5030 CommentCreate(title, text, True, EditCommentCallback, 4);
5031 XtRealizeWidget(editShell);
5032 CatchDeleteWindow(editShell, "EditCommentPopDown");
5034 edit = XtNameToWidget(editShell, "*form.text");
5036 XtSetArg(args[j], XtNstring, text); j++;
5037 XtSetValues(edit, args, j);
5039 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
5040 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
5041 XtSetValues(editShell, args, j);
5044 XtPopup(editShell, XtGrabNone);
5048 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
5049 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"),
5051 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"),
5055 void EditCommentCallback(w, client_data, call_data)
5057 XtPointer client_data, call_data;
5065 XtSetArg(args[j], XtNlabel, &name); j++;
5066 XtGetValues(w, args, j);
5068 if (strcmp(name, _("ok")) == 0) {
5069 edit = XtNameToWidget(editShell, "*form.text");
5071 XtSetArg(args[j], XtNstring, &val); j++;
5072 XtGetValues(edit, args, j);
5073 ReplaceComment(savedIndex, val);
5074 EditCommentPopDown();
5075 } else if (strcmp(name, _("cancel")) == 0) {
5076 EditCommentPopDown();
5077 } else if (strcmp(name, _("clear")) == 0) {
5078 edit = XtNameToWidget(editShell, "*form.text");
5079 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5080 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5084 void EditCommentPopDown()
5089 if (!editUp) return;
5091 XtSetArg(args[j], XtNx, &commentX); j++;
5092 XtSetArg(args[j], XtNy, &commentY); j++;
5093 XtSetArg(args[j], XtNheight, &commentH); j++;
5094 XtSetArg(args[j], XtNwidth, &commentW); j++;
5095 XtGetValues(editShell, args, j);
5096 XtPopdown(editShell);
5099 XtSetArg(args[j], XtNleftBitmap, None); j++;
5100 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"),
5102 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"),
5106 void ICSInputBoxPopUp()
5111 char *title = _("ICS Input");
5114 if (ICSInputShell == NULL) {
5115 ICSInputShell = MiscCreate(title, "", True, NULL, 1);
5116 tr = XtParseTranslationTable(ICSInputTranslations);
5117 edit = XtNameToWidget(ICSInputShell, "*form.text");
5118 XtOverrideTranslations(edit, tr);
5119 XtRealizeWidget(ICSInputShell);
5120 CatchDeleteWindow(ICSInputShell, "ICSInputBoxPopDown");
5123 edit = XtNameToWidget(ICSInputShell, "*form.text");
5125 XtSetArg(args[j], XtNstring, ""); j++;
5126 XtSetValues(edit, args, j);
5128 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
5129 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
5130 XtSetValues(ICSInputShell, args, j);
5133 XtPopup(ICSInputShell, XtGrabNone);
5134 XtSetKeyboardFocus(ICSInputShell, edit);
5136 ICSInputBoxUp = True;
5138 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
5139 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.ICS Input Box"),
5143 void ICSInputSendText()
5150 edit = XtNameToWidget(ICSInputShell, "*form.text");
5152 XtSetArg(args[j], XtNstring, &val); j++;
5153 XtGetValues(edit, args, j);
5155 SendMultiLineToICS(val);
5156 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5157 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5160 void ICSInputBoxPopDown()
5165 if (!ICSInputBoxUp) return;
5167 XtPopdown(ICSInputShell);
5168 ICSInputBoxUp = False;
5170 XtSetArg(args[j], XtNleftBitmap, None); j++;
5171 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.ICS Input Box"),
5175 void CommentPopUp(title, text)
5182 savedIndex = currentMove; // [HGM] vari
5183 if (commentShell == NULL) {
5185 CommentCreate(title, text, False, CommentCallback, 4);
5186 XtRealizeWidget(commentShell);
5187 CatchDeleteWindow(commentShell, "CommentPopDown");
5189 edit = XtNameToWidget(commentShell, "*form.text");
5191 XtSetArg(args[j], XtNstring, text); j++;
5192 XtSetValues(edit, args, j);
5194 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
5195 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
5196 XtSetValues(commentShell, args, j);
5199 XtPopup(commentShell, XtGrabNone);
5200 XSync(xDisplay, False);
5205 void CommentCallback(w, client_data, call_data)
5207 XtPointer client_data, call_data;
5214 XtSetArg(args[j], XtNlabel, &name); j++;
5215 XtGetValues(w, args, j);
5217 if (strcmp(name, _("close")) == 0) {
5219 } else if (strcmp(name, _("edit")) == 0) {
5226 void CommentPopDown()
5231 if (!commentUp) return;
5233 XtSetArg(args[j], XtNx, &commentX); j++;
5234 XtSetArg(args[j], XtNy, &commentY); j++;
5235 XtSetArg(args[j], XtNwidth, &commentW); j++;
5236 XtSetArg(args[j], XtNheight, &commentH); j++;
5237 XtGetValues(commentShell, args, j);
5238 XtPopdown(commentShell);
5239 XSync(xDisplay, False);
5243 void FileNamePopUp(label, def, proc, openMode)
5249 fileProc = proc; /* I can't see a way not */
5250 fileOpenMode = openMode; /* to use globals here */
5251 { // [HGM] use file-selector dialog stolen from Ghostview
5253 int index; // this is not supported yet
5255 if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
5256 def, openMode, NULL, &name))
5257 (void) (*fileProc)(f, index=0, name);
5261 void FileNamePopDown()
5263 if (!filenameUp) return;
5264 XtPopdown(fileNameShell);
5265 XtDestroyWidget(fileNameShell);
5270 void FileNameCallback(w, client_data, call_data)
5272 XtPointer client_data, call_data;
5277 XtSetArg(args[0], XtNlabel, &name);
5278 XtGetValues(w, args, 1);
5280 if (strcmp(name, _("cancel")) == 0) {
5285 FileNameAction(w, NULL, NULL, NULL);
5288 void FileNameAction(w, event, prms, nprms)
5300 name = XawDialogGetValueString(w = XtParent(w));
5302 if ((name != NULL) && (*name != NULLCHAR)) {
5303 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5304 XtPopdown(w = XtParent(XtParent(w)));
5308 p = strrchr(buf, ' ');
5315 fullname = ExpandPathName(buf);
5317 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5320 f = fopen(fullname, fileOpenMode);
5322 DisplayError(_("Failed to open file"), errno);
5324 (void) (*fileProc)(f, index, buf);
5331 XtPopdown(w = XtParent(XtParent(w)));
5337 void PromotionPopUp()
5340 Widget dialog, layout;
5342 Dimension bw_width, pw_width;
5346 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5347 XtGetValues(boardWidget, args, j);
5350 XtSetArg(args[j], XtNresizable, True); j++;
5351 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5353 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5354 shellWidget, args, j);
5356 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5357 layoutArgs, XtNumber(layoutArgs));
5360 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5361 XtSetArg(args[j], XtNborderWidth, 0); j++;
5362 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5365 if(gameInfo.variant != VariantShogi) {
5366 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
5367 (XtPointer) dialog);
5368 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
5369 (XtPointer) dialog);
5370 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
5371 (XtPointer) dialog);
5372 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
5373 (XtPointer) dialog);
5374 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5375 gameInfo.variant == VariantGiveaway) {
5376 XawDialogAddButton(dialog, _("King"), PromotionCallback,
5377 (XtPointer) dialog);
5379 if(gameInfo.variant == VariantCapablanca ||
5380 gameInfo.variant == VariantGothic ||
5381 gameInfo.variant == VariantCapaRandom) {
5382 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5383 (XtPointer) dialog);
5384 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5385 (XtPointer) dialog);
5387 } else // [HGM] shogi
5389 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5390 (XtPointer) dialog);
5391 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5392 (XtPointer) dialog);
5394 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5395 (XtPointer) dialog);
5397 XtRealizeWidget(promotionShell);
5398 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5401 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5402 XtGetValues(promotionShell, args, j);
5404 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5405 lineGap + squareSize/3 +
5406 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5407 0 : 6*(squareSize + lineGap)), &x, &y);
5410 XtSetArg(args[j], XtNx, x); j++;
5411 XtSetArg(args[j], XtNy, y); j++;
5412 XtSetValues(promotionShell, args, j);
5414 XtPopup(promotionShell, XtGrabNone);
5419 void PromotionPopDown()
5421 if (!promotionUp) return;
5422 XtPopdown(promotionShell);
5423 XtDestroyWidget(promotionShell);
5424 promotionUp = False;
5427 void PromotionCallback(w, client_data, call_data)
5429 XtPointer client_data, call_data;
5435 XtSetArg(args[0], XtNlabel, &name);
5436 XtGetValues(w, args, 1);
5440 if (fromX == -1) return;
5442 if (strcmp(name, _("cancel")) == 0) {
5446 } else if (strcmp(name, _("Knight")) == 0) {
5448 } else if (strcmp(name, _("Promote")) == 0) {
5450 } else if (strcmp(name, _("Defer")) == 0) {
5453 promoChar = ToLower(name[0]);
5456 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5458 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5459 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5464 void ErrorCallback(w, client_data, call_data)
5466 XtPointer client_data, call_data;
5469 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5471 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5477 if (!errorUp) return;
5479 XtPopdown(errorShell);
5480 XtDestroyWidget(errorShell);
5481 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5484 void ErrorPopUp(title, label, modal)
5485 char *title, *label;
5489 Widget dialog, layout;
5493 Dimension bw_width, pw_width;
5494 Dimension pw_height;
5498 XtSetArg(args[i], XtNresizable, True); i++;
5499 XtSetArg(args[i], XtNtitle, title); i++;
5501 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5502 shellWidget, args, i);
5504 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5505 layoutArgs, XtNumber(layoutArgs));
5508 XtSetArg(args[i], XtNlabel, label); i++;
5509 XtSetArg(args[i], XtNborderWidth, 0); i++;
5510 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5513 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5515 XtRealizeWidget(errorShell);
5516 CatchDeleteWindow(errorShell, "ErrorPopDown");
5519 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5520 XtGetValues(boardWidget, args, i);
5522 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5523 XtSetArg(args[i], XtNheight, &pw_height); i++;
5524 XtGetValues(errorShell, args, i);
5527 /* This code seems to tickle an X bug if it is executed too soon
5528 after xboard starts up. The coordinates get transformed as if
5529 the main window was positioned at (0, 0).
5531 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5532 0 - pw_height + squareSize / 3, &x, &y);
5534 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5535 RootWindowOfScreen(XtScreen(boardWidget)),
5536 (bw_width - pw_width) / 2,
5537 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5541 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5544 XtSetArg(args[i], XtNx, x); i++;
5545 XtSetArg(args[i], XtNy, y); i++;
5546 XtSetValues(errorShell, args, i);
5549 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5552 /* Disable all user input other than deleting the window */
5553 static int frozen = 0;
5557 /* Grab by a widget that doesn't accept input */
5558 XtAddGrab(messageWidget, TRUE, FALSE);
5562 /* Undo a FreezeUI */
5565 if (!frozen) return;
5566 XtRemoveGrab(messageWidget);
5570 char *ModeToWidgetName(mode)
5574 case BeginningOfGame:
5575 if (appData.icsActive)
5576 return "menuMode.ICS Client";
5577 else if (appData.noChessProgram ||
5578 *appData.cmailGameName != NULLCHAR)
5579 return "menuMode.Edit Game";
5581 return "menuMode.Machine Black";
5582 case MachinePlaysBlack:
5583 return "menuMode.Machine Black";
5584 case MachinePlaysWhite:
5585 return "menuMode.Machine White";
5587 return "menuMode.Analysis Mode";
5589 return "menuMode.Analyze File";
5590 case TwoMachinesPlay:
5591 return "menuMode.Two Machines";
5593 return "menuMode.Edit Game";
5594 case PlayFromGameFile:
5595 return "menuFile.Load Game";
5597 return "menuMode.Edit Position";
5599 return "menuMode.Training";
5600 case IcsPlayingWhite:
5601 case IcsPlayingBlack:
5605 return "menuMode.ICS Client";
5612 void ModeHighlight()
5615 static int oldPausing = FALSE;
5616 static GameMode oldmode = (GameMode) -1;
5619 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5621 if (pausing != oldPausing) {
5622 oldPausing = pausing;
5624 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5626 XtSetArg(args[0], XtNleftBitmap, None);
5628 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5631 if (appData.showButtonBar) {
5632 /* Always toggle, don't set. Previous code messes up when
5633 invoked while the button is pressed, as releasing it
5634 toggles the state again. */
5637 XtSetArg(args[0], XtNbackground, &oldbg);
5638 XtSetArg(args[1], XtNforeground, &oldfg);
5639 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5641 XtSetArg(args[0], XtNbackground, oldfg);
5642 XtSetArg(args[1], XtNforeground, oldbg);
5644 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5648 wname = ModeToWidgetName(oldmode);
5649 if (wname != NULL) {
5650 XtSetArg(args[0], XtNleftBitmap, None);
5651 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5653 wname = ModeToWidgetName(gameMode);
5654 if (wname != NULL) {
5655 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5656 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5660 /* Maybe all the enables should be handled here, not just this one */
5661 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5662 gameMode == Training || gameMode == PlayFromGameFile);
5667 * Button/menu procedures
5669 void ResetProc(w, event, prms, nprms)
5678 int LoadGamePopUp(f, gameNumber, title)
5683 cmailMsgLoaded = FALSE;
5684 if (gameNumber == 0) {
5685 int error = GameListBuild(f);
5687 DisplayError(_("Cannot build game list"), error);
5688 } else if (!ListEmpty(&gameList) &&
5689 ((ListGame *) gameList.tailPred)->number > 1) {
5690 GameListPopUp(f, title);
5696 return LoadGame(f, gameNumber, title, FALSE);
5699 void LoadGameProc(w, event, prms, nprms)
5705 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5708 FileNamePopUp(_("Load game file name?"), "", LoadGamePopUp, "rb");
5711 void LoadNextGameProc(w, event, prms, nprms)
5720 void LoadPrevGameProc(w, event, prms, nprms)
5729 void ReloadGameProc(w, event, prms, nprms)
5738 void LoadNextPositionProc(w, event, prms, nprms)
5747 void LoadPrevPositionProc(w, event, prms, nprms)
5756 void ReloadPositionProc(w, event, prms, nprms)
5765 void LoadPositionProc(w, event, prms, nprms)
5771 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5774 FileNamePopUp(_("Load position file name?"), "", LoadPosition, "rb");
5777 void SaveGameProc(w, event, prms, nprms)
5783 FileNamePopUp(_("Save game file name?"),
5784 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5788 void SavePositionProc(w, event, prms, nprms)
5794 FileNamePopUp(_("Save position file name?"),
5795 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5799 void ReloadCmailMsgProc(w, event, prms, nprms)
5805 ReloadCmailMsgEvent(FALSE);
5808 void MailMoveProc(w, event, prms, nprms)
5817 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5818 char *selected_fen_position=NULL;
5821 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5822 Atom *type_return, XtPointer *value_return,
5823 unsigned long *length_return, int *format_return)
5825 char *selection_tmp;
5827 if (!selected_fen_position) return False; /* should never happen */
5828 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5829 /* note: since no XtSelectionDoneProc was registered, Xt will
5830 * automatically call XtFree on the value returned. So have to
5831 * make a copy of it allocated with XtMalloc */
5832 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5833 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5835 *value_return=selection_tmp;
5836 *length_return=strlen(selection_tmp);
5837 *type_return=*target;
5838 *format_return = 8; /* bits per byte */
5840 } else if (*target == XA_TARGETS(xDisplay)) {
5841 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5842 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5843 targets_tmp[1] = XA_STRING;
5844 *value_return = targets_tmp;
5845 *type_return = XA_ATOM;
5847 *format_return = 8 * sizeof(Atom);
5848 if (*format_return > 32) {
5849 *length_return *= *format_return / 32;
5850 *format_return = 32;
5858 /* note: when called from menu all parameters are NULL, so no clue what the
5859 * Widget which was clicked on was, or what the click event was
5861 void CopyPositionProc(w, event, prms, nprms)
5868 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5869 * have a notion of a position that is selected but not copied.
5870 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5872 if(gameMode == EditPosition) EditPositionDone(TRUE);
5873 if (selected_fen_position) free(selected_fen_position);
5874 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5875 if (!selected_fen_position) return;
5876 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5878 SendPositionSelection,
5879 NULL/* lose_ownership_proc */ ,
5880 NULL/* transfer_done_proc */);
5881 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5883 SendPositionSelection,
5884 NULL/* lose_ownership_proc */ ,
5885 NULL/* transfer_done_proc */);
5888 /* function called when the data to Paste is ready */
5890 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5891 Atom *type, XtPointer value, unsigned long *len, int *format)
5894 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5895 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5896 EditPositionPasteFEN(fenstr);
5900 /* called when Paste Position button is pressed,
5901 * all parameters will be NULL */
5902 void PastePositionProc(w, event, prms, nprms)
5908 XtGetSelectionValue(menuBarWidget,
5909 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5910 /* (XtSelectionCallbackProc) */ PastePositionCB,
5911 NULL, /* client_data passed to PastePositionCB */
5913 /* better to use the time field from the event that triggered the
5914 * call to this function, but that isn't trivial to get
5922 SendGameSelection(Widget w, Atom *selection, Atom *target,
5923 Atom *type_return, XtPointer *value_return,
5924 unsigned long *length_return, int *format_return)
5926 char *selection_tmp;
5928 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5929 FILE* f = fopen(gameCopyFilename, "r");
5932 if (f == NULL) return False;
5936 selection_tmp = XtMalloc(len + 1);
5937 count = fread(selection_tmp, 1, len, f);
5939 XtFree(selection_tmp);
5942 selection_tmp[len] = NULLCHAR;
5943 *value_return = selection_tmp;
5944 *length_return = len;
5945 *type_return = *target;
5946 *format_return = 8; /* bits per byte */
5948 } else if (*target == XA_TARGETS(xDisplay)) {
5949 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5950 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5951 targets_tmp[1] = XA_STRING;
5952 *value_return = targets_tmp;
5953 *type_return = XA_ATOM;
5955 *format_return = 8 * sizeof(Atom);
5956 if (*format_return > 32) {
5957 *length_return *= *format_return / 32;
5958 *format_return = 32;
5966 /* note: when called from menu all parameters are NULL, so no clue what the
5967 * Widget which was clicked on was, or what the click event was
5969 void CopyGameProc(w, event, prms, nprms)
5977 ret = SaveGameToFile(gameCopyFilename, FALSE);
5981 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5982 * have a notion of a game that is selected but not copied.
5983 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5985 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5988 NULL/* lose_ownership_proc */ ,
5989 NULL/* transfer_done_proc */);
5990 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5993 NULL/* lose_ownership_proc */ ,
5994 NULL/* transfer_done_proc */);
5997 /* function called when the data to Paste is ready */
5999 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
6000 Atom *type, XtPointer value, unsigned long *len, int *format)
6003 if (value == NULL || *len == 0) {
6004 return; /* nothing had been selected to copy */
6006 f = fopen(gamePasteFilename, "w");
6008 DisplayError(_("Can't open temp file"), errno);
6011 fwrite(value, 1, *len, f);
6014 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
6017 /* called when Paste Game button is pressed,
6018 * all parameters will be NULL */
6019 void PasteGameProc(w, event, prms, nprms)
6025 XtGetSelectionValue(menuBarWidget,
6026 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
6027 /* (XtSelectionCallbackProc) */ PasteGameCB,
6028 NULL, /* client_data passed to PasteGameCB */
6030 /* better to use the time field from the event that triggered the
6031 * call to this function, but that isn't trivial to get
6041 SaveGameProc(NULL, NULL, NULL, NULL);
6045 void QuitProc(w, event, prms, nprms)
6054 void PauseProc(w, event, prms, nprms)
6064 void MachineBlackProc(w, event, prms, nprms)
6070 MachineBlackEvent();
6073 void MachineWhiteProc(w, event, prms, nprms)
6079 MachineWhiteEvent();
6082 void AnalyzeModeProc(w, event, prms, nprms)
6090 if (!first.analysisSupport) {
6091 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
6092 DisplayError(buf, 0);
6095 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
6096 if (appData.icsActive) {
6097 if (gameMode != IcsObserving) {
6098 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
6099 DisplayError(buf, 0);
6101 if (appData.icsEngineAnalyze) {
6102 if (appData.debugMode)
6103 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
6109 /* if enable, use want disable icsEngineAnalyze */
6110 if (appData.icsEngineAnalyze) {
6115 appData.icsEngineAnalyze = TRUE;
6116 if (appData.debugMode)
6117 fprintf(debugFP, _("ICS engine analyze starting... \n"));
6119 if (!appData.showThinking)
6120 ShowThinkingProc(w,event,prms,nprms);
6125 void AnalyzeFileProc(w, event, prms, nprms)
6131 if (!first.analysisSupport) {
6133 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
6134 DisplayError(buf, 0);
6139 if (!appData.showThinking)
6140 ShowThinkingProc(w,event,prms,nprms);
6143 FileNamePopUp(_("File to analyze"), "", LoadGamePopUp, "rb");
6144 AnalysisPeriodicEvent(1);
6147 void TwoMachinesProc(w, event, prms, nprms)
6156 void IcsClientProc(w, event, prms, nprms)
6165 void EditGameProc(w, event, prms, nprms)
6174 void EditPositionProc(w, event, prms, nprms)
6180 EditPositionEvent();
6183 void TrainingProc(w, event, prms, nprms)
6192 void EditCommentProc(w, event, prms, nprms)
6199 EditCommentPopDown();
6205 void IcsInputBoxProc(w, event, prms, nprms)
6211 if (ICSInputBoxUp) {
6212 ICSInputBoxPopDown();
6218 void AcceptProc(w, event, prms, nprms)
6227 void DeclineProc(w, event, prms, nprms)
6236 void RematchProc(w, event, prms, nprms)
6245 void CallFlagProc(w, event, prms, nprms)
6254 void DrawProc(w, event, prms, nprms)
6263 void AbortProc(w, event, prms, nprms)
6272 void AdjournProc(w, event, prms, nprms)
6281 void ResignProc(w, event, prms, nprms)
6290 void AdjuWhiteProc(w, event, prms, nprms)
6296 UserAdjudicationEvent(+1);
6299 void AdjuBlackProc(w, event, prms, nprms)
6305 UserAdjudicationEvent(-1);
6308 void AdjuDrawProc(w, event, prms, nprms)
6314 UserAdjudicationEvent(0);
6317 void EnterKeyProc(w, event, prms, nprms)
6323 if (ICSInputBoxUp == True)
6327 void UpKeyProc(w, event, prms, nprms)
6332 { // [HGM] input: let up-arrow recall previous line from history
6339 if (!ICSInputBoxUp) return;
6340 edit = XtNameToWidget(ICSInputShell, "*form.text");
6342 XtSetArg(args[j], XtNstring, &val); j++;
6343 XtGetValues(edit, args, j);
6344 val = PrevInHistory(val);
6345 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6346 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6348 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6349 XawTextReplace(edit, 0, 0, &t);
6350 XawTextSetInsertionPoint(edit, 9999);
6354 void DownKeyProc(w, event, prms, nprms)
6359 { // [HGM] input: let down-arrow recall next line from history
6364 if (!ICSInputBoxUp) return;
6365 edit = XtNameToWidget(ICSInputShell, "*form.text");
6366 val = NextInHistory();
6367 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6368 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6370 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6371 XawTextReplace(edit, 0, 0, &t);
6372 XawTextSetInsertionPoint(edit, 9999);
6376 void StopObservingProc(w, event, prms, nprms)
6382 StopObservingEvent();
6385 void StopExaminingProc(w, event, prms, nprms)
6391 StopExaminingEvent();
6394 void UploadProc(w, event, prms, nprms)
6404 void ForwardProc(w, event, prms, nprms)
6414 void BackwardProc(w, event, prms, nprms)
6423 void ToStartProc(w, event, prms, nprms)
6432 void ToEndProc(w, event, prms, nprms)
6441 void RevertProc(w, event, prms, nprms)
6450 void AnnotateProc(w, event, prms, nprms)
6459 void TruncateGameProc(w, event, prms, nprms)
6465 TruncateGameEvent();
6467 void RetractMoveProc(w, event, prms, nprms)
6476 void MoveNowProc(w, event, prms, nprms)
6486 void AlwaysQueenProc(w, event, prms, nprms)
6494 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6496 if (appData.alwaysPromoteToQueen) {
6497 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6499 XtSetArg(args[0], XtNleftBitmap, None);
6501 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6505 void AnimateDraggingProc(w, event, prms, nprms)
6513 appData.animateDragging = !appData.animateDragging;
6515 if (appData.animateDragging) {
6516 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6519 XtSetArg(args[0], XtNleftBitmap, None);
6521 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6525 void AnimateMovingProc(w, event, prms, nprms)
6533 appData.animate = !appData.animate;
6535 if (appData.animate) {
6536 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6539 XtSetArg(args[0], XtNleftBitmap, None);
6541 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6545 void AutocommProc(w, event, prms, nprms)
6553 appData.autoComment = !appData.autoComment;
6555 if (appData.autoComment) {
6556 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6558 XtSetArg(args[0], XtNleftBitmap, None);
6560 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Comment"),
6565 void AutoflagProc(w, event, prms, nprms)
6573 appData.autoCallFlag = !appData.autoCallFlag;
6575 if (appData.autoCallFlag) {
6576 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6578 XtSetArg(args[0], XtNleftBitmap, None);
6580 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6584 void AutoflipProc(w, event, prms, nprms)
6592 appData.autoFlipView = !appData.autoFlipView;
6594 if (appData.autoFlipView) {
6595 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6597 XtSetArg(args[0], XtNleftBitmap, None);
6599 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6603 void AutobsProc(w, event, prms, nprms)
6611 appData.autoObserve = !appData.autoObserve;
6613 if (appData.autoObserve) {
6614 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6616 XtSetArg(args[0], XtNleftBitmap, None);
6618 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Observe"),
6622 void AutoraiseProc(w, event, prms, nprms)
6630 appData.autoRaiseBoard = !appData.autoRaiseBoard;
6632 if (appData.autoRaiseBoard) {
6633 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6635 XtSetArg(args[0], XtNleftBitmap, None);
6637 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Raise Board"),
6641 void AutosaveProc(w, event, prms, nprms)
6649 appData.autoSaveGames = !appData.autoSaveGames;
6651 if (appData.autoSaveGames) {
6652 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6654 XtSetArg(args[0], XtNleftBitmap, None);
6656 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
6660 void BlindfoldProc(w, event, prms, nprms)
6668 appData.blindfold = !appData.blindfold;
6670 if (appData.blindfold) {
6671 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6673 XtSetArg(args[0], XtNleftBitmap, None);
6675 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6678 DrawPosition(True, NULL);
6681 void TestLegalityProc(w, event, prms, nprms)
6689 appData.testLegality = !appData.testLegality;
6691 if (appData.testLegality) {
6692 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6694 XtSetArg(args[0], XtNleftBitmap, None);
6696 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6701 void FlashMovesProc(w, event, prms, nprms)
6709 if (appData.flashCount == 0) {
6710 appData.flashCount = 3;
6712 appData.flashCount = -appData.flashCount;
6715 if (appData.flashCount > 0) {
6716 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6718 XtSetArg(args[0], XtNleftBitmap, None);
6720 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6724 void FlipViewProc(w, event, prms, nprms)
6730 flipView = !flipView;
6731 DrawPosition(True, NULL);
6734 void GetMoveListProc(w, event, prms, nprms)
6742 appData.getMoveList = !appData.getMoveList;
6744 if (appData.getMoveList) {
6745 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6748 XtSetArg(args[0], XtNleftBitmap, None);
6750 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Get Move List"),
6755 void HighlightDraggingProc(w, event, prms, nprms)
6763 appData.highlightDragging = !appData.highlightDragging;
6765 if (appData.highlightDragging) {
6766 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6768 XtSetArg(args[0], XtNleftBitmap, None);
6770 XtSetValues(XtNameToWidget(menuBarWidget,
6771 "menuOptions.Highlight Dragging"), args, 1);
6775 void HighlightLastMoveProc(w, event, prms, nprms)
6783 appData.highlightLastMove = !appData.highlightLastMove;
6785 if (appData.highlightLastMove) {
6786 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6788 XtSetArg(args[0], XtNleftBitmap, None);
6790 XtSetValues(XtNameToWidget(menuBarWidget,
6791 "menuOptions.Highlight Last Move"), args, 1);
6794 void IcsAlarmProc(w, event, prms, nprms)
6802 appData.icsAlarm = !appData.icsAlarm;
6804 if (appData.icsAlarm) {
6805 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6807 XtSetArg(args[0], XtNleftBitmap, None);
6809 XtSetValues(XtNameToWidget(menuBarWidget,
6810 "menuOptions.ICS Alarm"), args, 1);
6813 void MoveSoundProc(w, event, prms, nprms)
6821 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6823 if (appData.ringBellAfterMoves) {
6824 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6826 XtSetArg(args[0], XtNleftBitmap, None);
6828 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6833 void OldSaveStyleProc(w, event, prms, nprms)
6841 appData.oldSaveStyle = !appData.oldSaveStyle;
6843 if (appData.oldSaveStyle) {
6844 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6846 XtSetArg(args[0], XtNleftBitmap, None);
6848 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Old Save Style"),
6852 void PeriodicUpdatesProc(w, event, prms, nprms)
6860 PeriodicUpdatesEvent(!appData.periodicUpdates);
6862 if (appData.periodicUpdates) {
6863 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6865 XtSetArg(args[0], XtNleftBitmap, None);
6867 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6871 void PonderNextMoveProc(w, event, prms, nprms)
6879 PonderNextMoveEvent(!appData.ponderNextMove);
6881 if (appData.ponderNextMove) {
6882 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6884 XtSetArg(args[0], XtNleftBitmap, None);
6886 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6890 void PopupExitMessageProc(w, event, prms, nprms)
6898 appData.popupExitMessage = !appData.popupExitMessage;
6900 if (appData.popupExitMessage) {
6901 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6903 XtSetArg(args[0], XtNleftBitmap, None);
6905 XtSetValues(XtNameToWidget(menuBarWidget,
6906 "menuOptions.Popup Exit Message"), args, 1);
6909 void PopupMoveErrorsProc(w, event, prms, nprms)
6917 appData.popupMoveErrors = !appData.popupMoveErrors;
6919 if (appData.popupMoveErrors) {
6920 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6922 XtSetArg(args[0], XtNleftBitmap, None);
6924 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6928 void PremoveProc(w, event, prms, nprms)
6936 appData.premove = !appData.premove;
6938 if (appData.premove) {
6939 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6941 XtSetArg(args[0], XtNleftBitmap, None);
6943 XtSetValues(XtNameToWidget(menuBarWidget,
6944 "menuOptions.Premove"), args, 1);
6947 void QuietPlayProc(w, event, prms, nprms)
6955 appData.quietPlay = !appData.quietPlay;
6957 if (appData.quietPlay) {
6958 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6960 XtSetArg(args[0], XtNleftBitmap, None);
6962 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Quiet Play"),
6966 void ShowCoordsProc(w, event, prms, nprms)
6974 appData.showCoords = !appData.showCoords;
6976 if (appData.showCoords) {
6977 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6979 XtSetArg(args[0], XtNleftBitmap, None);
6981 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6984 DrawPosition(True, NULL);
6987 void ShowThinkingProc(w, event, prms, nprms)
6993 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6994 ShowThinkingEvent();
6997 void HideThinkingProc(w, event, prms, nprms)
7005 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
7006 ShowThinkingEvent();
7008 if (appData.hideThinkingFromHuman) {
7009 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7011 XtSetArg(args[0], XtNleftBitmap, None);
7013 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
7017 void SaveOnExitProc(w, event, prms, nprms)
7025 saveSettingsOnExit = !saveSettingsOnExit;
7027 if (saveSettingsOnExit) {
7028 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7030 XtSetArg(args[0], XtNleftBitmap, None);
7032 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
7036 void SaveSettingsProc(w, event, prms, nprms)
7042 SaveSettings(settingsFileName);
7045 void InfoProc(w, event, prms, nprms)
7052 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
7057 void ManProc(w, event, prms, nprms)
7065 if (nprms && *nprms > 0)
7069 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
7073 void HintProc(w, event, prms, nprms)
7082 void BookProc(w, event, prms, nprms)
7091 void AboutProc(w, event, prms, nprms)
7099 char *zippy = " (with Zippy code)";
7103 snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
7104 programVersion, zippy,
7105 "Copyright 1991 Digital Equipment Corporation",
7106 "Enhancements Copyright 1992-2009 Free Software Foundation",
7107 "Enhancements Copyright 2005 Alessandro Scotti",
7108 PACKAGE, " is free software and carries NO WARRANTY;",
7109 "see the file COPYING for more information.");
7110 ErrorPopUp(_("About XBoard"), buf, FALSE);
7113 void DebugProc(w, event, prms, nprms)
7119 appData.debugMode = !appData.debugMode;
7122 void AboutGameProc(w, event, prms, nprms)
7131 void NothingProc(w, event, prms, nprms)
7140 void Iconify(w, event, prms, nprms)
7149 XtSetArg(args[0], XtNiconic, True);
7150 XtSetValues(shellWidget, args, 1);
7153 void DisplayMessage(message, extMessage)
7154 char *message, *extMessage;
7156 /* display a message in the message widget */
7165 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
7170 message = extMessage;
7174 /* need to test if messageWidget already exists, since this function
7175 can also be called during the startup, if for example a Xresource
7176 is not set up correctly */
7179 XtSetArg(arg, XtNlabel, message);
7180 XtSetValues(messageWidget, &arg, 1);
7186 void DisplayTitle(text)
7191 char title[MSG_SIZ];
7194 if (text == NULL) text = "";
7196 if (appData.titleInWindow) {
7198 XtSetArg(args[i], XtNlabel, text); i++;
7199 XtSetValues(titleWidget, args, i);
7202 if (*text != NULLCHAR) {
7203 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
7204 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
7205 } else if (appData.icsActive) {
7206 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
7207 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
7208 } else if (appData.cmailGameName[0] != NULLCHAR) {
7209 snprintf(icon, sizeof(icon), "%s", "CMail");
7210 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
7212 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
7213 } else if (gameInfo.variant == VariantGothic) {
7214 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
7215 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
7218 } else if (gameInfo.variant == VariantFalcon) {
7219 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
7220 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
7222 } else if (appData.noChessProgram) {
7223 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
7224 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
7226 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
7227 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
7230 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
7231 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
7232 XtSetValues(shellWidget, args, i);
7237 DisplayError(message, error)
7244 if (appData.debugMode || appData.matchMode) {
7245 fprintf(stderr, "%s: %s\n", programName, message);
7248 if (appData.debugMode || appData.matchMode) {
7249 fprintf(stderr, "%s: %s: %s\n",
7250 programName, message, strerror(error));
7252 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7255 ErrorPopUp(_("Error"), message, FALSE);
7259 void DisplayMoveError(message)
7264 DrawPosition(FALSE, NULL);
7265 if (appData.debugMode || appData.matchMode) {
7266 fprintf(stderr, "%s: %s\n", programName, message);
7268 if (appData.popupMoveErrors) {
7269 ErrorPopUp(_("Error"), message, FALSE);
7271 DisplayMessage(message, "");
7276 void DisplayFatalError(message, error, status)
7282 errorExitStatus = status;
7284 fprintf(stderr, "%s: %s\n", programName, message);
7286 fprintf(stderr, "%s: %s: %s\n",
7287 programName, message, strerror(error));
7288 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7291 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
7292 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
7298 void DisplayInformation(message)
7302 ErrorPopUp(_("Information"), message, TRUE);
7305 void DisplayNote(message)
7309 ErrorPopUp(_("Note"), message, FALSE);
7313 NullXErrorCheck(dpy, error_event)
7315 XErrorEvent *error_event;
7320 void DisplayIcsInteractionTitle(message)
7323 if (oldICSInteractionTitle == NULL) {
7324 /* Magic to find the old window title, adapted from vim */
7325 char *wina = getenv("WINDOWID");
7327 Window win = (Window) atoi(wina);
7328 Window root, parent, *children;
7329 unsigned int nchildren;
7330 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
7332 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
7333 if (!XQueryTree(xDisplay, win, &root, &parent,
7334 &children, &nchildren)) break;
7335 if (children) XFree((void *)children);
7336 if (parent == root || parent == 0) break;
7339 XSetErrorHandler(oldHandler);
7341 if (oldICSInteractionTitle == NULL) {
7342 oldICSInteractionTitle = "xterm";
7345 printf("\033]0;%s\007", message);
7349 char pendingReplyPrefix[MSG_SIZ];
7350 ProcRef pendingReplyPR;
7352 void AskQuestionProc(w, event, prms, nprms)
7359 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7363 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7366 void AskQuestionPopDown()
7368 if (!askQuestionUp) return;
7369 XtPopdown(askQuestionShell);
7370 XtDestroyWidget(askQuestionShell);
7371 askQuestionUp = False;
7374 void AskQuestionReplyAction(w, event, prms, nprms)
7384 reply = XawDialogGetValueString(w = XtParent(w));
7385 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
7386 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
7387 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
7388 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
7389 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7390 AskQuestionPopDown();
7392 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7395 void AskQuestionCallback(w, client_data, call_data)
7397 XtPointer client_data, call_data;
7402 XtSetArg(args[0], XtNlabel, &name);
7403 XtGetValues(w, args, 1);
7405 if (strcmp(name, _("cancel")) == 0) {
7406 AskQuestionPopDown();
7408 AskQuestionReplyAction(w, NULL, NULL, NULL);
7412 void AskQuestion(title, question, replyPrefix, pr)
7413 char *title, *question, *replyPrefix;
7417 Widget popup, layout, dialog, edit;
7423 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7424 pendingReplyPR = pr;
7427 XtSetArg(args[i], XtNresizable, True); i++;
7428 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7429 askQuestionShell = popup =
7430 XtCreatePopupShell(title, transientShellWidgetClass,
7431 shellWidget, args, i);
7434 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7435 layoutArgs, XtNumber(layoutArgs));
7438 XtSetArg(args[i], XtNlabel, question); i++;
7439 XtSetArg(args[i], XtNvalue, ""); i++;
7440 XtSetArg(args[i], XtNborderWidth, 0); i++;
7441 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7444 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7445 (XtPointer) dialog);
7446 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7447 (XtPointer) dialog);
7449 XtRealizeWidget(popup);
7450 CatchDeleteWindow(popup, "AskQuestionPopDown");
7452 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7453 &x, &y, &win_x, &win_y, &mask);
7455 XtSetArg(args[0], XtNx, x - 10);
7456 XtSetArg(args[1], XtNy, y - 30);
7457 XtSetValues(popup, args, 2);
7459 XtPopup(popup, XtGrabExclusive);
7460 askQuestionUp = True;
7462 edit = XtNameToWidget(dialog, "*value");
7463 XtSetKeyboardFocus(popup, edit);
7471 if (*name == NULLCHAR) {
7473 } else if (strcmp(name, "$") == 0) {
7474 putc(BELLCHAR, stderr);
7477 snprintf(buf, sizeof(buf), "%s '%s' &", appData.soundProgram, name);
7485 PlaySound(appData.soundMove);
7491 PlaySound(appData.soundIcsWin);
7497 PlaySound(appData.soundIcsLoss);
7503 PlaySound(appData.soundIcsDraw);
7507 PlayIcsUnfinishedSound()
7509 PlaySound(appData.soundIcsUnfinished);
7515 PlaySound(appData.soundIcsAlarm);
7521 system("stty echo");
7527 system("stty -echo");
7531 Colorize(cc, continuation)
7536 int count, outCount, error;
7538 if (textColors[(int)cc].bg > 0) {
7539 if (textColors[(int)cc].fg > 0) {
7540 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7541 textColors[(int)cc].fg, textColors[(int)cc].bg);
7543 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7544 textColors[(int)cc].bg);
7547 if (textColors[(int)cc].fg > 0) {
7548 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7549 textColors[(int)cc].fg);
7551 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7554 count = strlen(buf);
7555 outCount = OutputToProcess(NoProc, buf, count, &error);
7556 if (outCount < count) {
7557 DisplayFatalError(_("Error writing to display"), error, 1);
7560 if (continuation) return;
7563 PlaySound(appData.soundShout);
7566 PlaySound(appData.soundSShout);
7569 PlaySound(appData.soundChannel1);
7572 PlaySound(appData.soundChannel);
7575 PlaySound(appData.soundKibitz);
7578 PlaySound(appData.soundTell);
7580 case ColorChallenge:
7581 PlaySound(appData.soundChallenge);
7584 PlaySound(appData.soundRequest);
7587 PlaySound(appData.soundSeek);
7598 return getpwuid(getuid())->pw_name;
7602 ExpandPathName(path)
7605 static char static_buf[4*MSG_SIZ];
7606 char *d, *s, buf[4*MSG_SIZ];
7612 while (*s && isspace(*s))
7621 if (*(s+1) == '/') {
7622 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7626 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7627 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7628 pwd = getpwnam(buf);
7631 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7635 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7636 strcat(d, strchr(s+1, '/'));
7640 safeStrCpy(d, s, 4*MSG_SIZ );
7647 static char host_name[MSG_SIZ];
7649 #if HAVE_GETHOSTNAME
7650 gethostname(host_name, MSG_SIZ);
7652 #else /* not HAVE_GETHOSTNAME */
7653 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7654 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7656 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7658 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7659 #endif /* not HAVE_GETHOSTNAME */
7662 XtIntervalId delayedEventTimerXID = 0;
7663 DelayedEventCallback delayedEventCallback = 0;
7668 delayedEventTimerXID = 0;
7669 delayedEventCallback();
7673 ScheduleDelayedEvent(cb, millisec)
7674 DelayedEventCallback cb; long millisec;
7676 if(delayedEventTimerXID && delayedEventCallback == cb)
7677 // [HGM] alive: replace, rather than add or flush identical event
7678 XtRemoveTimeOut(delayedEventTimerXID);
7679 delayedEventCallback = cb;
7680 delayedEventTimerXID =
7681 XtAppAddTimeOut(appContext, millisec,
7682 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7685 DelayedEventCallback
7688 if (delayedEventTimerXID) {
7689 return delayedEventCallback;
7696 CancelDelayedEvent()
7698 if (delayedEventTimerXID) {
7699 XtRemoveTimeOut(delayedEventTimerXID);
7700 delayedEventTimerXID = 0;
7704 XtIntervalId loadGameTimerXID = 0;
7706 int LoadGameTimerRunning()
7708 return loadGameTimerXID != 0;
7711 int StopLoadGameTimer()
7713 if (loadGameTimerXID != 0) {
7714 XtRemoveTimeOut(loadGameTimerXID);
7715 loadGameTimerXID = 0;
7723 LoadGameTimerCallback(arg, id)
7727 loadGameTimerXID = 0;
7732 StartLoadGameTimer(millisec)
7736 XtAppAddTimeOut(appContext, millisec,
7737 (XtTimerCallbackProc) LoadGameTimerCallback,
7741 XtIntervalId analysisClockXID = 0;
7744 AnalysisClockCallback(arg, id)
7748 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7749 || appData.icsEngineAnalyze) { // [DM]
7750 AnalysisPeriodicEvent(0);
7751 StartAnalysisClock();
7756 StartAnalysisClock()
7759 XtAppAddTimeOut(appContext, 2000,
7760 (XtTimerCallbackProc) AnalysisClockCallback,
7764 XtIntervalId clockTimerXID = 0;
7766 int ClockTimerRunning()
7768 return clockTimerXID != 0;
7771 int StopClockTimer()
7773 if (clockTimerXID != 0) {
7774 XtRemoveTimeOut(clockTimerXID);
7783 ClockTimerCallback(arg, id)
7792 StartClockTimer(millisec)
7796 XtAppAddTimeOut(appContext, millisec,
7797 (XtTimerCallbackProc) ClockTimerCallback,
7802 DisplayTimerLabel(w, color, timer, highlight)
7811 /* check for low time warning */
7812 Pixel foregroundOrWarningColor = timerForegroundPixel;
7815 appData.lowTimeWarning &&
7816 (timer / 1000) < appData.icsAlarmTime)
7817 foregroundOrWarningColor = lowTimeWarningColor;
7819 if (appData.clockMode) {
7820 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7821 XtSetArg(args[0], XtNlabel, buf);
7823 snprintf(buf, MSG_SIZ, "%s ", color);
7824 XtSetArg(args[0], XtNlabel, buf);
7829 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7830 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7832 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7833 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7836 XtSetValues(w, args, 3);
7840 DisplayWhiteClock(timeRemaining, highlight)
7846 if(appData.noGUI) return;
7847 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7848 if (highlight && iconPixmap == bIconPixmap) {
7849 iconPixmap = wIconPixmap;
7850 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7851 XtSetValues(shellWidget, args, 1);
7856 DisplayBlackClock(timeRemaining, highlight)
7862 if(appData.noGUI) return;
7863 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7864 if (highlight && iconPixmap == wIconPixmap) {
7865 iconPixmap = bIconPixmap;
7866 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7867 XtSetValues(shellWidget, args, 1);
7885 int StartChildProcess(cmdLine, dir, pr)
7892 int to_prog[2], from_prog[2];
7896 if (appData.debugMode) {
7897 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7900 /* We do NOT feed the cmdLine to the shell; we just
7901 parse it into blank-separated arguments in the
7902 most simple-minded way possible.
7905 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7908 while(*p == ' ') p++;
7910 if(*p == '"' || *p == '\'')
7911 p = strchr(++argv[i-1], *p);
7912 else p = strchr(p, ' ');
7913 if (p == NULL) break;
7918 SetUpChildIO(to_prog, from_prog);
7920 if ((pid = fork()) == 0) {
7922 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7923 close(to_prog[1]); // first close the unused pipe ends
7924 close(from_prog[0]);
7925 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7926 dup2(from_prog[1], 1);
7927 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7928 close(from_prog[1]); // and closing again loses one of the pipes!
7929 if(fileno(stderr) >= 2) // better safe than sorry...
7930 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7932 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7937 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7939 execvp(argv[0], argv);
7941 /* If we get here, exec failed */
7946 /* Parent process */
7948 close(from_prog[1]);
7950 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7953 cp->fdFrom = from_prog[0];
7954 cp->fdTo = to_prog[1];
7959 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7960 static RETSIGTYPE AlarmCallBack(int n)
7966 DestroyChildProcess(pr, signalType)
7970 ChildProc *cp = (ChildProc *) pr;
7972 if (cp->kind != CPReal) return;
7974 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7975 signal(SIGALRM, AlarmCallBack);
7977 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7978 kill(cp->pid, SIGKILL); // kill it forcefully
7979 wait((int *) 0); // and wait again
7983 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7985 /* Process is exiting either because of the kill or because of
7986 a quit command sent by the backend; either way, wait for it to die.
7995 InterruptChildProcess(pr)
7998 ChildProc *cp = (ChildProc *) pr;
8000 if (cp->kind != CPReal) return;
8001 (void) kill(cp->pid, SIGINT); /* stop it thinking */
8004 int OpenTelnet(host, port, pr)
8009 char cmdLine[MSG_SIZ];
8011 if (port[0] == NULLCHAR) {
8012 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
8014 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
8016 return StartChildProcess(cmdLine, "", pr);
8019 int OpenTCP(host, port, pr)
8025 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
8026 #else /* !OMIT_SOCKETS */
8028 struct sockaddr_in sa;
8030 unsigned short uport;
8033 if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
8037 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
8038 sa.sin_family = AF_INET;
8039 sa.sin_addr.s_addr = INADDR_ANY;
8040 uport = (unsigned short) 0;
8041 sa.sin_port = htons(uport);
8042 if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
8046 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
8047 if (!(hp = gethostbyname(host))) {
8049 if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
8050 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
8051 hp->h_addrtype = AF_INET;
8053 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
8054 hp->h_addr_list[0] = (char *) malloc(4);
8055 hp->h_addr_list[0][0] = b0;
8056 hp->h_addr_list[0][1] = b1;
8057 hp->h_addr_list[0][2] = b2;
8058 hp->h_addr_list[0][3] = b3;
8063 sa.sin_family = hp->h_addrtype;
8064 uport = (unsigned short) atoi(port);
8065 sa.sin_port = htons(uport);
8066 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
8068 if (connect(s, (struct sockaddr *) &sa,
8069 sizeof(struct sockaddr_in)) < 0) {
8073 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
8080 #endif /* !OMIT_SOCKETS */
8085 int OpenCommPort(name, pr)
8092 fd = open(name, 2, 0);
8093 if (fd < 0) return errno;
8095 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
8105 int OpenLoopback(pr)
8111 SetUpChildIO(to, from);
8113 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
8116 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
8123 int OpenRcmd(host, user, cmd, pr)
8124 char *host, *user, *cmd;
8127 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
8131 #define INPUT_SOURCE_BUF_SIZE 8192
8140 char buf[INPUT_SOURCE_BUF_SIZE];
8145 DoInputCallback(closure, source, xid)
8150 InputSource *is = (InputSource *) closure;
8155 if (is->lineByLine) {
8156 count = read(is->fd, is->unused,
8157 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
8159 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
8162 is->unused += count;
8164 while (p < is->unused) {
8165 q = memchr(p, '\n', is->unused - p);
8166 if (q == NULL) break;
8168 (is->func)(is, is->closure, p, q - p, 0);
8172 while (p < is->unused) {
8177 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
8182 (is->func)(is, is->closure, is->buf, count, error);
8186 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
8193 ChildProc *cp = (ChildProc *) pr;
8195 is = (InputSource *) calloc(1, sizeof(InputSource));
8196 is->lineByLine = lineByLine;
8200 is->fd = fileno(stdin);
8202 is->kind = cp->kind;
8203 is->fd = cp->fdFrom;
8206 is->unused = is->buf;
8209 is->xid = XtAppAddInput(appContext, is->fd,
8210 (XtPointer) (XtInputReadMask),
8211 (XtInputCallbackProc) DoInputCallback,
8213 is->closure = closure;
8214 return (InputSourceRef) is;
8218 RemoveInputSource(isr)
8221 InputSource *is = (InputSource *) isr;
8223 if (is->xid == 0) return;
8224 XtRemoveInput(is->xid);
8228 int OutputToProcess(pr, message, count, outError)
8234 static int line = 0;
8235 ChildProc *cp = (ChildProc *) pr;
8240 if (appData.noJoin || !appData.useInternalWrap)
8241 outCount = fwrite(message, 1, count, stdout);
8244 int width = get_term_width();
8245 int len = wrap(NULL, message, count, width, &line);
8246 char *msg = malloc(len);
8250 outCount = fwrite(message, 1, count, stdout);
8253 dbgchk = wrap(msg, message, count, width, &line);
8254 if (dbgchk != len && appData.debugMode)
8255 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
8256 outCount = fwrite(msg, 1, dbgchk, stdout);
8262 outCount = write(cp->fdTo, message, count);
8272 /* Output message to process, with "ms" milliseconds of delay
8273 between each character. This is needed when sending the logon
8274 script to ICC, which for some reason doesn't like the
8275 instantaneous send. */
8276 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
8283 ChildProc *cp = (ChildProc *) pr;
8288 r = write(cp->fdTo, message++, 1);
8301 /**** Animation code by Hugh Fisher, DCS, ANU.
8303 Known problem: if a window overlapping the board is
8304 moved away while a piece is being animated underneath,
8305 the newly exposed area won't be updated properly.
8306 I can live with this.
8308 Known problem: if you look carefully at the animation
8309 of pieces in mono mode, they are being drawn as solid
8310 shapes without interior detail while moving. Fixing
8311 this would be a major complication for minimal return.
8314 /* Masks for XPM pieces. Black and white pieces can have
8315 different shapes, but in the interest of retaining my
8316 sanity pieces must have the same outline on both light
8317 and dark squares, and all pieces must use the same
8318 background square colors/images. */
8320 static int xpmDone = 0;
8323 CreateAnimMasks (pieceDepth)
8330 unsigned long plane;
8333 /* Need a bitmap just to get a GC with right depth */
8334 buf = XCreatePixmap(xDisplay, xBoardWindow,
8336 values.foreground = 1;
8337 values.background = 0;
8338 /* Don't use XtGetGC, not read only */
8339 maskGC = XCreateGC(xDisplay, buf,
8340 GCForeground | GCBackground, &values);
8341 XFreePixmap(xDisplay, buf);
8343 buf = XCreatePixmap(xDisplay, xBoardWindow,
8344 squareSize, squareSize, pieceDepth);
8345 values.foreground = XBlackPixel(xDisplay, xScreen);
8346 values.background = XWhitePixel(xDisplay, xScreen);
8347 bufGC = XCreateGC(xDisplay, buf,
8348 GCForeground | GCBackground, &values);
8350 for (piece = WhitePawn; piece <= BlackKing; piece++) {
8351 /* Begin with empty mask */
8352 if(!xpmDone) // [HGM] pieces: keep using existing
8353 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8354 squareSize, squareSize, 1);
8355 XSetFunction(xDisplay, maskGC, GXclear);
8356 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8357 0, 0, squareSize, squareSize);
8359 /* Take a copy of the piece */
8364 XSetFunction(xDisplay, bufGC, GXcopy);
8365 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8367 0, 0, squareSize, squareSize, 0, 0);
8369 /* XOR the background (light) over the piece */
8370 XSetFunction(xDisplay, bufGC, GXxor);
8372 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8373 0, 0, squareSize, squareSize, 0, 0);
8375 XSetForeground(xDisplay, bufGC, lightSquareColor);
8376 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8379 /* We now have an inverted piece image with the background
8380 erased. Construct mask by just selecting all the non-zero
8381 pixels - no need to reconstruct the original image. */
8382 XSetFunction(xDisplay, maskGC, GXor);
8384 /* Might be quicker to download an XImage and create bitmap
8385 data from it rather than this N copies per piece, but it
8386 only takes a fraction of a second and there is a much
8387 longer delay for loading the pieces. */
8388 for (n = 0; n < pieceDepth; n ++) {
8389 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8390 0, 0, squareSize, squareSize,
8396 XFreePixmap(xDisplay, buf);
8397 XFreeGC(xDisplay, bufGC);
8398 XFreeGC(xDisplay, maskGC);
8402 InitAnimState (anim, info)
8404 XWindowAttributes * info;
8409 /* Each buffer is square size, same depth as window */
8410 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8411 squareSize, squareSize, info->depth);
8412 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8413 squareSize, squareSize, info->depth);
8415 /* Create a plain GC for blitting */
8416 mask = GCForeground | GCBackground | GCFunction |
8417 GCPlaneMask | GCGraphicsExposures;
8418 values.foreground = XBlackPixel(xDisplay, xScreen);
8419 values.background = XWhitePixel(xDisplay, xScreen);
8420 values.function = GXcopy;
8421 values.plane_mask = AllPlanes;
8422 values.graphics_exposures = False;
8423 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8425 /* Piece will be copied from an existing context at
8426 the start of each new animation/drag. */
8427 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8429 /* Outline will be a read-only copy of an existing */
8430 anim->outlineGC = None;
8436 static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
8437 XWindowAttributes info;
8439 if (xpmDone && gameInfo.variant == old) return;
8440 if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet
8441 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8443 InitAnimState(&game, &info);
8444 InitAnimState(&player, &info);
8446 /* For XPM pieces, we need bitmaps to use as masks. */
8448 CreateAnimMasks(info.depth);
8454 static Boolean frameWaiting;
8456 static RETSIGTYPE FrameAlarm (sig)
8459 frameWaiting = False;
8460 /* In case System-V style signals. Needed?? */
8461 signal(SIGALRM, FrameAlarm);
8468 struct itimerval delay;
8470 XSync(xDisplay, False);
8473 frameWaiting = True;
8474 signal(SIGALRM, FrameAlarm);
8475 delay.it_interval.tv_sec =
8476 delay.it_value.tv_sec = time / 1000;
8477 delay.it_interval.tv_usec =
8478 delay.it_value.tv_usec = (time % 1000) * 1000;
8479 setitimer(ITIMER_REAL, &delay, NULL);
8480 while (frameWaiting) pause();
8481 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8482 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8483 setitimer(ITIMER_REAL, &delay, NULL);
8493 XSync(xDisplay, False);
8495 usleep(time * 1000);
8500 /* Convert board position to corner of screen rect and color */
8503 ScreenSquare(column, row, pt, color)
8504 int column; int row; XPoint * pt; int * color;
8507 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8508 pt->y = lineGap + row * (squareSize + lineGap);
8510 pt->x = lineGap + column * (squareSize + lineGap);
8511 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8513 *color = SquareColor(row, column);
8516 /* Convert window coords to square */
8519 BoardSquare(x, y, column, row)
8520 int x; int y; int * column; int * row;
8522 *column = EventToSquare(x, BOARD_WIDTH);
8523 if (flipView && *column >= 0)
8524 *column = BOARD_WIDTH - 1 - *column;
8525 *row = EventToSquare(y, BOARD_HEIGHT);
8526 if (!flipView && *row >= 0)
8527 *row = BOARD_HEIGHT - 1 - *row;
8532 #undef Max /* just in case */
8534 #define Max(a, b) ((a) > (b) ? (a) : (b))
8535 #define Min(a, b) ((a) < (b) ? (a) : (b))
8538 SetRect(rect, x, y, width, height)
8539 XRectangle * rect; int x; int y; int width; int height;
8543 rect->width = width;
8544 rect->height = height;
8547 /* Test if two frames overlap. If they do, return
8548 intersection rect within old and location of
8549 that rect within new. */
8552 Intersect(old, new, size, area, pt)
8553 XPoint * old; XPoint * new;
8554 int size; XRectangle * area; XPoint * pt;
8556 if (old->x > new->x + size || new->x > old->x + size ||
8557 old->y > new->y + size || new->y > old->y + size) {
8560 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8561 size - abs(old->x - new->x), size - abs(old->y - new->y));
8562 pt->x = Max(old->x - new->x, 0);
8563 pt->y = Max(old->y - new->y, 0);
8568 /* For two overlapping frames, return the rect(s)
8569 in the old that do not intersect with the new. */
8572 CalcUpdateRects(old, new, size, update, nUpdates)
8573 XPoint * old; XPoint * new; int size;
8574 XRectangle update[]; int * nUpdates;
8578 /* If old = new (shouldn't happen) then nothing to draw */
8579 if (old->x == new->x && old->y == new->y) {
8583 /* Work out what bits overlap. Since we know the rects
8584 are the same size we don't need a full intersect calc. */
8586 /* Top or bottom edge? */
8587 if (new->y > old->y) {
8588 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8590 } else if (old->y > new->y) {
8591 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8592 size, old->y - new->y);
8595 /* Left or right edge - don't overlap any update calculated above. */
8596 if (new->x > old->x) {
8597 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8598 new->x - old->x, size - abs(new->y - old->y));
8600 } else if (old->x > new->x) {
8601 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8602 old->x - new->x, size - abs(new->y - old->y));
8609 /* Generate a series of frame coords from start->mid->finish.
8610 The movement rate doubles until the half way point is
8611 reached, then halves back down to the final destination,
8612 which gives a nice slow in/out effect. The algorithmn
8613 may seem to generate too many intermediates for short
8614 moves, but remember that the purpose is to attract the
8615 viewers attention to the piece about to be moved and
8616 then to where it ends up. Too few frames would be less
8620 Tween(start, mid, finish, factor, frames, nFrames)
8621 XPoint * start; XPoint * mid;
8622 XPoint * finish; int factor;
8623 XPoint frames[]; int * nFrames;
8625 int fraction, n, count;
8629 /* Slow in, stepping 1/16th, then 1/8th, ... */
8631 for (n = 0; n < factor; n++)
8633 for (n = 0; n < factor; n++) {
8634 frames[count].x = start->x + (mid->x - start->x) / fraction;
8635 frames[count].y = start->y + (mid->y - start->y) / fraction;
8637 fraction = fraction / 2;
8641 frames[count] = *mid;
8644 /* Slow out, stepping 1/2, then 1/4, ... */
8646 for (n = 0; n < factor; n++) {
8647 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8648 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8650 fraction = fraction * 2;
8655 /* Draw a piece on the screen without disturbing what's there */
8658 SelectGCMask(piece, clip, outline, mask)
8659 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8663 /* Bitmap for piece being moved. */
8664 if (appData.monoMode) {
8665 *mask = *pieceToSolid(piece);
8666 } else if (useImages) {
8668 *mask = xpmMask[piece];
8670 *mask = ximMaskPm[piece];
8673 *mask = *pieceToSolid(piece);
8676 /* GC for piece being moved. Square color doesn't matter, but
8677 since it gets modified we make a copy of the original. */
8679 if (appData.monoMode)
8684 if (appData.monoMode)
8689 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8691 /* Outline only used in mono mode and is not modified */
8693 *outline = bwPieceGC;
8695 *outline = wbPieceGC;
8699 OverlayPiece(piece, clip, outline, dest)
8700 ChessSquare piece; GC clip; GC outline; Drawable dest;
8705 /* Draw solid rectangle which will be clipped to shape of piece */
8706 XFillRectangle(xDisplay, dest, clip,
8707 0, 0, squareSize, squareSize);
8708 if (appData.monoMode)
8709 /* Also draw outline in contrasting color for black
8710 on black / white on white cases */
8711 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8712 0, 0, squareSize, squareSize, 0, 0, 1);
8714 /* Copy the piece */
8719 if(appData.upsideDown && flipView) kind ^= 2;
8720 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8722 0, 0, squareSize, squareSize,
8727 /* Animate the movement of a single piece */
8730 BeginAnimation(anim, piece, startColor, start)
8738 /* The old buffer is initialised with the start square (empty) */
8739 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8740 anim->prevFrame = *start;
8742 /* The piece will be drawn using its own bitmap as a matte */
8743 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8744 XSetClipMask(xDisplay, anim->pieceGC, mask);
8748 AnimationFrame(anim, frame, piece)
8753 XRectangle updates[4];
8758 /* Save what we are about to draw into the new buffer */
8759 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8760 frame->x, frame->y, squareSize, squareSize,
8763 /* Erase bits of the previous frame */
8764 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8765 /* Where the new frame overlapped the previous,
8766 the contents in newBuf are wrong. */
8767 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8768 overlap.x, overlap.y,
8769 overlap.width, overlap.height,
8771 /* Repaint the areas in the old that don't overlap new */
8772 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8773 for (i = 0; i < count; i++)
8774 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8775 updates[i].x - anim->prevFrame.x,
8776 updates[i].y - anim->prevFrame.y,
8777 updates[i].width, updates[i].height,
8778 updates[i].x, updates[i].y);
8780 /* Easy when no overlap */
8781 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8782 0, 0, squareSize, squareSize,
8783 anim->prevFrame.x, anim->prevFrame.y);
8786 /* Save this frame for next time round */
8787 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8788 0, 0, squareSize, squareSize,
8790 anim->prevFrame = *frame;
8792 /* Draw piece over original screen contents, not current,
8793 and copy entire rect. Wipes out overlapping piece images. */
8794 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8795 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8796 0, 0, squareSize, squareSize,
8797 frame->x, frame->y);
8801 EndAnimation (anim, finish)
8805 XRectangle updates[4];
8810 /* The main code will redraw the final square, so we
8811 only need to erase the bits that don't overlap. */
8812 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8813 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8814 for (i = 0; i < count; i++)
8815 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8816 updates[i].x - anim->prevFrame.x,
8817 updates[i].y - anim->prevFrame.y,
8818 updates[i].width, updates[i].height,
8819 updates[i].x, updates[i].y);
8821 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8822 0, 0, squareSize, squareSize,
8823 anim->prevFrame.x, anim->prevFrame.y);
8828 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8830 ChessSquare piece; int startColor;
8831 XPoint * start; XPoint * finish;
8832 XPoint frames[]; int nFrames;
8836 BeginAnimation(anim, piece, startColor, start);
8837 for (n = 0; n < nFrames; n++) {
8838 AnimationFrame(anim, &(frames[n]), piece);
8839 FrameDelay(appData.animSpeed);
8841 EndAnimation(anim, finish);
8845 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8848 ChessSquare piece = board[fromY][toY];
8849 board[fromY][toY] = EmptySquare;
8850 DrawPosition(FALSE, board);
8852 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8853 y = lineGap + toY * (squareSize + lineGap);
8855 x = lineGap + toX * (squareSize + lineGap);
8856 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8858 for(i=1; i<4*kFactor; i++) {
8859 int r = squareSize * 9 * i/(20*kFactor - 5);
8860 XFillArc(xDisplay, xBoardWindow, highlineGC,
8861 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8862 FrameDelay(appData.animSpeed);
8864 board[fromY][toY] = piece;
8867 /* Main control logic for deciding what to animate and how */
8870 AnimateMove(board, fromX, fromY, toX, toY)
8879 XPoint start, finish, mid;
8880 XPoint frames[kFactor * 2 + 1];
8881 int nFrames, startColor, endColor;
8883 /* Are we animating? */
8884 if (!appData.animate || appData.blindfold)
8887 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8888 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8889 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8891 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8892 piece = board[fromY][fromX];
8893 if (piece >= EmptySquare) return;
8898 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8901 if (appData.debugMode) {
8902 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8903 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8904 piece, fromX, fromY, toX, toY); }
8906 ScreenSquare(fromX, fromY, &start, &startColor);
8907 ScreenSquare(toX, toY, &finish, &endColor);
8910 /* Knight: make straight movement then diagonal */
8911 if (abs(toY - fromY) < abs(toX - fromX)) {
8912 mid.x = start.x + (finish.x - start.x) / 2;
8916 mid.y = start.y + (finish.y - start.y) / 2;
8919 mid.x = start.x + (finish.x - start.x) / 2;
8920 mid.y = start.y + (finish.y - start.y) / 2;
8923 /* Don't use as many frames for very short moves */
8924 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8925 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8927 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8928 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8929 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8931 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8932 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8935 /* Be sure end square is redrawn */
8936 damage[0][toY][toX] = True;
8940 DragPieceBegin(x, y)
8943 int boardX, boardY, color;
8946 /* Are we animating? */
8947 if (!appData.animateDragging || appData.blindfold)
8950 /* Figure out which square we start in and the
8951 mouse position relative to top left corner. */
8952 BoardSquare(x, y, &boardX, &boardY);
8953 player.startBoardX = boardX;
8954 player.startBoardY = boardY;
8955 ScreenSquare(boardX, boardY, &corner, &color);
8956 player.startSquare = corner;
8957 player.startColor = color;
8958 /* As soon as we start dragging, the piece will jump slightly to
8959 be centered over the mouse pointer. */
8960 player.mouseDelta.x = squareSize/2;
8961 player.mouseDelta.y = squareSize/2;
8962 /* Initialise animation */
8963 player.dragPiece = PieceForSquare(boardX, boardY);
8965 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8966 player.dragActive = True;
8967 BeginAnimation(&player, player.dragPiece, color, &corner);
8968 /* Mark this square as needing to be redrawn. Note that
8969 we don't remove the piece though, since logically (ie
8970 as seen by opponent) the move hasn't been made yet. */
8971 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8972 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8973 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8974 corner.x, corner.y, squareSize, squareSize,
8975 0, 0); // [HGM] zh: unstack in stead of grab
8976 if(gatingPiece != EmptySquare) {
8977 /* Kludge alert: When gating we want the introduced
8978 piece to appear on the from square. To generate an
8979 image of it, we draw it on the board, copy the image,
8980 and draw the original piece again. */
8981 ChessSquare piece = boards[currentMove][boardY][boardX];
8982 DrawSquare(boardY, boardX, gatingPiece, 0);
8983 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8984 corner.x, corner.y, squareSize, squareSize, 0, 0);
8985 DrawSquare(boardY, boardX, piece, 0);
8987 damage[0][boardY][boardX] = True;
8989 player.dragActive = False;
8999 /* Are we animating? */
9000 if (!appData.animateDragging || appData.blindfold)
9004 if (! player.dragActive)
9006 /* Move piece, maintaining same relative position
9007 of mouse within square */
9008 corner.x = x - player.mouseDelta.x;
9009 corner.y = y - player.mouseDelta.y;
9010 AnimationFrame(&player, &corner, player.dragPiece);
9012 if (appData.highlightDragging) {
9014 BoardSquare(x, y, &boardX, &boardY);
9015 SetHighlights(fromX, fromY, boardX, boardY);
9024 int boardX, boardY, color;
9027 /* Are we animating? */
9028 if (!appData.animateDragging || appData.blindfold)
9032 if (! player.dragActive)
9034 /* Last frame in sequence is square piece is
9035 placed on, which may not match mouse exactly. */
9036 BoardSquare(x, y, &boardX, &boardY);
9037 ScreenSquare(boardX, boardY, &corner, &color);
9038 EndAnimation(&player, &corner);
9040 /* Be sure end square is redrawn */
9041 damage[0][boardY][boardX] = True;
9043 /* This prevents weird things happening with fast successive
9044 clicks which on my Sun at least can cause motion events
9045 without corresponding press/release. */
9046 player.dragActive = False;
9049 /* Handle expose event while piece being dragged */
9054 if (!player.dragActive || appData.blindfold)
9057 /* What we're doing: logically, the move hasn't been made yet,
9058 so the piece is still in it's original square. But visually
9059 it's being dragged around the board. So we erase the square
9060 that the piece is on and draw it at the last known drag point. */
9061 BlankSquare(player.startSquare.x, player.startSquare.y,
9062 player.startColor, EmptySquare, xBoardWindow, 1);
9063 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
9064 damage[0][player.startBoardY][player.startBoardX] = TRUE;
9067 #include <sys/ioctl.h>
9068 int get_term_width()
9070 int fd, default_width;
9073 default_width = 79; // this is FICS default anyway...
9075 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
9077 if (!ioctl(fd, TIOCGSIZE, &win))
9078 default_width = win.ts_cols;
9079 #elif defined(TIOCGWINSZ)
9081 if (!ioctl(fd, TIOCGWINSZ, &win))
9082 default_width = win.ws_col;
9084 return default_width;
9090 static int old_width = 0;
9091 int new_width = get_term_width();
9093 if (old_width != new_width)
9094 ics_printf("set width %d\n", new_width);
9095 old_width = new_width;
9098 void NotifyFrontendLogin()
9103 /* [AS] Arrow highlighting support */
9105 static double A_WIDTH = 5; /* Width of arrow body */
9107 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
9108 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
9110 static double Sqr( double x )
9115 static int Round( double x )
9117 return (int) (x + 0.5);
9120 void SquareToPos(int rank, int file, int *x, int *y)
9123 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
9124 *y = lineGap + rank * (squareSize + lineGap);
9126 *x = lineGap + file * (squareSize + lineGap);
9127 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
9131 /* Draw an arrow between two points using current settings */
9132 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
9135 double dx, dy, j, k, x, y;
9138 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
9140 arrow[0].x = s_x + A_WIDTH + 0.5;
9143 arrow[1].x = s_x + A_WIDTH + 0.5;
9144 arrow[1].y = d_y - h;
9146 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
9147 arrow[2].y = d_y - h;
9152 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
9153 arrow[5].y = d_y - h;
9155 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
9156 arrow[4].y = d_y - h;
9158 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
9161 else if( d_y == s_y ) {
9162 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
9165 arrow[0].y = s_y + A_WIDTH + 0.5;
9167 arrow[1].x = d_x - w;
9168 arrow[1].y = s_y + A_WIDTH + 0.5;
9170 arrow[2].x = d_x - w;
9171 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
9176 arrow[5].x = d_x - w;
9177 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
9179 arrow[4].x = d_x - w;
9180 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
9183 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
9186 /* [AS] Needed a lot of paper for this! :-) */
9187 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
9188 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
9190 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
9192 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
9197 arrow[0].x = Round(x - j);
9198 arrow[0].y = Round(y + j*dx);
9200 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
9201 arrow[1].y = Round(arrow[0].y - 2*j*dx);
9204 x = (double) d_x - k;
9205 y = (double) d_y - k*dy;
9208 x = (double) d_x + k;
9209 y = (double) d_y + k*dy;
9212 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
9214 arrow[6].x = Round(x - j);
9215 arrow[6].y = Round(y + j*dx);
9217 arrow[2].x = Round(arrow[6].x + 2*j);
9218 arrow[2].y = Round(arrow[6].y - 2*j*dx);
9220 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
9221 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
9226 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
9227 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
9230 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
9231 // Polygon( hdc, arrow, 7 );
9234 /* [AS] Draw an arrow between two squares */
9235 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
9237 int s_x, s_y, d_x, d_y, hor, vert, i;
9239 if( s_col == d_col && s_row == d_row ) {
9243 /* Get source and destination points */
9244 SquareToPos( s_row, s_col, &s_x, &s_y);
9245 SquareToPos( d_row, d_col, &d_x, &d_y);
9248 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
9250 else if( d_y < s_y ) {
9251 d_y += squareSize / 2 + squareSize / 4;
9254 d_y += squareSize / 2;
9258 d_x += squareSize / 2 - squareSize / 4;
9260 else if( d_x < s_x ) {
9261 d_x += squareSize / 2 + squareSize / 4;
9264 d_x += squareSize / 2;
9267 s_x += squareSize / 2;
9268 s_y += squareSize / 2;
9271 A_WIDTH = squareSize / 14.; //[HGM] make float
9273 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
9276 // this is a good idea, but it only works when lineGap == 0, because 'damage' on grid lines is not repaired
9277 hor = 64*s_col + 32; vert = 64*s_row + 32;
9278 for(i=0; i<= 64; i++) {
9279 damage[0][vert+6>>6][hor+6>>6] = True;
9280 damage[0][vert-6>>6][hor+6>>6] = True;
9281 damage[0][vert+6>>6][hor-6>>6] = True;
9282 damage[0][vert-6>>6][hor-6>>6] = True;
9283 hor += d_col - s_col; vert += d_row - s_row;
9288 Boolean IsDrawArrowEnabled()
9290 return appData.highlightMoveWithArrow && squareSize >= 32;
9293 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
9295 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
9296 DrawArrowBetweenSquares(fromX, fromY, toX, toY);