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 *filter, 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 MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
265 void HandleUserMove P((Widget w, XEvent *event,
266 String *prms, Cardinal *nprms));
267 void AnimateUserMove P((Widget w, XEvent * event,
268 String * params, Cardinal * nParams));
269 void HandlePV P((Widget w, XEvent * event,
270 String * params, Cardinal * nParams));
271 void SelectPV P((Widget w, XEvent * event,
272 String * params, Cardinal * nParams));
273 void StopPV P((Widget w, XEvent * event,
274 String * params, Cardinal * nParams));
275 void WhiteClock P((Widget w, XEvent *event,
276 String *prms, Cardinal *nprms));
277 void BlackClock P((Widget w, XEvent *event,
278 String *prms, Cardinal *nprms));
279 void DrawPositionProc P((Widget w, XEvent *event,
280 String *prms, Cardinal *nprms));
281 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
283 void CommentClick P((Widget w, XEvent * event,
284 String * params, Cardinal * nParams));
285 void CommentPopUp P((char *title, char *label));
286 void CommentPopDown P((void));
287 void ICSInputBoxPopUp P((void));
288 void ICSInputBoxPopDown P((void));
289 void FileNamePopUp P((char *label, char *def, char *filter,
290 FileProc proc, char *openMode));
291 void FileNamePopDown P((void));
292 void FileNameCallback P((Widget w, XtPointer client_data,
293 XtPointer call_data));
294 void FileNameAction P((Widget w, XEvent *event,
295 String *prms, Cardinal *nprms));
296 void AskQuestionReplyAction P((Widget w, XEvent *event,
297 String *prms, Cardinal *nprms));
298 void AskQuestionProc P((Widget w, XEvent *event,
299 String *prms, Cardinal *nprms));
300 void AskQuestionPopDown P((void));
301 void PromotionPopDown P((void));
302 void PromotionCallback P((Widget w, XtPointer client_data,
303 XtPointer call_data));
304 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
305 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
306 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
307 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
309 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
311 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
313 void LoadPositionProc P((Widget w, XEvent *event,
314 String *prms, Cardinal *nprms));
315 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
317 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
319 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
321 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
323 void PastePositionProc P((Widget w, XEvent *event, String *prms,
325 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
326 void CopyGameListProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
327 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
328 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
329 void SavePositionProc P((Widget w, XEvent *event,
330 String *prms, Cardinal *nprms));
331 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
332 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
334 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
335 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
336 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
338 void MachineWhiteProc P((Widget w, XEvent *event,
339 String *prms, Cardinal *nprms));
340 void AnalyzeModeProc P((Widget w, XEvent *event,
341 String *prms, Cardinal *nprms));
342 void AnalyzeFileProc P((Widget w, XEvent *event,
343 String *prms, Cardinal *nprms));
344 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
346 void MatchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
347 void MatchOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
348 void IcsClientProc P((Widget w, XEvent *event, String *prms,
350 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
351 void EditPositionProc P((Widget w, XEvent *event,
352 String *prms, Cardinal *nprms));
353 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
354 void EditCommentProc P((Widget w, XEvent *event,
355 String *prms, Cardinal *nprms));
356 void IcsInputBoxProc P((Widget w, XEvent *event,
357 String *prms, Cardinal *nprms));
358 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
359 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
360 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
361 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
362 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
363 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
364 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
365 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
366 void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
367 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
368 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
369 void TypeInProc 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 OptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
454 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
455 void IcsTextProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
456 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
457 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
458 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
459 void IcsOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
460 void SoundOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
461 void BoardOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
462 void LoadOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
463 void SaveOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
464 void GameListOptionsPopDown P(());
465 void ShufflePopDown P(());
466 void TimeControlPopDown P(());
467 void GenericPopDown P(());
468 void update_ics_width P(());
469 int get_term_width P(());
470 int CopyMemoProc P(());
471 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
472 Boolean IsDrawArrowEnabled P(());
475 * XBoard depends on Xt R4 or higher
477 int xtVersion = XtSpecificationRelease;
482 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
483 jailSquareColor, highlightSquareColor, premoveHighlightColor;
484 Pixel lowTimeWarningColor;
485 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
486 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
487 wjPieceGC, bjPieceGC, prelineGC, countGC;
488 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
489 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
490 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
491 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
492 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
493 ICSInputShell, fileNameShell, askQuestionShell;
494 Widget historyShell, evalGraphShell, gameListShell;
495 int hOffset; // [HGM] dual
496 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
497 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
498 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
499 Font clockFontID, coordFontID, countFontID;
500 XFontStruct *clockFontStruct, *coordFontStruct, *countFontStruct;
501 XtAppContext appContext;
503 char *oldICSInteractionTitle;
507 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
509 Position commentX = -1, commentY = -1;
510 Dimension commentW, commentH;
511 typedef unsigned int BoardSize;
513 Boolean chessProgram;
515 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
516 int squareSize, smallLayout = 0, tinyLayout = 0,
517 marginW, marginH, // [HGM] for run-time resizing
518 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
519 ICSInputBoxUp = False, askQuestionUp = False,
520 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
521 errorUp = False, errorExitStatus = -1, lineGap, defaultLineGap;
522 Pixel timerForegroundPixel, timerBackgroundPixel;
523 Pixel buttonForegroundPixel, buttonBackgroundPixel;
524 char *chessDir, *programName, *programVersion,
525 *gameCopyFilename, *gamePasteFilename;
526 Boolean alwaysOnTop = False;
527 Boolean saveSettingsOnExit;
528 char *settingsFileName;
529 char *icsTextMenuString;
531 char *firstChessProgramNames;
532 char *secondChessProgramNames;
534 WindowPlacement wpMain;
535 WindowPlacement wpConsole;
536 WindowPlacement wpComment;
537 WindowPlacement wpMoveHistory;
538 WindowPlacement wpEvalGraph;
539 WindowPlacement wpEngineOutput;
540 WindowPlacement wpGameList;
541 WindowPlacement wpTags;
543 extern Widget shells[];
544 extern Boolean shellUp[];
548 Pixmap pieceBitmap[2][(int)BlackPawn];
549 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
550 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
551 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
552 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
553 Pixmap xpmBoardBitmap[2];
554 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
555 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
556 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
557 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
558 XImage *ximLightSquare, *ximDarkSquare;
561 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
562 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
564 #define White(piece) ((int)(piece) < (int)BlackPawn)
566 /* Variables for doing smooth animation. This whole thing
567 would be much easier if the board was double-buffered,
568 but that would require a fairly major rewrite. */
573 GC blitGC, pieceGC, outlineGC;
574 XPoint startSquare, prevFrame, mouseDelta;
578 int startBoardX, startBoardY;
581 /* There can be two pieces being animated at once: a player
582 can begin dragging a piece before the remote opponent has moved. */
584 static AnimState game, player;
586 /* Bitmaps for use as masks when drawing XPM pieces.
587 Need one for each black and white piece. */
588 static Pixmap xpmMask[BlackKing + 1];
590 /* This magic number is the number of intermediate frames used
591 in each half of the animation. For short moves it's reduced
592 by 1. The total number of frames will be factor * 2 + 1. */
595 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
597 MenuItem fileMenu[] = {
598 {N_("New Game Ctrl+N"), "New Game", ResetProc},
599 {N_("New Shuffle Game ..."), "New Shuffle Game", ShuffleMenuProc},
600 {N_("New Variant ... Alt+Shift+V"), "New Variant", NewVariantProc}, // [HGM] variant: not functional yet
601 {"----", NULL, NothingProc},
602 {N_("Load Game Ctrl+O"), "Load Game", LoadGameProc},
603 {N_("Load Position Ctrl+Shift+O"), "Load Position", LoadPositionProc},
604 // {N_("Load Next Game"), "Load Next Game", LoadNextGameProc},
605 // {N_("Load Previous Game"), "Load Previous Game", LoadPrevGameProc},
606 // {N_("Reload Same Game"), "Reload Same Game", ReloadGameProc},
607 {N_("Next Position Shift+PgDn"), "Load Next Position", LoadNextPositionProc},
608 {N_("Prev Position Shift+PgUp"), "Load Previous Position", LoadPrevPositionProc},
609 {"----", NULL, NothingProc},
610 // {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
611 {N_("Save Game Ctrl+S"), "Save Game", SaveGameProc},
612 {N_("Save Position Ctrl+Shift+S"), "Save Position", SavePositionProc},
613 {"----", NULL, NothingProc},
614 {N_("Mail Move"), "Mail Move", MailMoveProc},
615 {N_("Reload CMail Message"), "Reload CMail Message", ReloadCmailMsgProc},
616 {"----", NULL, NothingProc},
617 {N_("Quit Ctr+Q"), "Exit", QuitProc},
621 MenuItem editMenu[] = {
622 {N_("Copy Game Ctrl+C"), "Copy Game", CopyGameProc},
623 {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
624 {N_("Copy Game List"), "Copy Game List", CopyGameListProc},
625 {"----", NULL, NothingProc},
626 {N_("Paste Game Ctrl+V"), "Paste Game", PasteGameProc},
627 {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
628 {"----", NULL, NothingProc},
629 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
630 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
631 {N_("Edit Tags"), "Edit Tags", EditTagsProc},
632 {N_("Edit Comment"), "Edit Comment", EditCommentProc},
633 {"----", NULL, NothingProc},
634 {N_("Revert Home"), "Revert", RevertProc},
635 {N_("Annotate"), "Annotate", AnnotateProc},
636 {N_("Truncate Game End"), "Truncate Game", TruncateGameProc},
637 {"----", NULL, NothingProc},
638 {N_("Backward Alt+Left"), "Backward", BackwardProc},
639 {N_("Forward Alt+Right"), "Forward", ForwardProc},
640 {N_("Back to Start Alt+Home"), "Back to Start", ToStartProc},
641 {N_("Forward to End Alt+End"), "Forward to End", ToEndProc},
645 MenuItem viewMenu[] = {
646 {N_("Flip View F2"), "Flip View", FlipViewProc},
647 {"----", NULL, NothingProc},
648 {N_("Engine Output Alt+Shift+O"), "Show Engine Output", EngineOutputProc},
649 {N_("Move History Alt+Shift+H"), "Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
650 {N_("Evaluation Graph Alt+Shift+E"), "Show Evaluation Graph", EvalGraphProc},
651 {N_("Game List Alt+Shift+G"), "Show Game List", ShowGameListProc},
652 {N_("ICS text menu"), "ICStex", IcsTextProc},
653 {"----", NULL, NothingProc},
654 {N_("Tags"), "Show Tags", EditTagsProc},
655 {N_("Comments"), "Show Comments", EditCommentProc},
656 {N_("ICS Input Box"), "ICS Input Box", IcsInputBoxProc},
657 {"----", NULL, NothingProc},
658 {N_("Board..."), "Board Options", BoardOptionsProc},
659 {N_("Game List Tags..."), "Game List", GameListOptionsPopUp},
663 MenuItem modeMenu[] = {
664 {N_("Machine White Ctrl+W"), "Machine White", MachineWhiteProc},
665 {N_("Machine Black Ctrl+B"), "Machine Black", MachineBlackProc},
666 {N_("Two Machines Ctrl+T"), "Two Machines", TwoMachinesProc},
667 {N_("Analysis Mode Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
668 {N_("Analyze File Ctrl+F"), "Analyze File", AnalyzeFileProc },
669 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
670 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
671 {N_("Training"), "Training", TrainingProc},
672 {N_("ICS Client"), "ICS Client", IcsClientProc},
673 {"----", NULL, NothingProc},
674 {N_("Machine Match"), "Machine Match", MatchProc},
675 {N_("Pause Pause"), "Pause", PauseProc},
679 MenuItem actionMenu[] = {
680 {N_("Accept F3"), "Accept", AcceptProc},
681 {N_("Decline F4"), "Decline", DeclineProc},
682 {N_("Rematch F12"), "Rematch", RematchProc},
683 {"----", NULL, NothingProc},
684 {N_("Call Flag F5"), "Call Flag", CallFlagProc},
685 {N_("Draw F6"), "Draw", DrawProc},
686 {N_("Adjourn F7"), "Adjourn", AdjournProc},
687 {N_("Abort F8"),"Abort", AbortProc},
688 {N_("Resign F9"), "Resign", ResignProc},
689 {"----", NULL, NothingProc},
690 {N_("Stop Observing F10"), "Stop Observing", StopObservingProc},
691 {N_("Stop Examining F11"), "Stop Examining", StopExaminingProc},
692 {N_("Upload to Examine"), "Upload to Examine", UploadProc},
693 {"----", NULL, NothingProc},
694 {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
695 {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
696 {N_("Adjudicate Draw"), "Adjudicate Draw", AdjuDrawProc},
700 MenuItem engineMenu[] = {
701 {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
702 {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
703 {"----", NULL, NothingProc},
704 {N_("Hint"), "Hint", HintProc},
705 {N_("Book"), "Book", BookProc},
706 {"----", NULL, NothingProc},
707 {N_("Move Now Ctrl+M"), "Move Now", MoveNowProc},
708 {N_("Retract Move Ctrl+X"), "Retract Move", RetractMoveProc},
712 MenuItem optionsMenu[] = {
713 #define OPTIONSDIALOG
715 {N_("General ..."), "General", OptionsProc},
717 {N_("Time Control ... Alt+Shift+T"), "Time Control", TimeControlProc},
718 {N_("Common Engine ... Alt+Shift+U"), "Common Engine", UciMenuProc},
719 {N_("Adjudications ... Alt+Shift+J"), "Adjudications", EngineMenuProc},
720 {N_("ICS ..."), "ICS", IcsOptionsProc},
721 {N_("Match ..."), "Match", MatchOptionsProc},
722 {N_("Load Game ..."), "Load Game", LoadOptionsProc},
723 {N_("Save Game ..."), "Save Game", SaveOptionsProc},
724 // {N_(" ..."), "", OptionsProc},
725 {N_("Game List ..."), "Game List", GameListOptionsPopUp},
726 {N_("Sounds ..."), "Sounds", SoundOptionsProc},
727 {"----", NULL, NothingProc},
728 #ifndef OPTIONSDIALOG
729 {N_("Always Queen Ctrl+Shift+Q"), "Always Queen", AlwaysQueenProc},
730 {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
731 {N_("Animate Moving Ctrl+Shift+A"), "Animate Moving", AnimateMovingProc},
732 {N_("Auto Flag Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
733 {N_("Auto Flip View"), "Auto Flip View", AutoflipProc},
734 {N_("Blindfold"), "Blindfold", BlindfoldProc},
735 {N_("Flash Moves"), "Flash Moves", FlashMovesProc},
737 {N_("Highlight Dragging"), "Highlight Dragging", HighlightDraggingProc},
739 {N_("Highlight Last Move"), "Highlight Last Move", HighlightLastMoveProc},
740 {N_("Highlight With Arrow"), "Arrow", HighlightArrowProc},
741 {N_("Move Sound"), "Move Sound", MoveSoundProc},
742 // {N_("ICS Alarm"), "ICS Alarm", IcsAlarmProc},
743 {N_("One-Click Moving"), "OneClick", OneClickProc},
744 {N_("Periodic Updates"), "Periodic Updates", PeriodicUpdatesProc},
745 {N_("Ponder Next Move Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
746 {N_("Popup Exit Message"), "Popup Exit Message", PopupExitMessageProc},
747 {N_("Popup Move Errors"), "Popup Move Errors", PopupMoveErrorsProc},
748 // {N_("Premove"), "Premove", PremoveProc},
749 {N_("Show Coords"), "Show Coords", ShowCoordsProc},
750 {N_("Hide Thinking Ctrl+Shift+H"), "Hide Thinking", HideThinkingProc},
751 {N_("Test Legality Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
752 {"----", NULL, NothingProc},
754 {N_("Save Settings Now"), "Save Settings Now", SaveSettingsProc},
755 {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
759 MenuItem helpMenu[] = {
760 {N_("Info XBoard"), "Info XBoard", InfoProc},
761 {N_("Man XBoard F1"), "Man XBoard", ManProc},
762 {"----", NULL, NothingProc},
763 {N_("About XBoard"), "About XBoard", AboutProc},
768 {N_("File"), "File", fileMenu},
769 {N_("Edit"), "Edit", editMenu},
770 {N_("View"), "View", viewMenu},
771 {N_("Mode"), "Mode", modeMenu},
772 {N_("Action"), "Action", actionMenu},
773 {N_("Engine"), "Engine", engineMenu},
774 {N_("Options"), "Options", optionsMenu},
775 {N_("Help"), "Help", helpMenu},
779 #define PAUSE_BUTTON "P"
780 MenuItem buttonBar[] = {
781 {"<<", "<<", ToStartProc},
782 {"<", "<", BackwardProc},
783 {PAUSE_BUTTON, PAUSE_BUTTON, PauseProc},
784 {">", ">", ForwardProc},
785 {">>", ">>", ToEndProc},
789 #define PIECE_MENU_SIZE 18
790 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
791 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
792 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
793 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
794 N_("Empty square"), N_("Clear board") },
795 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
796 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
797 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
798 N_("Empty square"), N_("Clear board") }
800 /* must be in same order as PieceMenuStrings! */
801 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
802 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
803 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
804 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
805 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
806 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
807 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
808 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
809 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
812 #define DROP_MENU_SIZE 6
813 String dropMenuStrings[DROP_MENU_SIZE] = {
814 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
816 /* must be in same order as PieceMenuStrings! */
817 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
818 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
819 WhiteRook, WhiteQueen
827 DropMenuEnables dmEnables[] = {
845 { XtNborderWidth, 0 },
846 { XtNdefaultDistance, 0 },
850 { XtNborderWidth, 0 },
851 { XtNresizable, (XtArgVal) True },
855 { XtNborderWidth, 0 },
861 { XtNjustify, (XtArgVal) XtJustifyRight },
862 { XtNlabel, (XtArgVal) "..." },
863 { XtNresizable, (XtArgVal) True },
864 { XtNresize, (XtArgVal) False }
867 Arg messageArgs[] = {
868 { XtNjustify, (XtArgVal) XtJustifyLeft },
869 { XtNlabel, (XtArgVal) "..." },
870 { XtNresizable, (XtArgVal) True },
871 { XtNresize, (XtArgVal) False }
875 { XtNborderWidth, 0 },
876 { XtNjustify, (XtArgVal) XtJustifyLeft }
879 XtResource clientResources[] = {
880 { "flashCount", "flashCount", XtRInt, sizeof(int),
881 XtOffset(AppDataPtr, flashCount), XtRImmediate,
882 (XtPointer) FLASH_COUNT },
885 XrmOptionDescRec shellOptions[] = {
886 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
887 { "-flash", "flashCount", XrmoptionNoArg, "3" },
888 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
891 XtActionsRec boardActions[] = {
892 { "DrawPosition", DrawPositionProc },
893 { "HandleUserMove", HandleUserMove },
894 { "AnimateUserMove", AnimateUserMove },
895 { "HandlePV", HandlePV },
896 { "SelectPV", SelectPV },
897 { "StopPV", StopPV },
898 { "FileNameAction", FileNameAction },
899 { "AskQuestionProc", AskQuestionProc },
900 { "AskQuestionReplyAction", AskQuestionReplyAction },
901 { "PieceMenuPopup", PieceMenuPopup },
902 { "WhiteClock", WhiteClock },
903 { "BlackClock", BlackClock },
904 { "Iconify", Iconify },
905 { "ResetProc", ResetProc },
906 { "NewVariantProc", NewVariantProc },
907 { "LoadGameProc", LoadGameProc },
908 { "LoadNextGameProc", LoadNextGameProc },
909 { "LoadPrevGameProc", LoadPrevGameProc },
910 { "LoadSelectedProc", LoadSelectedProc },
911 { "SetFilterProc", SetFilterProc },
912 { "ReloadGameProc", ReloadGameProc },
913 { "LoadPositionProc", LoadPositionProc },
914 { "LoadNextPositionProc", LoadNextPositionProc },
915 { "LoadPrevPositionProc", LoadPrevPositionProc },
916 { "ReloadPositionProc", ReloadPositionProc },
917 { "CopyPositionProc", CopyPositionProc },
918 { "PastePositionProc", PastePositionProc },
919 { "CopyGameProc", CopyGameProc },
920 { "CopyGameListProc", CopyGameListProc },
921 { "PasteGameProc", PasteGameProc },
922 { "SaveGameProc", SaveGameProc },
923 { "SavePositionProc", SavePositionProc },
924 { "MailMoveProc", MailMoveProc },
925 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
926 { "QuitProc", QuitProc },
927 { "MachineWhiteProc", MachineWhiteProc },
928 { "MachineBlackProc", MachineBlackProc },
929 { "AnalysisModeProc", AnalyzeModeProc },
930 { "AnalyzeFileProc", AnalyzeFileProc },
931 { "TwoMachinesProc", TwoMachinesProc },
932 { "IcsClientProc", IcsClientProc },
933 { "EditGameProc", EditGameProc },
934 { "EditPositionProc", EditPositionProc },
935 { "TrainingProc", EditPositionProc },
936 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
937 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
938 { "ShowGameListProc", ShowGameListProc },
939 { "ShowMoveListProc", HistoryShowProc},
940 { "EditTagsProc", EditCommentProc },
941 { "EditCommentProc", EditCommentProc },
942 { "IcsInputBoxProc", IcsInputBoxProc },
943 { "PauseProc", PauseProc },
944 { "AcceptProc", AcceptProc },
945 { "DeclineProc", DeclineProc },
946 { "RematchProc", RematchProc },
947 { "CallFlagProc", CallFlagProc },
948 { "DrawProc", DrawProc },
949 { "AdjournProc", AdjournProc },
950 { "AbortProc", AbortProc },
951 { "ResignProc", ResignProc },
952 { "AdjuWhiteProc", AdjuWhiteProc },
953 { "AdjuBlackProc", AdjuBlackProc },
954 { "AdjuDrawProc", AdjuDrawProc },
955 { "TypeInProc", TypeInProc },
956 { "EnterKeyProc", EnterKeyProc },
957 { "UpKeyProc", UpKeyProc },
958 { "DownKeyProc", DownKeyProc },
959 { "StopObservingProc", StopObservingProc },
960 { "StopExaminingProc", StopExaminingProc },
961 { "UploadProc", UploadProc },
962 { "BackwardProc", BackwardProc },
963 { "ForwardProc", ForwardProc },
964 { "ToStartProc", ToStartProc },
965 { "ToEndProc", ToEndProc },
966 { "RevertProc", RevertProc },
967 { "AnnotateProc", AnnotateProc },
968 { "TruncateGameProc", TruncateGameProc },
969 { "MoveNowProc", MoveNowProc },
970 { "RetractMoveProc", RetractMoveProc },
971 { "EngineMenuProc", (XtActionProc) EngineMenuProc },
972 { "UciMenuProc", (XtActionProc) UciMenuProc },
973 { "TimeControlProc", (XtActionProc) TimeControlProc },
974 { "FlipViewProc", FlipViewProc },
975 { "PonderNextMoveProc", PonderNextMoveProc },
976 #ifndef OPTIONSDIALOG
977 { "AlwaysQueenProc", AlwaysQueenProc },
978 { "AnimateDraggingProc", AnimateDraggingProc },
979 { "AnimateMovingProc", AnimateMovingProc },
980 { "AutoflagProc", AutoflagProc },
981 { "AutoflipProc", AutoflipProc },
982 { "BlindfoldProc", BlindfoldProc },
983 { "FlashMovesProc", FlashMovesProc },
985 { "HighlightDraggingProc", HighlightDraggingProc },
987 { "HighlightLastMoveProc", HighlightLastMoveProc },
988 // { "IcsAlarmProc", IcsAlarmProc },
989 { "MoveSoundProc", MoveSoundProc },
990 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
991 { "PopupExitMessageProc", PopupExitMessageProc },
992 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
993 // { "PremoveProc", PremoveProc },
994 { "ShowCoordsProc", ShowCoordsProc },
995 { "ShowThinkingProc", ShowThinkingProc },
996 { "HideThinkingProc", HideThinkingProc },
997 { "TestLegalityProc", TestLegalityProc },
999 { "SaveSettingsProc", SaveSettingsProc },
1000 { "SaveOnExitProc", SaveOnExitProc },
1001 { "InfoProc", InfoProc },
1002 { "ManProc", ManProc },
1003 { "HintProc", HintProc },
1004 { "BookProc", BookProc },
1005 { "AboutGameProc", AboutGameProc },
1006 { "AboutProc", AboutProc },
1007 { "DebugProc", DebugProc },
1008 { "NothingProc", NothingProc },
1009 { "CommentClick", (XtActionProc) CommentClick },
1010 { "CommentPopDown", (XtActionProc) CommentPopDown },
1011 { "TagsPopDown", (XtActionProc) TagsPopDown },
1012 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1013 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1014 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1015 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1016 { "GameListPopDown", (XtActionProc) GameListPopDown },
1017 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1018 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1019 { "HistoryPopDown", (XtActionProc) HistoryPopDown },
1020 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1021 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1022 { "ShufflePopDown", (XtActionProc) ShufflePopDown },
1023 { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
1024 { "GenericPopDown", (XtActionProc) GenericPopDown },
1025 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1028 char globalTranslations[] =
1029 ":<Key>F9: ResignProc() \n \
1030 :Ctrl<Key>n: ResetProc() \n \
1031 :Meta<Key>V: NewVariantProc() \n \
1032 :Ctrl<Key>o: LoadGameProc() \n \
1033 :Meta<Key>Next: LoadNextGameProc() \n \
1034 :Meta<Key>Prior: LoadPrevGameProc() \n \
1035 :Ctrl<Key>s: SaveGameProc() \n \
1036 :Ctrl<Key>c: CopyGameProc() \n \
1037 :Ctrl<Key>v: PasteGameProc() \n \
1038 :Ctrl<Key>O: LoadPositionProc() \n \
1039 :Shift<Key>Next: LoadNextPositionProc() \n \
1040 :Shift<Key>Prior: LoadPrevPositionProc() \n \
1041 :Ctrl<Key>S: SavePositionProc() \n \
1042 :Ctrl<Key>C: CopyPositionProc() \n \
1043 :Ctrl<Key>V: PastePositionProc() \n \
1044 :Ctrl<Key>q: QuitProc() \n \
1045 :Ctrl<Key>w: MachineWhiteProc() \n \
1046 :Ctrl<Key>b: MachineBlackProc() \n \
1047 :Ctrl<Key>t: TwoMachinesProc() \n \
1048 :Ctrl<Key>a: AnalysisModeProc() \n \
1049 :Ctrl<Key>f: AnalyzeFileProc() \n \
1050 :Ctrl<Key>e: EditGameProc() \n \
1051 :Ctrl<Key>E: EditPositionProc() \n \
1052 :Meta<Key>O: EngineOutputProc() \n \
1053 :Meta<Key>E: EvalGraphProc() \n \
1054 :Meta<Key>G: ShowGameListProc() \n \
1055 :Meta<Key>H: ShowMoveListProc() \n \
1056 :<Key>Pause: PauseProc() \n \
1057 :<Key>F3: AcceptProc() \n \
1058 :<Key>F4: DeclineProc() \n \
1059 :<Key>F12: RematchProc() \n \
1060 :<Key>F5: CallFlagProc() \n \
1061 :<Key>F6: DrawProc() \n \
1062 :<Key>F7: AdjournProc() \n \
1063 :<Key>F8: AbortProc() \n \
1064 :<Key>F10: StopObservingProc() \n \
1065 :<Key>F11: StopExaminingProc() \n \
1066 :Meta Ctrl<Key>F12: DebugProc() \n \
1067 :Meta<Key>End: ToEndProc() \n \
1068 :Meta<Key>Right: ForwardProc() \n \
1069 :Meta<Key>Home: ToStartProc() \n \
1070 :Meta<Key>Left: BackwardProc() \n \
1071 :<Key>Home: RevertProc() \n \
1072 :<Key>End: TruncateGameProc() \n \
1073 :Ctrl<Key>m: MoveNowProc() \n \
1074 :Ctrl<Key>x: RetractMoveProc() \n \
1075 :Meta<Key>J: EngineMenuProc() \n \
1076 :Meta<Key>U: UciMenuProc() \n \
1077 :Meta<Key>T: TimeControlProc() \n \
1078 :Ctrl<Key>P: PonderNextMoveProc() \n "
1079 #ifndef OPTIONSDIALOG
1081 :Ctrl<Key>Q: AlwaysQueenProc() \n \
1082 :Ctrl<Key>F: AutoflagProc() \n \
1083 :Ctrl<Key>A: AnimateMovingProc() \n \
1084 :Ctrl<Key>L: TestLegalityProc() \n \
1085 :Ctrl<Key>H: HideThinkingProc() \n "
1088 :<Key>-: Iconify() \n \
1089 :<Key>F1: ManProc() \n \
1090 :<Key>F2: FlipViewProc() \n \
1091 <KeyDown>.: BackwardProc() \n \
1092 <KeyUp>.: ForwardProc() \n \
1093 Shift<Key>1: AskQuestionProc(\"Direct command\",\
1094 \"Send to chess program:\",,1) \n \
1095 Shift<Key>2: AskQuestionProc(\"Direct command\",\
1096 \"Send to second chess program:\",,2) \n";
1098 char boardTranslations[] =
1099 "<Btn1Down>: HandleUserMove(0) \n \
1100 Shift<Btn1Up>: HandleUserMove(1) \n \
1101 <Btn1Up>: HandleUserMove(0) \n \
1102 <Btn1Motion>: AnimateUserMove() \n \
1103 <Btn3Motion>: HandlePV() \n \
1104 <Btn3Up>: PieceMenuPopup(menuB) \n \
1105 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1106 PieceMenuPopup(menuB) \n \
1107 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1108 PieceMenuPopup(menuW) \n \
1109 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1110 PieceMenuPopup(menuW) \n \
1111 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1112 PieceMenuPopup(menuB) \n";
1114 char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
1115 char blackTranslations[] = "<BtnDown>: BlackClock()\n";
1117 char ICSInputTranslations[] =
1118 "<Key>Up: UpKeyProc() \n "
1119 "<Key>Down: DownKeyProc() \n "
1120 "<Key>Return: EnterKeyProc() \n";
1122 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1123 // as the widget is destroyed before the up-click can call extend-end
1124 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1126 String xboardResources[] = {
1127 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1128 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1129 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1134 /* Max possible square size */
1135 #define MAXSQSIZE 256
1137 static int xpm_avail[MAXSQSIZE];
1139 #ifdef HAVE_DIR_STRUCT
1141 /* Extract piece size from filename */
1143 xpm_getsize(name, len, ext)
1154 if ((p=strchr(name, '.')) == NULL ||
1155 StrCaseCmp(p+1, ext) != 0)
1161 while (*p && isdigit(*p))
1168 /* Setup xpm_avail */
1170 xpm_getavail(dirname, ext)
1178 for (i=0; i<MAXSQSIZE; ++i)
1181 if (appData.debugMode)
1182 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1184 dir = opendir(dirname);
1187 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1188 programName, dirname);
1192 while ((ent=readdir(dir)) != NULL) {
1193 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1194 if (i > 0 && i < MAXSQSIZE)
1204 xpm_print_avail(fp, ext)
1210 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1211 for (i=1; i<MAXSQSIZE; ++i) {
1217 /* Return XPM piecesize closest to size */
1219 xpm_closest_to(dirname, size, ext)
1225 int sm_diff = MAXSQSIZE;
1229 xpm_getavail(dirname, ext);
1231 if (appData.debugMode)
1232 xpm_print_avail(stderr, ext);
1234 for (i=1; i<MAXSQSIZE; ++i) {
1237 diff = (diff<0) ? -diff : diff;
1238 if (diff < sm_diff) {
1246 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1252 #else /* !HAVE_DIR_STRUCT */
1253 /* If we are on a system without a DIR struct, we can't
1254 read the directory, so we can't collect a list of
1255 filenames, etc., so we can't do any size-fitting. */
1257 xpm_closest_to(dirname, size, ext)
1262 fprintf(stderr, _("\
1263 Warning: No DIR structure found on this system --\n\
1264 Unable to autosize for XPM/XIM pieces.\n\
1265 Please report this error to frankm@hiwaay.net.\n\
1266 Include system type & operating system in message.\n"));
1269 #endif /* HAVE_DIR_STRUCT */
1271 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1272 "magenta", "cyan", "white" };
1276 TextColors textColors[(int)NColorClasses];
1278 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1280 parse_color(str, which)
1284 char *p, buf[100], *d;
1287 if (strlen(str) > 99) /* watch bounds on buf */
1292 for (i=0; i<which; ++i) {
1299 /* Could be looking at something like:
1301 .. in which case we want to stop on a comma also */
1302 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1306 return -1; /* Use default for empty field */
1309 if (which == 2 || isdigit(*p))
1312 while (*p && isalpha(*p))
1317 for (i=0; i<8; ++i) {
1318 if (!StrCaseCmp(buf, cnames[i]))
1319 return which? (i+40) : (i+30);
1321 if (!StrCaseCmp(buf, "default")) return -1;
1323 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1328 parse_cpair(cc, str)
1332 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1333 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1338 /* bg and attr are optional */
1339 textColors[(int)cc].bg = parse_color(str, 1);
1340 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1341 textColors[(int)cc].attr = 0;
1347 /* Arrange to catch delete-window events */
1348 Atom wm_delete_window;
1350 CatchDeleteWindow(Widget w, String procname)
1353 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1354 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1355 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1362 XtSetArg(args[0], XtNiconic, False);
1363 XtSetValues(shellWidget, args, 1);
1365 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1368 //---------------------------------------------------------------------------------------------------------
1369 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1372 #define CW_USEDEFAULT (1<<31)
1373 #define ICS_TEXT_MENU_SIZE 90
1374 #define DEBUG_FILE "xboard.debug"
1375 #define SetCurrentDirectory chdir
1376 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1380 // these two must some day move to frontend.h, when they are implemented
1381 Boolean GameListIsUp();
1383 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1386 // front-end part of option handling
1388 // [HGM] This platform-dependent table provides the location for storing the color info
1389 extern char *crWhite, * crBlack;
1393 &appData.whitePieceColor,
1394 &appData.blackPieceColor,
1395 &appData.lightSquareColor,
1396 &appData.darkSquareColor,
1397 &appData.highlightSquareColor,
1398 &appData.premoveHighlightColor,
1399 &appData.lowTimeWarningColor,
1410 // [HGM] font: keep a font for each square size, even non-stndard ones
1411 #define NUM_SIZES 18
1412 #define MAX_SIZE 130
1413 Boolean fontSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1414 char *fontTable[NUM_FONTS][MAX_SIZE];
1417 ParseFont(char *name, int number)
1418 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1420 if(sscanf(name, "size%d:", &size)) {
1421 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1422 // defer processing it until we know if it matches our board size
1423 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1424 fontTable[number][size] = strdup(strchr(name, ':')+1);
1425 fontValid[number][size] = True;
1430 case 0: // CLOCK_FONT
1431 appData.clockFont = strdup(name);
1433 case 1: // MESSAGE_FONT
1434 appData.font = strdup(name);
1436 case 2: // COORD_FONT
1437 appData.coordFont = strdup(name);
1442 fontSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1447 { // only 2 fonts currently
1448 appData.clockFont = CLOCK_FONT_NAME;
1449 appData.coordFont = COORD_FONT_NAME;
1450 appData.font = DEFAULT_FONT_NAME;
1455 { // no-op, until we identify the code for this already in XBoard and move it here
1459 ParseColor(int n, char *name)
1460 { // in XBoard, just copy the color-name string
1461 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1465 ParseTextAttribs(ColorClass cc, char *s)
1467 (&appData.colorShout)[cc] = strdup(s);
1471 ParseBoardSize(void *addr, char *name)
1473 appData.boardSize = strdup(name);
1478 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1482 SetCommPortDefaults()
1483 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1486 // [HGM] args: these three cases taken out to stay in front-end
1488 SaveFontArg(FILE *f, ArgDescriptor *ad)
1491 int i, n = (int)(intptr_t)ad->argLoc;
1493 case 0: // CLOCK_FONT
1494 name = appData.clockFont;
1496 case 1: // MESSAGE_FONT
1497 name = appData.font;
1499 case 2: // COORD_FONT
1500 name = appData.coordFont;
1505 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1506 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1507 fontTable[n][squareSize] = strdup(name);
1508 fontValid[n][squareSize] = True;
1511 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1512 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1517 { // nothing to do, as the sounds are at all times represented by their text-string names already
1521 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1522 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1523 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1527 SaveColor(FILE *f, ArgDescriptor *ad)
1528 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1529 if(colorVariable[(int)(intptr_t)ad->argLoc])
1530 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1534 SaveBoardSize(FILE *f, char *name, void *addr)
1535 { // wrapper to shield back-end from BoardSize & sizeInfo
1536 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1540 ParseCommPortSettings(char *s)
1541 { // no such option in XBoard (yet)
1544 extern Widget engineOutputShell;
1547 GetActualPlacement(Widget wg, WindowPlacement *wp)
1557 XtSetArg(args[i], XtNx, &x); i++;
1558 XtSetArg(args[i], XtNy, &y); i++;
1559 XtSetArg(args[i], XtNwidth, &w); i++;
1560 XtSetArg(args[i], XtNheight, &h); i++;
1561 XtGetValues(wg, args, i);
1570 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1571 // In XBoard this will have to wait until awareness of window parameters is implemented
1572 GetActualPlacement(shellWidget, &wpMain);
1573 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1574 if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1575 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1576 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1577 if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1578 if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1582 PrintCommPortSettings(FILE *f, char *name)
1583 { // This option does not exist in XBoard
1587 MySearchPath(char *installDir, char *name, char *fullname)
1588 { // just append installDir and name. Perhaps ExpandPath should be used here?
1589 name = ExpandPathName(name);
1590 if(name && name[0] == '/')
1591 safeStrCpy(fullname, name, MSG_SIZ );
1593 sprintf(fullname, "%s%c%s", installDir, '/', name);
1599 MyGetFullPathName(char *name, char *fullname)
1600 { // should use ExpandPath?
1601 name = ExpandPathName(name);
1602 safeStrCpy(fullname, name, MSG_SIZ );
1607 EnsureOnScreen(int *x, int *y, int minX, int minY)
1614 { // [HGM] args: allows testing if main window is realized from back-end
1615 return xBoardWindow != 0;
1619 PopUpStartupDialog()
1620 { // start menu not implemented in XBoard
1624 ConvertToLine(int argc, char **argv)
1626 static char line[128*1024], buf[1024];
1630 for(i=1; i<argc; i++)
1632 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1633 && argv[i][0] != '{' )
1634 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1636 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1637 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1640 line[strlen(line)-1] = NULLCHAR;
1644 //--------------------------------------------------------------------------------------------
1646 extern Boolean twoBoards, partnerUp;
1649 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1651 #define BoardSize int
1652 void InitDrawingSizes(BoardSize boardSize, int flags)
1653 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1654 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1656 XtGeometryResult gres;
1659 if(!formWidget) return;
1662 * Enable shell resizing.
1664 shellArgs[0].value = (XtArgVal) &w;
1665 shellArgs[1].value = (XtArgVal) &h;
1666 XtGetValues(shellWidget, shellArgs, 2);
1668 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1669 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1670 XtSetValues(shellWidget, &shellArgs[2], 4);
1672 XtSetArg(args[0], XtNdefaultDistance, &sep);
1673 XtGetValues(formWidget, args, 1);
1675 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1676 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1677 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1679 hOffset = boardWidth + 10;
1680 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1681 secondSegments[i] = gridSegments[i];
1682 secondSegments[i].x1 += hOffset;
1683 secondSegments[i].x2 += hOffset;
1686 XtSetArg(args[0], XtNwidth, boardWidth);
1687 XtSetArg(args[1], XtNheight, boardHeight);
1688 XtSetValues(boardWidget, args, 2);
1690 timerWidth = (boardWidth - sep) / 2;
1691 XtSetArg(args[0], XtNwidth, timerWidth);
1692 XtSetValues(whiteTimerWidget, args, 1);
1693 XtSetValues(blackTimerWidget, args, 1);
1695 XawFormDoLayout(formWidget, False);
1697 if (appData.titleInWindow) {
1699 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1700 XtSetArg(args[i], XtNheight, &h); i++;
1701 XtGetValues(titleWidget, args, i);
1703 w = boardWidth - 2*bor;
1705 XtSetArg(args[0], XtNwidth, &w);
1706 XtGetValues(menuBarWidget, args, 1);
1707 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1710 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1711 if (gres != XtGeometryYes && appData.debugMode) {
1713 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1714 programName, gres, w, h, wr, hr);
1718 XawFormDoLayout(formWidget, True);
1721 * Inhibit shell resizing.
1723 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1724 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1725 shellArgs[4].value = shellArgs[2].value = w;
1726 shellArgs[5].value = shellArgs[3].value = h;
1727 XtSetValues(shellWidget, &shellArgs[0], 6);
1729 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1732 for(i=0; i<4; i++) {
1734 for(p=0; p<=(int)WhiteKing; p++)
1735 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1736 if(gameInfo.variant == VariantShogi) {
1737 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1738 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1739 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1740 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1741 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1744 if(gameInfo.variant == VariantGothic) {
1745 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1748 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1749 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1750 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1753 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1754 for(p=0; p<=(int)WhiteKing; p++)
1755 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1756 if(gameInfo.variant == VariantShogi) {
1757 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1758 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1759 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1760 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1761 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1764 if(gameInfo.variant == VariantGothic) {
1765 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1768 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1769 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1770 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1775 for(i=0; i<2; i++) {
1777 for(p=0; p<=(int)WhiteKing; p++)
1778 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1779 if(gameInfo.variant == VariantShogi) {
1780 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1781 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1782 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1783 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1784 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1787 if(gameInfo.variant == VariantGothic) {
1788 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1791 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1792 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1793 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1803 void ParseIcsTextColors()
1804 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1805 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1806 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1807 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1808 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1809 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1810 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1811 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1812 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1813 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1814 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1816 if (appData.colorize) {
1818 _("%s: can't parse color names; disabling colorization\n"),
1821 appData.colorize = FALSE;
1826 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1827 XrmValue vFrom, vTo;
1828 int forceMono = False;
1830 if (!appData.monoMode) {
1831 vFrom.addr = (caddr_t) appData.lightSquareColor;
1832 vFrom.size = strlen(appData.lightSquareColor);
1833 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1834 if (vTo.addr == NULL) {
1835 appData.monoMode = True;
1838 lightSquareColor = *(Pixel *) vTo.addr;
1841 if (!appData.monoMode) {
1842 vFrom.addr = (caddr_t) appData.darkSquareColor;
1843 vFrom.size = strlen(appData.darkSquareColor);
1844 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1845 if (vTo.addr == NULL) {
1846 appData.monoMode = True;
1849 darkSquareColor = *(Pixel *) vTo.addr;
1852 if (!appData.monoMode) {
1853 vFrom.addr = (caddr_t) appData.whitePieceColor;
1854 vFrom.size = strlen(appData.whitePieceColor);
1855 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1856 if (vTo.addr == NULL) {
1857 appData.monoMode = True;
1860 whitePieceColor = *(Pixel *) vTo.addr;
1863 if (!appData.monoMode) {
1864 vFrom.addr = (caddr_t) appData.blackPieceColor;
1865 vFrom.size = strlen(appData.blackPieceColor);
1866 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1867 if (vTo.addr == NULL) {
1868 appData.monoMode = True;
1871 blackPieceColor = *(Pixel *) vTo.addr;
1875 if (!appData.monoMode) {
1876 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1877 vFrom.size = strlen(appData.highlightSquareColor);
1878 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1879 if (vTo.addr == NULL) {
1880 appData.monoMode = True;
1883 highlightSquareColor = *(Pixel *) vTo.addr;
1887 if (!appData.monoMode) {
1888 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1889 vFrom.size = strlen(appData.premoveHighlightColor);
1890 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1891 if (vTo.addr == NULL) {
1892 appData.monoMode = True;
1895 premoveHighlightColor = *(Pixel *) vTo.addr;
1906 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1907 XSetWindowAttributes window_attributes;
1909 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1910 XrmValue vFrom, vTo;
1911 XtGeometryResult gres;
1914 int forceMono = False;
1916 srandom(time(0)); // [HGM] book: make random truly random
1918 setbuf(stdout, NULL);
1919 setbuf(stderr, NULL);
1922 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1923 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1927 programName = strrchr(argv[0], '/');
1928 if (programName == NULL)
1929 programName = argv[0];
1934 XtSetLanguageProc(NULL, NULL, NULL);
1935 bindtextdomain(PACKAGE, LOCALEDIR);
1936 textdomain(PACKAGE);
1940 XtAppInitialize(&appContext, "XBoard", shellOptions,
1941 XtNumber(shellOptions),
1942 &argc, argv, xboardResources, NULL, 0);
1943 appData.boardSize = "";
1944 InitAppData(ConvertToLine(argc, argv));
1946 if (p == NULL) p = "/tmp";
1947 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1948 gameCopyFilename = (char*) malloc(i);
1949 gamePasteFilename = (char*) malloc(i);
1950 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1951 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1953 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1954 clientResources, XtNumber(clientResources),
1957 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1958 static char buf[MSG_SIZ];
1959 EscapeExpand(buf, appData.initString);
1960 appData.initString = strdup(buf);
1961 EscapeExpand(buf, appData.secondInitString);
1962 appData.secondInitString = strdup(buf);
1963 EscapeExpand(buf, appData.firstComputerString);
1964 appData.firstComputerString = strdup(buf);
1965 EscapeExpand(buf, appData.secondComputerString);
1966 appData.secondComputerString = strdup(buf);
1969 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1972 if (chdir(chessDir) != 0) {
1973 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1979 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1980 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1981 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1982 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1985 setbuf(debugFP, NULL);
1988 /* [HGM,HR] make sure board size is acceptable */
1989 if(appData.NrFiles > BOARD_FILES ||
1990 appData.NrRanks > BOARD_RANKS )
1991 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1994 /* This feature does not work; animation needs a rewrite */
1995 appData.highlightDragging = FALSE;
1999 xDisplay = XtDisplay(shellWidget);
2000 xScreen = DefaultScreen(xDisplay);
2001 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2003 gameInfo.variant = StringToVariant(appData.variant);
2004 InitPosition(FALSE);
2007 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2009 if (isdigit(appData.boardSize[0])) {
2010 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2011 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2012 &fontPxlSize, &smallLayout, &tinyLayout);
2014 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2015 programName, appData.boardSize);
2019 /* Find some defaults; use the nearest known size */
2020 SizeDefaults *szd, *nearest;
2021 int distance = 99999;
2022 nearest = szd = sizeDefaults;
2023 while (szd->name != NULL) {
2024 if (abs(szd->squareSize - squareSize) < distance) {
2026 distance = abs(szd->squareSize - squareSize);
2027 if (distance == 0) break;
2031 if (i < 2) lineGap = nearest->lineGap;
2032 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2033 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2034 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2035 if (i < 6) smallLayout = nearest->smallLayout;
2036 if (i < 7) tinyLayout = nearest->tinyLayout;
2039 SizeDefaults *szd = sizeDefaults;
2040 if (*appData.boardSize == NULLCHAR) {
2041 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2042 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2045 if (szd->name == NULL) szd--;
2046 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2048 while (szd->name != NULL &&
2049 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2050 if (szd->name == NULL) {
2051 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2052 programName, appData.boardSize);
2056 squareSize = szd->squareSize;
2057 lineGap = szd->lineGap;
2058 clockFontPxlSize = szd->clockFontPxlSize;
2059 coordFontPxlSize = szd->coordFontPxlSize;
2060 fontPxlSize = szd->fontPxlSize;
2061 smallLayout = szd->smallLayout;
2062 tinyLayout = szd->tinyLayout;
2063 // [HGM] font: use defaults from settings file if available and not overruled
2065 if(!fontSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2066 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2067 if(!fontSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2068 appData.font = fontTable[MESSAGE_FONT][squareSize];
2069 if(!fontSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2070 appData.coordFont = fontTable[COORD_FONT][squareSize];
2072 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2073 if (strlen(appData.pixmapDirectory) > 0) {
2074 p = ExpandPathName(appData.pixmapDirectory);
2076 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2077 appData.pixmapDirectory);
2080 if (appData.debugMode) {
2081 fprintf(stderr, _("\
2082 XBoard square size (hint): %d\n\
2083 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2085 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2086 if (appData.debugMode) {
2087 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2090 defaultLineGap = lineGap;
2091 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2093 /* [HR] height treated separately (hacked) */
2094 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2095 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2096 if (appData.showJail == 1) {
2097 /* Jail on top and bottom */
2098 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2099 XtSetArg(boardArgs[2], XtNheight,
2100 boardHeight + 2*(lineGap + squareSize));
2101 } else if (appData.showJail == 2) {
2103 XtSetArg(boardArgs[1], XtNwidth,
2104 boardWidth + 2*(lineGap + squareSize));
2105 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2108 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2109 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2113 * Determine what fonts to use.
2115 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2116 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2117 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2118 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2119 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2120 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2121 appData.font = FindFont(appData.font, fontPxlSize);
2122 countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
2123 countFontStruct = XQueryFont(xDisplay, countFontID);
2124 // appData.font = FindFont(appData.font, fontPxlSize);
2126 xdb = XtDatabase(xDisplay);
2127 XrmPutStringResource(&xdb, "*font", appData.font);
2130 * Detect if there are not enough colors available and adapt.
2132 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2133 appData.monoMode = True;
2136 forceMono = MakeColors();
2139 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2142 if (appData.bitmapDirectory == NULL ||
2143 appData.bitmapDirectory[0] == NULLCHAR)
2144 appData.bitmapDirectory = DEF_BITMAP_DIR;
2147 if (appData.lowTimeWarning && !appData.monoMode) {
2148 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2149 vFrom.size = strlen(appData.lowTimeWarningColor);
2150 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2151 if (vTo.addr == NULL)
2152 appData.monoMode = True;
2154 lowTimeWarningColor = *(Pixel *) vTo.addr;
2157 if (appData.monoMode && appData.debugMode) {
2158 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2159 (unsigned long) XWhitePixel(xDisplay, xScreen),
2160 (unsigned long) XBlackPixel(xDisplay, xScreen));
2163 ParseIcsTextColors();
2164 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2165 textColors[ColorNone].attr = 0;
2167 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2173 layoutName = "tinyLayout";
2174 } else if (smallLayout) {
2175 layoutName = "smallLayout";
2177 layoutName = "normalLayout";
2179 /* Outer layoutWidget is there only to provide a name for use in
2180 resources that depend on the layout style */
2182 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2183 layoutArgs, XtNumber(layoutArgs));
2185 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2186 formArgs, XtNumber(formArgs));
2187 XtSetArg(args[0], XtNdefaultDistance, &sep);
2188 XtGetValues(formWidget, args, 1);
2191 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2192 XtSetArg(args[0], XtNtop, XtChainTop);
2193 XtSetArg(args[1], XtNbottom, XtChainTop);
2194 XtSetArg(args[2], XtNright, XtChainLeft);
2195 XtSetValues(menuBarWidget, args, 3);
2197 widgetList[j++] = whiteTimerWidget =
2198 XtCreateWidget("whiteTime", labelWidgetClass,
2199 formWidget, timerArgs, XtNumber(timerArgs));
2200 XtSetArg(args[0], XtNfont, clockFontStruct);
2201 XtSetArg(args[1], XtNtop, XtChainTop);
2202 XtSetArg(args[2], XtNbottom, XtChainTop);
2203 XtSetValues(whiteTimerWidget, args, 3);
2205 widgetList[j++] = blackTimerWidget =
2206 XtCreateWidget("blackTime", labelWidgetClass,
2207 formWidget, timerArgs, XtNumber(timerArgs));
2208 XtSetArg(args[0], XtNfont, clockFontStruct);
2209 XtSetArg(args[1], XtNtop, XtChainTop);
2210 XtSetArg(args[2], XtNbottom, XtChainTop);
2211 XtSetValues(blackTimerWidget, args, 3);
2213 if (appData.titleInWindow) {
2214 widgetList[j++] = titleWidget =
2215 XtCreateWidget("title", labelWidgetClass, formWidget,
2216 titleArgs, XtNumber(titleArgs));
2217 XtSetArg(args[0], XtNtop, XtChainTop);
2218 XtSetArg(args[1], XtNbottom, XtChainTop);
2219 XtSetValues(titleWidget, args, 2);
2222 if (appData.showButtonBar) {
2223 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2224 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2225 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2226 XtSetArg(args[2], XtNtop, XtChainTop);
2227 XtSetArg(args[3], XtNbottom, XtChainTop);
2228 XtSetValues(buttonBarWidget, args, 4);
2231 widgetList[j++] = messageWidget =
2232 XtCreateWidget("message", labelWidgetClass, formWidget,
2233 messageArgs, XtNumber(messageArgs));
2234 XtSetArg(args[0], XtNtop, XtChainTop);
2235 XtSetArg(args[1], XtNbottom, XtChainTop);
2236 XtSetValues(messageWidget, args, 2);
2238 widgetList[j++] = boardWidget =
2239 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2240 XtNumber(boardArgs));
2242 XtManageChildren(widgetList, j);
2244 timerWidth = (boardWidth - sep) / 2;
2245 XtSetArg(args[0], XtNwidth, timerWidth);
2246 XtSetValues(whiteTimerWidget, args, 1);
2247 XtSetValues(blackTimerWidget, args, 1);
2249 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2250 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2251 XtGetValues(whiteTimerWidget, args, 2);
2253 if (appData.showButtonBar) {
2254 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2255 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2256 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2260 * formWidget uses these constraints but they are stored
2264 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2265 XtSetValues(menuBarWidget, args, i);
2266 if (appData.titleInWindow) {
2269 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2270 XtSetValues(whiteTimerWidget, args, i);
2272 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2273 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2274 XtSetValues(blackTimerWidget, args, i);
2276 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2277 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2278 XtSetValues(titleWidget, args, i);
2280 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2281 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2282 XtSetValues(messageWidget, args, i);
2283 if (appData.showButtonBar) {
2285 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2286 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2287 XtSetValues(buttonBarWidget, args, i);
2291 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2292 XtSetValues(whiteTimerWidget, args, i);
2294 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2295 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2296 XtSetValues(blackTimerWidget, args, i);
2298 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2299 XtSetValues(titleWidget, args, i);
2301 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2302 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2303 XtSetValues(messageWidget, args, i);
2304 if (appData.showButtonBar) {
2306 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2307 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2308 XtSetValues(buttonBarWidget, args, i);
2313 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2314 XtSetValues(whiteTimerWidget, args, i);
2316 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2317 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2318 XtSetValues(blackTimerWidget, args, i);
2320 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2321 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2322 XtSetValues(messageWidget, args, i);
2323 if (appData.showButtonBar) {
2325 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2326 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2327 XtSetValues(buttonBarWidget, args, i);
2331 XtSetArg(args[0], XtNfromVert, messageWidget);
2332 XtSetArg(args[1], XtNtop, XtChainTop);
2333 XtSetArg(args[2], XtNbottom, XtChainBottom);
2334 XtSetArg(args[3], XtNleft, XtChainLeft);
2335 XtSetArg(args[4], XtNright, XtChainRight);
2336 XtSetValues(boardWidget, args, 5);
2338 XtRealizeWidget(shellWidget);
2341 XtSetArg(args[0], XtNx, wpMain.x);
2342 XtSetArg(args[1], XtNy, wpMain.y);
2343 XtSetValues(shellWidget, args, 2);
2347 * Correct the width of the message and title widgets.
2348 * It is not known why some systems need the extra fudge term.
2349 * The value "2" is probably larger than needed.
2351 XawFormDoLayout(formWidget, False);
2353 #define WIDTH_FUDGE 2
2355 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2356 XtSetArg(args[i], XtNheight, &h); i++;
2357 XtGetValues(messageWidget, args, i);
2358 if (appData.showButtonBar) {
2360 XtSetArg(args[i], XtNwidth, &w); i++;
2361 XtGetValues(buttonBarWidget, args, i);
2362 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2364 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2367 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2368 if (gres != XtGeometryYes && appData.debugMode) {
2369 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2370 programName, gres, w, h, wr, hr);
2373 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2374 /* The size used for the child widget in layout lags one resize behind
2375 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2377 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2378 if (gres != XtGeometryYes && appData.debugMode) {
2379 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2380 programName, gres, w, h, wr, hr);
2383 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2384 XtSetArg(args[1], XtNright, XtChainRight);
2385 XtSetValues(messageWidget, args, 2);
2387 if (appData.titleInWindow) {
2389 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2390 XtSetArg(args[i], XtNheight, &h); i++;
2391 XtGetValues(titleWidget, args, i);
2393 w = boardWidth - 2*bor;
2395 XtSetArg(args[0], XtNwidth, &w);
2396 XtGetValues(menuBarWidget, args, 1);
2397 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2400 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2401 if (gres != XtGeometryYes && appData.debugMode) {
2403 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2404 programName, gres, w, h, wr, hr);
2407 XawFormDoLayout(formWidget, True);
2409 xBoardWindow = XtWindow(boardWidget);
2411 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2412 // not need to go into InitDrawingSizes().
2416 * Create X checkmark bitmap and initialize option menu checks.
2418 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2419 checkmark_bits, checkmark_width, checkmark_height);
2420 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2421 #ifndef OPTIONSDIALOG
2422 if (appData.alwaysPromoteToQueen) {
2423 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2426 if (appData.animateDragging) {
2427 XtSetValues(XtNameToWidget(menuBarWidget,
2428 "menuOptions.Animate Dragging"),
2431 if (appData.animate) {
2432 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2435 if (appData.autoCallFlag) {
2436 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2439 if (appData.autoFlipView) {
2440 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2443 if (appData.blindfold) {
2444 XtSetValues(XtNameToWidget(menuBarWidget,
2445 "menuOptions.Blindfold"), args, 1);
2447 if (appData.flashCount > 0) {
2448 XtSetValues(XtNameToWidget(menuBarWidget,
2449 "menuOptions.Flash Moves"),
2453 if (appData.highlightDragging) {
2454 XtSetValues(XtNameToWidget(menuBarWidget,
2455 "menuOptions.Highlight Dragging"),
2459 if (appData.highlightLastMove) {
2460 XtSetValues(XtNameToWidget(menuBarWidget,
2461 "menuOptions.Highlight Last Move"),
2464 if (appData.highlightMoveWithArrow) {
2465 XtSetValues(XtNameToWidget(menuBarWidget,
2466 "menuOptions.Arrow"),
2469 // if (appData.icsAlarm) {
2470 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2473 if (appData.ringBellAfterMoves) {
2474 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2477 if (appData.oneClick) {
2478 XtSetValues(XtNameToWidget(menuBarWidget,
2479 "menuOptions.OneClick"), args, 1);
2481 if (appData.periodicUpdates) {
2482 XtSetValues(XtNameToWidget(menuBarWidget,
2483 "menuOptions.Periodic Updates"), args, 1);
2485 if (appData.ponderNextMove) {
2486 XtSetValues(XtNameToWidget(menuBarWidget,
2487 "menuOptions.Ponder Next Move"), args, 1);
2489 if (appData.popupExitMessage) {
2490 XtSetValues(XtNameToWidget(menuBarWidget,
2491 "menuOptions.Popup Exit Message"), args, 1);
2493 if (appData.popupMoveErrors) {
2494 XtSetValues(XtNameToWidget(menuBarWidget,
2495 "menuOptions.Popup Move Errors"), args, 1);
2497 // if (appData.premove) {
2498 // XtSetValues(XtNameToWidget(menuBarWidget,
2499 // "menuOptions.Premove"), args, 1);
2501 if (appData.showCoords) {
2502 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2505 if (appData.hideThinkingFromHuman) {
2506 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2509 if (appData.testLegality) {
2510 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2514 if (saveSettingsOnExit) {
2515 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2522 ReadBitmap(&wIconPixmap, "icon_white.bm",
2523 icon_white_bits, icon_white_width, icon_white_height);
2524 ReadBitmap(&bIconPixmap, "icon_black.bm",
2525 icon_black_bits, icon_black_width, icon_black_height);
2526 iconPixmap = wIconPixmap;
2528 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2529 XtSetValues(shellWidget, args, i);
2532 * Create a cursor for the board widget.
2534 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2535 XChangeWindowAttributes(xDisplay, xBoardWindow,
2536 CWCursor, &window_attributes);
2539 * Inhibit shell resizing.
2541 shellArgs[0].value = (XtArgVal) &w;
2542 shellArgs[1].value = (XtArgVal) &h;
2543 XtGetValues(shellWidget, shellArgs, 2);
2544 shellArgs[4].value = shellArgs[2].value = w;
2545 shellArgs[5].value = shellArgs[3].value = h;
2546 XtSetValues(shellWidget, &shellArgs[2], 4);
2547 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2548 marginH = h - boardHeight;
2550 CatchDeleteWindow(shellWidget, "QuitProc");
2555 if (appData.bitmapDirectory[0] != NULLCHAR) {
2559 CreateXPMBoard(appData.liteBackTextureFile, 1);
2560 CreateXPMBoard(appData.darkBackTextureFile, 0);
2564 /* Create regular pieces */
2565 if (!useImages) CreatePieces();
2570 if (appData.animate || appData.animateDragging)
2573 XtAugmentTranslations(formWidget,
2574 XtParseTranslationTable(globalTranslations));
2575 XtAugmentTranslations(boardWidget,
2576 XtParseTranslationTable(boardTranslations));
2577 XtAugmentTranslations(whiteTimerWidget,
2578 XtParseTranslationTable(whiteTranslations));
2579 XtAugmentTranslations(blackTimerWidget,
2580 XtParseTranslationTable(blackTranslations));
2582 /* Why is the following needed on some versions of X instead
2583 * of a translation? */
2584 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2585 (XtEventHandler) EventProc, NULL);
2587 XtAddEventHandler(formWidget, KeyPressMask, False,
2588 (XtEventHandler) MoveTypeInProc, NULL);
2590 /* [AS] Restore layout */
2591 if( wpMoveHistory.visible ) {
2595 if( wpEvalGraph.visible )
2600 if( wpEngineOutput.visible ) {
2601 EngineOutputPopUp();
2606 if (errorExitStatus == -1) {
2607 if (appData.icsActive) {
2608 /* We now wait until we see "login:" from the ICS before
2609 sending the logon script (problems with timestamp otherwise) */
2610 /*ICSInitScript();*/
2611 if (appData.icsInputBox) ICSInputBoxPopUp();
2615 signal(SIGWINCH, TermSizeSigHandler);
2617 signal(SIGINT, IntSigHandler);
2618 signal(SIGTERM, IntSigHandler);
2619 if (*appData.cmailGameName != NULLCHAR) {
2620 signal(SIGUSR1, CmailSigHandler);
2623 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2625 // XtSetKeyboardFocus(shellWidget, formWidget);
2626 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2628 XtAppMainLoop(appContext);
2629 if (appData.debugMode) fclose(debugFP); // [DM] debug
2636 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2637 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2639 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2640 unlink(gameCopyFilename);
2641 unlink(gamePasteFilename);
2644 RETSIGTYPE TermSizeSigHandler(int sig)
2657 CmailSigHandler(sig)
2663 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2665 /* Activate call-back function CmailSigHandlerCallBack() */
2666 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2668 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2672 CmailSigHandlerCallBack(isr, closure, message, count, error)
2680 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2682 /**** end signal code ****/
2688 /* try to open the icsLogon script, either in the location given
2689 * or in the users HOME directory
2696 f = fopen(appData.icsLogon, "r");
2699 homedir = getenv("HOME");
2700 if (homedir != NULL)
2702 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2703 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2704 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2705 f = fopen(buf, "r");
2710 ProcessICSInitScript(f);
2712 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2735 if (!menuBarWidget) return;
2736 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2738 DisplayError("menuEdit.Revert", 0);
2740 XtSetSensitive(w, !grey);
2742 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2744 DisplayError("menuEdit.Annotate", 0);
2746 XtSetSensitive(w, !grey);
2751 SetMenuEnables(enab)
2755 if (!menuBarWidget) return;
2756 while (enab->name != NULL) {
2757 w = XtNameToWidget(menuBarWidget, enab->name);
2759 DisplayError(enab->name, 0);
2761 XtSetSensitive(w, enab->value);
2767 Enables icsEnables[] = {
2768 { "menuFile.Mail Move", False },
2769 { "menuFile.Reload CMail Message", False },
2770 { "menuMode.Machine Black", False },
2771 { "menuMode.Machine White", False },
2772 { "menuMode.Analysis Mode", False },
2773 { "menuMode.Analyze File", False },
2774 { "menuMode.Two Machines", False },
2775 { "menuMode.Machine Match", False },
2777 { "menuEngine.Hint", False },
2778 { "menuEngine.Book", False },
2779 { "menuEngine.Move Now", False },
2780 #ifndef OPTIONSDIALOG
2781 { "menuOptions.Periodic Updates", False },
2782 { "menuOptions.Hide Thinking", False },
2783 { "menuOptions.Ponder Next Move", False },
2785 { "menuEngine.Engine #1 Settings", False },
2787 { "menuEngine.Engine #2 Settings", False },
2788 { "menuEdit.Annotate", False },
2792 Enables ncpEnables[] = {
2793 { "menuFile.Mail Move", False },
2794 { "menuFile.Reload CMail Message", False },
2795 { "menuMode.Machine White", False },
2796 { "menuMode.Machine Black", False },
2797 { "menuMode.Analysis Mode", False },
2798 { "menuMode.Analyze File", False },
2799 { "menuMode.Two Machines", False },
2800 { "menuMode.Machine Match", False },
2801 { "menuMode.ICS Client", False },
2802 { "menuView.ICStex", False },
2803 { "menuView.ICS Input Box", False },
2804 { "Action", False },
2805 { "menuEdit.Revert", False },
2806 { "menuEdit.Annotate", False },
2807 { "menuEngine.Engine #1 Settings", False },
2808 { "menuEngine.Engine #2 Settings", False },
2809 { "menuEngine.Move Now", False },
2810 { "menuEngine.Retract Move", False },
2811 { "menuOptions.ICS", False },
2812 #ifndef OPTIONSDIALOG
2813 { "menuOptions.Auto Flag", False },
2814 { "menuOptions.Auto Flip View", False },
2815 // { "menuOptions.ICS Alarm", False },
2816 { "menuOptions.Move Sound", False },
2817 { "menuOptions.Hide Thinking", False },
2818 { "menuOptions.Periodic Updates", False },
2819 { "menuOptions.Ponder Next Move", False },
2821 { "menuEngine.Hint", False },
2822 { "menuEngine.Book", False },
2826 Enables gnuEnables[] = {
2827 { "menuMode.ICS Client", False },
2828 { "menuView.ICStex", False },
2829 { "menuView.ICS Input Box", False },
2830 { "menuAction.Accept", False },
2831 { "menuAction.Decline", False },
2832 { "menuAction.Rematch", False },
2833 { "menuAction.Adjourn", False },
2834 { "menuAction.Stop Examining", False },
2835 { "menuAction.Stop Observing", False },
2836 { "menuAction.Upload to Examine", False },
2837 { "menuEdit.Revert", False },
2838 { "menuEdit.Annotate", False },
2839 { "menuOptions.ICS", False },
2841 /* The next two options rely on SetCmailMode being called *after* */
2842 /* SetGNUMode so that when GNU is being used to give hints these */
2843 /* menu options are still available */
2845 { "menuFile.Mail Move", False },
2846 { "menuFile.Reload CMail Message", False },
2850 Enables cmailEnables[] = {
2852 { "menuAction.Call Flag", False },
2853 { "menuAction.Draw", True },
2854 { "menuAction.Adjourn", False },
2855 { "menuAction.Abort", False },
2856 { "menuAction.Stop Observing", False },
2857 { "menuAction.Stop Examining", False },
2858 { "menuFile.Mail Move", True },
2859 { "menuFile.Reload CMail Message", True },
2863 Enables trainingOnEnables[] = {
2864 { "menuMode.Edit Comment", False },
2865 { "menuMode.Pause", False },
2866 { "menuEdit.Forward", False },
2867 { "menuEdit.Backward", False },
2868 { "menuEdit.Forward to End", False },
2869 { "menuEdit.Back to Start", False },
2870 { "menuEngine.Move Now", False },
2871 { "menuEdit.Truncate Game", False },
2875 Enables trainingOffEnables[] = {
2876 { "menuMode.Edit Comment", True },
2877 { "menuMode.Pause", True },
2878 { "menuEdit.Forward", True },
2879 { "menuEdit.Backward", True },
2880 { "menuEdit.Forward to End", True },
2881 { "menuEdit.Back to Start", True },
2882 { "menuEngine.Move Now", True },
2883 { "menuEdit.Truncate Game", True },
2887 Enables machineThinkingEnables[] = {
2888 { "menuFile.Load Game", False },
2889 // { "menuFile.Load Next Game", False },
2890 // { "menuFile.Load Previous Game", False },
2891 // { "menuFile.Reload Same Game", False },
2892 { "menuEdit.Paste Game", False },
2893 { "menuFile.Load Position", False },
2894 // { "menuFile.Load Next Position", False },
2895 // { "menuFile.Load Previous Position", False },
2896 // { "menuFile.Reload Same Position", False },
2897 { "menuEdit.Paste Position", False },
2898 { "menuMode.Machine White", False },
2899 { "menuMode.Machine Black", False },
2900 { "menuMode.Two Machines", False },
2901 { "menuMode.Machine Match", False },
2902 { "menuEngine.Retract Move", False },
2906 Enables userThinkingEnables[] = {
2907 { "menuFile.Load Game", True },
2908 // { "menuFile.Load Next Game", True },
2909 // { "menuFile.Load Previous Game", True },
2910 // { "menuFile.Reload Same Game", True },
2911 { "menuEdit.Paste Game", True },
2912 { "menuFile.Load Position", True },
2913 // { "menuFile.Load Next Position", True },
2914 // { "menuFile.Load Previous Position", True },
2915 // { "menuFile.Reload Same Position", True },
2916 { "menuEdit.Paste Position", True },
2917 { "menuMode.Machine White", True },
2918 { "menuMode.Machine Black", True },
2919 { "menuMode.Two Machines", True },
2920 { "menuMode.Machine Match", True },
2921 { "menuEngine.Retract Move", True },
2927 SetMenuEnables(icsEnables);
2930 if (appData.zippyPlay && !appData.noChessProgram) /* [DM] icsEngineAnalyze */
2931 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2938 SetMenuEnables(ncpEnables);
2944 SetMenuEnables(gnuEnables);
2950 SetMenuEnables(cmailEnables);
2956 SetMenuEnables(trainingOnEnables);
2957 if (appData.showButtonBar) {
2958 XtSetSensitive(buttonBarWidget, False);
2964 SetTrainingModeOff()
2966 SetMenuEnables(trainingOffEnables);
2967 if (appData.showButtonBar) {
2968 XtSetSensitive(buttonBarWidget, True);
2973 SetUserThinkingEnables()
2975 if (appData.noChessProgram) return;
2976 SetMenuEnables(userThinkingEnables);
2980 SetMachineThinkingEnables()
2982 if (appData.noChessProgram) return;
2983 SetMenuEnables(machineThinkingEnables);
2985 case MachinePlaysBlack:
2986 case MachinePlaysWhite:
2987 case TwoMachinesPlay:
2988 XtSetSensitive(XtNameToWidget(menuBarWidget,
2989 ModeToWidgetName(gameMode)), True);
2996 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
2997 #define HISTORY_SIZE 64
2998 static char *history[HISTORY_SIZE];
2999 int histIn = 0, histP = 0;
3002 SaveInHistory(char *cmd)
3004 if (history[histIn] != NULL) {
3005 free(history[histIn]);
3006 history[histIn] = NULL;
3008 if (*cmd == NULLCHAR) return;
3009 history[histIn] = StrSave(cmd);
3010 histIn = (histIn + 1) % HISTORY_SIZE;
3011 if (history[histIn] != NULL) {
3012 free(history[histIn]);
3013 history[histIn] = NULL;
3019 PrevInHistory(char *cmd)
3022 if (histP == histIn) {
3023 if (history[histIn] != NULL) free(history[histIn]);
3024 history[histIn] = StrSave(cmd);
3026 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3027 if (newhp == histIn || history[newhp] == NULL) return NULL;
3029 return history[histP];
3035 if (histP == histIn) return NULL;
3036 histP = (histP + 1) % HISTORY_SIZE;
3037 return history[histP];
3039 // end of borrowed code
3041 #define Abs(n) ((n)<0 ? -(n) : (n))
3044 * Find a font that matches "pattern" that is as close as
3045 * possible to the targetPxlSize. Prefer fonts that are k
3046 * pixels smaller to fonts that are k pixels larger. The
3047 * pattern must be in the X Consortium standard format,
3048 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3049 * The return value should be freed with XtFree when no
3053 FindFont(pattern, targetPxlSize)
3057 char **fonts, *p, *best, *scalable, *scalableTail;
3058 int i, j, nfonts, minerr, err, pxlSize;
3061 char **missing_list;
3063 char *def_string, *base_fnt_lst, strInt[3];
3065 XFontStruct **fnt_list;
3066 base_fnt_lst = calloc(1, strlen(pattern) + 3);
3067 snprintf(strInt, sizeof(strInt)/sizeof(strInt[0]), "%d", targetPxlSize);
3068 p = strstr(pattern, "--");
3069 strncpy(base_fnt_lst, pattern, p - pattern + 2);
3070 strcat(base_fnt_lst, strInt);
3071 strcat(base_fnt_lst, strchr(p + 2, '-'));
3073 if ((fntSet = XCreateFontSet(xDisplay,
3077 &def_string)) == NULL) {
3079 fprintf(stderr, _("Unable to create font set.\n"));
3083 nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
3085 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3087 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3088 programName, pattern);
3096 for (i=0; i<nfonts; i++) {
3099 if (*p != '-') continue;
3101 if (*p == NULLCHAR) break;
3102 if (*p++ == '-') j++;
3104 if (j < 7) continue;
3107 scalable = fonts[i];
3110 err = pxlSize - targetPxlSize;
3111 if (Abs(err) < Abs(minerr) ||
3112 (minerr > 0 && err < 0 && -err == minerr)) {
3118 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3119 /* If the error is too big and there is a scalable font,
3120 use the scalable font. */
3121 int headlen = scalableTail - scalable;
3122 p = (char *) XtMalloc(strlen(scalable) + 10);
3123 while (isdigit(*scalableTail)) scalableTail++;
3124 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3126 p = (char *) XtMalloc(strlen(best) + 2);
3127 safeStrCpy(p, best, strlen(best)+1 );
3129 if (appData.debugMode) {
3130 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3131 pattern, targetPxlSize, p);
3134 if (missing_count > 0)
3135 XFreeStringList(missing_list);
3136 XFreeFontSet(xDisplay, fntSet);
3138 XFreeFontNames(fonts);
3144 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3145 // must be called before all non-first callse to CreateGCs()
3146 XtReleaseGC(shellWidget, highlineGC);
3147 XtReleaseGC(shellWidget, lightSquareGC);
3148 XtReleaseGC(shellWidget, darkSquareGC);
3149 XtReleaseGC(shellWidget, lineGC);
3150 if (appData.monoMode) {
3151 if (DefaultDepth(xDisplay, xScreen) == 1) {
3152 XtReleaseGC(shellWidget, wbPieceGC);
3154 XtReleaseGC(shellWidget, bwPieceGC);
3157 XtReleaseGC(shellWidget, prelineGC);
3158 XtReleaseGC(shellWidget, jailSquareGC);
3159 XtReleaseGC(shellWidget, wdPieceGC);
3160 XtReleaseGC(shellWidget, wlPieceGC);
3161 XtReleaseGC(shellWidget, wjPieceGC);
3162 XtReleaseGC(shellWidget, bdPieceGC);
3163 XtReleaseGC(shellWidget, blPieceGC);
3164 XtReleaseGC(shellWidget, bjPieceGC);
3168 void CreateGCs(int redo)
3170 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3171 | GCBackground | GCFunction | GCPlaneMask;
3172 XGCValues gc_values;
3175 gc_values.plane_mask = AllPlanes;
3176 gc_values.line_width = lineGap;
3177 gc_values.line_style = LineSolid;
3178 gc_values.function = GXcopy;
3181 DeleteGCs(); // called a second time; clean up old GCs first
3182 } else { // [HGM] grid and font GCs created on first call only
3183 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3184 gc_values.background = XWhitePixel(xDisplay, xScreen);
3185 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3186 XSetFont(xDisplay, coordGC, coordFontID);
3188 // [HGM] make font for holdings counts (white on black)
3189 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3190 gc_values.background = XBlackPixel(xDisplay, xScreen);
3191 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3192 XSetFont(xDisplay, countGC, countFontID);
3194 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3195 gc_values.background = XBlackPixel(xDisplay, xScreen);
3196 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3198 if (appData.monoMode) {
3199 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3200 gc_values.background = XWhitePixel(xDisplay, xScreen);
3201 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3203 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3204 gc_values.background = XBlackPixel(xDisplay, xScreen);
3205 lightSquareGC = wbPieceGC
3206 = XtGetGC(shellWidget, value_mask, &gc_values);
3208 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3209 gc_values.background = XWhitePixel(xDisplay, xScreen);
3210 darkSquareGC = bwPieceGC
3211 = XtGetGC(shellWidget, value_mask, &gc_values);
3213 if (DefaultDepth(xDisplay, xScreen) == 1) {
3214 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3215 gc_values.function = GXcopyInverted;
3216 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3217 gc_values.function = GXcopy;
3218 if (XBlackPixel(xDisplay, xScreen) == 1) {
3219 bwPieceGC = darkSquareGC;
3220 wbPieceGC = copyInvertedGC;
3222 bwPieceGC = copyInvertedGC;
3223 wbPieceGC = lightSquareGC;
3227 gc_values.foreground = highlightSquareColor;
3228 gc_values.background = highlightSquareColor;
3229 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3231 gc_values.foreground = premoveHighlightColor;
3232 gc_values.background = premoveHighlightColor;
3233 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3235 gc_values.foreground = lightSquareColor;
3236 gc_values.background = darkSquareColor;
3237 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3239 gc_values.foreground = darkSquareColor;
3240 gc_values.background = lightSquareColor;
3241 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3243 gc_values.foreground = jailSquareColor;
3244 gc_values.background = jailSquareColor;
3245 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3247 gc_values.foreground = whitePieceColor;
3248 gc_values.background = darkSquareColor;
3249 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3251 gc_values.foreground = whitePieceColor;
3252 gc_values.background = lightSquareColor;
3253 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3255 gc_values.foreground = whitePieceColor;
3256 gc_values.background = jailSquareColor;
3257 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3259 gc_values.foreground = blackPieceColor;
3260 gc_values.background = darkSquareColor;
3261 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3263 gc_values.foreground = blackPieceColor;
3264 gc_values.background = lightSquareColor;
3265 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3267 gc_values.foreground = blackPieceColor;
3268 gc_values.background = jailSquareColor;
3269 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3273 void loadXIM(xim, xmask, filename, dest, mask)
3286 fp = fopen(filename, "rb");
3288 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3295 for (y=0; y<h; ++y) {
3296 for (x=0; x<h; ++x) {
3301 XPutPixel(xim, x, y, blackPieceColor);
3303 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3306 XPutPixel(xim, x, y, darkSquareColor);
3308 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3311 XPutPixel(xim, x, y, whitePieceColor);
3313 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3316 XPutPixel(xim, x, y, lightSquareColor);
3318 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3326 /* create Pixmap of piece */
3327 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3329 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3332 /* create Pixmap of clipmask
3333 Note: We assume the white/black pieces have the same
3334 outline, so we make only 6 masks. This is okay
3335 since the XPM clipmask routines do the same. */
3337 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3339 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3342 /* now create the 1-bit version */
3343 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3346 values.foreground = 1;
3347 values.background = 0;
3349 /* Don't use XtGetGC, not read only */
3350 maskGC = XCreateGC(xDisplay, *mask,
3351 GCForeground | GCBackground, &values);
3352 XCopyPlane(xDisplay, temp, *mask, maskGC,
3353 0, 0, squareSize, squareSize, 0, 0, 1);
3354 XFreePixmap(xDisplay, temp);
3359 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3361 void CreateXIMPieces()
3366 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3371 /* The XSynchronize calls were copied from CreatePieces.
3372 Not sure if needed, but can't hurt */
3373 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3376 /* temp needed by loadXIM() */
3377 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3378 0, 0, ss, ss, AllPlanes, XYPixmap);
3380 if (strlen(appData.pixmapDirectory) == 0) {
3384 if (appData.monoMode) {
3385 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3389 fprintf(stderr, _("\nLoading XIMs...\n"));
3391 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3392 fprintf(stderr, "%d", piece+1);
3393 for (kind=0; kind<4; kind++) {
3394 fprintf(stderr, ".");
3395 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3396 ExpandPathName(appData.pixmapDirectory),
3397 piece <= (int) WhiteKing ? "" : "w",
3398 pieceBitmapNames[piece],
3400 ximPieceBitmap[kind][piece] =
3401 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3402 0, 0, ss, ss, AllPlanes, XYPixmap);
3403 if (appData.debugMode)
3404 fprintf(stderr, _("(File:%s:) "), buf);
3405 loadXIM(ximPieceBitmap[kind][piece],
3407 &(xpmPieceBitmap2[kind][piece]),
3408 &(ximMaskPm2[piece]));
3409 if(piece <= (int)WhiteKing)
3410 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3412 fprintf(stderr," ");
3414 /* Load light and dark squares */
3415 /* If the LSQ and DSQ pieces don't exist, we will
3416 draw them with solid squares. */
3417 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3418 if (access(buf, 0) != 0) {
3422 fprintf(stderr, _("light square "));
3424 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3425 0, 0, ss, ss, AllPlanes, XYPixmap);
3426 if (appData.debugMode)
3427 fprintf(stderr, _("(File:%s:) "), buf);
3429 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3430 fprintf(stderr, _("dark square "));
3431 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3432 ExpandPathName(appData.pixmapDirectory), ss);
3433 if (appData.debugMode)
3434 fprintf(stderr, _("(File:%s:) "), buf);
3436 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3437 0, 0, ss, ss, AllPlanes, XYPixmap);
3438 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3439 xpmJailSquare = xpmLightSquare;
3441 fprintf(stderr, _("Done.\n"));
3443 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3446 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3449 void CreateXPMBoard(char *s, int kind)
3453 if(s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3454 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3455 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3459 void FreeXPMPieces()
3460 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3461 // thisroutine has to be called t free the old piece pixmaps
3463 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3464 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3466 XFreePixmap(xDisplay, xpmLightSquare);
3467 XFreePixmap(xDisplay, xpmDarkSquare);
3471 void CreateXPMPieces()
3475 u_int ss = squareSize;
3477 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3478 XpmColorSymbol symbols[4];
3479 static int redo = False;
3481 if(redo) FreeXPMPieces(); else redo = 1;
3483 /* The XSynchronize calls were copied from CreatePieces.
3484 Not sure if needed, but can't hurt */
3485 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3487 /* Setup translations so piece colors match square colors */
3488 symbols[0].name = "light_piece";
3489 symbols[0].value = appData.whitePieceColor;
3490 symbols[1].name = "dark_piece";
3491 symbols[1].value = appData.blackPieceColor;
3492 symbols[2].name = "light_square";
3493 symbols[2].value = appData.lightSquareColor;
3494 symbols[3].name = "dark_square";
3495 symbols[3].value = appData.darkSquareColor;
3497 attr.valuemask = XpmColorSymbols;
3498 attr.colorsymbols = symbols;
3499 attr.numsymbols = 4;
3501 if (appData.monoMode) {
3502 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3506 if (strlen(appData.pixmapDirectory) == 0) {
3507 XpmPieces* pieces = builtInXpms;
3510 while (pieces->size != squareSize && pieces->size) pieces++;
3511 if (!pieces->size) {
3512 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3515 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3516 for (kind=0; kind<4; kind++) {
3518 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3519 pieces->xpm[piece][kind],
3520 &(xpmPieceBitmap2[kind][piece]),
3521 NULL, &attr)) != 0) {
3522 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3526 if(piece <= (int) WhiteKing)
3527 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3531 xpmJailSquare = xpmLightSquare;
3535 fprintf(stderr, _("\nLoading XPMs...\n"));
3538 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3539 fprintf(stderr, "%d ", piece+1);
3540 for (kind=0; kind<4; kind++) {
3541 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3542 ExpandPathName(appData.pixmapDirectory),
3543 piece > (int) WhiteKing ? "w" : "",
3544 pieceBitmapNames[piece],
3546 if (appData.debugMode) {
3547 fprintf(stderr, _("(File:%s:) "), buf);
3549 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3550 &(xpmPieceBitmap2[kind][piece]),
3551 NULL, &attr)) != 0) {
3552 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3553 // [HGM] missing: read of unorthodox piece failed; substitute King.
3554 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3555 ExpandPathName(appData.pixmapDirectory),
3557 if (appData.debugMode) {
3558 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3560 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3561 &(xpmPieceBitmap2[kind][piece]),
3565 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3570 if(piece <= (int) WhiteKing)
3571 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3574 /* Load light and dark squares */
3575 /* If the LSQ and DSQ pieces don't exist, we will
3576 draw them with solid squares. */
3577 fprintf(stderr, _("light square "));
3578 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3579 if (access(buf, 0) != 0) {
3583 if (appData.debugMode)
3584 fprintf(stderr, _("(File:%s:) "), buf);
3586 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3587 &xpmLightSquare, NULL, &attr)) != 0) {
3588 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3591 fprintf(stderr, _("dark square "));
3592 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3593 ExpandPathName(appData.pixmapDirectory), ss);
3594 if (appData.debugMode) {
3595 fprintf(stderr, _("(File:%s:) "), buf);
3597 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3598 &xpmDarkSquare, NULL, &attr)) != 0) {
3599 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3603 xpmJailSquare = xpmLightSquare;
3604 fprintf(stderr, _("Done.\n"));
3606 oldVariant = -1; // kludge to force re-makig of animation masks
3607 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3610 #endif /* HAVE_LIBXPM */
3613 /* No built-in bitmaps */
3618 u_int ss = squareSize;
3620 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3623 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3624 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3625 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3626 pieceBitmapNames[piece],
3627 ss, kind == SOLID ? 's' : 'o');
3628 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3629 if(piece <= (int)WhiteKing)
3630 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3634 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3638 /* With built-in bitmaps */
3641 BuiltInBits* bib = builtInBits;
3644 u_int ss = squareSize;
3646 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3649 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3651 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3652 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3653 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3654 pieceBitmapNames[piece],
3655 ss, kind == SOLID ? 's' : 'o');
3656 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3657 bib->bits[kind][piece], ss, ss);
3658 if(piece <= (int)WhiteKing)
3659 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3663 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3668 void ReadBitmap(pm, name, bits, wreq, hreq)
3671 unsigned char bits[];
3677 char msg[MSG_SIZ], fullname[MSG_SIZ];
3679 if (*appData.bitmapDirectory != NULLCHAR) {
3680 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3681 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3682 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3683 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3684 &w, &h, pm, &x_hot, &y_hot);
3685 fprintf(stderr, "load %s\n", name);
3686 if (errcode != BitmapSuccess) {
3688 case BitmapOpenFailed:
3689 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3691 case BitmapFileInvalid:
3692 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3694 case BitmapNoMemory:
3695 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3699 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3703 fprintf(stderr, _("%s: %s...using built-in\n"),
3705 } else if (w != wreq || h != hreq) {
3707 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3708 programName, fullname, w, h, wreq, hreq);
3714 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3723 if (lineGap == 0) return;
3725 /* [HR] Split this into 2 loops for non-square boards. */
3727 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3728 gridSegments[i].x1 = 0;
3729 gridSegments[i].x2 =
3730 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3731 gridSegments[i].y1 = gridSegments[i].y2
3732 = lineGap / 2 + (i * (squareSize + lineGap));
3735 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3736 gridSegments[j + i].y1 = 0;
3737 gridSegments[j + i].y2 =
3738 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3739 gridSegments[j + i].x1 = gridSegments[j + i].x2
3740 = lineGap / 2 + (j * (squareSize + lineGap));
3744 static void MenuBarSelect(w, addr, index)
3749 XtActionProc proc = (XtActionProc) addr;
3751 (proc)(NULL, NULL, NULL, NULL);
3754 void CreateMenuBarPopup(parent, name, mb)
3764 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3767 XtSetArg(args[j], XtNleftMargin, 20); j++;
3768 XtSetArg(args[j], XtNrightMargin, 20); j++;
3770 while (mi->string != NULL) {
3771 if (strcmp(mi->string, "----") == 0) {
3772 entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
3775 XtSetArg(args[j], XtNlabel, XtNewString(mi->string));
3776 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3778 XtAddCallback(entry, XtNcallback,
3779 (XtCallbackProc) MenuBarSelect,
3780 (caddr_t) mi->proc);
3786 Widget CreateMenuBar(mb)
3790 Widget anchor, menuBar;
3792 char menuName[MSG_SIZ];
3795 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3796 XtSetArg(args[j], XtNvSpace, 0); j++;
3797 XtSetArg(args[j], XtNborderWidth, 0); j++;
3798 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3799 formWidget, args, j);
3801 while (mb->name != NULL) {
3802 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3803 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3805 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3808 shortName[0] = mb->name[0];
3809 shortName[1] = NULLCHAR;
3810 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3813 XtSetArg(args[j], XtNlabel, XtNewString(mb->name)); j++;
3816 XtSetArg(args[j], XtNborderWidth, 0); j++;
3817 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3819 CreateMenuBarPopup(menuBar, menuName, mb);
3825 Widget CreateButtonBar(mi)
3829 Widget button, buttonBar;
3833 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3835 XtSetArg(args[j], XtNhSpace, 0); j++;
3837 XtSetArg(args[j], XtNborderWidth, 0); j++;
3838 XtSetArg(args[j], XtNvSpace, 0); j++;
3839 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3840 formWidget, args, j);
3842 while (mi->string != NULL) {
3845 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3846 XtSetArg(args[j], XtNborderWidth, 0); j++;
3848 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3849 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3850 buttonBar, args, j);
3851 XtAddCallback(button, XtNcallback,
3852 (XtCallbackProc) MenuBarSelect,
3853 (caddr_t) mi->proc);
3860 CreatePieceMenu(name, color)
3867 ChessSquare selection;
3869 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3870 boardWidget, args, 0);
3872 for (i = 0; i < PIECE_MENU_SIZE; i++) {
3873 String item = pieceMenuStrings[color][i];
3875 if (strcmp(item, "----") == 0) {
3876 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3879 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3880 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3882 selection = pieceMenuTranslation[color][i];
3883 XtAddCallback(entry, XtNcallback,
3884 (XtCallbackProc) PieceMenuSelect,
3885 (caddr_t) selection);
3886 if (selection == WhitePawn || selection == BlackPawn) {
3887 XtSetArg(args[0], XtNpopupOnEntry, entry);
3888 XtSetValues(menu, args, 1);
3901 ChessSquare selection;
3903 whitePieceMenu = CreatePieceMenu("menuW", 0);
3904 blackPieceMenu = CreatePieceMenu("menuB", 1);
3906 XtRegisterGrabAction(PieceMenuPopup, True,
3907 (unsigned)(ButtonPressMask|ButtonReleaseMask),
3908 GrabModeAsync, GrabModeAsync);
3910 XtSetArg(args[0], XtNlabel, _("Drop"));
3911 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3912 boardWidget, args, 1);
3913 for (i = 0; i < DROP_MENU_SIZE; i++) {
3914 String item = dropMenuStrings[i];
3916 if (strcmp(item, "----") == 0) {
3917 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3920 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3921 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3923 selection = dropMenuTranslation[i];
3924 XtAddCallback(entry, XtNcallback,
3925 (XtCallbackProc) DropMenuSelect,
3926 (caddr_t) selection);
3931 void SetupDropMenu()
3939 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3940 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3941 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3942 dmEnables[i].piece);
3943 XtSetSensitive(entry, p != NULL || !appData.testLegality
3944 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3945 && !appData.icsActive));
3947 while (p && *p++ == dmEnables[i].piece) count++;
3948 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3950 XtSetArg(args[j], XtNlabel, label); j++;
3951 XtSetValues(entry, args, j);
3955 void PieceMenuPopup(w, event, params, num_params)
3959 Cardinal *num_params;
3961 String whichMenu; int menuNr;
3962 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3963 if (event->type == ButtonRelease)
3964 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3965 else if (event->type == ButtonPress)
3966 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3968 case 0: whichMenu = params[0]; break;
3969 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3971 case -1: if (errorUp) ErrorPopDown();
3974 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3977 static void PieceMenuSelect(w, piece, junk)
3982 if (pmFromX < 0 || pmFromY < 0) return;
3983 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3986 static void DropMenuSelect(w, piece, junk)
3991 if (pmFromX < 0 || pmFromY < 0) return;
3992 DropMenuEvent(piece, pmFromX, pmFromY);
3995 void WhiteClock(w, event, prms, nprms)
4004 void BlackClock(w, event, prms, nprms)
4015 * If the user selects on a border boundary, return -1; if off the board,
4016 * return -2. Otherwise map the event coordinate to the square.
4018 int EventToSquare(x, limit)
4026 if ((x % (squareSize + lineGap)) >= squareSize)
4028 x /= (squareSize + lineGap);
4034 static void do_flash_delay(msec)
4040 static void drawHighlight(file, rank, gc)
4046 if (lineGap == 0) return;
4049 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4050 (squareSize + lineGap);
4051 y = lineGap/2 + rank * (squareSize + lineGap);
4053 x = lineGap/2 + file * (squareSize + lineGap);
4054 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4055 (squareSize + lineGap);
4058 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4059 squareSize+lineGap, squareSize+lineGap);
4062 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4063 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4066 SetHighlights(fromX, fromY, toX, toY)
4067 int fromX, fromY, toX, toY;
4069 if (hi1X != fromX || hi1Y != fromY) {
4070 if (hi1X >= 0 && hi1Y >= 0) {
4071 drawHighlight(hi1X, hi1Y, lineGC);
4073 } // [HGM] first erase both, then draw new!
4074 if (hi2X != toX || hi2Y != toY) {
4075 if (hi2X >= 0 && hi2Y >= 0) {
4076 drawHighlight(hi2X, hi2Y, lineGC);
4079 if (hi1X != fromX || hi1Y != fromY) {
4080 if (fromX >= 0 && fromY >= 0) {
4081 drawHighlight(fromX, fromY, highlineGC);
4084 if (hi2X != toX || hi2Y != toY) {
4085 if (toX >= 0 && toY >= 0) {
4086 drawHighlight(toX, toY, highlineGC);
4098 SetHighlights(-1, -1, -1, -1);
4103 SetPremoveHighlights(fromX, fromY, toX, toY)
4104 int fromX, fromY, toX, toY;
4106 if (pm1X != fromX || pm1Y != fromY) {
4107 if (pm1X >= 0 && pm1Y >= 0) {
4108 drawHighlight(pm1X, pm1Y, lineGC);
4110 if (fromX >= 0 && fromY >= 0) {
4111 drawHighlight(fromX, fromY, prelineGC);
4114 if (pm2X != toX || pm2Y != toY) {
4115 if (pm2X >= 0 && pm2Y >= 0) {
4116 drawHighlight(pm2X, pm2Y, lineGC);
4118 if (toX >= 0 && toY >= 0) {
4119 drawHighlight(toX, toY, prelineGC);
4129 ClearPremoveHighlights()
4131 SetPremoveHighlights(-1, -1, -1, -1);
4134 static int CutOutSquare(x, y, x0, y0, kind)
4135 int x, y, *x0, *y0, kind;
4137 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4138 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4140 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4141 if(textureW[kind] < W*squareSize)
4142 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4144 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4145 if(textureH[kind] < H*squareSize)
4146 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4148 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4152 static void BlankSquare(x, y, color, piece, dest, fac)
4153 int x, y, color, fac;
4156 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4158 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4159 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4160 squareSize, squareSize, x*fac, y*fac);
4162 if (useImages && useImageSqs) {
4166 pm = xpmLightSquare;
4171 case 2: /* neutral */
4176 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4177 squareSize, squareSize, x*fac, y*fac);
4187 case 2: /* neutral */
4192 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4197 I split out the routines to draw a piece so that I could
4198 make a generic flash routine.
4200 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4202 int square_color, x, y;
4205 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4206 switch (square_color) {
4208 case 2: /* neutral */
4210 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4211 ? *pieceToOutline(piece)
4212 : *pieceToSolid(piece),
4213 dest, bwPieceGC, 0, 0,
4214 squareSize, squareSize, x, y);
4217 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4218 ? *pieceToSolid(piece)
4219 : *pieceToOutline(piece),
4220 dest, wbPieceGC, 0, 0,
4221 squareSize, squareSize, x, y);
4226 static void monoDrawPiece(piece, square_color, x, y, dest)
4228 int square_color, x, y;
4231 switch (square_color) {
4233 case 2: /* neutral */
4235 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4236 ? *pieceToOutline(piece)
4237 : *pieceToSolid(piece),
4238 dest, bwPieceGC, 0, 0,
4239 squareSize, squareSize, x, y, 1);
4242 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4243 ? *pieceToSolid(piece)
4244 : *pieceToOutline(piece),
4245 dest, wbPieceGC, 0, 0,
4246 squareSize, squareSize, x, y, 1);
4251 static void colorDrawPiece(piece, square_color, x, y, dest)
4253 int square_color, x, y;
4256 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4257 switch (square_color) {
4259 XCopyPlane(xDisplay, *pieceToSolid(piece),
4260 dest, (int) piece < (int) BlackPawn
4261 ? wlPieceGC : blPieceGC, 0, 0,
4262 squareSize, squareSize, x, y, 1);
4265 XCopyPlane(xDisplay, *pieceToSolid(piece),
4266 dest, (int) piece < (int) BlackPawn
4267 ? wdPieceGC : bdPieceGC, 0, 0,
4268 squareSize, squareSize, x, y, 1);
4270 case 2: /* neutral */
4272 XCopyPlane(xDisplay, *pieceToSolid(piece),
4273 dest, (int) piece < (int) BlackPawn
4274 ? wjPieceGC : bjPieceGC, 0, 0,
4275 squareSize, squareSize, x, y, 1);
4280 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4282 int square_color, x, y;
4285 int kind, p = piece;
4287 switch (square_color) {
4289 case 2: /* neutral */
4291 if ((int)piece < (int) BlackPawn) {
4299 if ((int)piece < (int) BlackPawn) {
4307 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4308 if(useTexture & square_color+1) {
4309 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4310 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4311 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4312 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4313 XSetClipMask(xDisplay, wlPieceGC, None);
4314 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4316 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4317 dest, wlPieceGC, 0, 0,
4318 squareSize, squareSize, x, y);
4321 typedef void (*DrawFunc)();
4323 DrawFunc ChooseDrawFunc()
4325 if (appData.monoMode) {
4326 if (DefaultDepth(xDisplay, xScreen) == 1) {
4327 return monoDrawPiece_1bit;
4329 return monoDrawPiece;
4333 return colorDrawPieceImage;
4335 return colorDrawPiece;
4339 /* [HR] determine square color depending on chess variant. */
4340 static int SquareColor(row, column)
4345 if (gameInfo.variant == VariantXiangqi) {
4346 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4348 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4350 } else if (row <= 4) {
4356 square_color = ((column + row) % 2) == 1;
4359 /* [hgm] holdings: next line makes all holdings squares light */
4360 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4362 return square_color;
4365 void DrawSquare(row, column, piece, do_flash)
4366 int row, column, do_flash;
4369 int square_color, x, y, direction, font_ascent, font_descent;
4372 XCharStruct overall;
4376 /* Calculate delay in milliseconds (2-delays per complete flash) */
4377 flash_delay = 500 / appData.flashRate;
4380 x = lineGap + ((BOARD_WIDTH-1)-column) *
4381 (squareSize + lineGap);
4382 y = lineGap + row * (squareSize + lineGap);
4384 x = lineGap + column * (squareSize + lineGap);
4385 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4386 (squareSize + lineGap);
4389 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4391 square_color = SquareColor(row, column);
4393 if ( // [HGM] holdings: blank out area between board and holdings
4394 column == BOARD_LEFT-1 || column == BOARD_RGHT
4395 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4396 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4397 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4399 // [HGM] print piece counts next to holdings
4400 string[1] = NULLCHAR;
4401 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4402 string[0] = '0' + piece;
4403 XTextExtents(countFontStruct, string, 1, &direction,
4404 &font_ascent, &font_descent, &overall);
4405 if (appData.monoMode) {
4406 XDrawImageString(xDisplay, xBoardWindow, countGC,
4407 x + squareSize - overall.width - 2,
4408 y + font_ascent + 1, string, 1);
4410 XDrawString(xDisplay, xBoardWindow, countGC,
4411 x + squareSize - overall.width - 2,
4412 y + font_ascent + 1, string, 1);
4415 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4416 string[0] = '0' + piece;
4417 XTextExtents(countFontStruct, string, 1, &direction,
4418 &font_ascent, &font_descent, &overall);
4419 if (appData.monoMode) {
4420 XDrawImageString(xDisplay, xBoardWindow, countGC,
4421 x + 2, y + font_ascent + 1, string, 1);
4423 XDrawString(xDisplay, xBoardWindow, countGC,
4424 x + 2, y + font_ascent + 1, string, 1);
4428 if (piece == EmptySquare || appData.blindfold) {
4429 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4431 drawfunc = ChooseDrawFunc();
4433 if (do_flash && appData.flashCount > 0) {
4434 for (i=0; i<appData.flashCount; ++i) {
4435 drawfunc(piece, square_color, x, y, xBoardWindow);
4436 XSync(xDisplay, False);
4437 do_flash_delay(flash_delay);
4439 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4440 XSync(xDisplay, False);
4441 do_flash_delay(flash_delay);
4444 drawfunc(piece, square_color, x, y, xBoardWindow);
4448 string[1] = NULLCHAR;
4449 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4450 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4451 string[0] = 'a' + column - BOARD_LEFT;
4452 XTextExtents(coordFontStruct, string, 1, &direction,
4453 &font_ascent, &font_descent, &overall);
4454 if (appData.monoMode) {
4455 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4456 x + squareSize - overall.width - 2,
4457 y + squareSize - font_descent - 1, string, 1);
4459 XDrawString(xDisplay, xBoardWindow, coordGC,
4460 x + squareSize - overall.width - 2,
4461 y + squareSize - font_descent - 1, string, 1);
4464 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4465 string[0] = ONE + row;
4466 XTextExtents(coordFontStruct, string, 1, &direction,
4467 &font_ascent, &font_descent, &overall);
4468 if (appData.monoMode) {
4469 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4470 x + 2, y + font_ascent + 1, string, 1);
4472 XDrawString(xDisplay, xBoardWindow, coordGC,
4473 x + 2, y + font_ascent + 1, string, 1);
4476 if(!partnerUp && marker[row][column]) {
4477 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4478 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4483 /* Why is this needed on some versions of X? */
4484 void EventProc(widget, unused, event)
4489 if (!XtIsRealized(widget))
4492 switch (event->type) {
4494 if (event->xexpose.count > 0) return; /* no clipping is done */
4495 XDrawPosition(widget, True, NULL);
4496 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4497 flipView = !flipView; partnerUp = !partnerUp;
4498 XDrawPosition(widget, True, NULL);
4499 flipView = !flipView; partnerUp = !partnerUp;
4503 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4510 void DrawPosition(fullRedraw, board)
4511 /*Boolean*/int fullRedraw;
4514 XDrawPosition(boardWidget, fullRedraw, board);
4517 /* Returns 1 if there are "too many" differences between b1 and b2
4518 (i.e. more than 1 move was made) */
4519 static int too_many_diffs(b1, b2)
4525 for (i=0; i<BOARD_HEIGHT; ++i) {
4526 for (j=0; j<BOARD_WIDTH; ++j) {
4527 if (b1[i][j] != b2[i][j]) {
4528 if (++c > 4) /* Castling causes 4 diffs */
4536 /* Matrix describing castling maneuvers */
4537 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4538 static int castling_matrix[4][5] = {
4539 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4540 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4541 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4542 { 7, 7, 4, 5, 6 } /* 0-0, black */
4545 /* Checks whether castling occurred. If it did, *rrow and *rcol
4546 are set to the destination (row,col) of the rook that moved.
4548 Returns 1 if castling occurred, 0 if not.
4550 Note: Only handles a max of 1 castling move, so be sure
4551 to call too_many_diffs() first.
4553 static int check_castle_draw(newb, oldb, rrow, rcol)
4560 /* For each type of castling... */
4561 for (i=0; i<4; ++i) {
4562 r = castling_matrix[i];
4564 /* Check the 4 squares involved in the castling move */
4566 for (j=1; j<=4; ++j) {
4567 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4574 /* All 4 changed, so it must be a castling move */
4583 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4584 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4586 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4589 void DrawSeekBackground( int left, int top, int right, int bottom )
4591 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4594 void DrawSeekText(char *buf, int x, int y)
4596 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4599 void DrawSeekDot(int x, int y, int colorNr)
4601 int square = colorNr & 0x80;
4604 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4606 XFillRectangle(xDisplay, xBoardWindow, color,
4607 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4609 XFillArc(xDisplay, xBoardWindow, color,
4610 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4613 static int damage[2][BOARD_RANKS][BOARD_FILES];
4616 * event handler for redrawing the board
4618 void XDrawPosition(w, repaint, board)
4620 /*Boolean*/int repaint;
4624 static int lastFlipView = 0;
4625 static int lastBoardValid[2] = {0, 0};
4626 static Board lastBoard[2];
4629 int nr = twoBoards*partnerUp;
4631 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4633 if (board == NULL) {
4634 if (!lastBoardValid[nr]) return;
4635 board = lastBoard[nr];
4637 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4638 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4639 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4644 * It would be simpler to clear the window with XClearWindow()
4645 * but this causes a very distracting flicker.
4648 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4650 if ( lineGap && IsDrawArrowEnabled())
4651 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4652 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4654 /* If too much changes (begin observing new game, etc.), don't
4656 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4658 /* Special check for castling so we don't flash both the king
4659 and the rook (just flash the king). */
4661 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4662 /* Draw rook with NO flashing. King will be drawn flashing later */
4663 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4664 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4668 /* First pass -- Draw (newly) empty squares and repair damage.
4669 This prevents you from having a piece show up twice while it
4670 is flashing on its new square */
4671 for (i = 0; i < BOARD_HEIGHT; i++)
4672 for (j = 0; j < BOARD_WIDTH; j++)
4673 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4674 || damage[nr][i][j]) {
4675 DrawSquare(i, j, board[i][j], 0);
4676 damage[nr][i][j] = False;
4679 /* Second pass -- Draw piece(s) in new position and flash them */
4680 for (i = 0; i < BOARD_HEIGHT; i++)
4681 for (j = 0; j < BOARD_WIDTH; j++)
4682 if (board[i][j] != lastBoard[nr][i][j]) {
4683 DrawSquare(i, j, board[i][j], do_flash);
4687 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4688 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4689 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4691 for (i = 0; i < BOARD_HEIGHT; i++)
4692 for (j = 0; j < BOARD_WIDTH; j++) {
4693 DrawSquare(i, j, board[i][j], 0);
4694 damage[nr][i][j] = False;
4698 CopyBoard(lastBoard[nr], board);
4699 lastBoardValid[nr] = 1;
4700 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4701 lastFlipView = flipView;
4703 /* Draw highlights */
4704 if (pm1X >= 0 && pm1Y >= 0) {
4705 drawHighlight(pm1X, pm1Y, prelineGC);
4707 if (pm2X >= 0 && pm2Y >= 0) {
4708 drawHighlight(pm2X, pm2Y, prelineGC);
4710 if (hi1X >= 0 && hi1Y >= 0) {
4711 drawHighlight(hi1X, hi1Y, highlineGC);
4713 if (hi2X >= 0 && hi2Y >= 0) {
4714 drawHighlight(hi2X, hi2Y, highlineGC);
4716 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4718 /* If piece being dragged around board, must redraw that too */
4721 XSync(xDisplay, False);
4726 * event handler for redrawing the board
4728 void DrawPositionProc(w, event, prms, nprms)
4734 XDrawPosition(w, True, NULL);
4739 * event handler for parsing user moves
4741 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4742 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4743 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4744 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4745 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4746 // and at the end FinishMove() to perform the move after optional promotion popups.
4747 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4748 void HandleUserMove(w, event, prms, nprms)
4754 if (w != boardWidget || errorExitStatus != -1) return;
4755 if(nprms) shiftKey = !strcmp(prms[0], "1");
4758 if (event->type == ButtonPress) {
4759 XtPopdown(promotionShell);
4760 XtDestroyWidget(promotionShell);
4761 promotionUp = False;
4769 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4770 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4771 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4774 void AnimateUserMove (Widget w, XEvent * event,
4775 String * params, Cardinal * nParams)
4777 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4778 DragPieceMove(event->xmotion.x, event->xmotion.y);
4781 void HandlePV (Widget w, XEvent * event,
4782 String * params, Cardinal * nParams)
4783 { // [HGM] pv: walk PV
4784 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4787 static int savedIndex; /* gross that this is global */
4789 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4792 XawTextPosition index, dummy;
4795 XawTextGetSelectionPos(w, &index, &dummy);
4796 XtSetArg(arg, XtNstring, &val);
4797 XtGetValues(w, &arg, 1);
4798 ReplaceComment(savedIndex, val);
4799 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4800 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4803 void EditCommentPopUp(index, title, text)
4808 if (text == NULL) text = "";
4809 NewCommentPopup(title, text, index);
4812 void ICSInputBoxPopUp()
4817 extern Option boxOptions[];
4819 void ICSInputSendText()
4826 edit = boxOptions[0].handle;
4828 XtSetArg(args[j], XtNstring, &val); j++;
4829 XtGetValues(edit, args, j);
4831 SendMultiLineToICS(val);
4832 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4833 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4836 void ICSInputBoxPopDown()
4841 void CommentPopUp(title, text)
4844 savedIndex = currentMove; // [HGM] vari
4845 NewCommentPopup(title, text, currentMove);
4848 void CommentPopDown()
4853 void FileNamePopUp(label, def, filter, proc, openMode)
4860 fileProc = proc; /* I can't see a way not */
4861 fileOpenMode = openMode; /* to use globals here */
4862 { // [HGM] use file-selector dialog stolen from Ghostview
4864 int index; // this is not supported yet
4866 if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
4867 (def[0] ? def : NULL), filter, openMode, NULL, &name))
4868 (void) (*fileProc)(f, index=0, name);
4872 void FileNamePopDown()
4874 if (!filenameUp) return;
4875 XtPopdown(fileNameShell);
4876 XtDestroyWidget(fileNameShell);
4881 void FileNameCallback(w, client_data, call_data)
4883 XtPointer client_data, call_data;
4888 XtSetArg(args[0], XtNlabel, &name);
4889 XtGetValues(w, args, 1);
4891 if (strcmp(name, _("cancel")) == 0) {
4896 FileNameAction(w, NULL, NULL, NULL);
4899 void FileNameAction(w, event, prms, nprms)
4911 name = XawDialogGetValueString(w = XtParent(w));
4913 if ((name != NULL) && (*name != NULLCHAR)) {
4914 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
4915 XtPopdown(w = XtParent(XtParent(w)));
4919 p = strrchr(buf, ' ');
4926 fullname = ExpandPathName(buf);
4928 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
4931 f = fopen(fullname, fileOpenMode);
4933 DisplayError(_("Failed to open file"), errno);
4935 (void) (*fileProc)(f, index, buf);
4942 XtPopdown(w = XtParent(XtParent(w)));
4948 void PromotionPopUp()
4951 Widget dialog, layout;
4953 Dimension bw_width, pw_width;
4957 XtSetArg(args[j], XtNwidth, &bw_width); j++;
4958 XtGetValues(boardWidget, args, j);
4961 XtSetArg(args[j], XtNresizable, True); j++;
4962 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
4964 XtCreatePopupShell("Promotion", transientShellWidgetClass,
4965 shellWidget, args, j);
4967 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
4968 layoutArgs, XtNumber(layoutArgs));
4971 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
4972 XtSetArg(args[j], XtNborderWidth, 0); j++;
4973 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
4976 if(gameInfo.variant != VariantShogi) {
4977 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
4978 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback,
4979 (XtPointer) dialog);
4980 XawDialogAddButton(dialog, _("General"), PromotionCallback,
4981 (XtPointer) dialog);
4982 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback,
4983 (XtPointer) dialog);
4984 XawDialogAddButton(dialog, _("Captain"), PromotionCallback,
4985 (XtPointer) dialog);
4987 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
4988 (XtPointer) dialog);
4989 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
4990 (XtPointer) dialog);
4991 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
4992 (XtPointer) dialog);
4993 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
4994 (XtPointer) dialog);
4996 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
4997 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4998 gameInfo.variant == VariantGiveaway) {
4999 XawDialogAddButton(dialog, _("King"), PromotionCallback,
5000 (XtPointer) dialog);
5002 if(gameInfo.variant == VariantCapablanca ||
5003 gameInfo.variant == VariantGothic ||
5004 gameInfo.variant == VariantCapaRandom) {
5005 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5006 (XtPointer) dialog);
5007 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5008 (XtPointer) dialog);
5010 } else // [HGM] shogi
5012 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5013 (XtPointer) dialog);
5014 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5015 (XtPointer) dialog);
5017 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5018 (XtPointer) dialog);
5020 XtRealizeWidget(promotionShell);
5021 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5024 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5025 XtGetValues(promotionShell, args, j);
5027 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5028 lineGap + squareSize/3 +
5029 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5030 0 : 6*(squareSize + lineGap)), &x, &y);
5033 XtSetArg(args[j], XtNx, x); j++;
5034 XtSetArg(args[j], XtNy, y); j++;
5035 XtSetValues(promotionShell, args, j);
5037 XtPopup(promotionShell, XtGrabNone);
5042 void PromotionPopDown()
5044 if (!promotionUp) return;
5045 XtPopdown(promotionShell);
5046 XtDestroyWidget(promotionShell);
5047 promotionUp = False;
5050 void PromotionCallback(w, client_data, call_data)
5052 XtPointer client_data, call_data;
5058 XtSetArg(args[0], XtNlabel, &name);
5059 XtGetValues(w, args, 1);
5063 if (fromX == -1) return;
5065 if (strcmp(name, _("cancel")) == 0) {
5069 } else if (strcmp(name, _("Knight")) == 0) {
5071 } else if (strcmp(name, _("Promote")) == 0) {
5073 } else if (strcmp(name, _("Defer")) == 0) {
5076 promoChar = ToLower(name[0]);
5079 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5081 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5082 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5087 void ErrorCallback(w, client_data, call_data)
5089 XtPointer client_data, call_data;
5092 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5094 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5100 if (!errorUp) return;
5102 XtPopdown(errorShell);
5103 XtDestroyWidget(errorShell);
5104 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5107 void ErrorPopUp(title, label, modal)
5108 char *title, *label;
5112 Widget dialog, layout;
5116 Dimension bw_width, pw_width;
5117 Dimension pw_height;
5121 XtSetArg(args[i], XtNresizable, True); i++;
5122 XtSetArg(args[i], XtNtitle, title); i++;
5124 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5125 shellWidget, args, i);
5127 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5128 layoutArgs, XtNumber(layoutArgs));
5131 XtSetArg(args[i], XtNlabel, label); i++;
5132 XtSetArg(args[i], XtNborderWidth, 0); i++;
5133 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5136 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5138 XtRealizeWidget(errorShell);
5139 CatchDeleteWindow(errorShell, "ErrorPopDown");
5142 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5143 XtGetValues(boardWidget, args, i);
5145 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5146 XtSetArg(args[i], XtNheight, &pw_height); i++;
5147 XtGetValues(errorShell, args, i);
5150 /* This code seems to tickle an X bug if it is executed too soon
5151 after xboard starts up. The coordinates get transformed as if
5152 the main window was positioned at (0, 0).
5154 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5155 0 - pw_height + squareSize / 3, &x, &y);
5157 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5158 RootWindowOfScreen(XtScreen(boardWidget)),
5159 (bw_width - pw_width) / 2,
5160 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5164 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5167 XtSetArg(args[i], XtNx, x); i++;
5168 XtSetArg(args[i], XtNy, y); i++;
5169 XtSetValues(errorShell, args, i);
5172 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5175 /* Disable all user input other than deleting the window */
5176 static int frozen = 0;
5180 /* Grab by a widget that doesn't accept input */
5181 XtAddGrab(messageWidget, TRUE, FALSE);
5185 /* Undo a FreezeUI */
5188 if (!frozen) return;
5189 XtRemoveGrab(messageWidget);
5193 char *ModeToWidgetName(mode)
5197 case BeginningOfGame:
5198 if (appData.icsActive)
5199 return "menuMode.ICS Client";
5200 else if (appData.noChessProgram ||
5201 *appData.cmailGameName != NULLCHAR)
5202 return "menuMode.Edit Game";
5204 return "menuMode.Machine Black";
5205 case MachinePlaysBlack:
5206 return "menuMode.Machine Black";
5207 case MachinePlaysWhite:
5208 return "menuMode.Machine White";
5210 return "menuMode.Analysis Mode";
5212 return "menuMode.Analyze File";
5213 case TwoMachinesPlay:
5214 return "menuMode.Two Machines";
5216 return "menuMode.Edit Game";
5217 case PlayFromGameFile:
5218 return "menuFile.Load Game";
5220 return "menuMode.Edit Position";
5222 return "menuMode.Training";
5223 case IcsPlayingWhite:
5224 case IcsPlayingBlack:
5228 return "menuMode.ICS Client";
5235 void ModeHighlight()
5238 static int oldPausing = FALSE;
5239 static GameMode oldmode = (GameMode) -1;
5242 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5244 if (pausing != oldPausing) {
5245 oldPausing = pausing;
5247 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5249 XtSetArg(args[0], XtNleftBitmap, None);
5251 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5254 if (appData.showButtonBar) {
5255 /* Always toggle, don't set. Previous code messes up when
5256 invoked while the button is pressed, as releasing it
5257 toggles the state again. */
5260 XtSetArg(args[0], XtNbackground, &oldbg);
5261 XtSetArg(args[1], XtNforeground, &oldfg);
5262 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5264 XtSetArg(args[0], XtNbackground, oldfg);
5265 XtSetArg(args[1], XtNforeground, oldbg);
5267 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5271 wname = ModeToWidgetName(oldmode);
5272 if (wname != NULL) {
5273 XtSetArg(args[0], XtNleftBitmap, None);
5274 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5276 wname = ModeToWidgetName(gameMode);
5277 if (wname != NULL) {
5278 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5279 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5283 /* Maybe all the enables should be handled here, not just this one */
5284 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5285 gameMode == Training || gameMode == PlayFromGameFile);
5290 * Button/menu procedures
5292 void ResetProc(w, event, prms, nprms)
5301 int LoadGamePopUp(f, gameNumber, title)
5306 cmailMsgLoaded = FALSE;
5307 if (gameNumber == 0) {
5308 int error = GameListBuild(f);
5310 DisplayError(_("Cannot build game list"), error);
5311 } else if (!ListEmpty(&gameList) &&
5312 ((ListGame *) gameList.tailPred)->number > 1) {
5313 GameListPopUp(f, title);
5319 return LoadGame(f, gameNumber, title, FALSE);
5322 void LoadGameProc(w, event, prms, nprms)
5328 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5331 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5334 void LoadNextGameProc(w, event, prms, nprms)
5343 void LoadPrevGameProc(w, event, prms, nprms)
5352 void ReloadGameProc(w, event, prms, nprms)
5361 void LoadNextPositionProc(w, event, prms, nprms)
5370 void LoadPrevPositionProc(w, event, prms, nprms)
5379 void ReloadPositionProc(w, event, prms, nprms)
5388 void LoadPositionProc(w, event, prms, nprms)
5394 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5397 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5400 void SaveGameProc(w, event, prms, nprms)
5406 FileNamePopUp(_("Save game file name?"),
5407 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5408 appData.oldSaveStyle ? ".game" : ".pgn",
5412 void SavePositionProc(w, event, prms, nprms)
5418 FileNamePopUp(_("Save position file name?"),
5419 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5420 appData.oldSaveStyle ? ".pos" : ".fen",
5424 void ReloadCmailMsgProc(w, event, prms, nprms)
5430 ReloadCmailMsgEvent(FALSE);
5433 void MailMoveProc(w, event, prms, nprms)
5442 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5443 char *selected_fen_position=NULL;
5446 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5447 Atom *type_return, XtPointer *value_return,
5448 unsigned long *length_return, int *format_return)
5450 char *selection_tmp;
5452 if (!selected_fen_position) return False; /* should never happen */
5453 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5454 /* note: since no XtSelectionDoneProc was registered, Xt will
5455 * automatically call XtFree on the value returned. So have to
5456 * make a copy of it allocated with XtMalloc */
5457 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5458 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5460 *value_return=selection_tmp;
5461 *length_return=strlen(selection_tmp);
5462 *type_return=*target;
5463 *format_return = 8; /* bits per byte */
5465 } else if (*target == XA_TARGETS(xDisplay)) {
5466 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5467 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5468 targets_tmp[1] = XA_STRING;
5469 *value_return = targets_tmp;
5470 *type_return = XA_ATOM;
5472 *format_return = 8 * sizeof(Atom);
5473 if (*format_return > 32) {
5474 *length_return *= *format_return / 32;
5475 *format_return = 32;
5483 /* note: when called from menu all parameters are NULL, so no clue what the
5484 * Widget which was clicked on was, or what the click event was
5486 void CopyPositionProc(w, event, prms, nprms)
5493 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5494 * have a notion of a position that is selected but not copied.
5495 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5497 if(gameMode == EditPosition) EditPositionDone(TRUE);
5498 if (selected_fen_position) free(selected_fen_position);
5499 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5500 if (!selected_fen_position) return;
5501 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5503 SendPositionSelection,
5504 NULL/* lose_ownership_proc */ ,
5505 NULL/* transfer_done_proc */);
5506 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5508 SendPositionSelection,
5509 NULL/* lose_ownership_proc */ ,
5510 NULL/* transfer_done_proc */);
5513 /* function called when the data to Paste is ready */
5515 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5516 Atom *type, XtPointer value, unsigned long *len, int *format)
5519 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5520 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5521 EditPositionPasteFEN(fenstr);
5525 /* called when Paste Position button is pressed,
5526 * all parameters will be NULL */
5527 void PastePositionProc(w, event, prms, nprms)
5533 XtGetSelectionValue(menuBarWidget,
5534 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5535 /* (XtSelectionCallbackProc) */ PastePositionCB,
5536 NULL, /* client_data passed to PastePositionCB */
5538 /* better to use the time field from the event that triggered the
5539 * call to this function, but that isn't trivial to get
5547 SendGameSelection(Widget w, Atom *selection, Atom *target,
5548 Atom *type_return, XtPointer *value_return,
5549 unsigned long *length_return, int *format_return)
5551 char *selection_tmp;
5553 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5554 FILE* f = fopen(gameCopyFilename, "r");
5557 if (f == NULL) return False;
5561 selection_tmp = XtMalloc(len + 1);
5562 count = fread(selection_tmp, 1, len, f);
5565 XtFree(selection_tmp);
5568 selection_tmp[len] = NULLCHAR;
5569 *value_return = selection_tmp;
5570 *length_return = len;
5571 *type_return = *target;
5572 *format_return = 8; /* bits per byte */
5574 } else if (*target == XA_TARGETS(xDisplay)) {
5575 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5576 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5577 targets_tmp[1] = XA_STRING;
5578 *value_return = targets_tmp;
5579 *type_return = XA_ATOM;
5581 *format_return = 8 * sizeof(Atom);
5582 if (*format_return > 32) {
5583 *length_return *= *format_return / 32;
5584 *format_return = 32;
5592 void CopySomething()
5597 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5598 * have a notion of a game that is selected but not copied.
5599 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5601 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5604 NULL/* lose_ownership_proc */ ,
5605 NULL/* transfer_done_proc */);
5606 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5609 NULL/* lose_ownership_proc */ ,
5610 NULL/* transfer_done_proc */);
5613 /* note: when called from menu all parameters are NULL, so no clue what the
5614 * Widget which was clicked on was, or what the click event was
5616 void CopyGameProc(w, event, prms, nprms)
5624 ret = SaveGameToFile(gameCopyFilename, FALSE);
5630 void CopyGameListProc(w, event, prms, nprms)
5636 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5640 /* function called when the data to Paste is ready */
5642 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5643 Atom *type, XtPointer value, unsigned long *len, int *format)
5646 if (value == NULL || *len == 0) {
5647 return; /* nothing had been selected to copy */
5649 f = fopen(gamePasteFilename, "w");
5651 DisplayError(_("Can't open temp file"), errno);
5654 fwrite(value, 1, *len, f);
5657 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5660 /* called when Paste Game button is pressed,
5661 * all parameters will be NULL */
5662 void PasteGameProc(w, event, prms, nprms)
5668 XtGetSelectionValue(menuBarWidget,
5669 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5670 /* (XtSelectionCallbackProc) */ PasteGameCB,
5671 NULL, /* client_data passed to PasteGameCB */
5673 /* better to use the time field from the event that triggered the
5674 * call to this function, but that isn't trivial to get
5684 SaveGameProc(NULL, NULL, NULL, NULL);
5688 void QuitProc(w, event, prms, nprms)
5697 void PauseProc(w, event, prms, nprms)
5707 void MachineBlackProc(w, event, prms, nprms)
5713 MachineBlackEvent();
5716 void MachineWhiteProc(w, event, prms, nprms)
5722 MachineWhiteEvent();
5725 void AnalyzeModeProc(w, event, prms, nprms)
5733 if (!first.analysisSupport) {
5734 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5735 DisplayError(buf, 0);
5738 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5739 if (appData.icsActive) {
5740 if (gameMode != IcsObserving) {
5741 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5742 DisplayError(buf, 0);
5744 if (appData.icsEngineAnalyze) {
5745 if (appData.debugMode)
5746 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5752 /* if enable, use want disable icsEngineAnalyze */
5753 if (appData.icsEngineAnalyze) {
5758 appData.icsEngineAnalyze = TRUE;
5759 if (appData.debugMode)
5760 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5762 #ifndef OPTIONSDIALOG
5763 if (!appData.showThinking)
5764 ShowThinkingProc(w,event,prms,nprms);
5770 void AnalyzeFileProc(w, event, prms, nprms)
5776 if (!first.analysisSupport) {
5778 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5779 DisplayError(buf, 0);
5783 #ifndef OPTIONSDIALOG
5784 if (!appData.showThinking)
5785 ShowThinkingProc(w,event,prms,nprms);
5788 FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5789 AnalysisPeriodicEvent(1);
5792 void TwoMachinesProc(w, event, prms, nprms)
5801 void MatchProc(w, event, prms, nprms)
5807 if(gameMode != BeginningOfGame) { DisplayError(_("You can only start a match from the initial position."), 0); return; }
5808 appData.matchGames = appData.defaultMatchGames;
5812 void IcsClientProc(w, event, prms, nprms)
5821 void EditGameProc(w, event, prms, nprms)
5830 void EditPositionProc(w, event, prms, nprms)
5836 EditPositionEvent();
5839 void TrainingProc(w, event, prms, nprms)
5848 void EditCommentProc(w, event, prms, nprms)
5856 if (PopDown(1)) { // popdown succesful
5858 XtSetArg(args[j], XtNleftBitmap, None); j++;
5859 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
5860 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
5861 } else // was not up
5865 void IcsInputBoxProc(w, event, prms, nprms)
5871 if (!PopDown(4)) ICSInputBoxPopUp();
5874 void AcceptProc(w, event, prms, nprms)
5883 void DeclineProc(w, event, prms, nprms)
5892 void RematchProc(w, event, prms, nprms)
5901 void CallFlagProc(w, event, prms, nprms)
5910 void DrawProc(w, event, prms, nprms)
5919 void AbortProc(w, event, prms, nprms)
5928 void AdjournProc(w, event, prms, nprms)
5937 void ResignProc(w, event, prms, nprms)
5946 void AdjuWhiteProc(w, event, prms, nprms)
5952 UserAdjudicationEvent(+1);
5955 void AdjuBlackProc(w, event, prms, nprms)
5961 UserAdjudicationEvent(-1);
5964 void AdjuDrawProc(w, event, prms, nprms)
5970 UserAdjudicationEvent(0);
5973 void EnterKeyProc(w, event, prms, nprms)
5979 if (shellUp[4] == True)
5983 void UpKeyProc(w, event, prms, nprms)
5988 { // [HGM] input: let up-arrow recall previous line from history
5995 if (!shellUp[4]) return;
5996 edit = boxOptions[0].handle;
5998 XtSetArg(args[j], XtNstring, &val); j++;
5999 XtGetValues(edit, args, j);
6000 val = PrevInHistory(val);
6001 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6002 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6004 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6005 XawTextReplace(edit, 0, 0, &t);
6006 XawTextSetInsertionPoint(edit, 9999);
6010 void DownKeyProc(w, event, prms, nprms)
6015 { // [HGM] input: let down-arrow recall next line from history
6020 if (!shellUp[4]) return;
6021 edit = boxOptions[0].handle;
6022 val = NextInHistory();
6023 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6024 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6026 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6027 XawTextReplace(edit, 0, 0, &t);
6028 XawTextSetInsertionPoint(edit, 9999);
6032 void StopObservingProc(w, event, prms, nprms)
6038 StopObservingEvent();
6041 void StopExaminingProc(w, event, prms, nprms)
6047 StopExaminingEvent();
6050 void UploadProc(w, event, prms, nprms)
6060 void ForwardProc(w, event, prms, nprms)
6070 void BackwardProc(w, event, prms, nprms)
6079 void ToStartProc(w, event, prms, nprms)
6088 void ToEndProc(w, event, prms, nprms)
6097 void RevertProc(w, event, prms, nprms)
6106 void AnnotateProc(w, event, prms, nprms)
6115 void TruncateGameProc(w, event, prms, nprms)
6121 TruncateGameEvent();
6123 void RetractMoveProc(w, event, prms, nprms)
6132 void MoveNowProc(w, event, prms, nprms)
6141 void FlipViewProc(w, event, prms, nprms)
6147 flipView = !flipView;
6148 DrawPosition(True, NULL);
6151 void PonderNextMoveProc(w, event, prms, nprms)
6159 PonderNextMoveEvent(!appData.ponderNextMove);
6160 #ifndef OPTIONSDIALOG
6161 if (appData.ponderNextMove) {
6162 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6164 XtSetArg(args[0], XtNleftBitmap, None);
6166 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6171 #ifndef OPTIONSDIALOG
6172 void AlwaysQueenProc(w, event, prms, nprms)
6180 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6182 if (appData.alwaysPromoteToQueen) {
6183 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6185 XtSetArg(args[0], XtNleftBitmap, None);
6187 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6191 void AnimateDraggingProc(w, event, prms, nprms)
6199 appData.animateDragging = !appData.animateDragging;
6201 if (appData.animateDragging) {
6202 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6205 XtSetArg(args[0], XtNleftBitmap, None);
6207 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6211 void AnimateMovingProc(w, event, prms, nprms)
6219 appData.animate = !appData.animate;
6221 if (appData.animate) {
6222 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6225 XtSetArg(args[0], XtNleftBitmap, None);
6227 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6231 void AutoflagProc(w, event, prms, nprms)
6239 appData.autoCallFlag = !appData.autoCallFlag;
6241 if (appData.autoCallFlag) {
6242 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6244 XtSetArg(args[0], XtNleftBitmap, None);
6246 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6250 void AutoflipProc(w, event, prms, nprms)
6258 appData.autoFlipView = !appData.autoFlipView;
6260 if (appData.autoFlipView) {
6261 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6263 XtSetArg(args[0], XtNleftBitmap, None);
6265 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6269 void BlindfoldProc(w, event, prms, nprms)
6277 appData.blindfold = !appData.blindfold;
6279 if (appData.blindfold) {
6280 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6282 XtSetArg(args[0], XtNleftBitmap, None);
6284 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6287 DrawPosition(True, NULL);
6290 void TestLegalityProc(w, event, prms, nprms)
6298 appData.testLegality = !appData.testLegality;
6300 if (appData.testLegality) {
6301 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6303 XtSetArg(args[0], XtNleftBitmap, None);
6305 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6310 void FlashMovesProc(w, event, prms, nprms)
6318 if (appData.flashCount == 0) {
6319 appData.flashCount = 3;
6321 appData.flashCount = -appData.flashCount;
6324 if (appData.flashCount > 0) {
6325 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6327 XtSetArg(args[0], XtNleftBitmap, None);
6329 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6334 void HighlightDraggingProc(w, event, prms, nprms)
6342 appData.highlightDragging = !appData.highlightDragging;
6344 if (appData.highlightDragging) {
6345 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6347 XtSetArg(args[0], XtNleftBitmap, None);
6349 XtSetValues(XtNameToWidget(menuBarWidget,
6350 "menuOptions.Highlight Dragging"), args, 1);
6354 void HighlightLastMoveProc(w, event, prms, nprms)
6362 appData.highlightLastMove = !appData.highlightLastMove;
6364 if (appData.highlightLastMove) {
6365 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6367 XtSetArg(args[0], XtNleftBitmap, None);
6369 XtSetValues(XtNameToWidget(menuBarWidget,
6370 "menuOptions.Highlight Last Move"), args, 1);
6373 void HighlightArrowProc(w, event, prms, nprms)
6381 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6383 if (appData.highlightMoveWithArrow) {
6384 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6386 XtSetArg(args[0], XtNleftBitmap, None);
6388 XtSetValues(XtNameToWidget(menuBarWidget,
6389 "menuOptions.Arrow"), args, 1);
6393 void IcsAlarmProc(w, event, prms, nprms)
6401 appData.icsAlarm = !appData.icsAlarm;
6403 if (appData.icsAlarm) {
6404 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6406 XtSetArg(args[0], XtNleftBitmap, None);
6408 XtSetValues(XtNameToWidget(menuBarWidget,
6409 "menuOptions.ICS Alarm"), args, 1);
6413 void MoveSoundProc(w, event, prms, nprms)
6421 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6423 if (appData.ringBellAfterMoves) {
6424 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6426 XtSetArg(args[0], XtNleftBitmap, None);
6428 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6432 void OneClickProc(w, event, prms, nprms)
6440 appData.oneClick = !appData.oneClick;
6442 if (appData.oneClick) {
6443 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6445 XtSetArg(args[0], XtNleftBitmap, None);
6447 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6451 void PeriodicUpdatesProc(w, event, prms, nprms)
6459 PeriodicUpdatesEvent(!appData.periodicUpdates);
6461 if (appData.periodicUpdates) {
6462 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6464 XtSetArg(args[0], XtNleftBitmap, None);
6466 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6470 void PopupExitMessageProc(w, event, prms, nprms)
6478 appData.popupExitMessage = !appData.popupExitMessage;
6480 if (appData.popupExitMessage) {
6481 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6483 XtSetArg(args[0], XtNleftBitmap, None);
6485 XtSetValues(XtNameToWidget(menuBarWidget,
6486 "menuOptions.Popup Exit Message"), args, 1);
6489 void PopupMoveErrorsProc(w, event, prms, nprms)
6497 appData.popupMoveErrors = !appData.popupMoveErrors;
6499 if (appData.popupMoveErrors) {
6500 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6502 XtSetArg(args[0], XtNleftBitmap, None);
6504 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6509 void PremoveProc(w, event, prms, nprms)
6517 appData.premove = !appData.premove;
6519 if (appData.premove) {
6520 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6522 XtSetArg(args[0], XtNleftBitmap, None);
6524 XtSetValues(XtNameToWidget(menuBarWidget,
6525 "menuOptions.Premove"), args, 1);
6529 void ShowCoordsProc(w, event, prms, nprms)
6537 appData.showCoords = !appData.showCoords;
6539 if (appData.showCoords) {
6540 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6542 XtSetArg(args[0], XtNleftBitmap, None);
6544 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6547 DrawPosition(True, NULL);
6550 void ShowThinkingProc(w, event, prms, nprms)
6556 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6557 ShowThinkingEvent();
6560 void HideThinkingProc(w, event, prms, nprms)
6568 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6569 ShowThinkingEvent();
6571 if (appData.hideThinkingFromHuman) {
6572 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6574 XtSetArg(args[0], XtNleftBitmap, None);
6576 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6581 void SaveOnExitProc(w, event, prms, nprms)
6589 saveSettingsOnExit = !saveSettingsOnExit;
6591 if (saveSettingsOnExit) {
6592 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6594 XtSetArg(args[0], XtNleftBitmap, None);
6596 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6600 void SaveSettingsProc(w, event, prms, nprms)
6606 SaveSettings(settingsFileName);
6609 void InfoProc(w, event, prms, nprms)
6616 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6621 void ManProc(w, event, prms, nprms)
6629 if (nprms && *nprms > 0)
6633 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6637 void HintProc(w, event, prms, nprms)
6646 void BookProc(w, event, prms, nprms)
6655 void AboutProc(w, event, prms, nprms)
6663 char *zippy = " (with Zippy code)";
6667 snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
6668 programVersion, zippy,
6669 "Copyright 1991 Digital Equipment Corporation",
6670 "Enhancements Copyright 1992-2009 Free Software Foundation",
6671 "Enhancements Copyright 2005 Alessandro Scotti",
6672 PACKAGE, " is free software and carries NO WARRANTY;",
6673 "see the file COPYING for more information.");
6674 ErrorPopUp(_("About XBoard"), buf, FALSE);
6677 void DebugProc(w, event, prms, nprms)
6683 appData.debugMode = !appData.debugMode;
6686 void AboutGameProc(w, event, prms, nprms)
6695 void NothingProc(w, event, prms, nprms)
6704 void Iconify(w, event, prms, nprms)
6713 XtSetArg(args[0], XtNiconic, True);
6714 XtSetValues(shellWidget, args, 1);
6717 void DisplayMessage(message, extMessage)
6718 char *message, *extMessage;
6720 /* display a message in the message widget */
6729 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6734 message = extMessage;
6738 /* need to test if messageWidget already exists, since this function
6739 can also be called during the startup, if for example a Xresource
6740 is not set up correctly */
6743 XtSetArg(arg, XtNlabel, message);
6744 XtSetValues(messageWidget, &arg, 1);
6750 void DisplayTitle(text)
6755 char title[MSG_SIZ];
6758 if (text == NULL) text = "";
6760 if (appData.titleInWindow) {
6762 XtSetArg(args[i], XtNlabel, text); i++;
6763 XtSetValues(titleWidget, args, i);
6766 if (*text != NULLCHAR) {
6767 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6768 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6769 } else if (appData.icsActive) {
6770 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6771 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6772 } else if (appData.cmailGameName[0] != NULLCHAR) {
6773 snprintf(icon, sizeof(icon), "%s", "CMail");
6774 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6776 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6777 } else if (gameInfo.variant == VariantGothic) {
6778 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6779 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6782 } else if (gameInfo.variant == VariantFalcon) {
6783 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6784 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6786 } else if (appData.noChessProgram) {
6787 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6788 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6790 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6791 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6794 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6795 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6796 XtSetValues(shellWidget, args, i);
6801 DisplayError(message, error)
6808 if (appData.debugMode || appData.matchMode) {
6809 fprintf(stderr, "%s: %s\n", programName, message);
6812 if (appData.debugMode || appData.matchMode) {
6813 fprintf(stderr, "%s: %s: %s\n",
6814 programName, message, strerror(error));
6816 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6819 ErrorPopUp(_("Error"), message, FALSE);
6823 void DisplayMoveError(message)
6828 DrawPosition(FALSE, NULL);
6829 if (appData.debugMode || appData.matchMode) {
6830 fprintf(stderr, "%s: %s\n", programName, message);
6832 if (appData.popupMoveErrors) {
6833 ErrorPopUp(_("Error"), message, FALSE);
6835 DisplayMessage(message, "");
6840 void DisplayFatalError(message, error, status)
6846 errorExitStatus = status;
6848 fprintf(stderr, "%s: %s\n", programName, message);
6850 fprintf(stderr, "%s: %s: %s\n",
6851 programName, message, strerror(error));
6852 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6855 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6856 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6862 void DisplayInformation(message)
6866 ErrorPopUp(_("Information"), message, TRUE);
6869 void DisplayNote(message)
6873 ErrorPopUp(_("Note"), message, FALSE);
6877 NullXErrorCheck(dpy, error_event)
6879 XErrorEvent *error_event;
6884 void DisplayIcsInteractionTitle(message)
6887 if (oldICSInteractionTitle == NULL) {
6888 /* Magic to find the old window title, adapted from vim */
6889 char *wina = getenv("WINDOWID");
6891 Window win = (Window) atoi(wina);
6892 Window root, parent, *children;
6893 unsigned int nchildren;
6894 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6896 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6897 if (!XQueryTree(xDisplay, win, &root, &parent,
6898 &children, &nchildren)) break;
6899 if (children) XFree((void *)children);
6900 if (parent == root || parent == 0) break;
6903 XSetErrorHandler(oldHandler);
6905 if (oldICSInteractionTitle == NULL) {
6906 oldICSInteractionTitle = "xterm";
6909 printf("\033]0;%s\007", message);
6913 char pendingReplyPrefix[MSG_SIZ];
6914 ProcRef pendingReplyPR;
6916 void AskQuestionProc(w, event, prms, nprms)
6923 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
6927 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6930 void AskQuestionPopDown()
6932 if (!askQuestionUp) return;
6933 XtPopdown(askQuestionShell);
6934 XtDestroyWidget(askQuestionShell);
6935 askQuestionUp = False;
6938 void AskQuestionReplyAction(w, event, prms, nprms)
6948 reply = XawDialogGetValueString(w = XtParent(w));
6949 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
6950 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
6951 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
6952 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
6953 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6954 AskQuestionPopDown();
6956 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
6959 void AskQuestionCallback(w, client_data, call_data)
6961 XtPointer client_data, call_data;
6966 XtSetArg(args[0], XtNlabel, &name);
6967 XtGetValues(w, args, 1);
6969 if (strcmp(name, _("cancel")) == 0) {
6970 AskQuestionPopDown();
6972 AskQuestionReplyAction(w, NULL, NULL, NULL);
6976 void AskQuestion(title, question, replyPrefix, pr)
6977 char *title, *question, *replyPrefix;
6981 Widget popup, layout, dialog, edit;
6987 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
6988 pendingReplyPR = pr;
6991 XtSetArg(args[i], XtNresizable, True); i++;
6992 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
6993 askQuestionShell = popup =
6994 XtCreatePopupShell(title, transientShellWidgetClass,
6995 shellWidget, args, i);
6998 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
6999 layoutArgs, XtNumber(layoutArgs));
7002 XtSetArg(args[i], XtNlabel, question); i++;
7003 XtSetArg(args[i], XtNvalue, ""); i++;
7004 XtSetArg(args[i], XtNborderWidth, 0); i++;
7005 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7008 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7009 (XtPointer) dialog);
7010 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7011 (XtPointer) dialog);
7013 XtRealizeWidget(popup);
7014 CatchDeleteWindow(popup, "AskQuestionPopDown");
7016 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7017 &x, &y, &win_x, &win_y, &mask);
7019 XtSetArg(args[0], XtNx, x - 10);
7020 XtSetArg(args[1], XtNy, y - 30);
7021 XtSetValues(popup, args, 2);
7023 XtPopup(popup, XtGrabExclusive);
7024 askQuestionUp = True;
7026 edit = XtNameToWidget(dialog, "*value");
7027 XtSetKeyboardFocus(popup, edit);
7035 if (*name == NULLCHAR) {
7037 } else if (strcmp(name, "$") == 0) {
7038 putc(BELLCHAR, stderr);
7041 char *prefix = "", *sep = "";
7042 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7043 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7051 PlaySound(appData.soundMove);
7057 PlaySound(appData.soundIcsWin);
7063 PlaySound(appData.soundIcsLoss);
7069 PlaySound(appData.soundIcsDraw);
7073 PlayIcsUnfinishedSound()
7075 PlaySound(appData.soundIcsUnfinished);
7081 PlaySound(appData.soundIcsAlarm);
7087 system("stty echo");
7093 system("stty -echo");
7097 Colorize(cc, continuation)
7102 int count, outCount, error;
7104 if (textColors[(int)cc].bg > 0) {
7105 if (textColors[(int)cc].fg > 0) {
7106 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7107 textColors[(int)cc].fg, textColors[(int)cc].bg);
7109 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7110 textColors[(int)cc].bg);
7113 if (textColors[(int)cc].fg > 0) {
7114 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7115 textColors[(int)cc].fg);
7117 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7120 count = strlen(buf);
7121 outCount = OutputToProcess(NoProc, buf, count, &error);
7122 if (outCount < count) {
7123 DisplayFatalError(_("Error writing to display"), error, 1);
7126 if (continuation) return;
7129 PlaySound(appData.soundShout);
7132 PlaySound(appData.soundSShout);
7135 PlaySound(appData.soundChannel1);
7138 PlaySound(appData.soundChannel);
7141 PlaySound(appData.soundKibitz);
7144 PlaySound(appData.soundTell);
7146 case ColorChallenge:
7147 PlaySound(appData.soundChallenge);
7150 PlaySound(appData.soundRequest);
7153 PlaySound(appData.soundSeek);
7164 return getpwuid(getuid())->pw_name;
7168 ExpandPathName(path)
7171 static char static_buf[4*MSG_SIZ];
7172 char *d, *s, buf[4*MSG_SIZ];
7178 while (*s && isspace(*s))
7187 if (*(s+1) == '/') {
7188 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7192 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7193 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7194 pwd = getpwnam(buf);
7197 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7201 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7202 strcat(d, strchr(s+1, '/'));
7206 safeStrCpy(d, s, 4*MSG_SIZ );
7213 static char host_name[MSG_SIZ];
7215 #if HAVE_GETHOSTNAME
7216 gethostname(host_name, MSG_SIZ);
7218 #else /* not HAVE_GETHOSTNAME */
7219 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7220 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7222 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7224 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7225 #endif /* not HAVE_GETHOSTNAME */
7228 XtIntervalId delayedEventTimerXID = 0;
7229 DelayedEventCallback delayedEventCallback = 0;
7234 delayedEventTimerXID = 0;
7235 delayedEventCallback();
7239 ScheduleDelayedEvent(cb, millisec)
7240 DelayedEventCallback cb; long millisec;
7242 if(delayedEventTimerXID && delayedEventCallback == cb)
7243 // [HGM] alive: replace, rather than add or flush identical event
7244 XtRemoveTimeOut(delayedEventTimerXID);
7245 delayedEventCallback = cb;
7246 delayedEventTimerXID =
7247 XtAppAddTimeOut(appContext, millisec,
7248 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7251 DelayedEventCallback
7254 if (delayedEventTimerXID) {
7255 return delayedEventCallback;
7262 CancelDelayedEvent()
7264 if (delayedEventTimerXID) {
7265 XtRemoveTimeOut(delayedEventTimerXID);
7266 delayedEventTimerXID = 0;
7270 XtIntervalId loadGameTimerXID = 0;
7272 int LoadGameTimerRunning()
7274 return loadGameTimerXID != 0;
7277 int StopLoadGameTimer()
7279 if (loadGameTimerXID != 0) {
7280 XtRemoveTimeOut(loadGameTimerXID);
7281 loadGameTimerXID = 0;
7289 LoadGameTimerCallback(arg, id)
7293 loadGameTimerXID = 0;
7298 StartLoadGameTimer(millisec)
7302 XtAppAddTimeOut(appContext, millisec,
7303 (XtTimerCallbackProc) LoadGameTimerCallback,
7307 XtIntervalId analysisClockXID = 0;
7310 AnalysisClockCallback(arg, id)
7314 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7315 || appData.icsEngineAnalyze) { // [DM]
7316 AnalysisPeriodicEvent(0);
7317 StartAnalysisClock();
7322 StartAnalysisClock()
7325 XtAppAddTimeOut(appContext, 2000,
7326 (XtTimerCallbackProc) AnalysisClockCallback,
7330 XtIntervalId clockTimerXID = 0;
7332 int ClockTimerRunning()
7334 return clockTimerXID != 0;
7337 int StopClockTimer()
7339 if (clockTimerXID != 0) {
7340 XtRemoveTimeOut(clockTimerXID);
7349 ClockTimerCallback(arg, id)
7358 StartClockTimer(millisec)
7362 XtAppAddTimeOut(appContext, millisec,
7363 (XtTimerCallbackProc) ClockTimerCallback,
7368 DisplayTimerLabel(w, color, timer, highlight)
7377 /* check for low time warning */
7378 Pixel foregroundOrWarningColor = timerForegroundPixel;
7381 appData.lowTimeWarning &&
7382 (timer / 1000) < appData.icsAlarmTime)
7383 foregroundOrWarningColor = lowTimeWarningColor;
7385 if (appData.clockMode) {
7386 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7387 XtSetArg(args[0], XtNlabel, buf);
7389 snprintf(buf, MSG_SIZ, "%s ", color);
7390 XtSetArg(args[0], XtNlabel, buf);
7395 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7396 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7398 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7399 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7402 XtSetValues(w, args, 3);
7406 DisplayWhiteClock(timeRemaining, highlight)
7412 if(appData.noGUI) return;
7413 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7414 if (highlight && iconPixmap == bIconPixmap) {
7415 iconPixmap = wIconPixmap;
7416 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7417 XtSetValues(shellWidget, args, 1);
7422 DisplayBlackClock(timeRemaining, highlight)
7428 if(appData.noGUI) return;
7429 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7430 if (highlight && iconPixmap == wIconPixmap) {
7431 iconPixmap = bIconPixmap;
7432 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7433 XtSetValues(shellWidget, args, 1);
7451 int StartChildProcess(cmdLine, dir, pr)
7458 int to_prog[2], from_prog[2];
7462 if (appData.debugMode) {
7463 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7466 /* We do NOT feed the cmdLine to the shell; we just
7467 parse it into blank-separated arguments in the
7468 most simple-minded way possible.
7471 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7474 while(*p == ' ') p++;
7476 if(*p == '"' || *p == '\'')
7477 p = strchr(++argv[i-1], *p);
7478 else p = strchr(p, ' ');
7479 if (p == NULL) break;
7484 SetUpChildIO(to_prog, from_prog);
7486 if ((pid = fork()) == 0) {
7488 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7489 close(to_prog[1]); // first close the unused pipe ends
7490 close(from_prog[0]);
7491 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7492 dup2(from_prog[1], 1);
7493 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7494 close(from_prog[1]); // and closing again loses one of the pipes!
7495 if(fileno(stderr) >= 2) // better safe than sorry...
7496 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7498 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7503 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7505 execvp(argv[0], argv);
7507 /* If we get here, exec failed */
7512 /* Parent process */
7514 close(from_prog[1]);
7516 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7519 cp->fdFrom = from_prog[0];
7520 cp->fdTo = to_prog[1];
7525 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7526 static RETSIGTYPE AlarmCallBack(int n)
7532 DestroyChildProcess(pr, signalType)
7536 ChildProc *cp = (ChildProc *) pr;
7538 if (cp->kind != CPReal) return;
7540 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7541 signal(SIGALRM, AlarmCallBack);
7543 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7544 kill(cp->pid, SIGKILL); // kill it forcefully
7545 wait((int *) 0); // and wait again
7549 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7551 /* Process is exiting either because of the kill or because of
7552 a quit command sent by the backend; either way, wait for it to die.
7561 InterruptChildProcess(pr)
7564 ChildProc *cp = (ChildProc *) pr;
7566 if (cp->kind != CPReal) return;
7567 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7570 int OpenTelnet(host, port, pr)
7575 char cmdLine[MSG_SIZ];
7577 if (port[0] == NULLCHAR) {
7578 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7580 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7582 return StartChildProcess(cmdLine, "", pr);
7585 int OpenTCP(host, port, pr)
7591 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7592 #else /* !OMIT_SOCKETS */
7594 struct sockaddr_in sa;
7596 unsigned short uport;
7599 if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
7603 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7604 sa.sin_family = AF_INET;
7605 sa.sin_addr.s_addr = INADDR_ANY;
7606 uport = (unsigned short) 0;
7607 sa.sin_port = htons(uport);
7608 if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
7612 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7613 if (!(hp = gethostbyname(host))) {
7615 if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
7616 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
7617 hp->h_addrtype = AF_INET;
7619 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
7620 hp->h_addr_list[0] = (char *) malloc(4);
7621 hp->h_addr_list[0][0] = b0;
7622 hp->h_addr_list[0][1] = b1;
7623 hp->h_addr_list[0][2] = b2;
7624 hp->h_addr_list[0][3] = b3;
7629 sa.sin_family = hp->h_addrtype;
7630 uport = (unsigned short) atoi(port);
7631 sa.sin_port = htons(uport);
7632 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
7634 if (connect(s, (struct sockaddr *) &sa,
7635 sizeof(struct sockaddr_in)) < 0) {
7639 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7646 #endif /* !OMIT_SOCKETS */
7651 int OpenCommPort(name, pr)
7658 fd = open(name, 2, 0);
7659 if (fd < 0) return errno;
7661 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7671 int OpenLoopback(pr)
7677 SetUpChildIO(to, from);
7679 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7682 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7689 int OpenRcmd(host, user, cmd, pr)
7690 char *host, *user, *cmd;
7693 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7697 #define INPUT_SOURCE_BUF_SIZE 8192
7706 char buf[INPUT_SOURCE_BUF_SIZE];
7711 DoInputCallback(closure, source, xid)
7716 InputSource *is = (InputSource *) closure;
7721 if (is->lineByLine) {
7722 count = read(is->fd, is->unused,
7723 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7725 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7728 is->unused += count;
7730 while (p < is->unused) {
7731 q = memchr(p, '\n', is->unused - p);
7732 if (q == NULL) break;
7734 (is->func)(is, is->closure, p, q - p, 0);
7738 while (p < is->unused) {
7743 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7748 (is->func)(is, is->closure, is->buf, count, error);
7752 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7759 ChildProc *cp = (ChildProc *) pr;
7761 is = (InputSource *) calloc(1, sizeof(InputSource));
7762 is->lineByLine = lineByLine;
7766 is->fd = fileno(stdin);
7768 is->kind = cp->kind;
7769 is->fd = cp->fdFrom;
7772 is->unused = is->buf;
7775 is->xid = XtAppAddInput(appContext, is->fd,
7776 (XtPointer) (XtInputReadMask),
7777 (XtInputCallbackProc) DoInputCallback,
7779 is->closure = closure;
7780 return (InputSourceRef) is;
7784 RemoveInputSource(isr)
7787 InputSource *is = (InputSource *) isr;
7789 if (is->xid == 0) return;
7790 XtRemoveInput(is->xid);
7794 int OutputToProcess(pr, message, count, outError)
7800 static int line = 0;
7801 ChildProc *cp = (ChildProc *) pr;
7806 if (appData.noJoin || !appData.useInternalWrap)
7807 outCount = fwrite(message, 1, count, stdout);
7810 int width = get_term_width();
7811 int len = wrap(NULL, message, count, width, &line);
7812 char *msg = malloc(len);
7816 outCount = fwrite(message, 1, count, stdout);
7819 dbgchk = wrap(msg, message, count, width, &line);
7820 if (dbgchk != len && appData.debugMode)
7821 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7822 outCount = fwrite(msg, 1, dbgchk, stdout);
7828 outCount = write(cp->fdTo, message, count);
7838 /* Output message to process, with "ms" milliseconds of delay
7839 between each character. This is needed when sending the logon
7840 script to ICC, which for some reason doesn't like the
7841 instantaneous send. */
7842 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
7849 ChildProc *cp = (ChildProc *) pr;
7854 r = write(cp->fdTo, message++, 1);
7867 /**** Animation code by Hugh Fisher, DCS, ANU.
7869 Known problem: if a window overlapping the board is
7870 moved away while a piece is being animated underneath,
7871 the newly exposed area won't be updated properly.
7872 I can live with this.
7874 Known problem: if you look carefully at the animation
7875 of pieces in mono mode, they are being drawn as solid
7876 shapes without interior detail while moving. Fixing
7877 this would be a major complication for minimal return.
7880 /* Masks for XPM pieces. Black and white pieces can have
7881 different shapes, but in the interest of retaining my
7882 sanity pieces must have the same outline on both light
7883 and dark squares, and all pieces must use the same
7884 background square colors/images. */
7886 static int xpmDone = 0;
7889 CreateAnimMasks (pieceDepth)
7896 unsigned long plane;
7899 /* Need a bitmap just to get a GC with right depth */
7900 buf = XCreatePixmap(xDisplay, xBoardWindow,
7902 values.foreground = 1;
7903 values.background = 0;
7904 /* Don't use XtGetGC, not read only */
7905 maskGC = XCreateGC(xDisplay, buf,
7906 GCForeground | GCBackground, &values);
7907 XFreePixmap(xDisplay, buf);
7909 buf = XCreatePixmap(xDisplay, xBoardWindow,
7910 squareSize, squareSize, pieceDepth);
7911 values.foreground = XBlackPixel(xDisplay, xScreen);
7912 values.background = XWhitePixel(xDisplay, xScreen);
7913 bufGC = XCreateGC(xDisplay, buf,
7914 GCForeground | GCBackground, &values);
7916 for (piece = WhitePawn; piece <= BlackKing; piece++) {
7917 /* Begin with empty mask */
7918 if(!xpmDone) // [HGM] pieces: keep using existing
7919 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7920 squareSize, squareSize, 1);
7921 XSetFunction(xDisplay, maskGC, GXclear);
7922 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7923 0, 0, squareSize, squareSize);
7925 /* Take a copy of the piece */
7930 XSetFunction(xDisplay, bufGC, GXcopy);
7931 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
7933 0, 0, squareSize, squareSize, 0, 0);
7935 /* XOR the background (light) over the piece */
7936 XSetFunction(xDisplay, bufGC, GXxor);
7938 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7939 0, 0, squareSize, squareSize, 0, 0);
7941 XSetForeground(xDisplay, bufGC, lightSquareColor);
7942 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7945 /* We now have an inverted piece image with the background
7946 erased. Construct mask by just selecting all the non-zero
7947 pixels - no need to reconstruct the original image. */
7948 XSetFunction(xDisplay, maskGC, GXor);
7950 /* Might be quicker to download an XImage and create bitmap
7951 data from it rather than this N copies per piece, but it
7952 only takes a fraction of a second and there is a much
7953 longer delay for loading the pieces. */
7954 for (n = 0; n < pieceDepth; n ++) {
7955 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7956 0, 0, squareSize, squareSize,
7962 XFreePixmap(xDisplay, buf);
7963 XFreeGC(xDisplay, bufGC);
7964 XFreeGC(xDisplay, maskGC);
7968 InitAnimState (anim, info)
7970 XWindowAttributes * info;
7975 /* Each buffer is square size, same depth as window */
7976 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
7977 squareSize, squareSize, info->depth);
7978 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
7979 squareSize, squareSize, info->depth);
7981 /* Create a plain GC for blitting */
7982 mask = GCForeground | GCBackground | GCFunction |
7983 GCPlaneMask | GCGraphicsExposures;
7984 values.foreground = XBlackPixel(xDisplay, xScreen);
7985 values.background = XWhitePixel(xDisplay, xScreen);
7986 values.function = GXcopy;
7987 values.plane_mask = AllPlanes;
7988 values.graphics_exposures = False;
7989 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
7991 /* Piece will be copied from an existing context at
7992 the start of each new animation/drag. */
7993 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
7995 /* Outline will be a read-only copy of an existing */
7996 anim->outlineGC = None;
8002 XWindowAttributes info;
8004 if (xpmDone && gameInfo.variant == oldVariant) return;
8005 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
8006 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8008 InitAnimState(&game, &info);
8009 InitAnimState(&player, &info);
8011 /* For XPM pieces, we need bitmaps to use as masks. */
8013 CreateAnimMasks(info.depth);
8019 static Boolean frameWaiting;
8021 static RETSIGTYPE FrameAlarm (sig)
8024 frameWaiting = False;
8025 /* In case System-V style signals. Needed?? */
8026 signal(SIGALRM, FrameAlarm);
8033 struct itimerval delay;
8035 XSync(xDisplay, False);
8038 frameWaiting = True;
8039 signal(SIGALRM, FrameAlarm);
8040 delay.it_interval.tv_sec =
8041 delay.it_value.tv_sec = time / 1000;
8042 delay.it_interval.tv_usec =
8043 delay.it_value.tv_usec = (time % 1000) * 1000;
8044 setitimer(ITIMER_REAL, &delay, NULL);
8045 while (frameWaiting) pause();
8046 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8047 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8048 setitimer(ITIMER_REAL, &delay, NULL);
8058 XSync(xDisplay, False);
8060 usleep(time * 1000);
8065 /* Convert board position to corner of screen rect and color */
8068 ScreenSquare(column, row, pt, color)
8069 int column; int row; XPoint * pt; int * color;
8072 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8073 pt->y = lineGap + row * (squareSize + lineGap);
8075 pt->x = lineGap + column * (squareSize + lineGap);
8076 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8078 *color = SquareColor(row, column);
8081 /* Convert window coords to square */
8084 BoardSquare(x, y, column, row)
8085 int x; int y; int * column; int * row;
8087 *column = EventToSquare(x, BOARD_WIDTH);
8088 if (flipView && *column >= 0)
8089 *column = BOARD_WIDTH - 1 - *column;
8090 *row = EventToSquare(y, BOARD_HEIGHT);
8091 if (!flipView && *row >= 0)
8092 *row = BOARD_HEIGHT - 1 - *row;
8097 #undef Max /* just in case */
8099 #define Max(a, b) ((a) > (b) ? (a) : (b))
8100 #define Min(a, b) ((a) < (b) ? (a) : (b))
8103 SetRect(rect, x, y, width, height)
8104 XRectangle * rect; int x; int y; int width; int height;
8108 rect->width = width;
8109 rect->height = height;
8112 /* Test if two frames overlap. If they do, return
8113 intersection rect within old and location of
8114 that rect within new. */
8117 Intersect(old, new, size, area, pt)
8118 XPoint * old; XPoint * new;
8119 int size; XRectangle * area; XPoint * pt;
8121 if (old->x > new->x + size || new->x > old->x + size ||
8122 old->y > new->y + size || new->y > old->y + size) {
8125 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8126 size - abs(old->x - new->x), size - abs(old->y - new->y));
8127 pt->x = Max(old->x - new->x, 0);
8128 pt->y = Max(old->y - new->y, 0);
8133 /* For two overlapping frames, return the rect(s)
8134 in the old that do not intersect with the new. */
8137 CalcUpdateRects(old, new, size, update, nUpdates)
8138 XPoint * old; XPoint * new; int size;
8139 XRectangle update[]; int * nUpdates;
8143 /* If old = new (shouldn't happen) then nothing to draw */
8144 if (old->x == new->x && old->y == new->y) {
8148 /* Work out what bits overlap. Since we know the rects
8149 are the same size we don't need a full intersect calc. */
8151 /* Top or bottom edge? */
8152 if (new->y > old->y) {
8153 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8155 } else if (old->y > new->y) {
8156 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8157 size, old->y - new->y);
8160 /* Left or right edge - don't overlap any update calculated above. */
8161 if (new->x > old->x) {
8162 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8163 new->x - old->x, size - abs(new->y - old->y));
8165 } else if (old->x > new->x) {
8166 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8167 old->x - new->x, size - abs(new->y - old->y));
8174 /* Generate a series of frame coords from start->mid->finish.
8175 The movement rate doubles until the half way point is
8176 reached, then halves back down to the final destination,
8177 which gives a nice slow in/out effect. The algorithmn
8178 may seem to generate too many intermediates for short
8179 moves, but remember that the purpose is to attract the
8180 viewers attention to the piece about to be moved and
8181 then to where it ends up. Too few frames would be less
8185 Tween(start, mid, finish, factor, frames, nFrames)
8186 XPoint * start; XPoint * mid;
8187 XPoint * finish; int factor;
8188 XPoint frames[]; int * nFrames;
8190 int fraction, n, count;
8194 /* Slow in, stepping 1/16th, then 1/8th, ... */
8196 for (n = 0; n < factor; n++)
8198 for (n = 0; n < factor; n++) {
8199 frames[count].x = start->x + (mid->x - start->x) / fraction;
8200 frames[count].y = start->y + (mid->y - start->y) / fraction;
8202 fraction = fraction / 2;
8206 frames[count] = *mid;
8209 /* Slow out, stepping 1/2, then 1/4, ... */
8211 for (n = 0; n < factor; n++) {
8212 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8213 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8215 fraction = fraction * 2;
8220 /* Draw a piece on the screen without disturbing what's there */
8223 SelectGCMask(piece, clip, outline, mask)
8224 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8228 /* Bitmap for piece being moved. */
8229 if (appData.monoMode) {
8230 *mask = *pieceToSolid(piece);
8231 } else if (useImages) {
8233 *mask = xpmMask[piece];
8235 *mask = ximMaskPm[piece];
8238 *mask = *pieceToSolid(piece);
8241 /* GC for piece being moved. Square color doesn't matter, but
8242 since it gets modified we make a copy of the original. */
8244 if (appData.monoMode)
8249 if (appData.monoMode)
8254 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8256 /* Outline only used in mono mode and is not modified */
8258 *outline = bwPieceGC;
8260 *outline = wbPieceGC;
8264 OverlayPiece(piece, clip, outline, dest)
8265 ChessSquare piece; GC clip; GC outline; Drawable dest;
8270 /* Draw solid rectangle which will be clipped to shape of piece */
8271 XFillRectangle(xDisplay, dest, clip,
8272 0, 0, squareSize, squareSize);
8273 if (appData.monoMode)
8274 /* Also draw outline in contrasting color for black
8275 on black / white on white cases */
8276 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8277 0, 0, squareSize, squareSize, 0, 0, 1);
8279 /* Copy the piece */
8284 if(appData.upsideDown && flipView) kind ^= 2;
8285 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8287 0, 0, squareSize, squareSize,
8292 /* Animate the movement of a single piece */
8295 BeginAnimation(anim, piece, startColor, start)
8303 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8304 /* The old buffer is initialised with the start square (empty) */
8305 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8306 anim->prevFrame = *start;
8308 /* The piece will be drawn using its own bitmap as a matte */
8309 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8310 XSetClipMask(xDisplay, anim->pieceGC, mask);
8314 AnimationFrame(anim, frame, piece)
8319 XRectangle updates[4];
8324 /* Save what we are about to draw into the new buffer */
8325 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8326 frame->x, frame->y, squareSize, squareSize,
8329 /* Erase bits of the previous frame */
8330 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8331 /* Where the new frame overlapped the previous,
8332 the contents in newBuf are wrong. */
8333 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8334 overlap.x, overlap.y,
8335 overlap.width, overlap.height,
8337 /* Repaint the areas in the old that don't overlap new */
8338 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8339 for (i = 0; i < count; i++)
8340 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8341 updates[i].x - anim->prevFrame.x,
8342 updates[i].y - anim->prevFrame.y,
8343 updates[i].width, updates[i].height,
8344 updates[i].x, updates[i].y);
8346 /* Easy when no overlap */
8347 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8348 0, 0, squareSize, squareSize,
8349 anim->prevFrame.x, anim->prevFrame.y);
8352 /* Save this frame for next time round */
8353 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8354 0, 0, squareSize, squareSize,
8356 anim->prevFrame = *frame;
8358 /* Draw piece over original screen contents, not current,
8359 and copy entire rect. Wipes out overlapping piece images. */
8360 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8361 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8362 0, 0, squareSize, squareSize,
8363 frame->x, frame->y);
8367 EndAnimation (anim, finish)
8371 XRectangle updates[4];
8376 /* The main code will redraw the final square, so we
8377 only need to erase the bits that don't overlap. */
8378 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8379 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8380 for (i = 0; i < count; i++)
8381 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8382 updates[i].x - anim->prevFrame.x,
8383 updates[i].y - anim->prevFrame.y,
8384 updates[i].width, updates[i].height,
8385 updates[i].x, updates[i].y);
8387 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8388 0, 0, squareSize, squareSize,
8389 anim->prevFrame.x, anim->prevFrame.y);
8394 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8396 ChessSquare piece; int startColor;
8397 XPoint * start; XPoint * finish;
8398 XPoint frames[]; int nFrames;
8402 BeginAnimation(anim, piece, startColor, start);
8403 for (n = 0; n < nFrames; n++) {
8404 AnimationFrame(anim, &(frames[n]), piece);
8405 FrameDelay(appData.animSpeed);
8407 EndAnimation(anim, finish);
8411 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8414 ChessSquare piece = board[fromY][toY];
8415 board[fromY][toY] = EmptySquare;
8416 DrawPosition(FALSE, board);
8418 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8419 y = lineGap + toY * (squareSize + lineGap);
8421 x = lineGap + toX * (squareSize + lineGap);
8422 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8424 for(i=1; i<4*kFactor; i++) {
8425 int r = squareSize * 9 * i/(20*kFactor - 5);
8426 XFillArc(xDisplay, xBoardWindow, highlineGC,
8427 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8428 FrameDelay(appData.animSpeed);
8430 board[fromY][toY] = piece;
8433 /* Main control logic for deciding what to animate and how */
8436 AnimateMove(board, fromX, fromY, toX, toY)
8445 XPoint start, finish, mid;
8446 XPoint frames[kFactor * 2 + 1];
8447 int nFrames, startColor, endColor;
8449 /* Are we animating? */
8450 if (!appData.animate || appData.blindfold)
8453 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8454 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8455 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8457 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8458 piece = board[fromY][fromX];
8459 if (piece >= EmptySquare) return;
8464 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8467 if (appData.debugMode) {
8468 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8469 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8470 piece, fromX, fromY, toX, toY); }
8472 ScreenSquare(fromX, fromY, &start, &startColor);
8473 ScreenSquare(toX, toY, &finish, &endColor);
8476 /* Knight: make straight movement then diagonal */
8477 if (abs(toY - fromY) < abs(toX - fromX)) {
8478 mid.x = start.x + (finish.x - start.x) / 2;
8482 mid.y = start.y + (finish.y - start.y) / 2;
8485 mid.x = start.x + (finish.x - start.x) / 2;
8486 mid.y = start.y + (finish.y - start.y) / 2;
8489 /* Don't use as many frames for very short moves */
8490 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8491 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8493 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8494 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8495 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8497 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8498 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8501 /* Be sure end square is redrawn */
8502 damage[0][toY][toX] = True;
8506 DragPieceBegin(x, y)
8509 int boardX, boardY, color;
8512 /* Are we animating? */
8513 if (!appData.animateDragging || appData.blindfold)
8516 /* Figure out which square we start in and the
8517 mouse position relative to top left corner. */
8518 BoardSquare(x, y, &boardX, &boardY);
8519 player.startBoardX = boardX;
8520 player.startBoardY = boardY;
8521 ScreenSquare(boardX, boardY, &corner, &color);
8522 player.startSquare = corner;
8523 player.startColor = color;
8524 /* As soon as we start dragging, the piece will jump slightly to
8525 be centered over the mouse pointer. */
8526 player.mouseDelta.x = squareSize/2;
8527 player.mouseDelta.y = squareSize/2;
8528 /* Initialise animation */
8529 player.dragPiece = PieceForSquare(boardX, boardY);
8531 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8532 player.dragActive = True;
8533 BeginAnimation(&player, player.dragPiece, color, &corner);
8534 /* Mark this square as needing to be redrawn. Note that
8535 we don't remove the piece though, since logically (ie
8536 as seen by opponent) the move hasn't been made yet. */
8537 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8538 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8539 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8540 corner.x, corner.y, squareSize, squareSize,
8541 0, 0); // [HGM] zh: unstack in stead of grab
8542 if(gatingPiece != EmptySquare) {
8543 /* Kludge alert: When gating we want the introduced
8544 piece to appear on the from square. To generate an
8545 image of it, we draw it on the board, copy the image,
8546 and draw the original piece again. */
8547 ChessSquare piece = boards[currentMove][boardY][boardX];
8548 DrawSquare(boardY, boardX, gatingPiece, 0);
8549 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8550 corner.x, corner.y, squareSize, squareSize, 0, 0);
8551 DrawSquare(boardY, boardX, piece, 0);
8553 damage[0][boardY][boardX] = True;
8555 player.dragActive = False;
8560 ChangeDragPiece(ChessSquare piece)
8563 player.dragPiece = piece;
8564 /* The piece will be drawn using its own bitmap as a matte */
8565 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8566 XSetClipMask(xDisplay, player.pieceGC, mask);
8575 /* Are we animating? */
8576 if (!appData.animateDragging || appData.blindfold)
8580 if (! player.dragActive)
8582 /* Move piece, maintaining same relative position
8583 of mouse within square */
8584 corner.x = x - player.mouseDelta.x;
8585 corner.y = y - player.mouseDelta.y;
8586 AnimationFrame(&player, &corner, player.dragPiece);
8588 if (appData.highlightDragging) {
8590 BoardSquare(x, y, &boardX, &boardY);
8591 SetHighlights(fromX, fromY, boardX, boardY);
8600 int boardX, boardY, color;
8603 /* Are we animating? */
8604 if (!appData.animateDragging || appData.blindfold)
8608 if (! player.dragActive)
8610 /* Last frame in sequence is square piece is
8611 placed on, which may not match mouse exactly. */
8612 BoardSquare(x, y, &boardX, &boardY);
8613 ScreenSquare(boardX, boardY, &corner, &color);
8614 EndAnimation(&player, &corner);
8616 /* Be sure end square is redrawn */
8617 damage[0][boardY][boardX] = True;
8619 /* This prevents weird things happening with fast successive
8620 clicks which on my Sun at least can cause motion events
8621 without corresponding press/release. */
8622 player.dragActive = False;
8625 /* Handle expose event while piece being dragged */
8630 if (!player.dragActive || appData.blindfold)
8633 /* What we're doing: logically, the move hasn't been made yet,
8634 so the piece is still in it's original square. But visually
8635 it's being dragged around the board. So we erase the square
8636 that the piece is on and draw it at the last known drag point. */
8637 BlankSquare(player.startSquare.x, player.startSquare.y,
8638 player.startColor, EmptySquare, xBoardWindow, 1);
8639 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8640 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8643 #include <sys/ioctl.h>
8644 int get_term_width()
8646 int fd, default_width;
8649 default_width = 79; // this is FICS default anyway...
8651 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8653 if (!ioctl(fd, TIOCGSIZE, &win))
8654 default_width = win.ts_cols;
8655 #elif defined(TIOCGWINSZ)
8657 if (!ioctl(fd, TIOCGWINSZ, &win))
8658 default_width = win.ws_col;
8660 return default_width;
8666 static int old_width = 0;
8667 int new_width = get_term_width();
8669 if (old_width != new_width)
8670 ics_printf("set width %d\n", new_width);
8671 old_width = new_width;
8674 void NotifyFrontendLogin()
8679 /* [AS] Arrow highlighting support */
8681 static double A_WIDTH = 5; /* Width of arrow body */
8683 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8684 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8686 static double Sqr( double x )
8691 static int Round( double x )
8693 return (int) (x + 0.5);
8696 void SquareToPos(int rank, int file, int *x, int *y)
8699 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8700 *y = lineGap + rank * (squareSize + lineGap);
8702 *x = lineGap + file * (squareSize + lineGap);
8703 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8707 /* Draw an arrow between two points using current settings */
8708 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8711 double dx, dy, j, k, x, y;
8714 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8716 arrow[0].x = s_x + A_WIDTH + 0.5;
8719 arrow[1].x = s_x + A_WIDTH + 0.5;
8720 arrow[1].y = d_y - h;
8722 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8723 arrow[2].y = d_y - h;
8728 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8729 arrow[5].y = d_y - h;
8731 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8732 arrow[4].y = d_y - h;
8734 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8737 else if( d_y == s_y ) {
8738 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8741 arrow[0].y = s_y + A_WIDTH + 0.5;
8743 arrow[1].x = d_x - w;
8744 arrow[1].y = s_y + A_WIDTH + 0.5;
8746 arrow[2].x = d_x - w;
8747 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8752 arrow[5].x = d_x - w;
8753 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8755 arrow[4].x = d_x - w;
8756 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8759 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8762 /* [AS] Needed a lot of paper for this! :-) */
8763 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8764 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8766 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8768 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8773 arrow[0].x = Round(x - j);
8774 arrow[0].y = Round(y + j*dx);
8776 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8777 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8780 x = (double) d_x - k;
8781 y = (double) d_y - k*dy;
8784 x = (double) d_x + k;
8785 y = (double) d_y + k*dy;
8788 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8790 arrow[6].x = Round(x - j);
8791 arrow[6].y = Round(y + j*dx);
8793 arrow[2].x = Round(arrow[6].x + 2*j);
8794 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8796 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8797 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8802 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8803 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8806 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8807 // Polygon( hdc, arrow, 7 );
8810 /* [AS] Draw an arrow between two squares */
8811 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
8813 int s_x, s_y, d_x, d_y, hor, vert, i;
8815 if( s_col == d_col && s_row == d_row ) {
8819 /* Get source and destination points */
8820 SquareToPos( s_row, s_col, &s_x, &s_y);
8821 SquareToPos( d_row, d_col, &d_x, &d_y);
8824 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8826 else if( d_y < s_y ) {
8827 d_y += squareSize / 2 + squareSize / 4;
8830 d_y += squareSize / 2;
8834 d_x += squareSize / 2 - squareSize / 4;
8836 else if( d_x < s_x ) {
8837 d_x += squareSize / 2 + squareSize / 4;
8840 d_x += squareSize / 2;
8843 s_x += squareSize / 2;
8844 s_y += squareSize / 2;
8847 A_WIDTH = squareSize / 14.; //[HGM] make float
8849 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8851 hor = 64*s_col + 32; vert = 64*s_row + 32;
8852 for(i=0; i<= 64; i++) {
8853 damage[0][vert+6>>6][hor+6>>6] = True;
8854 damage[0][vert-6>>6][hor+6>>6] = True;
8855 damage[0][vert+6>>6][hor-6>>6] = True;
8856 damage[0][vert-6>>6][hor-6>>6] = True;
8857 hor += d_col - s_col; vert += d_row - s_row;
8861 Boolean IsDrawArrowEnabled()
8863 return appData.highlightMoveWithArrow && squareSize >= 32;
8866 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
8868 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8869 DrawArrowBetweenSquares(fromX, fromY, toX, toY);