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, 2011 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((int redo));
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 AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
396 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
397 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
398 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
400 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
401 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
403 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
405 void HighlightArrowProc P((Widget w, XEvent *event, String *prms,
407 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
408 //void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
409 void OneClickProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
410 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
412 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
414 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
416 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
418 //void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
419 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
421 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
423 void HideThinkingProc P((Widget w, XEvent *event, String *prms,
425 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
427 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
428 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
429 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
430 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
431 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
432 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
433 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
434 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
435 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
436 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
437 void Iconify P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
438 void DisplayMove P((int moveNumber));
439 void DisplayTitle P((char *title));
440 void ICSInitScript P((void));
441 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
442 void ErrorPopUp P((char *title, char *text, int modal));
443 void ErrorPopDown P((void));
444 static char *ExpandPathName P((char *path));
445 static void CreateAnimVars P((void));
446 static void DragPieceMove P((int x, int y));
447 static void DrawDragPiece P((void));
448 char *ModeToWidgetName P((GameMode mode));
449 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
450 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
451 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
452 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
453 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
454 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
455 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
456 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
457 void IcsOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
458 void SoundOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
459 void BoardOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
460 void LoadOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
461 void SaveOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
462 void GameListOptionsPopDown P(());
463 void ShufflePopDown P(());
464 void TimeControlPopDown P(());
465 void SettingsPopDown P(());
466 void update_ics_width P(());
467 int get_term_width P(());
468 int CopyMemoProc P(());
469 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
470 Boolean IsDrawArrowEnabled P(());
473 * XBoard depends on Xt R4 or higher
475 int xtVersion = XtSpecificationRelease;
480 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
481 jailSquareColor, highlightSquareColor, premoveHighlightColor;
482 Pixel lowTimeWarningColor;
483 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
484 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
485 wjPieceGC, bjPieceGC, prelineGC, countGC;
486 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
487 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
488 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
489 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
490 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
491 ICSInputShell, fileNameShell, askQuestionShell;
492 Widget historyShell, evalGraphShell, gameListShell;
493 int hOffset; // [HGM] dual
494 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
495 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
496 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
497 Font clockFontID, coordFontID, countFontID;
498 XFontStruct *clockFontStruct, *coordFontStruct, *countFontStruct;
499 XtAppContext appContext;
501 char *oldICSInteractionTitle;
505 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
507 Position commentX = -1, commentY = -1;
508 Dimension commentW, commentH;
509 typedef unsigned int BoardSize;
511 Boolean chessProgram;
513 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
514 int squareSize, smallLayout = 0, tinyLayout = 0,
515 marginW, marginH, // [HGM] for run-time resizing
516 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
517 ICSInputBoxUp = False, askQuestionUp = False,
518 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
519 editUp = False, errorUp = False, errorExitStatus = -1, lineGap;
520 Pixel timerForegroundPixel, timerBackgroundPixel;
521 Pixel buttonForegroundPixel, buttonBackgroundPixel;
522 char *chessDir, *programName, *programVersion,
523 *gameCopyFilename, *gamePasteFilename;
524 Boolean alwaysOnTop = False;
525 Boolean saveSettingsOnExit;
526 char *settingsFileName;
527 char *icsTextMenuString;
529 char *firstChessProgramNames;
530 char *secondChessProgramNames;
532 WindowPlacement wpMain;
533 WindowPlacement wpConsole;
534 WindowPlacement wpComment;
535 WindowPlacement wpMoveHistory;
536 WindowPlacement wpEvalGraph;
537 WindowPlacement wpEngineOutput;
538 WindowPlacement wpGameList;
539 WindowPlacement wpTags;
543 Pixmap pieceBitmap[2][(int)BlackPawn];
544 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
545 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
546 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
547 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
548 Pixmap xpmBoardBitmap[2];
549 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
550 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
551 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
552 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
553 XImage *ximLightSquare, *ximDarkSquare;
556 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
557 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
559 #define White(piece) ((int)(piece) < (int)BlackPawn)
561 /* Variables for doing smooth animation. This whole thing
562 would be much easier if the board was double-buffered,
563 but that would require a fairly major rewrite. */
568 GC blitGC, pieceGC, outlineGC;
569 XPoint startSquare, prevFrame, mouseDelta;
573 int startBoardX, startBoardY;
576 /* There can be two pieces being animated at once: a player
577 can begin dragging a piece before the remote opponent has moved. */
579 static AnimState game, player;
581 /* Bitmaps for use as masks when drawing XPM pieces.
582 Need one for each black and white piece. */
583 static Pixmap xpmMask[BlackKing + 1];
585 /* This magic number is the number of intermediate frames used
586 in each half of the animation. For short moves it's reduced
587 by 1. The total number of frames will be factor * 2 + 1. */
590 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
592 MenuItem fileMenu[] = {
593 {N_("New Game Ctrl+N"), "New Game", ResetProc},
594 {N_("New Shuffle Game ..."), "New Shuffle Game", ShuffleMenuProc},
595 {N_("New Variant ... Alt+Shift+V"), "New Variant", NewVariantProc}, // [HGM] variant: not functional yet
596 {"----", NULL, NothingProc},
597 {N_("Load Game Ctrl+O"), "Load Game", LoadGameProc},
598 {N_("Load Position Ctrl+Shift+O"), "Load Position", LoadPositionProc},
599 // {N_("Load Next Game"), "Load Next Game", LoadNextGameProc},
600 // {N_("Load Previous Game"), "Load Previous Game", LoadPrevGameProc},
601 // {N_("Reload Same Game"), "Reload Same Game", ReloadGameProc},
602 {N_("Next Position Shift+PgDn"), "Load Next Position", LoadNextPositionProc},
603 {N_("Prev Position Shift+PgUp"), "Load Previous Position", LoadPrevPositionProc},
604 {"----", NULL, NothingProc},
605 // {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
606 {N_("Save Game Ctrl+S"), "Save Game", SaveGameProc},
607 {N_("Save Position Ctrl+Shift+S"), "Save Position", SavePositionProc},
608 {"----", NULL, NothingProc},
609 {N_("Mail Move"), "Mail Move", MailMoveProc},
610 {N_("Reload CMail Message"), "Reload CMail Message", ReloadCmailMsgProc},
611 {"----", NULL, NothingProc},
612 {N_("Quit Ctr+Q"), "Exit", QuitProc},
616 MenuItem editMenu[] = {
617 {N_("Copy Game Ctrl+C"), "Copy Game", CopyGameProc},
618 {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
619 {"----", NULL, NothingProc},
620 {N_("Paste Game Ctrl+V"), "Paste Game", PasteGameProc},
621 {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
622 {"----", NULL, NothingProc},
623 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
624 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
625 {N_("Edit Tags"), "Edit Tags", EditTagsProc},
626 {N_("Edit Comment"), "Edit Comment", EditCommentProc},
627 {"----", NULL, NothingProc},
628 {N_("Revert Home"), "Revert", RevertProc},
629 {N_("Annotate"), "Annotate", AnnotateProc},
630 {N_("Truncate Game End"), "Truncate Game", TruncateGameProc},
631 {"----", NULL, NothingProc},
632 {N_("Backward Alt+Left"), "Backward", BackwardProc},
633 {N_("Forward Alt+Right"), "Forward", ForwardProc},
634 {N_("Back to Start Alt+Home"), "Back to Start", ToStartProc},
635 {N_("Forward to End Alt+End"), "Forward to End", ToEndProc},
639 MenuItem viewMenu[] = {
640 {N_("Flip View F2"), "Flip View", FlipViewProc},
641 {"----", NULL, NothingProc},
642 {N_("Engine Output Alt+Shift+O"), "Show Engine Output", EngineOutputProc},
643 {N_("Move History Alt+Shift+H"), "Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
644 {N_("Evaluation Graph Alt+Shift+E"), "Show Evaluation Graph", EvalGraphProc},
645 {N_("Game List Alt+Shift+G"), "Show Game List", ShowGameListProc},
646 {"----", NULL, NothingProc},
647 {N_("Tags"), "Show Tags", EditTagsProc},
648 {N_("Comments"), "Show Comments", EditCommentProc},
649 {N_("ICS Input Box"), "ICS Input Box", IcsInputBoxProc},
650 {"----", NULL, NothingProc},
651 {N_("Board..."), "Board Options", BoardOptionsProc},
652 {N_("Game List Tags..."), "Game List", GameListOptionsPopUp},
656 MenuItem modeMenu[] = {
657 {N_("Machine White Ctrl+W"), "Machine White", MachineWhiteProc},
658 {N_("Machine Black Ctrl+B"), "Machine Black", MachineBlackProc},
659 {N_("Two Machines Ctrl+T"), "Two Machines", TwoMachinesProc},
660 {N_("Analysis Mode Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
661 {N_("Analyze File Ctrl+F"), "Analyze File", AnalyzeFileProc },
662 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
663 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
664 {N_("Training"), "Training", TrainingProc},
665 {N_("ICS Client"), "ICS Client", IcsClientProc},
666 {"----", NULL, NothingProc},
667 {N_("Pause Pause"), "Pause", PauseProc},
671 MenuItem actionMenu[] = {
672 {N_("Accept F3"), "Accept", AcceptProc},
673 {N_("Decline F4"), "Decline", DeclineProc},
674 {N_("Rematch F12"), "Rematch", RematchProc},
675 {"----", NULL, NothingProc},
676 {N_("Call Flag F5"), "Call Flag", CallFlagProc},
677 {N_("Draw F6"), "Draw", DrawProc},
678 {N_("Adjourn F7"), "Adjourn", AdjournProc},
679 {N_("Abort F8"),"Abort", AbortProc},
680 {N_("Resign F9"), "Resign", ResignProc},
681 {"----", NULL, NothingProc},
682 {N_("Stop Observing F10"), "Stop Observing", StopObservingProc},
683 {N_("Stop Examining F11"), "Stop Examining", StopExaminingProc},
684 {N_("Upload to Examine"), "Upload to Examine", UploadProc},
685 {"----", NULL, NothingProc},
686 {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
687 {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
688 {N_("Adjudicate Draw"), "Adjudicate Draw", AdjuDrawProc},
692 MenuItem engineMenu[] = {
693 {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
694 {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
695 {"----", NULL, NothingProc},
696 {N_("Hint"), "Hint", HintProc},
697 {N_("Book"), "Book", BookProc},
698 {"----", NULL, NothingProc},
699 {N_("Move Now Ctrl+M"), "Move Now", MoveNowProc},
700 {N_("Retract Move Ctrl+X"), "Retract Move", RetractMoveProc},
704 MenuItem optionsMenu[] = {
705 {N_("Time Control ... Alt+Shift+T"), "Time Control", TimeControlProc},
706 {N_("Common Engine ... Alt+Shift+U"), "Common Engine", UciMenuProc},
707 {N_("Adjudications ... Alt+Shift+J"), "Adjudications", EngineMenuProc},
708 {N_("ICS ..."), "ICS", IcsOptionsProc},
709 {N_("Load Game ..."), "Load Game", LoadOptionsProc},
710 {N_("Save Game ..."), "Save Game", SaveOptionsProc},
711 // {N_(" ..."), "", OptionsProc},
712 {N_("Game List ..."), "Game List", GameListOptionsPopUp},
713 {N_("Sounds ..."), "Sounds", SoundOptionsProc},
714 {"----", NULL, NothingProc},
715 {N_("Always Queen Ctrl+Shift+Q"), "Always Queen", AlwaysQueenProc},
716 {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
717 {N_("Animate Moving Ctrl+Shift+A"), "Animate Moving", AnimateMovingProc},
718 {N_("Auto Flag Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
719 {N_("Auto Flip View"), "Auto Flip View", AutoflipProc},
720 {N_("Blindfold"), "Blindfold", BlindfoldProc},
721 {N_("Flash Moves"), "Flash Moves", FlashMovesProc},
723 {N_("Highlight Dragging"), "Highlight Dragging", HighlightDraggingProc},
725 {N_("Highlight Last Move"), "Highlight Last Move", HighlightLastMoveProc},
726 {N_("Highlight With Arrow"), "Arrow", HighlightArrowProc},
727 {N_("Move Sound"), "Move Sound", MoveSoundProc},
728 // {N_("ICS Alarm"), "ICS Alarm", IcsAlarmProc},
729 {N_("One-Click Moving"), "OneClick", OneClickProc},
730 {N_("Periodic Updates"), "Periodic Updates", PeriodicUpdatesProc},
731 {N_("Ponder Next Move Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
732 {N_("Popup Exit Message"), "Popup Exit Message", PopupExitMessageProc},
733 {N_("Popup Move Errors"), "Popup Move Errors", PopupMoveErrorsProc},
734 // {N_("Premove"), "Premove", PremoveProc},
735 {N_("Show Coords"), "Show Coords", ShowCoordsProc},
736 {N_("Hide Thinking Ctrl+Shift+H"), "Hide Thinking", HideThinkingProc},
737 {N_("Test Legality Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
738 {"----", NULL, NothingProc},
739 {N_("Save Settings Now"), "Save Settings Now", SaveSettingsProc},
740 {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
744 MenuItem helpMenu[] = {
745 {N_("Info XBoard"), "Info XBoard", InfoProc},
746 {N_("Man XBoard F1"), "Man XBoard", ManProc},
747 {"----", NULL, NothingProc},
748 {N_("About XBoard"), "About XBoard", AboutProc},
753 {N_("File"), "File", fileMenu},
754 {N_("Edit"), "Edit", editMenu},
755 {N_("View"), "View", viewMenu},
756 {N_("Mode"), "Mode", modeMenu},
757 {N_("Action"), "Action", actionMenu},
758 {N_("Engine"), "Engine", engineMenu},
759 {N_("Options"), "Options", optionsMenu},
760 {N_("Help"), "Help", helpMenu},
764 #define PAUSE_BUTTON "P"
765 MenuItem buttonBar[] = {
766 {"<<", "<<", ToStartProc},
767 {"<", "<", BackwardProc},
768 {PAUSE_BUTTON, PAUSE_BUTTON, PauseProc},
769 {">", ">", ForwardProc},
770 {">>", ">>", ToEndProc},
774 #define PIECE_MENU_SIZE 18
775 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
776 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
777 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
778 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
779 N_("Empty square"), N_("Clear board") },
780 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
781 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
782 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
783 N_("Empty square"), N_("Clear board") }
785 /* must be in same order as PieceMenuStrings! */
786 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
787 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
788 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
789 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
790 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
791 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
792 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
793 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
794 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
797 #define DROP_MENU_SIZE 6
798 String dropMenuStrings[DROP_MENU_SIZE] = {
799 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
801 /* must be in same order as PieceMenuStrings! */
802 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
803 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
804 WhiteRook, WhiteQueen
812 DropMenuEnables dmEnables[] = {
830 { XtNborderWidth, 0 },
831 { XtNdefaultDistance, 0 },
835 { XtNborderWidth, 0 },
836 { XtNresizable, (XtArgVal) True },
840 { XtNborderWidth, 0 },
846 { XtNjustify, (XtArgVal) XtJustifyRight },
847 { XtNlabel, (XtArgVal) "..." },
848 { XtNresizable, (XtArgVal) True },
849 { XtNresize, (XtArgVal) False }
852 Arg messageArgs[] = {
853 { XtNjustify, (XtArgVal) XtJustifyLeft },
854 { XtNlabel, (XtArgVal) "..." },
855 { XtNresizable, (XtArgVal) True },
856 { XtNresize, (XtArgVal) False }
860 { XtNborderWidth, 0 },
861 { XtNjustify, (XtArgVal) XtJustifyLeft }
864 XtResource clientResources[] = {
865 { "flashCount", "flashCount", XtRInt, sizeof(int),
866 XtOffset(AppDataPtr, flashCount), XtRImmediate,
867 (XtPointer) FLASH_COUNT },
870 XrmOptionDescRec shellOptions[] = {
871 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
872 { "-flash", "flashCount", XrmoptionNoArg, "3" },
873 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
876 XtActionsRec boardActions[] = {
877 { "DrawPosition", DrawPositionProc },
878 { "HandleUserMove", HandleUserMove },
879 { "AnimateUserMove", AnimateUserMove },
880 { "HandlePV", HandlePV },
881 { "SelectPV", SelectPV },
882 { "StopPV", StopPV },
883 { "FileNameAction", FileNameAction },
884 { "AskQuestionProc", AskQuestionProc },
885 { "AskQuestionReplyAction", AskQuestionReplyAction },
886 { "PieceMenuPopup", PieceMenuPopup },
887 { "WhiteClock", WhiteClock },
888 { "BlackClock", BlackClock },
889 { "Iconify", Iconify },
890 { "ResetProc", ResetProc },
891 { "NewVariantProc", NewVariantProc },
892 { "LoadGameProc", LoadGameProc },
893 { "LoadNextGameProc", LoadNextGameProc },
894 { "LoadPrevGameProc", LoadPrevGameProc },
895 { "LoadSelectedProc", LoadSelectedProc },
896 { "SetFilterProc", SetFilterProc },
897 { "ReloadGameProc", ReloadGameProc },
898 { "LoadPositionProc", LoadPositionProc },
899 { "LoadNextPositionProc", LoadNextPositionProc },
900 { "LoadPrevPositionProc", LoadPrevPositionProc },
901 { "ReloadPositionProc", ReloadPositionProc },
902 { "CopyPositionProc", CopyPositionProc },
903 { "PastePositionProc", PastePositionProc },
904 { "CopyGameProc", CopyGameProc },
905 { "PasteGameProc", PasteGameProc },
906 { "SaveGameProc", SaveGameProc },
907 { "SavePositionProc", SavePositionProc },
908 { "MailMoveProc", MailMoveProc },
909 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
910 { "QuitProc", QuitProc },
911 { "MachineWhiteProc", MachineWhiteProc },
912 { "MachineBlackProc", MachineBlackProc },
913 { "AnalysisModeProc", AnalyzeModeProc },
914 { "AnalyzeFileProc", AnalyzeFileProc },
915 { "TwoMachinesProc", TwoMachinesProc },
916 { "IcsClientProc", IcsClientProc },
917 { "EditGameProc", EditGameProc },
918 { "EditPositionProc", EditPositionProc },
919 { "TrainingProc", EditPositionProc },
920 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
921 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
922 { "ShowGameListProc", ShowGameListProc },
923 { "ShowMoveListProc", HistoryShowProc},
924 { "EditTagsProc", EditCommentProc },
925 { "EditCommentProc", EditCommentProc },
926 { "IcsInputBoxProc", IcsInputBoxProc },
927 { "PauseProc", PauseProc },
928 { "AcceptProc", AcceptProc },
929 { "DeclineProc", DeclineProc },
930 { "RematchProc", RematchProc },
931 { "CallFlagProc", CallFlagProc },
932 { "DrawProc", DrawProc },
933 { "AdjournProc", AdjournProc },
934 { "AbortProc", AbortProc },
935 { "ResignProc", ResignProc },
936 { "AdjuWhiteProc", AdjuWhiteProc },
937 { "AdjuBlackProc", AdjuBlackProc },
938 { "AdjuDrawProc", AdjuDrawProc },
939 { "EnterKeyProc", EnterKeyProc },
940 { "UpKeyProc", UpKeyProc },
941 { "DownKeyProc", DownKeyProc },
942 { "StopObservingProc", StopObservingProc },
943 { "StopExaminingProc", StopExaminingProc },
944 { "UploadProc", UploadProc },
945 { "BackwardProc", BackwardProc },
946 { "ForwardProc", ForwardProc },
947 { "ToStartProc", ToStartProc },
948 { "ToEndProc", ToEndProc },
949 { "RevertProc", RevertProc },
950 { "AnnotateProc", AnnotateProc },
951 { "TruncateGameProc", TruncateGameProc },
952 { "MoveNowProc", MoveNowProc },
953 { "RetractMoveProc", RetractMoveProc },
954 { "EngineMenuProc", (XtActionProc) EngineMenuProc },
955 { "UciMenuProc", (XtActionProc) UciMenuProc },
956 { "TimeControlProc", (XtActionProc) TimeControlProc },
957 { "AlwaysQueenProc", AlwaysQueenProc },
958 { "AnimateDraggingProc", AnimateDraggingProc },
959 { "AnimateMovingProc", AnimateMovingProc },
960 { "AutoflagProc", AutoflagProc },
961 { "AutoflipProc", AutoflipProc },
962 { "BlindfoldProc", BlindfoldProc },
963 { "FlashMovesProc", FlashMovesProc },
964 { "FlipViewProc", FlipViewProc },
966 { "HighlightDraggingProc", HighlightDraggingProc },
968 { "HighlightLastMoveProc", HighlightLastMoveProc },
969 // { "IcsAlarmProc", IcsAlarmProc },
970 { "MoveSoundProc", MoveSoundProc },
971 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
972 { "PonderNextMoveProc", PonderNextMoveProc },
973 { "PopupExitMessageProc", PopupExitMessageProc },
974 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
975 // { "PremoveProc", PremoveProc },
976 { "ShowCoordsProc", ShowCoordsProc },
977 { "ShowThinkingProc", ShowThinkingProc },
978 { "HideThinkingProc", HideThinkingProc },
979 { "TestLegalityProc", TestLegalityProc },
980 { "SaveSettingsProc", SaveSettingsProc },
981 { "SaveOnExitProc", SaveOnExitProc },
982 { "InfoProc", InfoProc },
983 { "ManProc", ManProc },
984 { "HintProc", HintProc },
985 { "BookProc", BookProc },
986 { "AboutGameProc", AboutGameProc },
987 { "AboutProc", AboutProc },
988 { "DebugProc", DebugProc },
989 { "NothingProc", NothingProc },
990 { "CommentClick", (XtActionProc) CommentClick },
991 { "CommentPopDown", (XtActionProc) CommentPopDown },
992 { "EditCommentPopDown", (XtActionProc) EditCommentPopDown },
993 { "TagsPopDown", (XtActionProc) TagsPopDown },
994 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
995 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
996 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
997 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
998 { "GameListPopDown", (XtActionProc) GameListPopDown },
999 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1000 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1001 { "HistoryPopDown", (XtActionProc) HistoryPopDown },
1002 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1003 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1004 { "ShufflePopDown", (XtActionProc) ShufflePopDown },
1005 { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
1006 { "SettingsPopDown", (XtActionProc) SettingsPopDown },
1007 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1010 char globalTranslations[] =
1011 ":<Key>F9: ResignProc() \n \
1012 :Ctrl<Key>n: ResetProc() \n \
1013 :Meta<Key>V: NewVariantProc() \n \
1014 :Ctrl<Key>o: LoadGameProc() \n \
1015 :Meta<Key>Next: LoadNextGameProc() \n \
1016 :Meta<Key>Prior: LoadPrevGameProc() \n \
1017 :Ctrl<Key>s: SaveGameProc() \n \
1018 :Ctrl<Key>c: CopyGameProc() \n \
1019 :Ctrl<Key>v: PasteGameProc() \n \
1020 :Ctrl<Key>O: LoadPositionProc() \n \
1021 :Shift<Key>Next: LoadNextPositionProc() \n \
1022 :Shift<Key>Prior: LoadPrevPositionProc() \n \
1023 :Ctrl<Key>S: SavePositionProc() \n \
1024 :Ctrl<Key>C: CopyPositionProc() \n \
1025 :Ctrl<Key>V: PastePositionProc() \n \
1026 :Ctrl<Key>q: QuitProc() \n \
1027 :Ctrl<Key>w: MachineWhiteProc() \n \
1028 :Ctrl<Key>b: MachineBlackProc() \n \
1029 :Ctrl<Key>t: TwoMachinesProc() \n \
1030 :Ctrl<Key>a: AnalysisModeProc() \n \
1031 :Ctrl<Key>f: AnalyzeFileProc() \n \
1032 :Ctrl<Key>e: EditGameProc() \n \
1033 :Ctrl<Key>E: EditPositionProc() \n \
1034 :Meta<Key>O: EngineOutputProc() \n \
1035 :Meta<Key>E: EvalGraphProc() \n \
1036 :Meta<Key>G: ShowGameListProc() \n \
1037 :Meta<Key>H: ShowMoveListProc() \n \
1038 :<Key>Pause: PauseProc() \n \
1039 :<Key>F3: AcceptProc() \n \
1040 :<Key>F4: DeclineProc() \n \
1041 :<Key>F12: RematchProc() \n \
1042 :<Key>F5: CallFlagProc() \n \
1043 :<Key>F6: DrawProc() \n \
1044 :<Key>F7: AdjournProc() \n \
1045 :<Key>F8: AbortProc() \n \
1046 :<Key>F10: StopObservingProc() \n \
1047 :<Key>F11: StopExaminingProc() \n \
1048 :Meta Ctrl<Key>F12: DebugProc() \n \
1049 :Meta<Key>End: ToEndProc() \n \
1050 :Meta<Key>Right: ForwardProc() \n \
1051 :Meta<Key>Home: ToStartProc() \n \
1052 :Meta<Key>Left: BackwardProc() \n \
1053 :<Key>Home: RevertProc() \n \
1054 :<Key>End: TruncateGameProc() \n \
1055 :Ctrl<Key>m: MoveNowProc() \n \
1056 :Ctrl<Key>x: RetractMoveProc() \n \
1057 :Meta<Key>J: EngineMenuProc() \n \
1058 :Meta<Key>U: UciMenuProc() \n \
1059 :Meta<Key>T: TimeControlProc() \n \
1060 :Ctrl<Key>Q: AlwaysQueenProc() \n \
1061 :Ctrl<Key>F: AutoflagProc() \n \
1062 :Ctrl<Key>A: AnimateMovingProc() \n \
1063 :Ctrl<Key>P: PonderNextMoveProc() \n \
1064 :Ctrl<Key>L: TestLegalityProc() \n \
1065 :Ctrl<Key>H: HideThinkingProc() \n \
1066 :<Key>-: Iconify() \n \
1067 :<Key>F1: ManProc() \n \
1068 :<Key>F2: FlipViewProc() \n \
1069 <KeyDown>.: BackwardProc() \n \
1070 <KeyUp>.: ForwardProc() \n \
1071 Shift<Key>1: AskQuestionProc(\"Direct command\",\
1072 \"Send to chess program:\",,1) \n \
1073 Shift<Key>2: AskQuestionProc(\"Direct command\",\
1074 \"Send to second chess program:\",,2) \n";
1076 char boardTranslations[] =
1077 "<Btn1Down>: HandleUserMove(0) \n \
1078 Shift<Btn1Up>: HandleUserMove(1) \n \
1079 <Btn1Up>: HandleUserMove(0) \n \
1080 <Btn1Motion>: AnimateUserMove() \n \
1081 <Btn3Motion>: HandlePV() \n \
1082 <Btn3Up>: PieceMenuPopup(menuB) \n \
1083 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1084 PieceMenuPopup(menuB) \n \
1085 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1086 PieceMenuPopup(menuW) \n \
1087 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1088 PieceMenuPopup(menuW) \n \
1089 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1090 PieceMenuPopup(menuB) \n";
1092 char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
1093 char blackTranslations[] = "<BtnDown>: BlackClock()\n";
1095 char ICSInputTranslations[] =
1096 "<Key>Up: UpKeyProc() \n "
1097 "<Key>Down: DownKeyProc() \n "
1098 "<Key>Return: EnterKeyProc() \n";
1100 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1101 // as the widget is destroyed before the up-click can call extend-end
1102 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1104 String xboardResources[] = {
1105 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1106 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1107 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1112 /* Max possible square size */
1113 #define MAXSQSIZE 256
1115 static int xpm_avail[MAXSQSIZE];
1117 #ifdef HAVE_DIR_STRUCT
1119 /* Extract piece size from filename */
1121 xpm_getsize(name, len, ext)
1132 if ((p=strchr(name, '.')) == NULL ||
1133 StrCaseCmp(p+1, ext) != 0)
1139 while (*p && isdigit(*p))
1146 /* Setup xpm_avail */
1148 xpm_getavail(dirname, ext)
1156 for (i=0; i<MAXSQSIZE; ++i)
1159 if (appData.debugMode)
1160 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1162 dir = opendir(dirname);
1165 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1166 programName, dirname);
1170 while ((ent=readdir(dir)) != NULL) {
1171 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1172 if (i > 0 && i < MAXSQSIZE)
1182 xpm_print_avail(fp, ext)
1188 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1189 for (i=1; i<MAXSQSIZE; ++i) {
1195 /* Return XPM piecesize closest to size */
1197 xpm_closest_to(dirname, size, ext)
1203 int sm_diff = MAXSQSIZE;
1207 xpm_getavail(dirname, ext);
1209 if (appData.debugMode)
1210 xpm_print_avail(stderr, ext);
1212 for (i=1; i<MAXSQSIZE; ++i) {
1215 diff = (diff<0) ? -diff : diff;
1216 if (diff < sm_diff) {
1224 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1230 #else /* !HAVE_DIR_STRUCT */
1231 /* If we are on a system without a DIR struct, we can't
1232 read the directory, so we can't collect a list of
1233 filenames, etc., so we can't do any size-fitting. */
1235 xpm_closest_to(dirname, size, ext)
1240 fprintf(stderr, _("\
1241 Warning: No DIR structure found on this system --\n\
1242 Unable to autosize for XPM/XIM pieces.\n\
1243 Please report this error to frankm@hiwaay.net.\n\
1244 Include system type & operating system in message.\n"));
1247 #endif /* HAVE_DIR_STRUCT */
1249 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1250 "magenta", "cyan", "white" };
1254 TextColors textColors[(int)NColorClasses];
1256 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1258 parse_color(str, which)
1262 char *p, buf[100], *d;
1265 if (strlen(str) > 99) /* watch bounds on buf */
1270 for (i=0; i<which; ++i) {
1277 /* Could be looking at something like:
1279 .. in which case we want to stop on a comma also */
1280 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1284 return -1; /* Use default for empty field */
1287 if (which == 2 || isdigit(*p))
1290 while (*p && isalpha(*p))
1295 for (i=0; i<8; ++i) {
1296 if (!StrCaseCmp(buf, cnames[i]))
1297 return which? (i+40) : (i+30);
1299 if (!StrCaseCmp(buf, "default")) return -1;
1301 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1306 parse_cpair(cc, str)
1310 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1311 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1316 /* bg and attr are optional */
1317 textColors[(int)cc].bg = parse_color(str, 1);
1318 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1319 textColors[(int)cc].attr = 0;
1325 /* Arrange to catch delete-window events */
1326 Atom wm_delete_window;
1328 CatchDeleteWindow(Widget w, String procname)
1331 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1332 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1333 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1340 XtSetArg(args[0], XtNiconic, False);
1341 XtSetValues(shellWidget, args, 1);
1343 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1346 //---------------------------------------------------------------------------------------------------------
1347 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1350 #define CW_USEDEFAULT (1<<31)
1351 #define ICS_TEXT_MENU_SIZE 90
1352 #define DEBUG_FILE "xboard.debug"
1353 #define SetCurrentDirectory chdir
1354 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1358 // these two must some day move to frontend.h, when they are implemented
1359 Boolean GameListIsUp();
1361 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1364 // front-end part of option handling
1366 // [HGM] This platform-dependent table provides the location for storing the color info
1367 extern char *crWhite, * crBlack;
1371 &appData.whitePieceColor,
1372 &appData.blackPieceColor,
1373 &appData.lightSquareColor,
1374 &appData.darkSquareColor,
1375 &appData.highlightSquareColor,
1376 &appData.premoveHighlightColor,
1377 &appData.lowTimeWarningColor,
1388 // [HGM] font: keep a font for each square size, even non-stndard ones
1389 #define NUM_SIZES 18
1390 #define MAX_SIZE 130
1391 Boolean fontSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1392 char *fontTable[NUM_FONTS][MAX_SIZE];
1395 ParseFont(char *name, int number)
1396 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1398 if(sscanf(name, "size%d:", &size)) {
1399 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1400 // defer processing it until we know if it matches our board size
1401 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1402 fontTable[number][size] = strdup(strchr(name, ':')+1);
1403 fontValid[number][size] = True;
1408 case 0: // CLOCK_FONT
1409 appData.clockFont = strdup(name);
1411 case 1: // MESSAGE_FONT
1412 appData.font = strdup(name);
1414 case 2: // COORD_FONT
1415 appData.coordFont = strdup(name);
1420 fontSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1425 { // only 2 fonts currently
1426 appData.clockFont = CLOCK_FONT_NAME;
1427 appData.coordFont = COORD_FONT_NAME;
1428 appData.font = DEFAULT_FONT_NAME;
1433 { // no-op, until we identify the code for this already in XBoard and move it here
1437 ParseColor(int n, char *name)
1438 { // in XBoard, just copy the color-name string
1439 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1443 ParseTextAttribs(ColorClass cc, char *s)
1445 (&appData.colorShout)[cc] = strdup(s);
1449 ParseBoardSize(void *addr, char *name)
1451 appData.boardSize = strdup(name);
1456 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1460 SetCommPortDefaults()
1461 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1464 // [HGM] args: these three cases taken out to stay in front-end
1466 SaveFontArg(FILE *f, ArgDescriptor *ad)
1469 int i, n = (int)(intptr_t)ad->argLoc;
1471 case 0: // CLOCK_FONT
1472 name = appData.clockFont;
1474 case 1: // MESSAGE_FONT
1475 name = appData.font;
1477 case 2: // COORD_FONT
1478 name = appData.coordFont;
1483 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1484 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1485 fontTable[n][squareSize] = strdup(name);
1486 fontValid[n][squareSize] = True;
1489 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1490 fprintf(f, OPTCHAR "%s" SEPCHAR "size%d:%s\n", ad->argName, i, fontTable[n][i]);
1495 { // nothing to do, as the sounds are at all times represented by their text-string names already
1499 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1500 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1501 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1505 SaveColor(FILE *f, ArgDescriptor *ad)
1506 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1507 if(colorVariable[(int)(intptr_t)ad->argLoc])
1508 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1512 SaveBoardSize(FILE *f, char *name, void *addr)
1513 { // wrapper to shield back-end from BoardSize & sizeInfo
1514 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1518 ParseCommPortSettings(char *s)
1519 { // no such option in XBoard (yet)
1522 extern Widget engineOutputShell;
1523 extern Widget tagsShell, editTagsShell;
1525 GetActualPlacement(Widget wg, WindowPlacement *wp)
1535 XtSetArg(args[i], XtNx, &x); i++;
1536 XtSetArg(args[i], XtNy, &y); i++;
1537 XtSetArg(args[i], XtNwidth, &w); i++;
1538 XtSetArg(args[i], XtNheight, &h); i++;
1539 XtGetValues(wg, args, i);
1548 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1549 // In XBoard this will have to wait until awareness of window parameters is implemented
1550 GetActualPlacement(shellWidget, &wpMain);
1551 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1552 if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1553 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1554 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1555 if(commentShell) GetActualPlacement(commentShell, &wpComment);
1556 else GetActualPlacement(editShell, &wpComment);
1557 if(tagsShell) GetActualPlacement(tagsShell, &wpTags);
1558 else GetActualPlacement(editTagsShell, &wpTags);
1562 PrintCommPortSettings(FILE *f, char *name)
1563 { // This option does not exist in XBoard
1567 MySearchPath(char *installDir, char *name, char *fullname)
1568 { // just append installDir and name. Perhaps ExpandPath should be used here?
1569 name = ExpandPathName(name);
1570 if(name && name[0] == '/')
1571 safeStrCpy(fullname, name, MSG_SIZ );
1573 sprintf(fullname, "%s%c%s", installDir, '/', name);
1579 MyGetFullPathName(char *name, char *fullname)
1580 { // should use ExpandPath?
1581 name = ExpandPathName(name);
1582 safeStrCpy(fullname, name, MSG_SIZ );
1587 EnsureOnScreen(int *x, int *y, int minX, int minY)
1594 { // [HGM] args: allows testing if main window is realized from back-end
1595 return xBoardWindow != 0;
1599 PopUpStartupDialog()
1600 { // start menu not implemented in XBoard
1604 ConvertToLine(int argc, char **argv)
1606 static char line[128*1024], buf[1024];
1610 for(i=1; i<argc; i++)
1612 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1613 && argv[i][0] != '{' )
1614 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1616 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1617 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1620 line[strlen(line)-1] = NULLCHAR;
1624 //--------------------------------------------------------------------------------------------
1626 extern Boolean twoBoards, partnerUp;
1629 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1631 #define BoardSize int
1632 void InitDrawingSizes(BoardSize boardSize, int flags)
1633 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1634 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1636 XtGeometryResult gres;
1639 if(!formWidget) return;
1642 * Enable shell resizing.
1644 shellArgs[0].value = (XtArgVal) &w;
1645 shellArgs[1].value = (XtArgVal) &h;
1646 XtGetValues(shellWidget, shellArgs, 2);
1648 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1649 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1650 XtSetValues(shellWidget, &shellArgs[2], 4);
1652 XtSetArg(args[0], XtNdefaultDistance, &sep);
1653 XtGetValues(formWidget, args, 1);
1655 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1656 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1657 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1659 hOffset = boardWidth + 10;
1660 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1661 secondSegments[i] = gridSegments[i];
1662 secondSegments[i].x1 += hOffset;
1663 secondSegments[i].x2 += hOffset;
1666 XtSetArg(args[0], XtNwidth, boardWidth);
1667 XtSetArg(args[1], XtNheight, boardHeight);
1668 XtSetValues(boardWidget, args, 2);
1670 timerWidth = (boardWidth - sep) / 2;
1671 XtSetArg(args[0], XtNwidth, timerWidth);
1672 XtSetValues(whiteTimerWidget, args, 1);
1673 XtSetValues(blackTimerWidget, args, 1);
1675 XawFormDoLayout(formWidget, False);
1677 if (appData.titleInWindow) {
1679 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1680 XtSetArg(args[i], XtNheight, &h); i++;
1681 XtGetValues(titleWidget, args, i);
1683 w = boardWidth - 2*bor;
1685 XtSetArg(args[0], XtNwidth, &w);
1686 XtGetValues(menuBarWidget, args, 1);
1687 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1690 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1691 if (gres != XtGeometryYes && appData.debugMode) {
1693 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1694 programName, gres, w, h, wr, hr);
1698 XawFormDoLayout(formWidget, True);
1701 * Inhibit shell resizing.
1703 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1704 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1705 shellArgs[4].value = shellArgs[2].value = w;
1706 shellArgs[5].value = shellArgs[3].value = h;
1707 XtSetValues(shellWidget, &shellArgs[0], 6);
1709 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1712 for(i=0; i<4; i++) {
1714 for(p=0; p<=(int)WhiteKing; p++)
1715 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1716 if(gameInfo.variant == VariantShogi) {
1717 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1718 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1719 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1720 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1721 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1724 if(gameInfo.variant == VariantGothic) {
1725 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1728 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1729 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1730 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1733 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1734 for(p=0; p<=(int)WhiteKing; p++)
1735 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1736 if(gameInfo.variant == VariantShogi) {
1737 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1738 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1739 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1740 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1741 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1744 if(gameInfo.variant == VariantGothic) {
1745 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1748 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1749 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1750 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1755 for(i=0; i<2; i++) {
1757 for(p=0; p<=(int)WhiteKing; p++)
1758 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1759 if(gameInfo.variant == VariantShogi) {
1760 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1761 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1762 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1763 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1764 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1767 if(gameInfo.variant == VariantGothic) {
1768 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1771 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1772 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1773 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1783 void ParseIcsTextColors()
1784 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1785 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1786 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1787 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1788 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1789 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1790 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1791 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1792 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1793 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1794 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1796 if (appData.colorize) {
1798 _("%s: can't parse color names; disabling colorization\n"),
1801 appData.colorize = FALSE;
1806 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1807 XrmValue vFrom, vTo;
1808 int forceMono = False;
1810 if (!appData.monoMode) {
1811 vFrom.addr = (caddr_t) appData.lightSquareColor;
1812 vFrom.size = strlen(appData.lightSquareColor);
1813 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1814 if (vTo.addr == NULL) {
1815 appData.monoMode = True;
1818 lightSquareColor = *(Pixel *) vTo.addr;
1821 if (!appData.monoMode) {
1822 vFrom.addr = (caddr_t) appData.darkSquareColor;
1823 vFrom.size = strlen(appData.darkSquareColor);
1824 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1825 if (vTo.addr == NULL) {
1826 appData.monoMode = True;
1829 darkSquareColor = *(Pixel *) vTo.addr;
1832 if (!appData.monoMode) {
1833 vFrom.addr = (caddr_t) appData.whitePieceColor;
1834 vFrom.size = strlen(appData.whitePieceColor);
1835 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1836 if (vTo.addr == NULL) {
1837 appData.monoMode = True;
1840 whitePieceColor = *(Pixel *) vTo.addr;
1843 if (!appData.monoMode) {
1844 vFrom.addr = (caddr_t) appData.blackPieceColor;
1845 vFrom.size = strlen(appData.blackPieceColor);
1846 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1847 if (vTo.addr == NULL) {
1848 appData.monoMode = True;
1851 blackPieceColor = *(Pixel *) vTo.addr;
1855 if (!appData.monoMode) {
1856 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1857 vFrom.size = strlen(appData.highlightSquareColor);
1858 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1859 if (vTo.addr == NULL) {
1860 appData.monoMode = True;
1863 highlightSquareColor = *(Pixel *) vTo.addr;
1867 if (!appData.monoMode) {
1868 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1869 vFrom.size = strlen(appData.premoveHighlightColor);
1870 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1871 if (vTo.addr == NULL) {
1872 appData.monoMode = True;
1875 premoveHighlightColor = *(Pixel *) vTo.addr;
1886 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1887 XSetWindowAttributes window_attributes;
1889 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1890 XrmValue vFrom, vTo;
1891 XtGeometryResult gres;
1894 int forceMono = False;
1896 srandom(time(0)); // [HGM] book: make random truly random
1898 setbuf(stdout, NULL);
1899 setbuf(stderr, NULL);
1902 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1903 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1907 programName = strrchr(argv[0], '/');
1908 if (programName == NULL)
1909 programName = argv[0];
1914 XtSetLanguageProc(NULL, NULL, NULL);
1915 bindtextdomain(PACKAGE, LOCALEDIR);
1916 textdomain(PACKAGE);
1920 XtAppInitialize(&appContext, "XBoard", shellOptions,
1921 XtNumber(shellOptions),
1922 &argc, argv, xboardResources, NULL, 0);
1923 appData.boardSize = "";
1924 InitAppData(ConvertToLine(argc, argv));
1926 if (p == NULL) p = "/tmp";
1927 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1928 gameCopyFilename = (char*) malloc(i);
1929 gamePasteFilename = (char*) malloc(i);
1930 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1931 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1933 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1934 clientResources, XtNumber(clientResources),
1937 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1938 static char buf[MSG_SIZ];
1939 EscapeExpand(buf, appData.initString);
1940 appData.initString = strdup(buf);
1941 EscapeExpand(buf, appData.secondInitString);
1942 appData.secondInitString = strdup(buf);
1943 EscapeExpand(buf, appData.firstComputerString);
1944 appData.firstComputerString = strdup(buf);
1945 EscapeExpand(buf, appData.secondComputerString);
1946 appData.secondComputerString = strdup(buf);
1949 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1952 if (chdir(chessDir) != 0) {
1953 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1959 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1960 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1961 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1962 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1965 setbuf(debugFP, NULL);
1968 /* [HGM,HR] make sure board size is acceptable */
1969 if(appData.NrFiles > BOARD_FILES ||
1970 appData.NrRanks > BOARD_RANKS )
1971 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1974 /* This feature does not work; animation needs a rewrite */
1975 appData.highlightDragging = FALSE;
1979 xDisplay = XtDisplay(shellWidget);
1980 xScreen = DefaultScreen(xDisplay);
1981 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1983 gameInfo.variant = StringToVariant(appData.variant);
1984 InitPosition(FALSE);
1987 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1989 if (isdigit(appData.boardSize[0])) {
1990 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1991 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1992 &fontPxlSize, &smallLayout, &tinyLayout);
1994 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1995 programName, appData.boardSize);
1999 /* Find some defaults; use the nearest known size */
2000 SizeDefaults *szd, *nearest;
2001 int distance = 99999;
2002 nearest = szd = sizeDefaults;
2003 while (szd->name != NULL) {
2004 if (abs(szd->squareSize - squareSize) < distance) {
2006 distance = abs(szd->squareSize - squareSize);
2007 if (distance == 0) break;
2011 if (i < 2) lineGap = nearest->lineGap;
2012 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2013 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2014 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2015 if (i < 6) smallLayout = nearest->smallLayout;
2016 if (i < 7) tinyLayout = nearest->tinyLayout;
2019 SizeDefaults *szd = sizeDefaults;
2020 if (*appData.boardSize == NULLCHAR) {
2021 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2022 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2025 if (szd->name == NULL) szd--;
2026 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2028 while (szd->name != NULL &&
2029 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2030 if (szd->name == NULL) {
2031 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2032 programName, appData.boardSize);
2036 squareSize = szd->squareSize;
2037 lineGap = szd->lineGap;
2038 clockFontPxlSize = szd->clockFontPxlSize;
2039 coordFontPxlSize = szd->coordFontPxlSize;
2040 fontPxlSize = szd->fontPxlSize;
2041 smallLayout = szd->smallLayout;
2042 tinyLayout = szd->tinyLayout;
2043 // [HGM] font: use defaults from settings file if available and not overruled
2045 if(!fontSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2046 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2047 if(!fontSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2048 appData.font = fontTable[MESSAGE_FONT][squareSize];
2049 if(!fontSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2050 appData.coordFont = fontTable[COORD_FONT][squareSize];
2052 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2053 if (strlen(appData.pixmapDirectory) > 0) {
2054 p = ExpandPathName(appData.pixmapDirectory);
2056 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2057 appData.pixmapDirectory);
2060 if (appData.debugMode) {
2061 fprintf(stderr, _("\
2062 XBoard square size (hint): %d\n\
2063 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2065 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2066 if (appData.debugMode) {
2067 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2070 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2072 /* [HR] height treated separately (hacked) */
2073 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2074 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2075 if (appData.showJail == 1) {
2076 /* Jail on top and bottom */
2077 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2078 XtSetArg(boardArgs[2], XtNheight,
2079 boardHeight + 2*(lineGap + squareSize));
2080 } else if (appData.showJail == 2) {
2082 XtSetArg(boardArgs[1], XtNwidth,
2083 boardWidth + 2*(lineGap + squareSize));
2084 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2087 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2088 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2092 * Determine what fonts to use.
2094 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2095 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2096 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2097 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2098 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2099 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2100 appData.font = FindFont(appData.font, fontPxlSize);
2101 countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
2102 countFontStruct = XQueryFont(xDisplay, countFontID);
2103 // appData.font = FindFont(appData.font, fontPxlSize);
2105 xdb = XtDatabase(xDisplay);
2106 XrmPutStringResource(&xdb, "*font", appData.font);
2109 * Detect if there are not enough colors available and adapt.
2111 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2112 appData.monoMode = True;
2115 forceMono = MakeColors();
2118 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2121 if (appData.bitmapDirectory == NULL ||
2122 appData.bitmapDirectory[0] == NULLCHAR)
2123 appData.bitmapDirectory = DEF_BITMAP_DIR;
2126 if (appData.lowTimeWarning && !appData.monoMode) {
2127 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2128 vFrom.size = strlen(appData.lowTimeWarningColor);
2129 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2130 if (vTo.addr == NULL)
2131 appData.monoMode = True;
2133 lowTimeWarningColor = *(Pixel *) vTo.addr;
2136 if (appData.monoMode && appData.debugMode) {
2137 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2138 (unsigned long) XWhitePixel(xDisplay, xScreen),
2139 (unsigned long) XBlackPixel(xDisplay, xScreen));
2142 ParseIcsTextColors();
2143 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2144 textColors[ColorNone].attr = 0;
2146 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2152 layoutName = "tinyLayout";
2153 } else if (smallLayout) {
2154 layoutName = "smallLayout";
2156 layoutName = "normalLayout";
2158 /* Outer layoutWidget is there only to provide a name for use in
2159 resources that depend on the layout style */
2161 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2162 layoutArgs, XtNumber(layoutArgs));
2164 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2165 formArgs, XtNumber(formArgs));
2166 XtSetArg(args[0], XtNdefaultDistance, &sep);
2167 XtGetValues(formWidget, args, 1);
2170 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2171 XtSetArg(args[0], XtNtop, XtChainTop);
2172 XtSetArg(args[1], XtNbottom, XtChainTop);
2173 XtSetArg(args[2], XtNright, XtChainLeft);
2174 XtSetValues(menuBarWidget, args, 3);
2176 widgetList[j++] = whiteTimerWidget =
2177 XtCreateWidget("whiteTime", labelWidgetClass,
2178 formWidget, timerArgs, XtNumber(timerArgs));
2179 XtSetArg(args[0], XtNfont, clockFontStruct);
2180 XtSetArg(args[1], XtNtop, XtChainTop);
2181 XtSetArg(args[2], XtNbottom, XtChainTop);
2182 XtSetValues(whiteTimerWidget, args, 3);
2184 widgetList[j++] = blackTimerWidget =
2185 XtCreateWidget("blackTime", labelWidgetClass,
2186 formWidget, timerArgs, XtNumber(timerArgs));
2187 XtSetArg(args[0], XtNfont, clockFontStruct);
2188 XtSetArg(args[1], XtNtop, XtChainTop);
2189 XtSetArg(args[2], XtNbottom, XtChainTop);
2190 XtSetValues(blackTimerWidget, args, 3);
2192 if (appData.titleInWindow) {
2193 widgetList[j++] = titleWidget =
2194 XtCreateWidget("title", labelWidgetClass, formWidget,
2195 titleArgs, XtNumber(titleArgs));
2196 XtSetArg(args[0], XtNtop, XtChainTop);
2197 XtSetArg(args[1], XtNbottom, XtChainTop);
2198 XtSetValues(titleWidget, args, 2);
2201 if (appData.showButtonBar) {
2202 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2203 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2204 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2205 XtSetArg(args[2], XtNtop, XtChainTop);
2206 XtSetArg(args[3], XtNbottom, XtChainTop);
2207 XtSetValues(buttonBarWidget, args, 4);
2210 widgetList[j++] = messageWidget =
2211 XtCreateWidget("message", labelWidgetClass, formWidget,
2212 messageArgs, XtNumber(messageArgs));
2213 XtSetArg(args[0], XtNtop, XtChainTop);
2214 XtSetArg(args[1], XtNbottom, XtChainTop);
2215 XtSetValues(messageWidget, args, 2);
2217 widgetList[j++] = boardWidget =
2218 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2219 XtNumber(boardArgs));
2221 XtManageChildren(widgetList, j);
2223 timerWidth = (boardWidth - sep) / 2;
2224 XtSetArg(args[0], XtNwidth, timerWidth);
2225 XtSetValues(whiteTimerWidget, args, 1);
2226 XtSetValues(blackTimerWidget, args, 1);
2228 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2229 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2230 XtGetValues(whiteTimerWidget, args, 2);
2232 if (appData.showButtonBar) {
2233 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2234 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2235 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2239 * formWidget uses these constraints but they are stored
2243 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2244 XtSetValues(menuBarWidget, args, i);
2245 if (appData.titleInWindow) {
2248 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2249 XtSetValues(whiteTimerWidget, args, i);
2251 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2252 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2253 XtSetValues(blackTimerWidget, args, i);
2255 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2256 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2257 XtSetValues(titleWidget, args, i);
2259 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2260 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2261 XtSetValues(messageWidget, args, i);
2262 if (appData.showButtonBar) {
2264 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2265 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2266 XtSetValues(buttonBarWidget, args, i);
2270 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2271 XtSetValues(whiteTimerWidget, args, i);
2273 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2274 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2275 XtSetValues(blackTimerWidget, args, i);
2277 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2278 XtSetValues(titleWidget, args, i);
2280 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2281 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2282 XtSetValues(messageWidget, args, i);
2283 if (appData.showButtonBar) {
2285 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2286 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2287 XtSetValues(buttonBarWidget, args, i);
2292 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2293 XtSetValues(whiteTimerWidget, args, i);
2295 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2296 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2297 XtSetValues(blackTimerWidget, args, i);
2299 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2300 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2301 XtSetValues(messageWidget, args, i);
2302 if (appData.showButtonBar) {
2304 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2305 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2306 XtSetValues(buttonBarWidget, args, i);
2310 XtSetArg(args[0], XtNfromVert, messageWidget);
2311 XtSetArg(args[1], XtNtop, XtChainTop);
2312 XtSetArg(args[2], XtNbottom, XtChainBottom);
2313 XtSetArg(args[3], XtNleft, XtChainLeft);
2314 XtSetArg(args[4], XtNright, XtChainRight);
2315 XtSetValues(boardWidget, args, 5);
2317 XtRealizeWidget(shellWidget);
2320 XtSetArg(args[0], XtNx, wpMain.x);
2321 XtSetArg(args[1], XtNy, wpMain.y);
2322 XtSetValues(shellWidget, args, 2);
2326 * Correct the width of the message and title widgets.
2327 * It is not known why some systems need the extra fudge term.
2328 * The value "2" is probably larger than needed.
2330 XawFormDoLayout(formWidget, False);
2332 #define WIDTH_FUDGE 2
2334 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2335 XtSetArg(args[i], XtNheight, &h); i++;
2336 XtGetValues(messageWidget, args, i);
2337 if (appData.showButtonBar) {
2339 XtSetArg(args[i], XtNwidth, &w); i++;
2340 XtGetValues(buttonBarWidget, args, i);
2341 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2343 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2346 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2347 if (gres != XtGeometryYes && appData.debugMode) {
2348 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2349 programName, gres, w, h, wr, hr);
2352 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2353 /* The size used for the child widget in layout lags one resize behind
2354 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2356 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2357 if (gres != XtGeometryYes && appData.debugMode) {
2358 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2359 programName, gres, w, h, wr, hr);
2362 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2363 XtSetArg(args[1], XtNright, XtChainRight);
2364 XtSetValues(messageWidget, args, 2);
2366 if (appData.titleInWindow) {
2368 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2369 XtSetArg(args[i], XtNheight, &h); i++;
2370 XtGetValues(titleWidget, args, i);
2372 w = boardWidth - 2*bor;
2374 XtSetArg(args[0], XtNwidth, &w);
2375 XtGetValues(menuBarWidget, args, 1);
2376 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2379 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2380 if (gres != XtGeometryYes && appData.debugMode) {
2382 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2383 programName, gres, w, h, wr, hr);
2386 XawFormDoLayout(formWidget, True);
2388 xBoardWindow = XtWindow(boardWidget);
2390 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2391 // not need to go into InitDrawingSizes().
2395 * Create X checkmark bitmap and initialize option menu checks.
2397 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2398 checkmark_bits, checkmark_width, checkmark_height);
2399 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2400 if (appData.alwaysPromoteToQueen) {
2401 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2404 if (appData.animateDragging) {
2405 XtSetValues(XtNameToWidget(menuBarWidget,
2406 "menuOptions.Animate Dragging"),
2409 if (appData.animate) {
2410 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2413 if (appData.autoCallFlag) {
2414 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2417 if (appData.autoFlipView) {
2418 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2421 if (appData.blindfold) {
2422 XtSetValues(XtNameToWidget(menuBarWidget,
2423 "menuOptions.Blindfold"), args, 1);
2425 if (appData.flashCount > 0) {
2426 XtSetValues(XtNameToWidget(menuBarWidget,
2427 "menuOptions.Flash Moves"),
2431 if (appData.highlightDragging) {
2432 XtSetValues(XtNameToWidget(menuBarWidget,
2433 "menuOptions.Highlight Dragging"),
2437 if (appData.highlightLastMove) {
2438 XtSetValues(XtNameToWidget(menuBarWidget,
2439 "menuOptions.Highlight Last Move"),
2442 if (appData.highlightMoveWithArrow) {
2443 XtSetValues(XtNameToWidget(menuBarWidget,
2444 "menuOptions.Arrow"),
2447 // if (appData.icsAlarm) {
2448 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2451 if (appData.ringBellAfterMoves) {
2452 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2455 if (appData.oneClick) {
2456 XtSetValues(XtNameToWidget(menuBarWidget,
2457 "menuOptions.OneClick"), args, 1);
2459 if (appData.periodicUpdates) {
2460 XtSetValues(XtNameToWidget(menuBarWidget,
2461 "menuOptions.Periodic Updates"), args, 1);
2463 if (appData.ponderNextMove) {
2464 XtSetValues(XtNameToWidget(menuBarWidget,
2465 "menuOptions.Ponder Next Move"), args, 1);
2467 if (appData.popupExitMessage) {
2468 XtSetValues(XtNameToWidget(menuBarWidget,
2469 "menuOptions.Popup Exit Message"), args, 1);
2471 if (appData.popupMoveErrors) {
2472 XtSetValues(XtNameToWidget(menuBarWidget,
2473 "menuOptions.Popup Move Errors"), args, 1);
2475 // if (appData.premove) {
2476 // XtSetValues(XtNameToWidget(menuBarWidget,
2477 // "menuOptions.Premove"), args, 1);
2479 if (appData.showCoords) {
2480 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2483 if (appData.hideThinkingFromHuman) {
2484 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2487 if (appData.testLegality) {
2488 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2491 if (saveSettingsOnExit) {
2492 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2499 ReadBitmap(&wIconPixmap, "icon_white.bm",
2500 icon_white_bits, icon_white_width, icon_white_height);
2501 ReadBitmap(&bIconPixmap, "icon_black.bm",
2502 icon_black_bits, icon_black_width, icon_black_height);
2503 iconPixmap = wIconPixmap;
2505 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2506 XtSetValues(shellWidget, args, i);
2509 * Create a cursor for the board widget.
2511 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2512 XChangeWindowAttributes(xDisplay, xBoardWindow,
2513 CWCursor, &window_attributes);
2516 * Inhibit shell resizing.
2518 shellArgs[0].value = (XtArgVal) &w;
2519 shellArgs[1].value = (XtArgVal) &h;
2520 XtGetValues(shellWidget, shellArgs, 2);
2521 shellArgs[4].value = shellArgs[2].value = w;
2522 shellArgs[5].value = shellArgs[3].value = h;
2523 XtSetValues(shellWidget, &shellArgs[2], 4);
2524 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2525 marginH = h - boardHeight;
2527 CatchDeleteWindow(shellWidget, "QuitProc");
2532 if (appData.bitmapDirectory[0] != NULLCHAR) {
2536 CreateXPMBoard(appData.liteBackTextureFile, 1);
2537 CreateXPMBoard(appData.darkBackTextureFile, 0);
2541 /* Create regular pieces */
2542 if (!useImages) CreatePieces();
2547 if (appData.animate || appData.animateDragging)
2550 XtAugmentTranslations(formWidget,
2551 XtParseTranslationTable(globalTranslations));
2552 XtAugmentTranslations(boardWidget,
2553 XtParseTranslationTable(boardTranslations));
2554 XtAugmentTranslations(whiteTimerWidget,
2555 XtParseTranslationTable(whiteTranslations));
2556 XtAugmentTranslations(blackTimerWidget,
2557 XtParseTranslationTable(blackTranslations));
2559 /* Why is the following needed on some versions of X instead
2560 * of a translation? */
2561 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2562 (XtEventHandler) EventProc, NULL);
2565 /* [AS] Restore layout */
2566 if( wpMoveHistory.visible ) {
2570 if( wpEvalGraph.visible )
2575 if( wpEngineOutput.visible ) {
2576 EngineOutputPopUp();
2581 if (errorExitStatus == -1) {
2582 if (appData.icsActive) {
2583 /* We now wait until we see "login:" from the ICS before
2584 sending the logon script (problems with timestamp otherwise) */
2585 /*ICSInitScript();*/
2586 if (appData.icsInputBox) ICSInputBoxPopUp();
2590 signal(SIGWINCH, TermSizeSigHandler);
2592 signal(SIGINT, IntSigHandler);
2593 signal(SIGTERM, IntSigHandler);
2594 if (*appData.cmailGameName != NULLCHAR) {
2595 signal(SIGUSR1, CmailSigHandler);
2598 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2600 XtSetKeyboardFocus(shellWidget, formWidget);
2602 XtAppMainLoop(appContext);
2603 if (appData.debugMode) fclose(debugFP); // [DM] debug
2610 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2611 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2613 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2614 unlink(gameCopyFilename);
2615 unlink(gamePasteFilename);
2618 RETSIGTYPE TermSizeSigHandler(int sig)
2631 CmailSigHandler(sig)
2637 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2639 /* Activate call-back function CmailSigHandlerCallBack() */
2640 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2642 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2646 CmailSigHandlerCallBack(isr, closure, message, count, error)
2654 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2656 /**** end signal code ****/
2662 /* try to open the icsLogon script, either in the location given
2663 * or in the users HOME directory
2670 f = fopen(appData.icsLogon, "r");
2673 homedir = getenv("HOME");
2674 if (homedir != NULL)
2676 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2677 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2678 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2679 f = fopen(buf, "r");
2684 ProcessICSInitScript(f);
2686 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2695 EditCommentPopDown();
2710 if (!menuBarWidget) return;
2711 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2713 DisplayError("menuEdit.Revert", 0);
2715 XtSetSensitive(w, !grey);
2717 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2719 DisplayError("menuEdit.Annotate", 0);
2721 XtSetSensitive(w, !grey);
2726 SetMenuEnables(enab)
2730 if (!menuBarWidget) return;
2731 while (enab->name != NULL) {
2732 w = XtNameToWidget(menuBarWidget, enab->name);
2734 DisplayError(enab->name, 0);
2736 XtSetSensitive(w, enab->value);
2742 Enables icsEnables[] = {
2743 { "menuFile.Mail Move", False },
2744 { "menuFile.Reload CMail Message", False },
2745 { "menuMode.Machine Black", False },
2746 { "menuMode.Machine White", False },
2747 { "menuMode.Analysis Mode", False },
2748 { "menuMode.Analyze File", False },
2749 { "menuMode.Two Machines", False },
2751 { "menuEngine.Hint", False },
2752 { "menuEngine.Book", False },
2753 { "menuEngine.Move Now", False },
2754 { "menuOptions.Periodic Updates", False },
2755 { "menuOptions.Hide Thinking", False },
2756 { "menuOptions.Ponder Next Move", False },
2757 { "menuEngine.Engine #1 Settings", False },
2759 { "menuEngine.Engine #2 Settings", False },
2760 { "menuEdit.Annotate", False },
2764 Enables ncpEnables[] = {
2765 { "menuFile.Mail Move", False },
2766 { "menuFile.Reload CMail Message", False },
2767 { "menuMode.Machine White", False },
2768 { "menuMode.Machine Black", False },
2769 { "menuMode.Analysis Mode", False },
2770 { "menuMode.Analyze File", False },
2771 { "menuMode.Two Machines", False },
2772 { "menuMode.ICS Client", False },
2773 { "menuView.ICS Input Box", False },
2774 { "Action", False },
2775 { "menuEdit.Revert", False },
2776 { "menuEdit.Annotate", False },
2777 { "menuEngine.Engine #1 Settings", False },
2778 { "menuEngine.Engine #2 Settings", False },
2779 { "menuEngine.Move Now", False },
2780 { "menuEngine.Retract Move", False },
2781 { "menuOptions.Auto Flag", False },
2782 { "menuOptions.Auto Flip View", False },
2783 { "menuOptions.ICS", False },
2784 // { "menuOptions.ICS Alarm", False },
2785 { "menuOptions.Move Sound", False },
2786 { "menuOptions.Hide Thinking", False },
2787 { "menuOptions.Periodic Updates", False },
2788 { "menuOptions.Ponder Next Move", False },
2789 { "menuEngine.Hint", False },
2790 { "menuEngine.Book", False },
2794 Enables gnuEnables[] = {
2795 { "menuMode.ICS Client", False },
2796 { "menuView.ICS Input Box", False },
2797 { "menuAction.Accept", False },
2798 { "menuAction.Decline", False },
2799 { "menuAction.Rematch", False },
2800 { "menuAction.Adjourn", False },
2801 { "menuAction.Stop Examining", False },
2802 { "menuAction.Stop Observing", False },
2803 { "menuAction.Upload to Examine", False },
2804 { "menuEdit.Revert", False },
2805 { "menuEdit.Annotate", False },
2806 { "menuOptions.ICS", False },
2808 /* The next two options rely on SetCmailMode being called *after* */
2809 /* SetGNUMode so that when GNU is being used to give hints these */
2810 /* menu options are still available */
2812 { "menuFile.Mail Move", False },
2813 { "menuFile.Reload CMail Message", False },
2817 Enables cmailEnables[] = {
2819 { "menuAction.Call Flag", False },
2820 { "menuAction.Draw", True },
2821 { "menuAction.Adjourn", False },
2822 { "menuAction.Abort", False },
2823 { "menuAction.Stop Observing", False },
2824 { "menuAction.Stop Examining", False },
2825 { "menuFile.Mail Move", True },
2826 { "menuFile.Reload CMail Message", True },
2830 Enables trainingOnEnables[] = {
2831 { "menuMode.Edit Comment", False },
2832 { "menuMode.Pause", False },
2833 { "menuEdit.Forward", False },
2834 { "menuEdit.Backward", False },
2835 { "menuEdit.Forward to End", False },
2836 { "menuEdit.Back to Start", False },
2837 { "menuEngine.Move Now", False },
2838 { "menuEdit.Truncate Game", False },
2842 Enables trainingOffEnables[] = {
2843 { "menuMode.Edit Comment", True },
2844 { "menuMode.Pause", True },
2845 { "menuEdit.Forward", True },
2846 { "menuEdit.Backward", True },
2847 { "menuEdit.Forward to End", True },
2848 { "menuEdit.Back to Start", True },
2849 { "menuEngine.Move Now", True },
2850 { "menuEdit.Truncate Game", True },
2854 Enables machineThinkingEnables[] = {
2855 { "menuFile.Load Game", False },
2856 // { "menuFile.Load Next Game", False },
2857 // { "menuFile.Load Previous Game", False },
2858 // { "menuFile.Reload Same Game", False },
2859 { "menuEdit.Paste Game", False },
2860 { "menuFile.Load Position", False },
2861 // { "menuFile.Load Next Position", False },
2862 // { "menuFile.Load Previous Position", False },
2863 // { "menuFile.Reload Same Position", False },
2864 { "menuEdit.Paste Position", False },
2865 { "menuMode.Machine White", False },
2866 { "menuMode.Machine Black", False },
2867 { "menuMode.Two Machines", False },
2868 { "menuEngine.Retract Move", False },
2872 Enables userThinkingEnables[] = {
2873 { "menuFile.Load Game", True },
2874 // { "menuFile.Load Next Game", True },
2875 // { "menuFile.Load Previous Game", True },
2876 // { "menuFile.Reload Same Game", True },
2877 { "menuEdit.Paste Game", True },
2878 { "menuFile.Load Position", True },
2879 // { "menuFile.Load Next Position", True },
2880 // { "menuFile.Load Previous Position", True },
2881 // { "menuFile.Reload Same Position", True },
2882 { "menuEdit.Paste Position", True },
2883 { "menuMode.Machine White", True },
2884 { "menuMode.Machine Black", True },
2885 { "menuMode.Two Machines", True },
2886 { "menuEngine.Retract Move", True },
2892 SetMenuEnables(icsEnables);
2895 if (appData.zippyPlay && !appData.noChessProgram) /* [DM] icsEngineAnalyze */
2896 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2903 SetMenuEnables(ncpEnables);
2909 SetMenuEnables(gnuEnables);
2915 SetMenuEnables(cmailEnables);
2921 SetMenuEnables(trainingOnEnables);
2922 if (appData.showButtonBar) {
2923 XtSetSensitive(buttonBarWidget, False);
2929 SetTrainingModeOff()
2931 SetMenuEnables(trainingOffEnables);
2932 if (appData.showButtonBar) {
2933 XtSetSensitive(buttonBarWidget, True);
2938 SetUserThinkingEnables()
2940 if (appData.noChessProgram) return;
2941 SetMenuEnables(userThinkingEnables);
2945 SetMachineThinkingEnables()
2947 if (appData.noChessProgram) return;
2948 SetMenuEnables(machineThinkingEnables);
2950 case MachinePlaysBlack:
2951 case MachinePlaysWhite:
2952 case TwoMachinesPlay:
2953 XtSetSensitive(XtNameToWidget(menuBarWidget,
2954 ModeToWidgetName(gameMode)), True);
2961 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
2962 #define HISTORY_SIZE 64
2963 static char *history[HISTORY_SIZE];
2964 int histIn = 0, histP = 0;
2967 SaveInHistory(char *cmd)
2969 if (history[histIn] != NULL) {
2970 free(history[histIn]);
2971 history[histIn] = NULL;
2973 if (*cmd == NULLCHAR) return;
2974 history[histIn] = StrSave(cmd);
2975 histIn = (histIn + 1) % HISTORY_SIZE;
2976 if (history[histIn] != NULL) {
2977 free(history[histIn]);
2978 history[histIn] = NULL;
2984 PrevInHistory(char *cmd)
2987 if (histP == histIn) {
2988 if (history[histIn] != NULL) free(history[histIn]);
2989 history[histIn] = StrSave(cmd);
2991 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
2992 if (newhp == histIn || history[newhp] == NULL) return NULL;
2994 return history[histP];
3000 if (histP == histIn) return NULL;
3001 histP = (histP + 1) % HISTORY_SIZE;
3002 return history[histP];
3004 // end of borrowed code
3006 #define Abs(n) ((n)<0 ? -(n) : (n))
3009 * Find a font that matches "pattern" that is as close as
3010 * possible to the targetPxlSize. Prefer fonts that are k
3011 * pixels smaller to fonts that are k pixels larger. The
3012 * pattern must be in the X Consortium standard format,
3013 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3014 * The return value should be freed with XtFree when no
3018 FindFont(pattern, targetPxlSize)
3022 char **fonts, *p, *best, *scalable, *scalableTail;
3023 int i, j, nfonts, minerr, err, pxlSize;
3026 char **missing_list;
3028 char *def_string, *base_fnt_lst, strInt[3];
3030 XFontStruct **fnt_list;
3032 base_fnt_lst = calloc(1, strlen(pattern) + 3);
3033 snprintf(strInt, sizeof(strInt)/sizeof(strInt[0]), "%d", targetPxlSize);
3034 p = strstr(pattern, "--");
3035 strncpy(base_fnt_lst, pattern, p - pattern + 2);
3036 strcat(base_fnt_lst, strInt);
3037 strcat(base_fnt_lst, strchr(p + 2, '-'));
3039 if ((fntSet = XCreateFontSet(xDisplay,
3043 &def_string)) == NULL) {
3045 fprintf(stderr, _("Unable to create font set.\n"));
3049 nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
3051 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3053 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3054 programName, pattern);
3062 for (i=0; i<nfonts; i++) {
3065 if (*p != '-') continue;
3067 if (*p == NULLCHAR) break;
3068 if (*p++ == '-') j++;
3070 if (j < 7) continue;
3073 scalable = fonts[i];
3076 err = pxlSize - targetPxlSize;
3077 if (Abs(err) < Abs(minerr) ||
3078 (minerr > 0 && err < 0 && -err == minerr)) {
3084 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3085 /* If the error is too big and there is a scalable font,
3086 use the scalable font. */
3087 int headlen = scalableTail - scalable;
3088 p = (char *) XtMalloc(strlen(scalable) + 10);
3089 while (isdigit(*scalableTail)) scalableTail++;
3090 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3092 p = (char *) XtMalloc(strlen(best) + 2);
3093 safeStrCpy(p, best, strlen(best)+1 );
3095 if (appData.debugMode) {
3096 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3097 pattern, targetPxlSize, p);
3100 if (missing_count > 0)
3101 XFreeStringList(missing_list);
3102 XFreeFontSet(xDisplay, fntSet);
3104 XFreeFontNames(fonts);
3110 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3111 // must be called before all non-first callse to CreateGCs()
3112 XtReleaseGC(shellWidget, highlineGC);
3113 XtReleaseGC(shellWidget, lightSquareGC);
3114 XtReleaseGC(shellWidget, darkSquareGC);
3115 if (appData.monoMode) {
3116 if (DefaultDepth(xDisplay, xScreen) == 1) {
3117 XtReleaseGC(shellWidget, wbPieceGC);
3119 XtReleaseGC(shellWidget, bwPieceGC);
3122 XtReleaseGC(shellWidget, prelineGC);
3123 XtReleaseGC(shellWidget, jailSquareGC);
3124 XtReleaseGC(shellWidget, wdPieceGC);
3125 XtReleaseGC(shellWidget, wlPieceGC);
3126 XtReleaseGC(shellWidget, wjPieceGC);
3127 XtReleaseGC(shellWidget, bdPieceGC);
3128 XtReleaseGC(shellWidget, blPieceGC);
3129 XtReleaseGC(shellWidget, bjPieceGC);
3133 void CreateGCs(int redo)
3135 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3136 | GCBackground | GCFunction | GCPlaneMask;
3137 XGCValues gc_values;
3140 gc_values.plane_mask = AllPlanes;
3141 gc_values.line_width = lineGap;
3142 gc_values.line_style = LineSolid;
3143 gc_values.function = GXcopy;
3146 DeleteGCs(); // called a second time; clean up old GCs first
3147 } else { // [HGM] grid and font GCs created on first call only
3148 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3149 gc_values.background = XBlackPixel(xDisplay, xScreen);
3150 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3152 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3153 gc_values.background = XWhitePixel(xDisplay, xScreen);
3154 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3155 XSetFont(xDisplay, coordGC, coordFontID);
3157 // [HGM] make font for holdings counts (white on black)
3158 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3159 gc_values.background = XBlackPixel(xDisplay, xScreen);
3160 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3161 XSetFont(xDisplay, countGC, countFontID);
3163 if (appData.monoMode) {
3164 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3165 gc_values.background = XWhitePixel(xDisplay, xScreen);
3166 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3168 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3169 gc_values.background = XBlackPixel(xDisplay, xScreen);
3170 lightSquareGC = wbPieceGC
3171 = XtGetGC(shellWidget, value_mask, &gc_values);
3173 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3174 gc_values.background = XWhitePixel(xDisplay, xScreen);
3175 darkSquareGC = bwPieceGC
3176 = XtGetGC(shellWidget, value_mask, &gc_values);
3178 if (DefaultDepth(xDisplay, xScreen) == 1) {
3179 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3180 gc_values.function = GXcopyInverted;
3181 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3182 gc_values.function = GXcopy;
3183 if (XBlackPixel(xDisplay, xScreen) == 1) {
3184 bwPieceGC = darkSquareGC;
3185 wbPieceGC = copyInvertedGC;
3187 bwPieceGC = copyInvertedGC;
3188 wbPieceGC = lightSquareGC;
3192 gc_values.foreground = highlightSquareColor;
3193 gc_values.background = highlightSquareColor;
3194 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3196 gc_values.foreground = premoveHighlightColor;
3197 gc_values.background = premoveHighlightColor;
3198 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3200 gc_values.foreground = lightSquareColor;
3201 gc_values.background = darkSquareColor;
3202 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3204 gc_values.foreground = darkSquareColor;
3205 gc_values.background = lightSquareColor;
3206 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3208 gc_values.foreground = jailSquareColor;
3209 gc_values.background = jailSquareColor;
3210 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3212 gc_values.foreground = whitePieceColor;
3213 gc_values.background = darkSquareColor;
3214 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3216 gc_values.foreground = whitePieceColor;
3217 gc_values.background = lightSquareColor;
3218 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3220 gc_values.foreground = whitePieceColor;
3221 gc_values.background = jailSquareColor;
3222 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3224 gc_values.foreground = blackPieceColor;
3225 gc_values.background = darkSquareColor;
3226 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3228 gc_values.foreground = blackPieceColor;
3229 gc_values.background = lightSquareColor;
3230 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3232 gc_values.foreground = blackPieceColor;
3233 gc_values.background = jailSquareColor;
3234 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3238 void loadXIM(xim, xmask, filename, dest, mask)
3251 fp = fopen(filename, "rb");
3253 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3260 for (y=0; y<h; ++y) {
3261 for (x=0; x<h; ++x) {
3266 XPutPixel(xim, x, y, blackPieceColor);
3268 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3271 XPutPixel(xim, x, y, darkSquareColor);
3273 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3276 XPutPixel(xim, x, y, whitePieceColor);
3278 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3281 XPutPixel(xim, x, y, lightSquareColor);
3283 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3291 /* create Pixmap of piece */
3292 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3294 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3297 /* create Pixmap of clipmask
3298 Note: We assume the white/black pieces have the same
3299 outline, so we make only 6 masks. This is okay
3300 since the XPM clipmask routines do the same. */
3302 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3304 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3307 /* now create the 1-bit version */
3308 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3311 values.foreground = 1;
3312 values.background = 0;
3314 /* Don't use XtGetGC, not read only */
3315 maskGC = XCreateGC(xDisplay, *mask,
3316 GCForeground | GCBackground, &values);
3317 XCopyPlane(xDisplay, temp, *mask, maskGC,
3318 0, 0, squareSize, squareSize, 0, 0, 1);
3319 XFreePixmap(xDisplay, temp);
3324 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3326 void CreateXIMPieces()
3331 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3336 /* The XSynchronize calls were copied from CreatePieces.
3337 Not sure if needed, but can't hurt */
3338 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3341 /* temp needed by loadXIM() */
3342 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3343 0, 0, ss, ss, AllPlanes, XYPixmap);
3345 if (strlen(appData.pixmapDirectory) == 0) {
3349 if (appData.monoMode) {
3350 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3354 fprintf(stderr, _("\nLoading XIMs...\n"));
3356 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3357 fprintf(stderr, "%d", piece+1);
3358 for (kind=0; kind<4; kind++) {
3359 fprintf(stderr, ".");
3360 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3361 ExpandPathName(appData.pixmapDirectory),
3362 piece <= (int) WhiteKing ? "" : "w",
3363 pieceBitmapNames[piece],
3365 ximPieceBitmap[kind][piece] =
3366 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3367 0, 0, ss, ss, AllPlanes, XYPixmap);
3368 if (appData.debugMode)
3369 fprintf(stderr, _("(File:%s:) "), buf);
3370 loadXIM(ximPieceBitmap[kind][piece],
3372 &(xpmPieceBitmap2[kind][piece]),
3373 &(ximMaskPm2[piece]));
3374 if(piece <= (int)WhiteKing)
3375 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3377 fprintf(stderr," ");
3379 /* Load light and dark squares */
3380 /* If the LSQ and DSQ pieces don't exist, we will
3381 draw them with solid squares. */
3382 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3383 if (access(buf, 0) != 0) {
3387 fprintf(stderr, _("light square "));
3389 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3390 0, 0, ss, ss, AllPlanes, XYPixmap);
3391 if (appData.debugMode)
3392 fprintf(stderr, _("(File:%s:) "), buf);
3394 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3395 fprintf(stderr, _("dark square "));
3396 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3397 ExpandPathName(appData.pixmapDirectory), ss);
3398 if (appData.debugMode)
3399 fprintf(stderr, _("(File:%s:) "), buf);
3401 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3402 0, 0, ss, ss, AllPlanes, XYPixmap);
3403 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3404 xpmJailSquare = xpmLightSquare;
3406 fprintf(stderr, _("Done.\n"));
3408 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3412 void CreateXPMBoard(char *s, int kind)
3416 if(s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3417 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3418 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3422 void FreeXPMPieces()
3423 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3424 // thisroutine has to be called t free the old piece pixmaps
3426 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3427 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3429 XFreePixmap(xDisplay, xpmLightSquare);
3430 XFreePixmap(xDisplay, xpmDarkSquare);
3434 void CreateXPMPieces()
3438 u_int ss = squareSize;
3440 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3441 XpmColorSymbol symbols[4];
3442 static int redo = False;
3444 if(redo) FreeXPMPieces(); else redo = 1;
3446 /* The XSynchronize calls were copied from CreatePieces.
3447 Not sure if needed, but can't hurt */
3448 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3450 /* Setup translations so piece colors match square colors */
3451 symbols[0].name = "light_piece";
3452 symbols[0].value = appData.whitePieceColor;
3453 symbols[1].name = "dark_piece";
3454 symbols[1].value = appData.blackPieceColor;
3455 symbols[2].name = "light_square";
3456 symbols[2].value = appData.lightSquareColor;
3457 symbols[3].name = "dark_square";
3458 symbols[3].value = appData.darkSquareColor;
3460 attr.valuemask = XpmColorSymbols;
3461 attr.colorsymbols = symbols;
3462 attr.numsymbols = 4;
3464 if (appData.monoMode) {
3465 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3469 if (strlen(appData.pixmapDirectory) == 0) {
3470 XpmPieces* pieces = builtInXpms;
3473 while (pieces->size != squareSize && pieces->size) pieces++;
3474 if (!pieces->size) {
3475 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3478 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3479 for (kind=0; kind<4; kind++) {
3481 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3482 pieces->xpm[piece][kind],
3483 &(xpmPieceBitmap2[kind][piece]),
3484 NULL, &attr)) != 0) {
3485 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3489 if(piece <= (int) WhiteKing)
3490 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3494 xpmJailSquare = xpmLightSquare;
3498 fprintf(stderr, _("\nLoading XPMs...\n"));
3501 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3502 fprintf(stderr, "%d ", piece+1);
3503 for (kind=0; kind<4; kind++) {
3504 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3505 ExpandPathName(appData.pixmapDirectory),
3506 piece > (int) WhiteKing ? "w" : "",
3507 pieceBitmapNames[piece],
3509 if (appData.debugMode) {
3510 fprintf(stderr, _("(File:%s:) "), buf);
3512 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3513 &(xpmPieceBitmap2[kind][piece]),
3514 NULL, &attr)) != 0) {
3515 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3516 // [HGM] missing: read of unorthodox piece failed; substitute King.
3517 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3518 ExpandPathName(appData.pixmapDirectory),
3520 if (appData.debugMode) {
3521 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3523 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3524 &(xpmPieceBitmap2[kind][piece]),
3528 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3533 if(piece <= (int) WhiteKing)
3534 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3537 /* Load light and dark squares */
3538 /* If the LSQ and DSQ pieces don't exist, we will
3539 draw them with solid squares. */
3540 fprintf(stderr, _("light square "));
3541 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3542 if (access(buf, 0) != 0) {
3546 if (appData.debugMode)
3547 fprintf(stderr, _("(File:%s:) "), buf);
3549 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3550 &xpmLightSquare, NULL, &attr)) != 0) {
3551 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3554 fprintf(stderr, _("dark square "));
3555 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3556 ExpandPathName(appData.pixmapDirectory), ss);
3557 if (appData.debugMode) {
3558 fprintf(stderr, _("(File:%s:) "), buf);
3560 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3561 &xpmDarkSquare, NULL, &attr)) != 0) {
3562 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3566 xpmJailSquare = xpmLightSquare;
3567 fprintf(stderr, _("Done.\n"));
3569 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3572 #endif /* HAVE_LIBXPM */
3575 /* No built-in bitmaps */
3580 u_int ss = squareSize;
3582 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3585 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3586 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3587 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3588 pieceBitmapNames[piece],
3589 ss, kind == SOLID ? 's' : 'o');
3590 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3591 if(piece <= (int)WhiteKing)
3592 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3596 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3600 /* With built-in bitmaps */
3603 BuiltInBits* bib = builtInBits;
3606 u_int ss = squareSize;
3608 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3611 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3613 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3614 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3615 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3616 pieceBitmapNames[piece],
3617 ss, kind == SOLID ? 's' : 'o');
3618 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3619 bib->bits[kind][piece], ss, ss);
3620 if(piece <= (int)WhiteKing)
3621 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3625 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3630 void ReadBitmap(pm, name, bits, wreq, hreq)
3633 unsigned char bits[];
3639 char msg[MSG_SIZ], fullname[MSG_SIZ];
3641 if (*appData.bitmapDirectory != NULLCHAR) {
3642 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3643 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3644 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3645 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3646 &w, &h, pm, &x_hot, &y_hot);
3647 fprintf(stderr, "load %s\n", name);
3648 if (errcode != BitmapSuccess) {
3650 case BitmapOpenFailed:
3651 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3653 case BitmapFileInvalid:
3654 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3656 case BitmapNoMemory:
3657 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3661 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3665 fprintf(stderr, _("%s: %s...using built-in\n"),
3667 } else if (w != wreq || h != hreq) {
3669 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3670 programName, fullname, w, h, wreq, hreq);
3676 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3685 if (lineGap == 0) return;
3687 /* [HR] Split this into 2 loops for non-square boards. */
3689 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3690 gridSegments[i].x1 = 0;
3691 gridSegments[i].x2 =
3692 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3693 gridSegments[i].y1 = gridSegments[i].y2
3694 = lineGap / 2 + (i * (squareSize + lineGap));
3697 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3698 gridSegments[j + i].y1 = 0;
3699 gridSegments[j + i].y2 =
3700 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3701 gridSegments[j + i].x1 = gridSegments[j + i].x2
3702 = lineGap / 2 + (j * (squareSize + lineGap));
3706 static void MenuBarSelect(w, addr, index)
3711 XtActionProc proc = (XtActionProc) addr;
3713 (proc)(NULL, NULL, NULL, NULL);
3716 void CreateMenuBarPopup(parent, name, mb)
3726 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3729 XtSetArg(args[j], XtNleftMargin, 20); j++;
3730 XtSetArg(args[j], XtNrightMargin, 20); j++;
3732 while (mi->string != NULL) {
3733 if (strcmp(mi->string, "----") == 0) {
3734 entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
3737 XtSetArg(args[j], XtNlabel, XtNewString(mi->string));
3738 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3740 XtAddCallback(entry, XtNcallback,
3741 (XtCallbackProc) MenuBarSelect,
3742 (caddr_t) mi->proc);
3748 Widget CreateMenuBar(mb)
3752 Widget anchor, menuBar;
3754 char menuName[MSG_SIZ];
3757 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3758 XtSetArg(args[j], XtNvSpace, 0); j++;
3759 XtSetArg(args[j], XtNborderWidth, 0); j++;
3760 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3761 formWidget, args, j);
3763 while (mb->name != NULL) {
3764 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3765 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3767 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3770 shortName[0] = mb->name[0];
3771 shortName[1] = NULLCHAR;
3772 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3775 XtSetArg(args[j], XtNlabel, XtNewString(mb->name)); j++;
3778 XtSetArg(args[j], XtNborderWidth, 0); j++;
3779 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3781 CreateMenuBarPopup(menuBar, menuName, mb);
3787 Widget CreateButtonBar(mi)
3791 Widget button, buttonBar;
3795 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3797 XtSetArg(args[j], XtNhSpace, 0); j++;
3799 XtSetArg(args[j], XtNborderWidth, 0); j++;
3800 XtSetArg(args[j], XtNvSpace, 0); j++;
3801 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3802 formWidget, args, j);
3804 while (mi->string != NULL) {
3807 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3808 XtSetArg(args[j], XtNborderWidth, 0); j++;
3810 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3811 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3812 buttonBar, args, j);
3813 XtAddCallback(button, XtNcallback,
3814 (XtCallbackProc) MenuBarSelect,
3815 (caddr_t) mi->proc);
3822 CreatePieceMenu(name, color)
3829 ChessSquare selection;
3831 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3832 boardWidget, args, 0);
3834 for (i = 0; i < PIECE_MENU_SIZE; i++) {
3835 String item = pieceMenuStrings[color][i];
3837 if (strcmp(item, "----") == 0) {
3838 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3841 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3842 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3844 selection = pieceMenuTranslation[color][i];
3845 XtAddCallback(entry, XtNcallback,
3846 (XtCallbackProc) PieceMenuSelect,
3847 (caddr_t) selection);
3848 if (selection == WhitePawn || selection == BlackPawn) {
3849 XtSetArg(args[0], XtNpopupOnEntry, entry);
3850 XtSetValues(menu, args, 1);
3863 ChessSquare selection;
3865 whitePieceMenu = CreatePieceMenu("menuW", 0);
3866 blackPieceMenu = CreatePieceMenu("menuB", 1);
3868 XtRegisterGrabAction(PieceMenuPopup, True,
3869 (unsigned)(ButtonPressMask|ButtonReleaseMask),
3870 GrabModeAsync, GrabModeAsync);
3872 XtSetArg(args[0], XtNlabel, _("Drop"));
3873 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3874 boardWidget, args, 1);
3875 for (i = 0; i < DROP_MENU_SIZE; i++) {
3876 String item = dropMenuStrings[i];
3878 if (strcmp(item, "----") == 0) {
3879 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3882 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3883 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3885 selection = dropMenuTranslation[i];
3886 XtAddCallback(entry, XtNcallback,
3887 (XtCallbackProc) DropMenuSelect,
3888 (caddr_t) selection);
3893 void SetupDropMenu()
3901 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3902 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3903 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3904 dmEnables[i].piece);
3905 XtSetSensitive(entry, p != NULL || !appData.testLegality
3906 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3907 && !appData.icsActive));
3909 while (p && *p++ == dmEnables[i].piece) count++;
3910 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3912 XtSetArg(args[j], XtNlabel, label); j++;
3913 XtSetValues(entry, args, j);
3917 void PieceMenuPopup(w, event, params, num_params)
3921 Cardinal *num_params;
3923 String whichMenu; int menuNr;
3924 if (event->type == ButtonRelease)
3925 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3926 else if (event->type == ButtonPress)
3927 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3929 case 0: whichMenu = params[0]; break;
3930 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3932 case -1: if (errorUp) ErrorPopDown();
3935 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3938 static void PieceMenuSelect(w, piece, junk)
3943 if (pmFromX < 0 || pmFromY < 0) return;
3944 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3947 static void DropMenuSelect(w, piece, junk)
3952 if (pmFromX < 0 || pmFromY < 0) return;
3953 DropMenuEvent(piece, pmFromX, pmFromY);
3956 void WhiteClock(w, event, prms, nprms)
3965 void BlackClock(w, event, prms, nprms)
3976 * If the user selects on a border boundary, return -1; if off the board,
3977 * return -2. Otherwise map the event coordinate to the square.
3979 int EventToSquare(x, limit)
3987 if ((x % (squareSize + lineGap)) >= squareSize)
3989 x /= (squareSize + lineGap);
3995 static void do_flash_delay(msec)
4001 static void drawHighlight(file, rank, gc)
4007 if (lineGap == 0) return;
4010 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4011 (squareSize + lineGap);
4012 y = lineGap/2 + rank * (squareSize + lineGap);
4014 x = lineGap/2 + file * (squareSize + lineGap);
4015 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4016 (squareSize + lineGap);
4019 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4020 squareSize+lineGap, squareSize+lineGap);
4023 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4024 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4027 SetHighlights(fromX, fromY, toX, toY)
4028 int fromX, fromY, toX, toY;
4030 if (hi1X != fromX || hi1Y != fromY) {
4031 if (hi1X >= 0 && hi1Y >= 0) {
4032 drawHighlight(hi1X, hi1Y, lineGC);
4034 } // [HGM] first erase both, then draw new!
4035 if (hi2X != toX || hi2Y != toY) {
4036 if (hi2X >= 0 && hi2Y >= 0) {
4037 drawHighlight(hi2X, hi2Y, lineGC);
4040 if (hi1X != fromX || hi1Y != fromY) {
4041 if (fromX >= 0 && fromY >= 0) {
4042 drawHighlight(fromX, fromY, highlineGC);
4045 if (hi2X != toX || hi2Y != toY) {
4046 if (toX >= 0 && toY >= 0) {
4047 drawHighlight(toX, toY, highlineGC);
4059 SetHighlights(-1, -1, -1, -1);
4064 SetPremoveHighlights(fromX, fromY, toX, toY)
4065 int fromX, fromY, toX, toY;
4067 if (pm1X != fromX || pm1Y != fromY) {
4068 if (pm1X >= 0 && pm1Y >= 0) {
4069 drawHighlight(pm1X, pm1Y, lineGC);
4071 if (fromX >= 0 && fromY >= 0) {
4072 drawHighlight(fromX, fromY, prelineGC);
4075 if (pm2X != toX || pm2Y != toY) {
4076 if (pm2X >= 0 && pm2Y >= 0) {
4077 drawHighlight(pm2X, pm2Y, lineGC);
4079 if (toX >= 0 && toY >= 0) {
4080 drawHighlight(toX, toY, prelineGC);
4090 ClearPremoveHighlights()
4092 SetPremoveHighlights(-1, -1, -1, -1);
4095 static int CutOutSquare(x, y, x0, y0, kind)
4096 int x, y, *x0, *y0, kind;
4098 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4099 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4101 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4102 if(textureW[kind] < W*squareSize)
4103 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4105 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4106 if(textureH[kind] < H*squareSize)
4107 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4109 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4113 static void BlankSquare(x, y, color, piece, dest, fac)
4114 int x, y, color, fac;
4117 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4119 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4120 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4121 squareSize, squareSize, x*fac, y*fac);
4123 if (useImages && useImageSqs) {
4127 pm = xpmLightSquare;
4132 case 2: /* neutral */
4137 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4138 squareSize, squareSize, x*fac, y*fac);
4148 case 2: /* neutral */
4153 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4158 I split out the routines to draw a piece so that I could
4159 make a generic flash routine.
4161 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4163 int square_color, x, y;
4166 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4167 switch (square_color) {
4169 case 2: /* neutral */
4171 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4172 ? *pieceToOutline(piece)
4173 : *pieceToSolid(piece),
4174 dest, bwPieceGC, 0, 0,
4175 squareSize, squareSize, x, y);
4178 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4179 ? *pieceToSolid(piece)
4180 : *pieceToOutline(piece),
4181 dest, wbPieceGC, 0, 0,
4182 squareSize, squareSize, x, y);
4187 static void monoDrawPiece(piece, square_color, x, y, dest)
4189 int square_color, x, y;
4192 switch (square_color) {
4194 case 2: /* neutral */
4196 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4197 ? *pieceToOutline(piece)
4198 : *pieceToSolid(piece),
4199 dest, bwPieceGC, 0, 0,
4200 squareSize, squareSize, x, y, 1);
4203 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4204 ? *pieceToSolid(piece)
4205 : *pieceToOutline(piece),
4206 dest, wbPieceGC, 0, 0,
4207 squareSize, squareSize, x, y, 1);
4212 static void colorDrawPiece(piece, square_color, x, y, dest)
4214 int square_color, x, y;
4217 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4218 switch (square_color) {
4220 XCopyPlane(xDisplay, *pieceToSolid(piece),
4221 dest, (int) piece < (int) BlackPawn
4222 ? wlPieceGC : blPieceGC, 0, 0,
4223 squareSize, squareSize, x, y, 1);
4226 XCopyPlane(xDisplay, *pieceToSolid(piece),
4227 dest, (int) piece < (int) BlackPawn
4228 ? wdPieceGC : bdPieceGC, 0, 0,
4229 squareSize, squareSize, x, y, 1);
4231 case 2: /* neutral */
4233 XCopyPlane(xDisplay, *pieceToSolid(piece),
4234 dest, (int) piece < (int) BlackPawn
4235 ? wjPieceGC : bjPieceGC, 0, 0,
4236 squareSize, squareSize, x, y, 1);
4241 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4243 int square_color, x, y;
4246 int kind, p = piece;
4248 switch (square_color) {
4250 case 2: /* neutral */
4252 if ((int)piece < (int) BlackPawn) {
4260 if ((int)piece < (int) BlackPawn) {
4268 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4269 if(useTexture & square_color+1) {
4270 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4271 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4272 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4273 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4274 XSetClipMask(xDisplay, wlPieceGC, None);
4275 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4277 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4278 dest, wlPieceGC, 0, 0,
4279 squareSize, squareSize, x, y);
4282 typedef void (*DrawFunc)();
4284 DrawFunc ChooseDrawFunc()
4286 if (appData.monoMode) {
4287 if (DefaultDepth(xDisplay, xScreen) == 1) {
4288 return monoDrawPiece_1bit;
4290 return monoDrawPiece;
4294 return colorDrawPieceImage;
4296 return colorDrawPiece;
4300 /* [HR] determine square color depending on chess variant. */
4301 static int SquareColor(row, column)
4306 if (gameInfo.variant == VariantXiangqi) {
4307 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4309 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4311 } else if (row <= 4) {
4317 square_color = ((column + row) % 2) == 1;
4320 /* [hgm] holdings: next line makes all holdings squares light */
4321 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4323 return square_color;
4326 void DrawSquare(row, column, piece, do_flash)
4327 int row, column, do_flash;
4330 int square_color, x, y, direction, font_ascent, font_descent;
4333 XCharStruct overall;
4337 /* Calculate delay in milliseconds (2-delays per complete flash) */
4338 flash_delay = 500 / appData.flashRate;
4341 x = lineGap + ((BOARD_WIDTH-1)-column) *
4342 (squareSize + lineGap);
4343 y = lineGap + row * (squareSize + lineGap);
4345 x = lineGap + column * (squareSize + lineGap);
4346 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4347 (squareSize + lineGap);
4350 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4352 square_color = SquareColor(row, column);
4354 if ( // [HGM] holdings: blank out area between board and holdings
4355 column == BOARD_LEFT-1 || column == BOARD_RGHT
4356 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4357 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4358 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4360 // [HGM] print piece counts next to holdings
4361 string[1] = NULLCHAR;
4362 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4363 string[0] = '0' + piece;
4364 XTextExtents(countFontStruct, string, 1, &direction,
4365 &font_ascent, &font_descent, &overall);
4366 if (appData.monoMode) {
4367 XDrawImageString(xDisplay, xBoardWindow, countGC,
4368 x + squareSize - overall.width - 2,
4369 y + font_ascent + 1, string, 1);
4371 XDrawString(xDisplay, xBoardWindow, countGC,
4372 x + squareSize - overall.width - 2,
4373 y + font_ascent + 1, string, 1);
4376 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4377 string[0] = '0' + piece;
4378 XTextExtents(countFontStruct, string, 1, &direction,
4379 &font_ascent, &font_descent, &overall);
4380 if (appData.monoMode) {
4381 XDrawImageString(xDisplay, xBoardWindow, countGC,
4382 x + 2, y + font_ascent + 1, string, 1);
4384 XDrawString(xDisplay, xBoardWindow, countGC,
4385 x + 2, y + font_ascent + 1, string, 1);
4389 if (piece == EmptySquare || appData.blindfold) {
4390 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4392 drawfunc = ChooseDrawFunc();
4393 if (do_flash && appData.flashCount > 0) {
4394 for (i=0; i<appData.flashCount; ++i) {
4396 drawfunc(piece, square_color, x, y, xBoardWindow);
4397 XSync(xDisplay, False);
4398 do_flash_delay(flash_delay);
4400 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4401 XSync(xDisplay, False);
4402 do_flash_delay(flash_delay);
4405 drawfunc(piece, square_color, x, y, xBoardWindow);
4409 string[1] = NULLCHAR;
4410 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4411 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4412 string[0] = 'a' + column - BOARD_LEFT;
4413 XTextExtents(coordFontStruct, string, 1, &direction,
4414 &font_ascent, &font_descent, &overall);
4415 if (appData.monoMode) {
4416 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4417 x + squareSize - overall.width - 2,
4418 y + squareSize - font_descent - 1, string, 1);
4420 XDrawString(xDisplay, xBoardWindow, coordGC,
4421 x + squareSize - overall.width - 2,
4422 y + squareSize - font_descent - 1, string, 1);
4425 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4426 string[0] = ONE + row;
4427 XTextExtents(coordFontStruct, string, 1, &direction,
4428 &font_ascent, &font_descent, &overall);
4429 if (appData.monoMode) {
4430 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4431 x + 2, y + font_ascent + 1, string, 1);
4433 XDrawString(xDisplay, xBoardWindow, coordGC,
4434 x + 2, y + font_ascent + 1, string, 1);
4437 if(!partnerUp && marker[row][column]) {
4438 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4439 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4444 /* Why is this needed on some versions of X? */
4445 void EventProc(widget, unused, event)
4450 if (!XtIsRealized(widget))
4453 switch (event->type) {
4455 if (event->xexpose.count > 0) return; /* no clipping is done */
4456 XDrawPosition(widget, True, NULL);
4457 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4458 flipView = !flipView; partnerUp = !partnerUp;
4459 XDrawPosition(widget, True, NULL);
4460 flipView = !flipView; partnerUp = !partnerUp;
4464 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4471 void DrawPosition(fullRedraw, board)
4472 /*Boolean*/int fullRedraw;
4475 XDrawPosition(boardWidget, fullRedraw, board);
4478 /* Returns 1 if there are "too many" differences between b1 and b2
4479 (i.e. more than 1 move was made) */
4480 static int too_many_diffs(b1, b2)
4486 for (i=0; i<BOARD_HEIGHT; ++i) {
4487 for (j=0; j<BOARD_WIDTH; ++j) {
4488 if (b1[i][j] != b2[i][j]) {
4489 if (++c > 4) /* Castling causes 4 diffs */
4498 /* Matrix describing castling maneuvers */
4499 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4500 static int castling_matrix[4][5] = {
4501 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4502 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4503 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4504 { 7, 7, 4, 5, 6 } /* 0-0, black */
4507 /* Checks whether castling occurred. If it did, *rrow and *rcol
4508 are set to the destination (row,col) of the rook that moved.
4510 Returns 1 if castling occurred, 0 if not.
4512 Note: Only handles a max of 1 castling move, so be sure
4513 to call too_many_diffs() first.
4515 static int check_castle_draw(newb, oldb, rrow, rcol)
4522 /* For each type of castling... */
4523 for (i=0; i<4; ++i) {
4524 r = castling_matrix[i];
4526 /* Check the 4 squares involved in the castling move */
4528 for (j=1; j<=4; ++j) {
4529 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4536 /* All 4 changed, so it must be a castling move */
4545 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4546 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4548 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4551 void DrawSeekBackground( int left, int top, int right, int bottom )
4553 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4556 void DrawSeekText(char *buf, int x, int y)
4558 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4561 void DrawSeekDot(int x, int y, int colorNr)
4563 int square = colorNr & 0x80;
4566 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4568 XFillRectangle(xDisplay, xBoardWindow, color,
4569 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4571 XFillArc(xDisplay, xBoardWindow, color,
4572 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4575 static int damage[2][BOARD_RANKS][BOARD_FILES];
4578 * event handler for redrawing the board
4580 void XDrawPosition(w, repaint, board)
4582 /*Boolean*/int repaint;
4586 static int lastFlipView = 0;
4587 static int lastBoardValid[2] = {0, 0};
4588 static Board lastBoard[2];
4591 int nr = twoBoards*partnerUp;
4593 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4595 if (board == NULL) {
4596 if (!lastBoardValid[nr]) return;
4597 board = lastBoard[nr];
4599 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4600 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4601 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4606 * It would be simpler to clear the window with XClearWindow()
4607 * but this causes a very distracting flicker.
4610 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4612 if ( lineGap && IsDrawArrowEnabled())
4613 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4614 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4616 /* If too much changes (begin observing new game, etc.), don't
4618 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4620 /* Special check for castling so we don't flash both the king
4621 and the rook (just flash the king). */
4623 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4624 /* Draw rook with NO flashing. King will be drawn flashing later */
4625 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4626 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4630 /* First pass -- Draw (newly) empty squares and repair damage.
4631 This prevents you from having a piece show up twice while it
4632 is flashing on its new square */
4633 for (i = 0; i < BOARD_HEIGHT; i++)
4634 for (j = 0; j < BOARD_WIDTH; j++)
4635 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4636 || damage[nr][i][j]) {
4637 DrawSquare(i, j, board[i][j], 0);
4638 damage[nr][i][j] = False;
4641 /* Second pass -- Draw piece(s) in new position and flash them */
4642 for (i = 0; i < BOARD_HEIGHT; i++)
4643 for (j = 0; j < BOARD_WIDTH; j++)
4644 if (board[i][j] != lastBoard[nr][i][j]) {
4645 DrawSquare(i, j, board[i][j], do_flash);
4649 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4650 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4651 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4653 for (i = 0; i < BOARD_HEIGHT; i++)
4654 for (j = 0; j < BOARD_WIDTH; j++) {
4655 DrawSquare(i, j, board[i][j], 0);
4656 damage[nr][i][j] = False;
4660 CopyBoard(lastBoard[nr], board);
4661 lastBoardValid[nr] = 1;
4662 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4663 lastFlipView = flipView;
4665 /* Draw highlights */
4666 if (pm1X >= 0 && pm1Y >= 0) {
4667 drawHighlight(pm1X, pm1Y, prelineGC);
4669 if (pm2X >= 0 && pm2Y >= 0) {
4670 drawHighlight(pm2X, pm2Y, prelineGC);
4672 if (hi1X >= 0 && hi1Y >= 0) {
4673 drawHighlight(hi1X, hi1Y, highlineGC);
4675 if (hi2X >= 0 && hi2Y >= 0) {
4676 drawHighlight(hi2X, hi2Y, highlineGC);
4678 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4680 /* If piece being dragged around board, must redraw that too */
4683 XSync(xDisplay, False);
4688 * event handler for redrawing the board
4690 void DrawPositionProc(w, event, prms, nprms)
4696 XDrawPosition(w, True, NULL);
4701 * event handler for parsing user moves
4703 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4704 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4705 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4706 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4707 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4708 // and at the end FinishMove() to perform the move after optional promotion popups.
4709 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4710 void HandleUserMove(w, event, prms, nprms)
4716 if (w != boardWidget || errorExitStatus != -1) return;
4717 if(nprms) shiftKey = !strcmp(prms[0], "1");
4720 if (event->type == ButtonPress) {
4721 XtPopdown(promotionShell);
4722 XtDestroyWidget(promotionShell);
4723 promotionUp = False;
4731 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4732 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4733 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4736 void AnimateUserMove (Widget w, XEvent * event,
4737 String * params, Cardinal * nParams)
4739 DragPieceMove(event->xmotion.x, event->xmotion.y);
4742 void HandlePV (Widget w, XEvent * event,
4743 String * params, Cardinal * nParams)
4744 { // [HGM] pv: walk PV
4745 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4748 Widget CommentCreate(name, text, mutable, callback, lines)
4750 int /*Boolean*/ mutable;
4751 XtCallbackProc callback;
4755 Widget shell, layout, form, edit, b_ok, b_cancel, b_clear, b_close, b_edit;
4760 XtSetArg(args[j], XtNwidth, &bw_width); j++;
4761 XtGetValues(boardWidget, args, j);
4764 XtSetArg(args[j], XtNresizable, True); j++;
4767 XtCreatePopupShell(name, topLevelShellWidgetClass,
4768 shellWidget, args, j);
4771 XtCreatePopupShell(name, transientShellWidgetClass,
4772 shellWidget, args, j);
4775 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
4776 layoutArgs, XtNumber(layoutArgs));
4778 XtCreateManagedWidget("form", formWidgetClass, layout,
4779 formArgs, XtNumber(formArgs));
4783 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
4784 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
4786 XtSetArg(args[j], XtNstring, text); j++;
4787 XtSetArg(args[j], XtNtop, XtChainTop); j++;
4788 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4789 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4790 XtSetArg(args[j], XtNright, XtChainRight); j++;
4791 XtSetArg(args[j], XtNresizable, True); j++;
4792 XtSetArg(args[j], XtNwidth, bw_width); j++; /*force wider than buttons*/
4793 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
4794 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
4795 XtSetArg(args[j], XtNautoFill, True); j++;
4796 XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
4798 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
4799 XtOverrideTranslations(edit, XtParseTranslationTable(commentTranslations));
4803 XtSetArg(args[j], XtNfromVert, edit); j++;
4804 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4805 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4806 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4807 XtSetArg(args[j], XtNright, XtChainLeft); j++;
4809 XtCreateManagedWidget(_("ok"), commandWidgetClass, form, args, j);
4810 XtAddCallback(b_ok, XtNcallback, callback, (XtPointer) 0);
4813 XtSetArg(args[j], XtNfromVert, edit); j++;
4814 XtSetArg(args[j], XtNfromHoriz, b_ok); j++;
4815 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4816 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4817 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4818 XtSetArg(args[j], XtNright, XtChainLeft); j++;
4820 XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
4821 XtAddCallback(b_cancel, XtNcallback, callback, (XtPointer) 0);
4824 XtSetArg(args[j], XtNfromVert, edit); j++;
4825 XtSetArg(args[j], XtNfromHoriz, b_cancel); j++;
4826 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4827 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4828 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4829 XtSetArg(args[j], XtNright, XtChainLeft); j++;
4831 XtCreateManagedWidget(_("clear"), commandWidgetClass, form, args, j);
4832 XtAddCallback(b_clear, XtNcallback, callback, (XtPointer) 0);
4835 XtSetArg(args[j], XtNfromVert, edit); j++;
4836 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4837 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4838 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4839 XtSetArg(args[j], XtNright, XtChainLeft); j++;
4841 XtCreateManagedWidget(_("close"), commandWidgetClass, form, args, j);
4842 XtAddCallback(b_close, XtNcallback, callback, (XtPointer) 0);
4845 XtSetArg(args[j], XtNfromVert, edit); j++;
4846 XtSetArg(args[j], XtNfromHoriz, b_close); j++;
4847 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4848 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4849 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4850 XtSetArg(args[j], XtNright, XtChainLeft); j++;
4852 XtCreateManagedWidget(_("edit"), commandWidgetClass, form, args, j);
4853 XtAddCallback(b_edit, XtNcallback, callback, (XtPointer) 0);
4856 XtRealizeWidget(shell);
4858 if (commentX == -1) {
4861 Dimension pw_height;
4862 Dimension ew_height;
4865 XtSetArg(args[j], XtNheight, &ew_height); j++;
4866 XtGetValues(edit, args, j);
4869 XtSetArg(args[j], XtNheight, &pw_height); j++;
4870 XtGetValues(shell, args, j);
4871 commentH = pw_height + (lines - 1) * ew_height;
4872 commentW = bw_width - 16;
4874 XSync(xDisplay, False);
4876 /* This code seems to tickle an X bug if it is executed too soon
4877 after xboard starts up. The coordinates get transformed as if
4878 the main window was positioned at (0, 0).
4880 XtTranslateCoords(shellWidget,
4881 (bw_width - commentW) / 2, 0 - commentH / 2,
4882 &commentX, &commentY);
4884 XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
4885 RootWindowOfScreen(XtScreen(shellWidget)),
4886 (bw_width - commentW) / 2, 0 - commentH / 2,
4891 if (commentY < 0) commentY = 0; /*avoid positioning top offscreen*/
4894 if(wpComment.width > 0) {
4895 commentX = wpComment.x;
4896 commentY = wpComment.y;
4897 commentW = wpComment.width;
4898 commentH = wpComment.height;
4902 XtSetArg(args[j], XtNheight, commentH); j++;
4903 XtSetArg(args[j], XtNwidth, commentW); j++;
4904 XtSetArg(args[j], XtNx, commentX); j++;
4905 XtSetArg(args[j], XtNy, commentY); j++;
4906 XtSetValues(shell, args, j);
4907 XtSetKeyboardFocus(shell, edit);
4912 /* Used for analysis window and ICS input window */
4913 Widget MiscCreate(name, text, mutable, callback, lines)
4915 int /*Boolean*/ mutable;
4916 XtCallbackProc callback;
4920 Widget shell, layout, form, edit;
4922 Dimension bw_width, pw_height, ew_height, w, h;
4928 XtSetArg(args[j], XtNresizable, True); j++;
4931 XtCreatePopupShell(name, topLevelShellWidgetClass,
4932 shellWidget, args, j);
4935 XtCreatePopupShell(name, transientShellWidgetClass,
4936 shellWidget, args, j);
4939 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
4940 layoutArgs, XtNumber(layoutArgs));
4942 XtCreateManagedWidget("form", formWidgetClass, layout,
4943 formArgs, XtNumber(formArgs));
4947 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
4948 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
4950 XtSetArg(args[j], XtNstring, text); j++;
4951 XtSetArg(args[j], XtNtop, XtChainTop); j++;
4952 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4953 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4954 XtSetArg(args[j], XtNright, XtChainRight); j++;
4955 XtSetArg(args[j], XtNresizable, True); j++;
4956 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
4957 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
4958 XtSetArg(args[j], XtNautoFill, True); j++;
4959 XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
4961 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
4963 XtRealizeWidget(shell);
4966 XtSetArg(args[j], XtNwidth, &bw_width); j++;
4967 XtGetValues(boardWidget, args, j);
4970 XtSetArg(args[j], XtNheight, &ew_height); j++;
4971 XtGetValues(edit, args, j);
4974 XtSetArg(args[j], XtNheight, &pw_height); j++;
4975 XtGetValues(shell, args, j);
4976 h = pw_height + (lines - 1) * ew_height;
4979 XSync(xDisplay, False);
4981 /* This code seems to tickle an X bug if it is executed too soon
4982 after xboard starts up. The coordinates get transformed as if
4983 the main window was positioned at (0, 0).
4985 XtTranslateCoords(shellWidget, (bw_width - w) / 2, 0 - h / 2, &x, &y);
4987 XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
4988 RootWindowOfScreen(XtScreen(shellWidget)),
4989 (bw_width - w) / 2, 0 - h / 2, &xx, &yy, &junk);
4993 if (y < 0) y = 0; /*avoid positioning top offscreen*/
4996 XtSetArg(args[j], XtNheight, h); j++;
4997 XtSetArg(args[j], XtNwidth, w); j++;
4998 XtSetArg(args[j], XtNx, x); j++;
4999 XtSetArg(args[j], XtNy, y); j++;
5000 XtSetValues(shell, args, j);
5006 static int savedIndex; /* gross that this is global */
5008 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
5011 XawTextPosition index, dummy;
5014 XawTextGetSelectionPos(w, &index, &dummy);
5015 XtSetArg(arg, XtNstring, &val);
5016 XtGetValues(w, &arg, 1);
5017 ReplaceComment(savedIndex, val);
5018 if(savedIndex != currentMove) ToNrEvent(savedIndex);
5019 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
5022 void EditCommentPopUp(index, title, text)
5031 if (text == NULL) text = "";
5033 if (editShell == NULL) {
5035 CommentCreate(title, text, True, EditCommentCallback, 4);
5036 XtRealizeWidget(editShell);
5037 CatchDeleteWindow(editShell, "EditCommentPopDown");
5039 edit = XtNameToWidget(editShell, "*form.text");
5041 XtSetArg(args[j], XtNstring, text); j++;
5042 XtSetValues(edit, args, j);
5044 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
5045 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
5046 XtSetValues(editShell, args, j);
5049 XtPopup(editShell, XtGrabNone);
5053 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
5054 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"),
5056 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"),
5060 void EditCommentCallback(w, client_data, call_data)
5062 XtPointer client_data, call_data;
5070 XtSetArg(args[j], XtNlabel, &name); j++;
5071 XtGetValues(w, args, j);
5073 if (strcmp(name, _("ok")) == 0) {
5074 edit = XtNameToWidget(editShell, "*form.text");
5076 XtSetArg(args[j], XtNstring, &val); j++;
5077 XtGetValues(edit, args, j);
5078 ReplaceComment(savedIndex, val);
5079 EditCommentPopDown();
5080 } else if (strcmp(name, _("cancel")) == 0) {
5081 EditCommentPopDown();
5082 } else if (strcmp(name, _("clear")) == 0) {
5083 edit = XtNameToWidget(editShell, "*form.text");
5084 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5085 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5089 void EditCommentPopDown()
5094 if (!editUp) return;
5096 XtSetArg(args[j], XtNx, &commentX); j++;
5097 XtSetArg(args[j], XtNy, &commentY); j++;
5098 XtSetArg(args[j], XtNheight, &commentH); j++;
5099 XtSetArg(args[j], XtNwidth, &commentW); j++;
5100 XtGetValues(editShell, args, j);
5101 XtPopdown(editShell);
5104 XtSetArg(args[j], XtNleftBitmap, None); j++;
5105 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"),
5107 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"),
5111 void ICSInputBoxPopUp()
5116 char *title = _("ICS Input");
5119 if (ICSInputShell == NULL) {
5120 ICSInputShell = MiscCreate(title, "", True, NULL, 1);
5121 tr = XtParseTranslationTable(ICSInputTranslations);
5122 edit = XtNameToWidget(ICSInputShell, "*form.text");
5123 XtOverrideTranslations(edit, tr);
5124 XtRealizeWidget(ICSInputShell);
5125 CatchDeleteWindow(ICSInputShell, "ICSInputBoxPopDown");
5128 edit = XtNameToWidget(ICSInputShell, "*form.text");
5130 XtSetArg(args[j], XtNstring, ""); j++;
5131 XtSetValues(edit, args, j);
5133 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
5134 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
5135 XtSetValues(ICSInputShell, args, j);
5138 XtPopup(ICSInputShell, XtGrabNone);
5139 XtSetKeyboardFocus(ICSInputShell, edit);
5141 ICSInputBoxUp = True;
5143 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
5144 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.ICS Input Box"),
5148 void ICSInputSendText()
5155 edit = XtNameToWidget(ICSInputShell, "*form.text");
5157 XtSetArg(args[j], XtNstring, &val); j++;
5158 XtGetValues(edit, args, j);
5160 SendMultiLineToICS(val);
5161 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5162 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5165 void ICSInputBoxPopDown()
5170 if (!ICSInputBoxUp) return;
5172 XtPopdown(ICSInputShell);
5173 ICSInputBoxUp = False;
5175 XtSetArg(args[j], XtNleftBitmap, None); j++;
5176 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.ICS Input Box"),
5180 void CommentPopUp(title, text)
5187 savedIndex = currentMove; // [HGM] vari
5188 if (commentShell == NULL) {
5190 CommentCreate(title, text, False, CommentCallback, 4);
5191 XtRealizeWidget(commentShell);
5192 CatchDeleteWindow(commentShell, "CommentPopDown");
5194 edit = XtNameToWidget(commentShell, "*form.text");
5196 XtSetArg(args[j], XtNstring, text); j++;
5197 XtSetValues(edit, args, j);
5199 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
5200 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
5201 XtSetValues(commentShell, args, j);
5204 XtPopup(commentShell, XtGrabNone);
5205 XSync(xDisplay, False);
5210 void CommentCallback(w, client_data, call_data)
5212 XtPointer client_data, call_data;
5219 XtSetArg(args[j], XtNlabel, &name); j++;
5220 XtGetValues(w, args, j);
5222 if (strcmp(name, _("close")) == 0) {
5224 } else if (strcmp(name, _("edit")) == 0) {
5231 void CommentPopDown()
5236 if (!commentUp) return;
5238 XtSetArg(args[j], XtNx, &commentX); j++;
5239 XtSetArg(args[j], XtNy, &commentY); j++;
5240 XtSetArg(args[j], XtNwidth, &commentW); j++;
5241 XtSetArg(args[j], XtNheight, &commentH); j++;
5242 XtGetValues(commentShell, args, j);
5243 XtPopdown(commentShell);
5244 XSync(xDisplay, False);
5248 void FileNamePopUp(label, def, proc, openMode)
5254 fileProc = proc; /* I can't see a way not */
5255 fileOpenMode = openMode; /* to use globals here */
5256 { // [HGM] use file-selector dialog stolen from Ghostview
5258 int index; // this is not supported yet
5260 if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
5261 def, openMode, NULL, &name))
5262 (void) (*fileProc)(f, index=0, name);
5266 void FileNamePopDown()
5268 if (!filenameUp) return;
5269 XtPopdown(fileNameShell);
5270 XtDestroyWidget(fileNameShell);
5275 void FileNameCallback(w, client_data, call_data)
5277 XtPointer client_data, call_data;
5282 XtSetArg(args[0], XtNlabel, &name);
5283 XtGetValues(w, args, 1);
5285 if (strcmp(name, _("cancel")) == 0) {
5290 FileNameAction(w, NULL, NULL, NULL);
5293 void FileNameAction(w, event, prms, nprms)
5305 name = XawDialogGetValueString(w = XtParent(w));
5307 if ((name != NULL) && (*name != NULLCHAR)) {
5308 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5309 XtPopdown(w = XtParent(XtParent(w)));
5313 p = strrchr(buf, ' ');
5320 fullname = ExpandPathName(buf);
5322 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5325 f = fopen(fullname, fileOpenMode);
5327 DisplayError(_("Failed to open file"), errno);
5329 (void) (*fileProc)(f, index, buf);
5336 XtPopdown(w = XtParent(XtParent(w)));
5342 void PromotionPopUp()
5345 Widget dialog, layout;
5347 Dimension bw_width, pw_width;
5351 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5352 XtGetValues(boardWidget, args, j);
5355 XtSetArg(args[j], XtNresizable, True); j++;
5356 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5358 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5359 shellWidget, args, j);
5361 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5362 layoutArgs, XtNumber(layoutArgs));
5365 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5366 XtSetArg(args[j], XtNborderWidth, 0); j++;
5367 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5370 if(gameInfo.variant != VariantShogi) {
5371 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5372 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback,
5373 (XtPointer) dialog);
5374 XawDialogAddButton(dialog, _("General"), PromotionCallback,
5375 (XtPointer) dialog);
5376 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback,
5377 (XtPointer) dialog);
5378 XawDialogAddButton(dialog, _("Captain"), PromotionCallback,
5379 (XtPointer) dialog);
5381 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
5382 (XtPointer) dialog);
5383 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
5384 (XtPointer) dialog);
5385 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
5386 (XtPointer) dialog);
5387 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
5388 (XtPointer) dialog);
5390 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5391 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
5392 gameInfo.variant == VariantGiveaway) {
5393 XawDialogAddButton(dialog, _("King"), PromotionCallback,
5394 (XtPointer) dialog);
5396 if(gameInfo.variant == VariantCapablanca ||
5397 gameInfo.variant == VariantGothic ||
5398 gameInfo.variant == VariantCapaRandom) {
5399 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5400 (XtPointer) dialog);
5401 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5402 (XtPointer) dialog);
5404 } else // [HGM] shogi
5406 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5407 (XtPointer) dialog);
5408 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5409 (XtPointer) dialog);
5411 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5412 (XtPointer) dialog);
5414 XtRealizeWidget(promotionShell);
5415 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5418 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5419 XtGetValues(promotionShell, args, j);
5421 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5422 lineGap + squareSize/3 +
5423 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5424 0 : 6*(squareSize + lineGap)), &x, &y);
5427 XtSetArg(args[j], XtNx, x); j++;
5428 XtSetArg(args[j], XtNy, y); j++;
5429 XtSetValues(promotionShell, args, j);
5431 XtPopup(promotionShell, XtGrabNone);
5436 void PromotionPopDown()
5438 if (!promotionUp) return;
5439 XtPopdown(promotionShell);
5440 XtDestroyWidget(promotionShell);
5441 promotionUp = False;
5444 void PromotionCallback(w, client_data, call_data)
5446 XtPointer client_data, call_data;
5452 XtSetArg(args[0], XtNlabel, &name);
5453 XtGetValues(w, args, 1);
5457 if (fromX == -1) return;
5459 if (strcmp(name, _("cancel")) == 0) {
5463 } else if (strcmp(name, _("Knight")) == 0) {
5465 } else if (strcmp(name, _("Promote")) == 0) {
5467 } else if (strcmp(name, _("Defer")) == 0) {
5470 promoChar = ToLower(name[0]);
5473 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5475 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5476 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5481 void ErrorCallback(w, client_data, call_data)
5483 XtPointer client_data, call_data;
5486 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5488 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5494 if (!errorUp) return;
5496 XtPopdown(errorShell);
5497 XtDestroyWidget(errorShell);
5498 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5501 void ErrorPopUp(title, label, modal)
5502 char *title, *label;
5506 Widget dialog, layout;
5510 Dimension bw_width, pw_width;
5511 Dimension pw_height;
5515 XtSetArg(args[i], XtNresizable, True); i++;
5516 XtSetArg(args[i], XtNtitle, title); i++;
5518 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5519 shellWidget, args, i);
5521 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5522 layoutArgs, XtNumber(layoutArgs));
5525 XtSetArg(args[i], XtNlabel, label); i++;
5526 XtSetArg(args[i], XtNborderWidth, 0); i++;
5527 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5530 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5532 XtRealizeWidget(errorShell);
5533 CatchDeleteWindow(errorShell, "ErrorPopDown");
5536 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5537 XtGetValues(boardWidget, args, i);
5539 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5540 XtSetArg(args[i], XtNheight, &pw_height); i++;
5541 XtGetValues(errorShell, args, i);
5544 /* This code seems to tickle an X bug if it is executed too soon
5545 after xboard starts up. The coordinates get transformed as if
5546 the main window was positioned at (0, 0).
5548 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5549 0 - pw_height + squareSize / 3, &x, &y);
5551 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5552 RootWindowOfScreen(XtScreen(boardWidget)),
5553 (bw_width - pw_width) / 2,
5554 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5558 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5561 XtSetArg(args[i], XtNx, x); i++;
5562 XtSetArg(args[i], XtNy, y); i++;
5563 XtSetValues(errorShell, args, i);
5566 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5569 /* Disable all user input other than deleting the window */
5570 static int frozen = 0;
5574 /* Grab by a widget that doesn't accept input */
5575 XtAddGrab(messageWidget, TRUE, FALSE);
5579 /* Undo a FreezeUI */
5582 if (!frozen) return;
5583 XtRemoveGrab(messageWidget);
5587 char *ModeToWidgetName(mode)
5591 case BeginningOfGame:
5592 if (appData.icsActive)
5593 return "menuMode.ICS Client";
5594 else if (appData.noChessProgram ||
5595 *appData.cmailGameName != NULLCHAR)
5596 return "menuMode.Edit Game";
5598 return "menuMode.Machine Black";
5599 case MachinePlaysBlack:
5600 return "menuMode.Machine Black";
5601 case MachinePlaysWhite:
5602 return "menuMode.Machine White";
5604 return "menuMode.Analysis Mode";
5606 return "menuMode.Analyze File";
5607 case TwoMachinesPlay:
5608 return "menuMode.Two Machines";
5610 return "menuMode.Edit Game";
5611 case PlayFromGameFile:
5612 return "menuFile.Load Game";
5614 return "menuMode.Edit Position";
5616 return "menuMode.Training";
5617 case IcsPlayingWhite:
5618 case IcsPlayingBlack:
5622 return "menuMode.ICS Client";
5629 void ModeHighlight()
5632 static int oldPausing = FALSE;
5633 static GameMode oldmode = (GameMode) -1;
5636 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5638 if (pausing != oldPausing) {
5639 oldPausing = pausing;
5641 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5643 XtSetArg(args[0], XtNleftBitmap, None);
5645 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5648 if (appData.showButtonBar) {
5649 /* Always toggle, don't set. Previous code messes up when
5650 invoked while the button is pressed, as releasing it
5651 toggles the state again. */
5654 XtSetArg(args[0], XtNbackground, &oldbg);
5655 XtSetArg(args[1], XtNforeground, &oldfg);
5656 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5658 XtSetArg(args[0], XtNbackground, oldfg);
5659 XtSetArg(args[1], XtNforeground, oldbg);
5661 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5665 wname = ModeToWidgetName(oldmode);
5666 if (wname != NULL) {
5667 XtSetArg(args[0], XtNleftBitmap, None);
5668 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5670 wname = ModeToWidgetName(gameMode);
5671 if (wname != NULL) {
5672 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5673 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5677 /* Maybe all the enables should be handled here, not just this one */
5678 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5679 gameMode == Training || gameMode == PlayFromGameFile);
5684 * Button/menu procedures
5686 void ResetProc(w, event, prms, nprms)
5695 int LoadGamePopUp(f, gameNumber, title)
5700 cmailMsgLoaded = FALSE;
5701 if (gameNumber == 0) {
5702 int error = GameListBuild(f);
5704 DisplayError(_("Cannot build game list"), error);
5705 } else if (!ListEmpty(&gameList) &&
5706 ((ListGame *) gameList.tailPred)->number > 1) {
5707 GameListPopUp(f, title);
5713 return LoadGame(f, gameNumber, title, FALSE);
5716 void LoadGameProc(w, event, prms, nprms)
5722 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5725 FileNamePopUp(_("Load game file name?"), "", LoadGamePopUp, "rb");
5728 void LoadNextGameProc(w, event, prms, nprms)
5737 void LoadPrevGameProc(w, event, prms, nprms)
5746 void ReloadGameProc(w, event, prms, nprms)
5755 void LoadNextPositionProc(w, event, prms, nprms)
5764 void LoadPrevPositionProc(w, event, prms, nprms)
5773 void ReloadPositionProc(w, event, prms, nprms)
5782 void LoadPositionProc(w, event, prms, nprms)
5788 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5791 FileNamePopUp(_("Load position file name?"), "", LoadPosition, "rb");
5794 void SaveGameProc(w, event, prms, nprms)
5800 FileNamePopUp(_("Save game file name?"),
5801 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5805 void SavePositionProc(w, event, prms, nprms)
5811 FileNamePopUp(_("Save position file name?"),
5812 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5816 void ReloadCmailMsgProc(w, event, prms, nprms)
5822 ReloadCmailMsgEvent(FALSE);
5825 void MailMoveProc(w, event, prms, nprms)
5834 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5835 char *selected_fen_position=NULL;
5838 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5839 Atom *type_return, XtPointer *value_return,
5840 unsigned long *length_return, int *format_return)
5842 char *selection_tmp;
5844 if (!selected_fen_position) return False; /* should never happen */
5845 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5846 /* note: since no XtSelectionDoneProc was registered, Xt will
5847 * automatically call XtFree on the value returned. So have to
5848 * make a copy of it allocated with XtMalloc */
5849 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5850 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5852 *value_return=selection_tmp;
5853 *length_return=strlen(selection_tmp);
5854 *type_return=*target;
5855 *format_return = 8; /* bits per byte */
5857 } else if (*target == XA_TARGETS(xDisplay)) {
5858 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5859 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5860 targets_tmp[1] = XA_STRING;
5861 *value_return = targets_tmp;
5862 *type_return = XA_ATOM;
5864 *format_return = 8 * sizeof(Atom);
5865 if (*format_return > 32) {
5866 *length_return *= *format_return / 32;
5867 *format_return = 32;
5875 /* note: when called from menu all parameters are NULL, so no clue what the
5876 * Widget which was clicked on was, or what the click event was
5878 void CopyPositionProc(w, event, prms, nprms)
5885 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5886 * have a notion of a position that is selected but not copied.
5887 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5889 if(gameMode == EditPosition) EditPositionDone(TRUE);
5890 if (selected_fen_position) free(selected_fen_position);
5891 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5892 if (!selected_fen_position) return;
5893 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5895 SendPositionSelection,
5896 NULL/* lose_ownership_proc */ ,
5897 NULL/* transfer_done_proc */);
5898 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5900 SendPositionSelection,
5901 NULL/* lose_ownership_proc */ ,
5902 NULL/* transfer_done_proc */);
5905 /* function called when the data to Paste is ready */
5907 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5908 Atom *type, XtPointer value, unsigned long *len, int *format)
5911 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5912 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5913 EditPositionPasteFEN(fenstr);
5917 /* called when Paste Position button is pressed,
5918 * all parameters will be NULL */
5919 void PastePositionProc(w, event, prms, nprms)
5925 XtGetSelectionValue(menuBarWidget,
5926 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5927 /* (XtSelectionCallbackProc) */ PastePositionCB,
5928 NULL, /* client_data passed to PastePositionCB */
5930 /* better to use the time field from the event that triggered the
5931 * call to this function, but that isn't trivial to get
5939 SendGameSelection(Widget w, Atom *selection, Atom *target,
5940 Atom *type_return, XtPointer *value_return,
5941 unsigned long *length_return, int *format_return)
5943 char *selection_tmp;
5945 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5946 FILE* f = fopen(gameCopyFilename, "r");
5949 if (f == NULL) return False;
5953 selection_tmp = XtMalloc(len + 1);
5954 count = fread(selection_tmp, 1, len, f);
5957 XtFree(selection_tmp);
5960 selection_tmp[len] = NULLCHAR;
5961 *value_return = selection_tmp;
5962 *length_return = len;
5963 *type_return = *target;
5964 *format_return = 8; /* bits per byte */
5966 } else if (*target == XA_TARGETS(xDisplay)) {
5967 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5968 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5969 targets_tmp[1] = XA_STRING;
5970 *value_return = targets_tmp;
5971 *type_return = XA_ATOM;
5973 *format_return = 8 * sizeof(Atom);
5974 if (*format_return > 32) {
5975 *length_return *= *format_return / 32;
5976 *format_return = 32;
5984 /* note: when called from menu all parameters are NULL, so no clue what the
5985 * Widget which was clicked on was, or what the click event was
5987 void CopyGameProc(w, event, prms, nprms)
5995 ret = SaveGameToFile(gameCopyFilename, FALSE);
5999 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
6000 * have a notion of a game that is selected but not copied.
6001 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
6003 XtOwnSelection(menuBarWidget, XA_PRIMARY,
6006 NULL/* lose_ownership_proc */ ,
6007 NULL/* transfer_done_proc */);
6008 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
6011 NULL/* lose_ownership_proc */ ,
6012 NULL/* transfer_done_proc */);
6015 /* function called when the data to Paste is ready */
6017 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
6018 Atom *type, XtPointer value, unsigned long *len, int *format)
6021 if (value == NULL || *len == 0) {
6022 return; /* nothing had been selected to copy */
6024 f = fopen(gamePasteFilename, "w");
6026 DisplayError(_("Can't open temp file"), errno);
6029 fwrite(value, 1, *len, f);
6032 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
6035 /* called when Paste Game button is pressed,
6036 * all parameters will be NULL */
6037 void PasteGameProc(w, event, prms, nprms)
6043 XtGetSelectionValue(menuBarWidget,
6044 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
6045 /* (XtSelectionCallbackProc) */ PasteGameCB,
6046 NULL, /* client_data passed to PasteGameCB */
6048 /* better to use the time field from the event that triggered the
6049 * call to this function, but that isn't trivial to get
6059 SaveGameProc(NULL, NULL, NULL, NULL);
6063 void QuitProc(w, event, prms, nprms)
6072 void PauseProc(w, event, prms, nprms)
6082 void MachineBlackProc(w, event, prms, nprms)
6088 MachineBlackEvent();
6091 void MachineWhiteProc(w, event, prms, nprms)
6097 MachineWhiteEvent();
6100 void AnalyzeModeProc(w, event, prms, nprms)
6108 if (!first.analysisSupport) {
6109 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
6110 DisplayError(buf, 0);
6113 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
6114 if (appData.icsActive) {
6115 if (gameMode != IcsObserving) {
6116 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
6117 DisplayError(buf, 0);
6119 if (appData.icsEngineAnalyze) {
6120 if (appData.debugMode)
6121 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
6127 /* if enable, use want disable icsEngineAnalyze */
6128 if (appData.icsEngineAnalyze) {
6133 appData.icsEngineAnalyze = TRUE;
6134 if (appData.debugMode)
6135 fprintf(debugFP, _("ICS engine analyze starting... \n"));
6137 if (!appData.showThinking)
6138 ShowThinkingProc(w,event,prms,nprms);
6143 void AnalyzeFileProc(w, event, prms, nprms)
6149 if (!first.analysisSupport) {
6151 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
6152 DisplayError(buf, 0);
6157 if (!appData.showThinking)
6158 ShowThinkingProc(w,event,prms,nprms);
6161 FileNamePopUp(_("File to analyze"), "", LoadGamePopUp, "rb");
6162 AnalysisPeriodicEvent(1);
6165 void TwoMachinesProc(w, event, prms, nprms)
6174 void IcsClientProc(w, event, prms, nprms)
6183 void EditGameProc(w, event, prms, nprms)
6192 void EditPositionProc(w, event, prms, nprms)
6198 EditPositionEvent();
6201 void TrainingProc(w, event, prms, nprms)
6210 void EditCommentProc(w, event, prms, nprms)
6217 EditCommentPopDown();
6223 void IcsInputBoxProc(w, event, prms, nprms)
6229 if (ICSInputBoxUp) {
6230 ICSInputBoxPopDown();
6236 void AcceptProc(w, event, prms, nprms)
6245 void DeclineProc(w, event, prms, nprms)
6254 void RematchProc(w, event, prms, nprms)
6263 void CallFlagProc(w, event, prms, nprms)
6272 void DrawProc(w, event, prms, nprms)
6281 void AbortProc(w, event, prms, nprms)
6290 void AdjournProc(w, event, prms, nprms)
6299 void ResignProc(w, event, prms, nprms)
6308 void AdjuWhiteProc(w, event, prms, nprms)
6314 UserAdjudicationEvent(+1);
6317 void AdjuBlackProc(w, event, prms, nprms)
6323 UserAdjudicationEvent(-1);
6326 void AdjuDrawProc(w, event, prms, nprms)
6332 UserAdjudicationEvent(0);
6335 void EnterKeyProc(w, event, prms, nprms)
6341 if (ICSInputBoxUp == True)
6345 void UpKeyProc(w, event, prms, nprms)
6350 { // [HGM] input: let up-arrow recall previous line from history
6357 if (!ICSInputBoxUp) return;
6358 edit = XtNameToWidget(ICSInputShell, "*form.text");
6360 XtSetArg(args[j], XtNstring, &val); j++;
6361 XtGetValues(edit, args, j);
6362 val = PrevInHistory(val);
6363 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6364 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6366 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6367 XawTextReplace(edit, 0, 0, &t);
6368 XawTextSetInsertionPoint(edit, 9999);
6372 void DownKeyProc(w, event, prms, nprms)
6377 { // [HGM] input: let down-arrow recall next line from history
6382 if (!ICSInputBoxUp) return;
6383 edit = XtNameToWidget(ICSInputShell, "*form.text");
6384 val = NextInHistory();
6385 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6386 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6388 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6389 XawTextReplace(edit, 0, 0, &t);
6390 XawTextSetInsertionPoint(edit, 9999);
6394 void StopObservingProc(w, event, prms, nprms)
6400 StopObservingEvent();
6403 void StopExaminingProc(w, event, prms, nprms)
6409 StopExaminingEvent();
6412 void UploadProc(w, event, prms, nprms)
6422 void ForwardProc(w, event, prms, nprms)
6432 void BackwardProc(w, event, prms, nprms)
6441 void ToStartProc(w, event, prms, nprms)
6450 void ToEndProc(w, event, prms, nprms)
6459 void RevertProc(w, event, prms, nprms)
6468 void AnnotateProc(w, event, prms, nprms)
6477 void TruncateGameProc(w, event, prms, nprms)
6483 TruncateGameEvent();
6485 void RetractMoveProc(w, event, prms, nprms)
6494 void MoveNowProc(w, event, prms, nprms)
6504 void AlwaysQueenProc(w, event, prms, nprms)
6512 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6514 if (appData.alwaysPromoteToQueen) {
6515 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6517 XtSetArg(args[0], XtNleftBitmap, None);
6519 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6523 void AnimateDraggingProc(w, event, prms, nprms)
6531 appData.animateDragging = !appData.animateDragging;
6533 if (appData.animateDragging) {
6534 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6537 XtSetArg(args[0], XtNleftBitmap, None);
6539 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6543 void AnimateMovingProc(w, event, prms, nprms)
6551 appData.animate = !appData.animate;
6553 if (appData.animate) {
6554 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6557 XtSetArg(args[0], XtNleftBitmap, None);
6559 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6563 void AutoflagProc(w, event, prms, nprms)
6571 appData.autoCallFlag = !appData.autoCallFlag;
6573 if (appData.autoCallFlag) {
6574 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6576 XtSetArg(args[0], XtNleftBitmap, None);
6578 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6582 void AutoflipProc(w, event, prms, nprms)
6590 appData.autoFlipView = !appData.autoFlipView;
6592 if (appData.autoFlipView) {
6593 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6595 XtSetArg(args[0], XtNleftBitmap, None);
6597 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6601 void BlindfoldProc(w, event, prms, nprms)
6609 appData.blindfold = !appData.blindfold;
6611 if (appData.blindfold) {
6612 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6614 XtSetArg(args[0], XtNleftBitmap, None);
6616 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6619 DrawPosition(True, NULL);
6622 void TestLegalityProc(w, event, prms, nprms)
6630 appData.testLegality = !appData.testLegality;
6632 if (appData.testLegality) {
6633 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6635 XtSetArg(args[0], XtNleftBitmap, None);
6637 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6642 void FlashMovesProc(w, event, prms, nprms)
6650 if (appData.flashCount == 0) {
6651 appData.flashCount = 3;
6653 appData.flashCount = -appData.flashCount;
6656 if (appData.flashCount > 0) {
6657 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6659 XtSetArg(args[0], XtNleftBitmap, None);
6661 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6665 void FlipViewProc(w, event, prms, nprms)
6671 flipView = !flipView;
6672 DrawPosition(True, NULL);
6676 void HighlightDraggingProc(w, event, prms, nprms)
6684 appData.highlightDragging = !appData.highlightDragging;
6686 if (appData.highlightDragging) {
6687 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6689 XtSetArg(args[0], XtNleftBitmap, None);
6691 XtSetValues(XtNameToWidget(menuBarWidget,
6692 "menuOptions.Highlight Dragging"), args, 1);
6696 void HighlightLastMoveProc(w, event, prms, nprms)
6704 appData.highlightLastMove = !appData.highlightLastMove;
6706 if (appData.highlightLastMove) {
6707 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6709 XtSetArg(args[0], XtNleftBitmap, None);
6711 XtSetValues(XtNameToWidget(menuBarWidget,
6712 "menuOptions.Highlight Last Move"), args, 1);
6715 void HighlightArrowProc(w, event, prms, nprms)
6723 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6725 if (appData.highlightMoveWithArrow) {
6726 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6728 XtSetArg(args[0], XtNleftBitmap, None);
6730 XtSetValues(XtNameToWidget(menuBarWidget,
6731 "menuOptions.Arrow"), args, 1);
6735 void IcsAlarmProc(w, event, prms, nprms)
6743 appData.icsAlarm = !appData.icsAlarm;
6745 if (appData.icsAlarm) {
6746 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6748 XtSetArg(args[0], XtNleftBitmap, None);
6750 XtSetValues(XtNameToWidget(menuBarWidget,
6751 "menuOptions.ICS Alarm"), args, 1);
6755 void MoveSoundProc(w, event, prms, nprms)
6763 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6765 if (appData.ringBellAfterMoves) {
6766 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6768 XtSetArg(args[0], XtNleftBitmap, None);
6770 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6774 void OneClickProc(w, event, prms, nprms)
6782 appData.oneClick = !appData.oneClick;
6784 if (appData.oneClick) {
6785 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6787 XtSetArg(args[0], XtNleftBitmap, None);
6789 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6793 void PeriodicUpdatesProc(w, event, prms, nprms)
6801 PeriodicUpdatesEvent(!appData.periodicUpdates);
6803 if (appData.periodicUpdates) {
6804 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6806 XtSetArg(args[0], XtNleftBitmap, None);
6808 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6812 void PonderNextMoveProc(w, event, prms, nprms)
6820 PonderNextMoveEvent(!appData.ponderNextMove);
6822 if (appData.ponderNextMove) {
6823 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6825 XtSetArg(args[0], XtNleftBitmap, None);
6827 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6831 void PopupExitMessageProc(w, event, prms, nprms)
6839 appData.popupExitMessage = !appData.popupExitMessage;
6841 if (appData.popupExitMessage) {
6842 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6844 XtSetArg(args[0], XtNleftBitmap, None);
6846 XtSetValues(XtNameToWidget(menuBarWidget,
6847 "menuOptions.Popup Exit Message"), args, 1);
6850 void PopupMoveErrorsProc(w, event, prms, nprms)
6858 appData.popupMoveErrors = !appData.popupMoveErrors;
6860 if (appData.popupMoveErrors) {
6861 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6863 XtSetArg(args[0], XtNleftBitmap, None);
6865 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6870 void PremoveProc(w, event, prms, nprms)
6878 appData.premove = !appData.premove;
6880 if (appData.premove) {
6881 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6883 XtSetArg(args[0], XtNleftBitmap, None);
6885 XtSetValues(XtNameToWidget(menuBarWidget,
6886 "menuOptions.Premove"), args, 1);
6890 void ShowCoordsProc(w, event, prms, nprms)
6898 appData.showCoords = !appData.showCoords;
6900 if (appData.showCoords) {
6901 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6903 XtSetArg(args[0], XtNleftBitmap, None);
6905 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6908 DrawPosition(True, NULL);
6911 void ShowThinkingProc(w, event, prms, nprms)
6917 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6918 ShowThinkingEvent();
6921 void HideThinkingProc(w, event, prms, nprms)
6929 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6930 ShowThinkingEvent();
6932 if (appData.hideThinkingFromHuman) {
6933 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6935 XtSetArg(args[0], XtNleftBitmap, None);
6937 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6941 void SaveOnExitProc(w, event, prms, nprms)
6949 saveSettingsOnExit = !saveSettingsOnExit;
6951 if (saveSettingsOnExit) {
6952 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6954 XtSetArg(args[0], XtNleftBitmap, None);
6956 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6960 void SaveSettingsProc(w, event, prms, nprms)
6966 SaveSettings(settingsFileName);
6969 void InfoProc(w, event, prms, nprms)
6976 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6981 void ManProc(w, event, prms, nprms)
6989 if (nprms && *nprms > 0)
6993 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6997 void HintProc(w, event, prms, nprms)
7006 void BookProc(w, event, prms, nprms)
7015 void AboutProc(w, event, prms, nprms)
7023 char *zippy = " (with Zippy code)";
7027 snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
7028 programVersion, zippy,
7029 "Copyright 1991 Digital Equipment Corporation",
7030 "Enhancements Copyright 1992-2009 Free Software Foundation",
7031 "Enhancements Copyright 2005 Alessandro Scotti",
7032 PACKAGE, " is free software and carries NO WARRANTY;",
7033 "see the file COPYING for more information.");
7034 ErrorPopUp(_("About XBoard"), buf, FALSE);
7037 void DebugProc(w, event, prms, nprms)
7043 appData.debugMode = !appData.debugMode;
7046 void AboutGameProc(w, event, prms, nprms)
7055 void NothingProc(w, event, prms, nprms)
7064 void Iconify(w, event, prms, nprms)
7073 XtSetArg(args[0], XtNiconic, True);
7074 XtSetValues(shellWidget, args, 1);
7077 void DisplayMessage(message, extMessage)
7078 char *message, *extMessage;
7080 /* display a message in the message widget */
7089 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
7094 message = extMessage;
7098 /* need to test if messageWidget already exists, since this function
7099 can also be called during the startup, if for example a Xresource
7100 is not set up correctly */
7103 XtSetArg(arg, XtNlabel, message);
7104 XtSetValues(messageWidget, &arg, 1);
7110 void DisplayTitle(text)
7115 char title[MSG_SIZ];
7118 if (text == NULL) text = "";
7120 if (appData.titleInWindow) {
7122 XtSetArg(args[i], XtNlabel, text); i++;
7123 XtSetValues(titleWidget, args, i);
7126 if (*text != NULLCHAR) {
7127 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
7128 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
7129 } else if (appData.icsActive) {
7130 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
7131 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
7132 } else if (appData.cmailGameName[0] != NULLCHAR) {
7133 snprintf(icon, sizeof(icon), "%s", "CMail");
7134 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
7136 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
7137 } else if (gameInfo.variant == VariantGothic) {
7138 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
7139 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
7142 } else if (gameInfo.variant == VariantFalcon) {
7143 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
7144 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
7146 } else if (appData.noChessProgram) {
7147 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
7148 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
7150 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
7151 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
7154 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
7155 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
7156 XtSetValues(shellWidget, args, i);
7161 DisplayError(message, error)
7168 if (appData.debugMode || appData.matchMode) {
7169 fprintf(stderr, "%s: %s\n", programName, message);
7172 if (appData.debugMode || appData.matchMode) {
7173 fprintf(stderr, "%s: %s: %s\n",
7174 programName, message, strerror(error));
7176 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7179 ErrorPopUp(_("Error"), message, FALSE);
7183 void DisplayMoveError(message)
7188 DrawPosition(FALSE, NULL);
7189 if (appData.debugMode || appData.matchMode) {
7190 fprintf(stderr, "%s: %s\n", programName, message);
7192 if (appData.popupMoveErrors) {
7193 ErrorPopUp(_("Error"), message, FALSE);
7195 DisplayMessage(message, "");
7200 void DisplayFatalError(message, error, status)
7206 errorExitStatus = status;
7208 fprintf(stderr, "%s: %s\n", programName, message);
7210 fprintf(stderr, "%s: %s: %s\n",
7211 programName, message, strerror(error));
7212 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7215 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
7216 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
7222 void DisplayInformation(message)
7226 ErrorPopUp(_("Information"), message, TRUE);
7229 void DisplayNote(message)
7233 ErrorPopUp(_("Note"), message, FALSE);
7237 NullXErrorCheck(dpy, error_event)
7239 XErrorEvent *error_event;
7244 void DisplayIcsInteractionTitle(message)
7247 if (oldICSInteractionTitle == NULL) {
7248 /* Magic to find the old window title, adapted from vim */
7249 char *wina = getenv("WINDOWID");
7251 Window win = (Window) atoi(wina);
7252 Window root, parent, *children;
7253 unsigned int nchildren;
7254 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
7256 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
7257 if (!XQueryTree(xDisplay, win, &root, &parent,
7258 &children, &nchildren)) break;
7259 if (children) XFree((void *)children);
7260 if (parent == root || parent == 0) break;
7263 XSetErrorHandler(oldHandler);
7265 if (oldICSInteractionTitle == NULL) {
7266 oldICSInteractionTitle = "xterm";
7269 printf("\033]0;%s\007", message);
7273 char pendingReplyPrefix[MSG_SIZ];
7274 ProcRef pendingReplyPR;
7276 void AskQuestionProc(w, event, prms, nprms)
7283 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7287 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7290 void AskQuestionPopDown()
7292 if (!askQuestionUp) return;
7293 XtPopdown(askQuestionShell);
7294 XtDestroyWidget(askQuestionShell);
7295 askQuestionUp = False;
7298 void AskQuestionReplyAction(w, event, prms, nprms)
7308 reply = XawDialogGetValueString(w = XtParent(w));
7309 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
7310 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
7311 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
7312 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
7313 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7314 AskQuestionPopDown();
7316 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7319 void AskQuestionCallback(w, client_data, call_data)
7321 XtPointer client_data, call_data;
7326 XtSetArg(args[0], XtNlabel, &name);
7327 XtGetValues(w, args, 1);
7329 if (strcmp(name, _("cancel")) == 0) {
7330 AskQuestionPopDown();
7332 AskQuestionReplyAction(w, NULL, NULL, NULL);
7336 void AskQuestion(title, question, replyPrefix, pr)
7337 char *title, *question, *replyPrefix;
7341 Widget popup, layout, dialog, edit;
7347 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7348 pendingReplyPR = pr;
7351 XtSetArg(args[i], XtNresizable, True); i++;
7352 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7353 askQuestionShell = popup =
7354 XtCreatePopupShell(title, transientShellWidgetClass,
7355 shellWidget, args, i);
7358 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7359 layoutArgs, XtNumber(layoutArgs));
7362 XtSetArg(args[i], XtNlabel, question); i++;
7363 XtSetArg(args[i], XtNvalue, ""); i++;
7364 XtSetArg(args[i], XtNborderWidth, 0); i++;
7365 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7368 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7369 (XtPointer) dialog);
7370 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7371 (XtPointer) dialog);
7373 XtRealizeWidget(popup);
7374 CatchDeleteWindow(popup, "AskQuestionPopDown");
7376 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7377 &x, &y, &win_x, &win_y, &mask);
7379 XtSetArg(args[0], XtNx, x - 10);
7380 XtSetArg(args[1], XtNy, y - 30);
7381 XtSetValues(popup, args, 2);
7383 XtPopup(popup, XtGrabExclusive);
7384 askQuestionUp = True;
7386 edit = XtNameToWidget(dialog, "*value");
7387 XtSetKeyboardFocus(popup, edit);
7395 if (*name == NULLCHAR) {
7397 } else if (strcmp(name, "$") == 0) {
7398 putc(BELLCHAR, stderr);
7401 char *prefix = "", *sep = "";
7402 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7403 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7411 PlaySound(appData.soundMove);
7417 PlaySound(appData.soundIcsWin);
7423 PlaySound(appData.soundIcsLoss);
7429 PlaySound(appData.soundIcsDraw);
7433 PlayIcsUnfinishedSound()
7435 PlaySound(appData.soundIcsUnfinished);
7441 PlaySound(appData.soundIcsAlarm);
7447 system("stty echo");
7453 system("stty -echo");
7457 Colorize(cc, continuation)
7462 int count, outCount, error;
7464 if (textColors[(int)cc].bg > 0) {
7465 if (textColors[(int)cc].fg > 0) {
7466 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7467 textColors[(int)cc].fg, textColors[(int)cc].bg);
7469 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7470 textColors[(int)cc].bg);
7473 if (textColors[(int)cc].fg > 0) {
7474 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7475 textColors[(int)cc].fg);
7477 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7480 count = strlen(buf);
7481 outCount = OutputToProcess(NoProc, buf, count, &error);
7482 if (outCount < count) {
7483 DisplayFatalError(_("Error writing to display"), error, 1);
7486 if (continuation) return;
7489 PlaySound(appData.soundShout);
7492 PlaySound(appData.soundSShout);
7495 PlaySound(appData.soundChannel1);
7498 PlaySound(appData.soundChannel);
7501 PlaySound(appData.soundKibitz);
7504 PlaySound(appData.soundTell);
7506 case ColorChallenge:
7507 PlaySound(appData.soundChallenge);
7510 PlaySound(appData.soundRequest);
7513 PlaySound(appData.soundSeek);
7524 return getpwuid(getuid())->pw_name;
7528 ExpandPathName(path)
7531 static char static_buf[4*MSG_SIZ];
7532 char *d, *s, buf[4*MSG_SIZ];
7538 while (*s && isspace(*s))
7547 if (*(s+1) == '/') {
7548 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7552 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7553 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7554 pwd = getpwnam(buf);
7557 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7561 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7562 strcat(d, strchr(s+1, '/'));
7566 safeStrCpy(d, s, 4*MSG_SIZ );
7573 static char host_name[MSG_SIZ];
7575 #if HAVE_GETHOSTNAME
7576 gethostname(host_name, MSG_SIZ);
7578 #else /* not HAVE_GETHOSTNAME */
7579 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7580 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7582 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7584 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7585 #endif /* not HAVE_GETHOSTNAME */
7588 XtIntervalId delayedEventTimerXID = 0;
7589 DelayedEventCallback delayedEventCallback = 0;
7594 delayedEventTimerXID = 0;
7595 delayedEventCallback();
7599 ScheduleDelayedEvent(cb, millisec)
7600 DelayedEventCallback cb; long millisec;
7602 if(delayedEventTimerXID && delayedEventCallback == cb)
7603 // [HGM] alive: replace, rather than add or flush identical event
7604 XtRemoveTimeOut(delayedEventTimerXID);
7605 delayedEventCallback = cb;
7606 delayedEventTimerXID =
7607 XtAppAddTimeOut(appContext, millisec,
7608 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7611 DelayedEventCallback
7614 if (delayedEventTimerXID) {
7615 return delayedEventCallback;
7622 CancelDelayedEvent()
7624 if (delayedEventTimerXID) {
7625 XtRemoveTimeOut(delayedEventTimerXID);
7626 delayedEventTimerXID = 0;
7630 XtIntervalId loadGameTimerXID = 0;
7632 int LoadGameTimerRunning()
7634 return loadGameTimerXID != 0;
7637 int StopLoadGameTimer()
7639 if (loadGameTimerXID != 0) {
7640 XtRemoveTimeOut(loadGameTimerXID);
7641 loadGameTimerXID = 0;
7649 LoadGameTimerCallback(arg, id)
7653 loadGameTimerXID = 0;
7658 StartLoadGameTimer(millisec)
7662 XtAppAddTimeOut(appContext, millisec,
7663 (XtTimerCallbackProc) LoadGameTimerCallback,
7667 XtIntervalId analysisClockXID = 0;
7670 AnalysisClockCallback(arg, id)
7674 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7675 || appData.icsEngineAnalyze) { // [DM]
7676 AnalysisPeriodicEvent(0);
7677 StartAnalysisClock();
7682 StartAnalysisClock()
7685 XtAppAddTimeOut(appContext, 2000,
7686 (XtTimerCallbackProc) AnalysisClockCallback,
7690 XtIntervalId clockTimerXID = 0;
7692 int ClockTimerRunning()
7694 return clockTimerXID != 0;
7697 int StopClockTimer()
7699 if (clockTimerXID != 0) {
7700 XtRemoveTimeOut(clockTimerXID);
7709 ClockTimerCallback(arg, id)
7718 StartClockTimer(millisec)
7722 XtAppAddTimeOut(appContext, millisec,
7723 (XtTimerCallbackProc) ClockTimerCallback,
7728 DisplayTimerLabel(w, color, timer, highlight)
7737 /* check for low time warning */
7738 Pixel foregroundOrWarningColor = timerForegroundPixel;
7741 appData.lowTimeWarning &&
7742 (timer / 1000) < appData.icsAlarmTime)
7743 foregroundOrWarningColor = lowTimeWarningColor;
7745 if (appData.clockMode) {
7746 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7747 XtSetArg(args[0], XtNlabel, buf);
7749 snprintf(buf, MSG_SIZ, "%s ", color);
7750 XtSetArg(args[0], XtNlabel, buf);
7755 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7756 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7758 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7759 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7762 XtSetValues(w, args, 3);
7766 DisplayWhiteClock(timeRemaining, highlight)
7772 if(appData.noGUI) return;
7773 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7774 if (highlight && iconPixmap == bIconPixmap) {
7775 iconPixmap = wIconPixmap;
7776 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7777 XtSetValues(shellWidget, args, 1);
7782 DisplayBlackClock(timeRemaining, highlight)
7788 if(appData.noGUI) return;
7789 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7790 if (highlight && iconPixmap == wIconPixmap) {
7791 iconPixmap = bIconPixmap;
7792 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7793 XtSetValues(shellWidget, args, 1);
7811 int StartChildProcess(cmdLine, dir, pr)
7818 int to_prog[2], from_prog[2];
7822 if (appData.debugMode) {
7823 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7826 /* We do NOT feed the cmdLine to the shell; we just
7827 parse it into blank-separated arguments in the
7828 most simple-minded way possible.
7831 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7834 while(*p == ' ') p++;
7836 if(*p == '"' || *p == '\'')
7837 p = strchr(++argv[i-1], *p);
7838 else p = strchr(p, ' ');
7839 if (p == NULL) break;
7844 SetUpChildIO(to_prog, from_prog);
7846 if ((pid = fork()) == 0) {
7848 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7849 close(to_prog[1]); // first close the unused pipe ends
7850 close(from_prog[0]);
7851 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7852 dup2(from_prog[1], 1);
7853 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7854 close(from_prog[1]); // and closing again loses one of the pipes!
7855 if(fileno(stderr) >= 2) // better safe than sorry...
7856 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7858 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7863 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7865 execvp(argv[0], argv);
7867 /* If we get here, exec failed */
7872 /* Parent process */
7874 close(from_prog[1]);
7876 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7879 cp->fdFrom = from_prog[0];
7880 cp->fdTo = to_prog[1];
7885 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7886 static RETSIGTYPE AlarmCallBack(int n)
7892 DestroyChildProcess(pr, signalType)
7896 ChildProc *cp = (ChildProc *) pr;
7898 if (cp->kind != CPReal) return;
7900 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7901 signal(SIGALRM, AlarmCallBack);
7903 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7904 kill(cp->pid, SIGKILL); // kill it forcefully
7905 wait((int *) 0); // and wait again
7909 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7911 /* Process is exiting either because of the kill or because of
7912 a quit command sent by the backend; either way, wait for it to die.
7921 InterruptChildProcess(pr)
7924 ChildProc *cp = (ChildProc *) pr;
7926 if (cp->kind != CPReal) return;
7927 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7930 int OpenTelnet(host, port, pr)
7935 char cmdLine[MSG_SIZ];
7937 if (port[0] == NULLCHAR) {
7938 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7940 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7942 return StartChildProcess(cmdLine, "", pr);
7945 int OpenTCP(host, port, pr)
7951 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7952 #else /* !OMIT_SOCKETS */
7954 struct sockaddr_in sa;
7956 unsigned short uport;
7959 if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
7963 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7964 sa.sin_family = AF_INET;
7965 sa.sin_addr.s_addr = INADDR_ANY;
7966 uport = (unsigned short) 0;
7967 sa.sin_port = htons(uport);
7968 if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
7972 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7973 if (!(hp = gethostbyname(host))) {
7975 if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
7976 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
7977 hp->h_addrtype = AF_INET;
7979 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
7980 hp->h_addr_list[0] = (char *) malloc(4);
7981 hp->h_addr_list[0][0] = b0;
7982 hp->h_addr_list[0][1] = b1;
7983 hp->h_addr_list[0][2] = b2;
7984 hp->h_addr_list[0][3] = b3;
7989 sa.sin_family = hp->h_addrtype;
7990 uport = (unsigned short) atoi(port);
7991 sa.sin_port = htons(uport);
7992 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
7994 if (connect(s, (struct sockaddr *) &sa,
7995 sizeof(struct sockaddr_in)) < 0) {
7999 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
8006 #endif /* !OMIT_SOCKETS */
8011 int OpenCommPort(name, pr)
8018 fd = open(name, 2, 0);
8019 if (fd < 0) return errno;
8021 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
8031 int OpenLoopback(pr)
8037 SetUpChildIO(to, from);
8039 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
8042 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
8049 int OpenRcmd(host, user, cmd, pr)
8050 char *host, *user, *cmd;
8053 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
8057 #define INPUT_SOURCE_BUF_SIZE 8192
8066 char buf[INPUT_SOURCE_BUF_SIZE];
8071 DoInputCallback(closure, source, xid)
8076 InputSource *is = (InputSource *) closure;
8081 if (is->lineByLine) {
8082 count = read(is->fd, is->unused,
8083 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
8085 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
8088 is->unused += count;
8090 while (p < is->unused) {
8091 q = memchr(p, '\n', is->unused - p);
8092 if (q == NULL) break;
8094 (is->func)(is, is->closure, p, q - p, 0);
8098 while (p < is->unused) {
8103 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
8108 (is->func)(is, is->closure, is->buf, count, error);
8112 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
8119 ChildProc *cp = (ChildProc *) pr;
8121 is = (InputSource *) calloc(1, sizeof(InputSource));
8122 is->lineByLine = lineByLine;
8126 is->fd = fileno(stdin);
8128 is->kind = cp->kind;
8129 is->fd = cp->fdFrom;
8132 is->unused = is->buf;
8135 is->xid = XtAppAddInput(appContext, is->fd,
8136 (XtPointer) (XtInputReadMask),
8137 (XtInputCallbackProc) DoInputCallback,
8139 is->closure = closure;
8140 return (InputSourceRef) is;
8144 RemoveInputSource(isr)
8147 InputSource *is = (InputSource *) isr;
8149 if (is->xid == 0) return;
8150 XtRemoveInput(is->xid);
8154 int OutputToProcess(pr, message, count, outError)
8160 static int line = 0;
8161 ChildProc *cp = (ChildProc *) pr;
8166 if (appData.noJoin || !appData.useInternalWrap)
8167 outCount = fwrite(message, 1, count, stdout);
8170 int width = get_term_width();
8171 int len = wrap(NULL, message, count, width, &line);
8172 char *msg = malloc(len);
8176 outCount = fwrite(message, 1, count, stdout);
8179 dbgchk = wrap(msg, message, count, width, &line);
8180 if (dbgchk != len && appData.debugMode)
8181 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
8182 outCount = fwrite(msg, 1, dbgchk, stdout);
8188 outCount = write(cp->fdTo, message, count);
8198 /* Output message to process, with "ms" milliseconds of delay
8199 between each character. This is needed when sending the logon
8200 script to ICC, which for some reason doesn't like the
8201 instantaneous send. */
8202 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
8209 ChildProc *cp = (ChildProc *) pr;
8214 r = write(cp->fdTo, message++, 1);
8227 /**** Animation code by Hugh Fisher, DCS, ANU.
8229 Known problem: if a window overlapping the board is
8230 moved away while a piece is being animated underneath,
8231 the newly exposed area won't be updated properly.
8232 I can live with this.
8234 Known problem: if you look carefully at the animation
8235 of pieces in mono mode, they are being drawn as solid
8236 shapes without interior detail while moving. Fixing
8237 this would be a major complication for minimal return.
8240 /* Masks for XPM pieces. Black and white pieces can have
8241 different shapes, but in the interest of retaining my
8242 sanity pieces must have the same outline on both light
8243 and dark squares, and all pieces must use the same
8244 background square colors/images. */
8246 static int xpmDone = 0;
8249 CreateAnimMasks (pieceDepth)
8256 unsigned long plane;
8259 /* Need a bitmap just to get a GC with right depth */
8260 buf = XCreatePixmap(xDisplay, xBoardWindow,
8262 values.foreground = 1;
8263 values.background = 0;
8264 /* Don't use XtGetGC, not read only */
8265 maskGC = XCreateGC(xDisplay, buf,
8266 GCForeground | GCBackground, &values);
8267 XFreePixmap(xDisplay, buf);
8269 buf = XCreatePixmap(xDisplay, xBoardWindow,
8270 squareSize, squareSize, pieceDepth);
8271 values.foreground = XBlackPixel(xDisplay, xScreen);
8272 values.background = XWhitePixel(xDisplay, xScreen);
8273 bufGC = XCreateGC(xDisplay, buf,
8274 GCForeground | GCBackground, &values);
8276 for (piece = WhitePawn; piece <= BlackKing; piece++) {
8277 /* Begin with empty mask */
8278 if(!xpmDone) // [HGM] pieces: keep using existing
8279 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8280 squareSize, squareSize, 1);
8281 XSetFunction(xDisplay, maskGC, GXclear);
8282 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8283 0, 0, squareSize, squareSize);
8285 /* Take a copy of the piece */
8290 XSetFunction(xDisplay, bufGC, GXcopy);
8291 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8293 0, 0, squareSize, squareSize, 0, 0);
8295 /* XOR the background (light) over the piece */
8296 XSetFunction(xDisplay, bufGC, GXxor);
8298 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8299 0, 0, squareSize, squareSize, 0, 0);
8301 XSetForeground(xDisplay, bufGC, lightSquareColor);
8302 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8305 /* We now have an inverted piece image with the background
8306 erased. Construct mask by just selecting all the non-zero
8307 pixels - no need to reconstruct the original image. */
8308 XSetFunction(xDisplay, maskGC, GXor);
8310 /* Might be quicker to download an XImage and create bitmap
8311 data from it rather than this N copies per piece, but it
8312 only takes a fraction of a second and there is a much
8313 longer delay for loading the pieces. */
8314 for (n = 0; n < pieceDepth; n ++) {
8315 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8316 0, 0, squareSize, squareSize,
8322 XFreePixmap(xDisplay, buf);
8323 XFreeGC(xDisplay, bufGC);
8324 XFreeGC(xDisplay, maskGC);
8328 InitAnimState (anim, info)
8330 XWindowAttributes * info;
8335 /* Each buffer is square size, same depth as window */
8336 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8337 squareSize, squareSize, info->depth);
8338 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8339 squareSize, squareSize, info->depth);
8341 /* Create a plain GC for blitting */
8342 mask = GCForeground | GCBackground | GCFunction |
8343 GCPlaneMask | GCGraphicsExposures;
8344 values.foreground = XBlackPixel(xDisplay, xScreen);
8345 values.background = XWhitePixel(xDisplay, xScreen);
8346 values.function = GXcopy;
8347 values.plane_mask = AllPlanes;
8348 values.graphics_exposures = False;
8349 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8351 /* Piece will be copied from an existing context at
8352 the start of each new animation/drag. */
8353 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8355 /* Outline will be a read-only copy of an existing */
8356 anim->outlineGC = None;
8362 static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
8363 XWindowAttributes info;
8365 if (xpmDone && gameInfo.variant == old) return;
8366 if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet
8367 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8369 InitAnimState(&game, &info);
8370 InitAnimState(&player, &info);
8372 /* For XPM pieces, we need bitmaps to use as masks. */
8374 CreateAnimMasks(info.depth);
8380 static Boolean frameWaiting;
8382 static RETSIGTYPE FrameAlarm (sig)
8385 frameWaiting = False;
8386 /* In case System-V style signals. Needed?? */
8387 signal(SIGALRM, FrameAlarm);
8394 struct itimerval delay;
8396 XSync(xDisplay, False);
8399 frameWaiting = True;
8400 signal(SIGALRM, FrameAlarm);
8401 delay.it_interval.tv_sec =
8402 delay.it_value.tv_sec = time / 1000;
8403 delay.it_interval.tv_usec =
8404 delay.it_value.tv_usec = (time % 1000) * 1000;
8405 setitimer(ITIMER_REAL, &delay, NULL);
8406 while (frameWaiting) pause();
8407 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8408 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8409 setitimer(ITIMER_REAL, &delay, NULL);
8419 XSync(xDisplay, False);
8421 usleep(time * 1000);
8426 /* Convert board position to corner of screen rect and color */
8429 ScreenSquare(column, row, pt, color)
8430 int column; int row; XPoint * pt; int * color;
8433 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8434 pt->y = lineGap + row * (squareSize + lineGap);
8436 pt->x = lineGap + column * (squareSize + lineGap);
8437 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8439 *color = SquareColor(row, column);
8442 /* Convert window coords to square */
8445 BoardSquare(x, y, column, row)
8446 int x; int y; int * column; int * row;
8448 *column = EventToSquare(x, BOARD_WIDTH);
8449 if (flipView && *column >= 0)
8450 *column = BOARD_WIDTH - 1 - *column;
8451 *row = EventToSquare(y, BOARD_HEIGHT);
8452 if (!flipView && *row >= 0)
8453 *row = BOARD_HEIGHT - 1 - *row;
8458 #undef Max /* just in case */
8460 #define Max(a, b) ((a) > (b) ? (a) : (b))
8461 #define Min(a, b) ((a) < (b) ? (a) : (b))
8464 SetRect(rect, x, y, width, height)
8465 XRectangle * rect; int x; int y; int width; int height;
8469 rect->width = width;
8470 rect->height = height;
8473 /* Test if two frames overlap. If they do, return
8474 intersection rect within old and location of
8475 that rect within new. */
8478 Intersect(old, new, size, area, pt)
8479 XPoint * old; XPoint * new;
8480 int size; XRectangle * area; XPoint * pt;
8482 if (old->x > new->x + size || new->x > old->x + size ||
8483 old->y > new->y + size || new->y > old->y + size) {
8486 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8487 size - abs(old->x - new->x), size - abs(old->y - new->y));
8488 pt->x = Max(old->x - new->x, 0);
8489 pt->y = Max(old->y - new->y, 0);
8494 /* For two overlapping frames, return the rect(s)
8495 in the old that do not intersect with the new. */
8498 CalcUpdateRects(old, new, size, update, nUpdates)
8499 XPoint * old; XPoint * new; int size;
8500 XRectangle update[]; int * nUpdates;
8504 /* If old = new (shouldn't happen) then nothing to draw */
8505 if (old->x == new->x && old->y == new->y) {
8509 /* Work out what bits overlap. Since we know the rects
8510 are the same size we don't need a full intersect calc. */
8512 /* Top or bottom edge? */
8513 if (new->y > old->y) {
8514 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8516 } else if (old->y > new->y) {
8517 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8518 size, old->y - new->y);
8521 /* Left or right edge - don't overlap any update calculated above. */
8522 if (new->x > old->x) {
8523 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8524 new->x - old->x, size - abs(new->y - old->y));
8526 } else if (old->x > new->x) {
8527 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8528 old->x - new->x, size - abs(new->y - old->y));
8535 /* Generate a series of frame coords from start->mid->finish.
8536 The movement rate doubles until the half way point is
8537 reached, then halves back down to the final destination,
8538 which gives a nice slow in/out effect. The algorithmn
8539 may seem to generate too many intermediates for short
8540 moves, but remember that the purpose is to attract the
8541 viewers attention to the piece about to be moved and
8542 then to where it ends up. Too few frames would be less
8546 Tween(start, mid, finish, factor, frames, nFrames)
8547 XPoint * start; XPoint * mid;
8548 XPoint * finish; int factor;
8549 XPoint frames[]; int * nFrames;
8551 int fraction, n, count;
8555 /* Slow in, stepping 1/16th, then 1/8th, ... */
8557 for (n = 0; n < factor; n++)
8559 for (n = 0; n < factor; n++) {
8560 frames[count].x = start->x + (mid->x - start->x) / fraction;
8561 frames[count].y = start->y + (mid->y - start->y) / fraction;
8563 fraction = fraction / 2;
8567 frames[count] = *mid;
8570 /* Slow out, stepping 1/2, then 1/4, ... */
8572 for (n = 0; n < factor; n++) {
8573 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8574 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8576 fraction = fraction * 2;
8581 /* Draw a piece on the screen without disturbing what's there */
8584 SelectGCMask(piece, clip, outline, mask)
8585 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8589 /* Bitmap for piece being moved. */
8590 if (appData.monoMode) {
8591 *mask = *pieceToSolid(piece);
8592 } else if (useImages) {
8594 *mask = xpmMask[piece];
8596 *mask = ximMaskPm[piece];
8599 *mask = *pieceToSolid(piece);
8602 /* GC for piece being moved. Square color doesn't matter, but
8603 since it gets modified we make a copy of the original. */
8605 if (appData.monoMode)
8610 if (appData.monoMode)
8615 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8617 /* Outline only used in mono mode and is not modified */
8619 *outline = bwPieceGC;
8621 *outline = wbPieceGC;
8625 OverlayPiece(piece, clip, outline, dest)
8626 ChessSquare piece; GC clip; GC outline; Drawable dest;
8631 /* Draw solid rectangle which will be clipped to shape of piece */
8632 XFillRectangle(xDisplay, dest, clip,
8633 0, 0, squareSize, squareSize);
8634 if (appData.monoMode)
8635 /* Also draw outline in contrasting color for black
8636 on black / white on white cases */
8637 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8638 0, 0, squareSize, squareSize, 0, 0, 1);
8640 /* Copy the piece */
8645 if(appData.upsideDown && flipView) kind ^= 2;
8646 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8648 0, 0, squareSize, squareSize,
8653 /* Animate the movement of a single piece */
8656 BeginAnimation(anim, piece, startColor, start)
8664 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8665 /* The old buffer is initialised with the start square (empty) */
8666 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8667 anim->prevFrame = *start;
8669 /* The piece will be drawn using its own bitmap as a matte */
8670 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8671 XSetClipMask(xDisplay, anim->pieceGC, mask);
8675 AnimationFrame(anim, frame, piece)
8680 XRectangle updates[4];
8685 /* Save what we are about to draw into the new buffer */
8686 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8687 frame->x, frame->y, squareSize, squareSize,
8690 /* Erase bits of the previous frame */
8691 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8692 /* Where the new frame overlapped the previous,
8693 the contents in newBuf are wrong. */
8694 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8695 overlap.x, overlap.y,
8696 overlap.width, overlap.height,
8698 /* Repaint the areas in the old that don't overlap new */
8699 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8700 for (i = 0; i < count; i++)
8701 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8702 updates[i].x - anim->prevFrame.x,
8703 updates[i].y - anim->prevFrame.y,
8704 updates[i].width, updates[i].height,
8705 updates[i].x, updates[i].y);
8707 /* Easy when no overlap */
8708 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8709 0, 0, squareSize, squareSize,
8710 anim->prevFrame.x, anim->prevFrame.y);
8713 /* Save this frame for next time round */
8714 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8715 0, 0, squareSize, squareSize,
8717 anim->prevFrame = *frame;
8719 /* Draw piece over original screen contents, not current,
8720 and copy entire rect. Wipes out overlapping piece images. */
8721 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8722 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8723 0, 0, squareSize, squareSize,
8724 frame->x, frame->y);
8728 EndAnimation (anim, finish)
8732 XRectangle updates[4];
8737 /* The main code will redraw the final square, so we
8738 only need to erase the bits that don't overlap. */
8739 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8740 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8741 for (i = 0; i < count; i++)
8742 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8743 updates[i].x - anim->prevFrame.x,
8744 updates[i].y - anim->prevFrame.y,
8745 updates[i].width, updates[i].height,
8746 updates[i].x, updates[i].y);
8748 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8749 0, 0, squareSize, squareSize,
8750 anim->prevFrame.x, anim->prevFrame.y);
8755 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8757 ChessSquare piece; int startColor;
8758 XPoint * start; XPoint * finish;
8759 XPoint frames[]; int nFrames;
8763 BeginAnimation(anim, piece, startColor, start);
8764 for (n = 0; n < nFrames; n++) {
8765 AnimationFrame(anim, &(frames[n]), piece);
8766 FrameDelay(appData.animSpeed);
8768 EndAnimation(anim, finish);
8772 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8775 ChessSquare piece = board[fromY][toY];
8776 board[fromY][toY] = EmptySquare;
8777 DrawPosition(FALSE, board);
8779 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8780 y = lineGap + toY * (squareSize + lineGap);
8782 x = lineGap + toX * (squareSize + lineGap);
8783 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8785 for(i=1; i<4*kFactor; i++) {
8786 int r = squareSize * 9 * i/(20*kFactor - 5);
8787 XFillArc(xDisplay, xBoardWindow, highlineGC,
8788 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8789 FrameDelay(appData.animSpeed);
8791 board[fromY][toY] = piece;
8794 /* Main control logic for deciding what to animate and how */
8797 AnimateMove(board, fromX, fromY, toX, toY)
8806 XPoint start, finish, mid;
8807 XPoint frames[kFactor * 2 + 1];
8808 int nFrames, startColor, endColor;
8810 /* Are we animating? */
8811 if (!appData.animate || appData.blindfold)
8814 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8815 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8816 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8818 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8819 piece = board[fromY][fromX];
8820 if (piece >= EmptySquare) return;
8825 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8828 if (appData.debugMode) {
8829 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8830 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8831 piece, fromX, fromY, toX, toY); }
8833 ScreenSquare(fromX, fromY, &start, &startColor);
8834 ScreenSquare(toX, toY, &finish, &endColor);
8837 /* Knight: make straight movement then diagonal */
8838 if (abs(toY - fromY) < abs(toX - fromX)) {
8839 mid.x = start.x + (finish.x - start.x) / 2;
8843 mid.y = start.y + (finish.y - start.y) / 2;
8846 mid.x = start.x + (finish.x - start.x) / 2;
8847 mid.y = start.y + (finish.y - start.y) / 2;
8850 /* Don't use as many frames for very short moves */
8851 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8852 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8854 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8855 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8856 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8858 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8859 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8862 /* Be sure end square is redrawn */
8863 damage[0][toY][toX] = True;
8867 DragPieceBegin(x, y)
8870 int boardX, boardY, color;
8873 /* Are we animating? */
8874 if (!appData.animateDragging || appData.blindfold)
8877 /* Figure out which square we start in and the
8878 mouse position relative to top left corner. */
8879 BoardSquare(x, y, &boardX, &boardY);
8880 player.startBoardX = boardX;
8881 player.startBoardY = boardY;
8882 ScreenSquare(boardX, boardY, &corner, &color);
8883 player.startSquare = corner;
8884 player.startColor = color;
8885 /* As soon as we start dragging, the piece will jump slightly to
8886 be centered over the mouse pointer. */
8887 player.mouseDelta.x = squareSize/2;
8888 player.mouseDelta.y = squareSize/2;
8889 /* Initialise animation */
8890 player.dragPiece = PieceForSquare(boardX, boardY);
8892 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8893 player.dragActive = True;
8894 BeginAnimation(&player, player.dragPiece, color, &corner);
8895 /* Mark this square as needing to be redrawn. Note that
8896 we don't remove the piece though, since logically (ie
8897 as seen by opponent) the move hasn't been made yet. */
8898 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8899 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8900 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8901 corner.x, corner.y, squareSize, squareSize,
8902 0, 0); // [HGM] zh: unstack in stead of grab
8903 if(gatingPiece != EmptySquare) {
8904 /* Kludge alert: When gating we want the introduced
8905 piece to appear on the from square. To generate an
8906 image of it, we draw it on the board, copy the image,
8907 and draw the original piece again. */
8908 ChessSquare piece = boards[currentMove][boardY][boardX];
8909 DrawSquare(boardY, boardX, gatingPiece, 0);
8910 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8911 corner.x, corner.y, squareSize, squareSize, 0, 0);
8912 DrawSquare(boardY, boardX, piece, 0);
8914 damage[0][boardY][boardX] = True;
8916 player.dragActive = False;
8926 /* Are we animating? */
8927 if (!appData.animateDragging || appData.blindfold)
8931 if (! player.dragActive)
8933 /* Move piece, maintaining same relative position
8934 of mouse within square */
8935 corner.x = x - player.mouseDelta.x;
8936 corner.y = y - player.mouseDelta.y;
8937 AnimationFrame(&player, &corner, player.dragPiece);
8939 if (appData.highlightDragging) {
8941 BoardSquare(x, y, &boardX, &boardY);
8942 SetHighlights(fromX, fromY, boardX, boardY);
8951 int boardX, boardY, color;
8954 /* Are we animating? */
8955 if (!appData.animateDragging || appData.blindfold)
8959 if (! player.dragActive)
8961 /* Last frame in sequence is square piece is
8962 placed on, which may not match mouse exactly. */
8963 BoardSquare(x, y, &boardX, &boardY);
8964 ScreenSquare(boardX, boardY, &corner, &color);
8965 EndAnimation(&player, &corner);
8967 /* Be sure end square is redrawn */
8968 damage[0][boardY][boardX] = True;
8970 /* This prevents weird things happening with fast successive
8971 clicks which on my Sun at least can cause motion events
8972 without corresponding press/release. */
8973 player.dragActive = False;
8976 /* Handle expose event while piece being dragged */
8981 if (!player.dragActive || appData.blindfold)
8984 /* What we're doing: logically, the move hasn't been made yet,
8985 so the piece is still in it's original square. But visually
8986 it's being dragged around the board. So we erase the square
8987 that the piece is on and draw it at the last known drag point. */
8988 BlankSquare(player.startSquare.x, player.startSquare.y,
8989 player.startColor, EmptySquare, xBoardWindow, 1);
8990 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8991 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8994 #include <sys/ioctl.h>
8995 int get_term_width()
8997 int fd, default_width;
9000 default_width = 79; // this is FICS default anyway...
9002 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
9004 if (!ioctl(fd, TIOCGSIZE, &win))
9005 default_width = win.ts_cols;
9006 #elif defined(TIOCGWINSZ)
9008 if (!ioctl(fd, TIOCGWINSZ, &win))
9009 default_width = win.ws_col;
9011 return default_width;
9017 static int old_width = 0;
9018 int new_width = get_term_width();
9020 if (old_width != new_width)
9021 ics_printf("set width %d\n", new_width);
9022 old_width = new_width;
9025 void NotifyFrontendLogin()
9030 /* [AS] Arrow highlighting support */
9032 static double A_WIDTH = 5; /* Width of arrow body */
9034 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
9035 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
9037 static double Sqr( double x )
9042 static int Round( double x )
9044 return (int) (x + 0.5);
9047 void SquareToPos(int rank, int file, int *x, int *y)
9050 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
9051 *y = lineGap + rank * (squareSize + lineGap);
9053 *x = lineGap + file * (squareSize + lineGap);
9054 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
9058 /* Draw an arrow between two points using current settings */
9059 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
9062 double dx, dy, j, k, x, y;
9065 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
9067 arrow[0].x = s_x + A_WIDTH + 0.5;
9070 arrow[1].x = s_x + A_WIDTH + 0.5;
9071 arrow[1].y = d_y - h;
9073 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
9074 arrow[2].y = d_y - h;
9079 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
9080 arrow[5].y = d_y - h;
9082 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
9083 arrow[4].y = d_y - h;
9085 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
9088 else if( d_y == s_y ) {
9089 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
9092 arrow[0].y = s_y + A_WIDTH + 0.5;
9094 arrow[1].x = d_x - w;
9095 arrow[1].y = s_y + A_WIDTH + 0.5;
9097 arrow[2].x = d_x - w;
9098 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
9103 arrow[5].x = d_x - w;
9104 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
9106 arrow[4].x = d_x - w;
9107 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
9110 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
9113 /* [AS] Needed a lot of paper for this! :-) */
9114 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
9115 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
9117 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
9119 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
9124 arrow[0].x = Round(x - j);
9125 arrow[0].y = Round(y + j*dx);
9127 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
9128 arrow[1].y = Round(arrow[0].y - 2*j*dx);
9131 x = (double) d_x - k;
9132 y = (double) d_y - k*dy;
9135 x = (double) d_x + k;
9136 y = (double) d_y + k*dy;
9139 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
9141 arrow[6].x = Round(x - j);
9142 arrow[6].y = Round(y + j*dx);
9144 arrow[2].x = Round(arrow[6].x + 2*j);
9145 arrow[2].y = Round(arrow[6].y - 2*j*dx);
9147 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
9148 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
9153 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
9154 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
9157 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
9158 // Polygon( hdc, arrow, 7 );
9161 /* [AS] Draw an arrow between two squares */
9162 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
9164 int s_x, s_y, d_x, d_y, hor, vert, i;
9166 if( s_col == d_col && s_row == d_row ) {
9170 /* Get source and destination points */
9171 SquareToPos( s_row, s_col, &s_x, &s_y);
9172 SquareToPos( d_row, d_col, &d_x, &d_y);
9175 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
9177 else if( d_y < s_y ) {
9178 d_y += squareSize / 2 + squareSize / 4;
9181 d_y += squareSize / 2;
9185 d_x += squareSize / 2 - squareSize / 4;
9187 else if( d_x < s_x ) {
9188 d_x += squareSize / 2 + squareSize / 4;
9191 d_x += squareSize / 2;
9194 s_x += squareSize / 2;
9195 s_y += squareSize / 2;
9198 A_WIDTH = squareSize / 14.; //[HGM] make float
9200 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
9202 hor = 64*s_col + 32; vert = 64*s_row + 32;
9203 for(i=0; i<= 64; i++) {
9204 damage[0][vert+6>>6][hor+6>>6] = True;
9205 damage[0][vert-6>>6][hor+6>>6] = True;
9206 damage[0][vert+6>>6][hor-6>>6] = True;
9207 damage[0][vert-6>>6][hor-6>>6] = True;
9208 hor += d_col - s_col; vert += d_row - s_row;
9212 Boolean IsDrawArrowEnabled()
9214 return appData.highlightMoveWithArrow && squareSize >= 32;
9217 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
9219 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
9220 DrawArrowBetweenSquares(fromX, fromY, toX, toY);