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 CreateAnyPieces P((void));
247 void CreateXIMPieces P((void));
248 void CreateXPMPieces P((void));
249 void CreateXPMBoard P((char *s, int n));
250 void CreatePieces P((void));
251 void CreatePieceMenus P((void));
252 Widget CreateMenuBar P((Menu *mb));
253 Widget CreateButtonBar P ((MenuItem *mi));
254 char *FindFont P((char *pattern, int targetPxlSize));
255 void PieceMenuPopup P((Widget w, XEvent *event,
256 String *params, Cardinal *num_params));
257 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
258 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
259 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
260 u_int wreq, u_int hreq));
261 void CreateGrid P((void));
262 int EventToSquare P((int x, int limit));
263 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
264 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
265 void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
266 void HandleUserMove P((Widget w, XEvent *event,
267 String *prms, Cardinal *nprms));
268 void AnimateUserMove P((Widget w, XEvent * event,
269 String * params, Cardinal * nParams));
270 void HandlePV P((Widget w, XEvent * event,
271 String * params, Cardinal * nParams));
272 void SelectPV P((Widget w, XEvent * event,
273 String * params, Cardinal * nParams));
274 void StopPV P((Widget w, XEvent * event,
275 String * params, Cardinal * nParams));
276 void WhiteClock P((Widget w, XEvent *event,
277 String *prms, Cardinal *nprms));
278 void BlackClock P((Widget w, XEvent *event,
279 String *prms, Cardinal *nprms));
280 void DrawPositionProc P((Widget w, XEvent *event,
281 String *prms, Cardinal *nprms));
282 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
284 void CommentClick P((Widget w, XEvent * event,
285 String * params, Cardinal * nParams));
286 void CommentPopUp P((char *title, char *label));
287 void CommentPopDown P((void));
288 void ICSInputBoxPopUp P((void));
289 void ICSInputBoxPopDown P((void));
290 void FileNamePopUp P((char *label, char *def, char *filter,
291 FileProc proc, char *openMode));
292 void FileNamePopDown P((void));
293 void FileNameCallback P((Widget w, XtPointer client_data,
294 XtPointer call_data));
295 void FileNameAction P((Widget w, XEvent *event,
296 String *prms, Cardinal *nprms));
297 void AskQuestionReplyAction P((Widget w, XEvent *event,
298 String *prms, Cardinal *nprms));
299 void AskQuestionProc P((Widget w, XEvent *event,
300 String *prms, Cardinal *nprms));
301 void AskQuestionPopDown P((void));
302 void PromotionPopDown P((void));
303 void PromotionCallback P((Widget w, XtPointer client_data,
304 XtPointer call_data));
305 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
306 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
307 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
308 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
310 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
312 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
314 void LoadPositionProc P((Widget w, XEvent *event,
315 String *prms, Cardinal *nprms));
316 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
318 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
320 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
322 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
324 void PastePositionProc P((Widget w, XEvent *event, String *prms,
326 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
327 void CopyGameListProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
328 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
329 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
330 void SavePositionProc P((Widget w, XEvent *event,
331 String *prms, Cardinal *nprms));
332 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
333 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
335 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
336 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
337 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
339 void MachineWhiteProc P((Widget w, XEvent *event,
340 String *prms, Cardinal *nprms));
341 void AnalyzeModeProc P((Widget w, XEvent *event,
342 String *prms, Cardinal *nprms));
343 void AnalyzeFileProc P((Widget w, XEvent *event,
344 String *prms, Cardinal *nprms));
345 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
347 void MatchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
348 void MatchOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
349 void IcsClientProc P((Widget w, XEvent *event, String *prms,
351 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
352 void EditPositionProc P((Widget w, XEvent *event,
353 String *prms, Cardinal *nprms));
354 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
355 void EditCommentProc P((Widget w, XEvent *event,
356 String *prms, Cardinal *nprms));
357 void IcsInputBoxProc P((Widget w, XEvent *event,
358 String *prms, Cardinal *nprms));
359 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
360 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
361 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
362 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
363 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
364 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
365 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
366 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
367 void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
368 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
369 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
370 void TypeInProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
371 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
372 void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
373 void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
374 void StopObservingProc P((Widget w, XEvent *event, String *prms,
376 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
378 void UploadProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
379 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
380 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
381 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
382 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
383 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
384 void AnnotateProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
385 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
387 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
389 void MoveNowProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
390 void AlwaysQueenProc P((Widget w, XEvent *event, String *prms,
392 void AnimateDraggingProc P((Widget w, XEvent *event, String *prms,
394 void AnimateMovingProc P((Widget w, XEvent *event, String *prms,
396 void AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
397 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
398 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
399 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
401 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
402 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
404 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
406 void HighlightArrowProc P((Widget w, XEvent *event, String *prms,
408 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
409 //void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
410 void OneClickProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
411 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
413 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
415 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
417 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
419 //void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
420 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
422 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
424 void HideThinkingProc P((Widget w, XEvent *event, String *prms,
426 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
428 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
429 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
430 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
431 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
432 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
433 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
434 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
435 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
436 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
437 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
438 void Iconify P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
439 void DisplayMove P((int moveNumber));
440 void DisplayTitle P((char *title));
441 void ICSInitScript P((void));
442 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
443 void ErrorPopUp P((char *title, char *text, int modal));
444 void ErrorPopDown P((void));
445 static char *ExpandPathName P((char *path));
446 static void CreateAnimVars P((void));
447 static void DragPieceMove P((int x, int y));
448 static void DrawDragPiece P((void));
449 char *ModeToWidgetName P((GameMode mode));
450 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
451 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
452 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
453 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
454 void OptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
455 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
456 void IcsTextProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
457 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
458 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
459 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
460 void IcsOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
461 void SoundOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
462 void BoardOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
463 void LoadOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
464 void SaveOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
465 void GameListOptionsPopDown P(());
466 void ShufflePopDown P(());
467 void TimeControlPopDown P(());
468 void GenericPopDown P(());
469 void update_ics_width P(());
470 int get_term_width P(());
471 int CopyMemoProc P(());
472 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
473 Boolean IsDrawArrowEnabled P(());
476 * XBoard depends on Xt R4 or higher
478 int xtVersion = XtSpecificationRelease;
483 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
484 jailSquareColor, highlightSquareColor, premoveHighlightColor;
485 Pixel lowTimeWarningColor;
486 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
487 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
488 wjPieceGC, bjPieceGC, prelineGC, countGC;
489 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
490 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
491 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
492 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
493 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
494 ICSInputShell, fileNameShell, askQuestionShell;
495 Widget historyShell, evalGraphShell, gameListShell;
496 int hOffset; // [HGM] dual
497 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
498 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
499 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
500 Font clockFontID, coordFontID, countFontID;
501 XFontStruct *clockFontStruct, *coordFontStruct, *countFontStruct;
502 XtAppContext appContext;
504 char *oldICSInteractionTitle;
508 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
510 Position commentX = -1, commentY = -1;
511 Dimension commentW, commentH;
512 typedef unsigned int BoardSize;
514 Boolean chessProgram;
516 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
517 int squareSize, smallLayout = 0, tinyLayout = 0,
518 marginW, marginH, // [HGM] for run-time resizing
519 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
520 ICSInputBoxUp = False, askQuestionUp = False,
521 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
522 errorUp = False, errorExitStatus = -1, lineGap, defaultLineGap;
523 Pixel timerForegroundPixel, timerBackgroundPixel;
524 Pixel buttonForegroundPixel, buttonBackgroundPixel;
525 char *chessDir, *programName, *programVersion,
526 *gameCopyFilename, *gamePasteFilename;
527 Boolean alwaysOnTop = False;
528 Boolean saveSettingsOnExit;
529 char *settingsFileName;
530 char *icsTextMenuString;
532 char *firstChessProgramNames;
533 char *secondChessProgramNames;
535 WindowPlacement wpMain;
536 WindowPlacement wpConsole;
537 WindowPlacement wpComment;
538 WindowPlacement wpMoveHistory;
539 WindowPlacement wpEvalGraph;
540 WindowPlacement wpEngineOutput;
541 WindowPlacement wpGameList;
542 WindowPlacement wpTags;
544 extern Widget shells[];
545 extern Boolean shellUp[];
549 Pixmap pieceBitmap[2][(int)BlackPawn];
550 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
551 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
552 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
553 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
554 Pixmap xpmBoardBitmap[2];
555 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
556 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
557 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
558 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
559 XImage *ximLightSquare, *ximDarkSquare;
562 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
563 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
565 #define White(piece) ((int)(piece) < (int)BlackPawn)
567 /* Variables for doing smooth animation. This whole thing
568 would be much easier if the board was double-buffered,
569 but that would require a fairly major rewrite. */
574 GC blitGC, pieceGC, outlineGC;
575 XPoint startSquare, prevFrame, mouseDelta;
579 int startBoardX, startBoardY;
582 /* There can be two pieces being animated at once: a player
583 can begin dragging a piece before the remote opponent has moved. */
585 static AnimState game, player;
587 /* Bitmaps for use as masks when drawing XPM pieces.
588 Need one for each black and white piece. */
589 static Pixmap xpmMask[BlackKing + 1];
591 /* This magic number is the number of intermediate frames used
592 in each half of the animation. For short moves it's reduced
593 by 1. The total number of frames will be factor * 2 + 1. */
596 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
598 MenuItem fileMenu[] = {
599 {N_("New Game Ctrl+N"), "New Game", ResetProc},
600 {N_("New Shuffle Game ..."), "New Shuffle Game", ShuffleMenuProc},
601 {N_("New Variant ... Alt+Shift+V"), "New Variant", NewVariantProc}, // [HGM] variant: not functional yet
602 {"----", NULL, NothingProc},
603 {N_("Load Game Ctrl+O"), "Load Game", LoadGameProc},
604 {N_("Load Position Ctrl+Shift+O"), "Load Position", LoadPositionProc},
605 // {N_("Load Next Game"), "Load Next Game", LoadNextGameProc},
606 // {N_("Load Previous Game"), "Load Previous Game", LoadPrevGameProc},
607 // {N_("Reload Same Game"), "Reload Same Game", ReloadGameProc},
608 {N_("Next Position Shift+PgDn"), "Load Next Position", LoadNextPositionProc},
609 {N_("Prev Position Shift+PgUp"), "Load Previous Position", LoadPrevPositionProc},
610 {"----", NULL, NothingProc},
611 // {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
612 {N_("Save Game Ctrl+S"), "Save Game", SaveGameProc},
613 {N_("Save Position Ctrl+Shift+S"), "Save Position", SavePositionProc},
614 {"----", NULL, NothingProc},
615 {N_("Mail Move"), "Mail Move", MailMoveProc},
616 {N_("Reload CMail Message"), "Reload CMail Message", ReloadCmailMsgProc},
617 {"----", NULL, NothingProc},
618 {N_("Quit Ctr+Q"), "Exit", QuitProc},
622 MenuItem editMenu[] = {
623 {N_("Copy Game Ctrl+C"), "Copy Game", CopyGameProc},
624 {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
625 {N_("Copy Game List"), "Copy Game List", CopyGameListProc},
626 {"----", NULL, NothingProc},
627 {N_("Paste Game Ctrl+V"), "Paste Game", PasteGameProc},
628 {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
629 {"----", NULL, NothingProc},
630 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
631 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
632 {N_("Edit Tags"), "Edit Tags", EditTagsProc},
633 {N_("Edit Comment"), "Edit Comment", EditCommentProc},
634 {"----", NULL, NothingProc},
635 {N_("Revert Home"), "Revert", RevertProc},
636 {N_("Annotate"), "Annotate", AnnotateProc},
637 {N_("Truncate Game End"), "Truncate Game", TruncateGameProc},
638 {"----", NULL, NothingProc},
639 {N_("Backward Alt+Left"), "Backward", BackwardProc},
640 {N_("Forward Alt+Right"), "Forward", ForwardProc},
641 {N_("Back to Start Alt+Home"), "Back to Start", ToStartProc},
642 {N_("Forward to End Alt+End"), "Forward to End", ToEndProc},
646 MenuItem viewMenu[] = {
647 {N_("Flip View F2"), "Flip View", FlipViewProc},
648 {"----", NULL, NothingProc},
649 {N_("Engine Output Alt+Shift+O"), "Show Engine Output", EngineOutputProc},
650 {N_("Move History Alt+Shift+H"), "Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
651 {N_("Evaluation Graph Alt+Shift+E"), "Show Evaluation Graph", EvalGraphProc},
652 {N_("Game List Alt+Shift+G"), "Show Game List", ShowGameListProc},
653 {N_("ICS text menu"), "ICStex", IcsTextProc},
654 {"----", NULL, NothingProc},
655 {N_("Tags"), "Show Tags", EditTagsProc},
656 {N_("Comments"), "Show Comments", EditCommentProc},
657 {N_("ICS Input Box"), "ICS Input Box", IcsInputBoxProc},
658 {"----", NULL, NothingProc},
659 {N_("Board..."), "Board Options", BoardOptionsProc},
660 {N_("Game List Tags..."), "Game List", GameListOptionsPopUp},
664 MenuItem modeMenu[] = {
665 {N_("Machine White Ctrl+W"), "Machine White", MachineWhiteProc},
666 {N_("Machine Black Ctrl+B"), "Machine Black", MachineBlackProc},
667 {N_("Two Machines Ctrl+T"), "Two Machines", TwoMachinesProc},
668 {N_("Analysis Mode Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
669 {N_("Analyze File Ctrl+F"), "Analyze File", AnalyzeFileProc },
670 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
671 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
672 {N_("Training"), "Training", TrainingProc},
673 {N_("ICS Client"), "ICS Client", IcsClientProc},
674 {"----", NULL, NothingProc},
675 {N_("Machine Match"), "Machine Match", MatchProc},
676 {N_("Pause Pause"), "Pause", PauseProc},
680 MenuItem actionMenu[] = {
681 {N_("Accept F3"), "Accept", AcceptProc},
682 {N_("Decline F4"), "Decline", DeclineProc},
683 {N_("Rematch F12"), "Rematch", RematchProc},
684 {"----", NULL, NothingProc},
685 {N_("Call Flag F5"), "Call Flag", CallFlagProc},
686 {N_("Draw F6"), "Draw", DrawProc},
687 {N_("Adjourn F7"), "Adjourn", AdjournProc},
688 {N_("Abort F8"),"Abort", AbortProc},
689 {N_("Resign F9"), "Resign", ResignProc},
690 {"----", NULL, NothingProc},
691 {N_("Stop Observing F10"), "Stop Observing", StopObservingProc},
692 {N_("Stop Examining F11"), "Stop Examining", StopExaminingProc},
693 {N_("Upload to Examine"), "Upload to Examine", UploadProc},
694 {"----", NULL, NothingProc},
695 {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
696 {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
697 {N_("Adjudicate Draw"), "Adjudicate Draw", AdjuDrawProc},
701 MenuItem engineMenu[] = {
702 {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
703 {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
704 {"----", NULL, NothingProc},
705 {N_("Hint"), "Hint", HintProc},
706 {N_("Book"), "Book", BookProc},
707 {"----", NULL, NothingProc},
708 {N_("Move Now Ctrl+M"), "Move Now", MoveNowProc},
709 {N_("Retract Move Ctrl+X"), "Retract Move", RetractMoveProc},
713 MenuItem optionsMenu[] = {
714 #define OPTIONSDIALOG
716 {N_("General ..."), "General", OptionsProc},
718 {N_("Time Control ... Alt+Shift+T"), "Time Control", TimeControlProc},
719 {N_("Common Engine ... Alt+Shift+U"), "Common Engine", UciMenuProc},
720 {N_("Adjudications ... Alt+Shift+J"), "Adjudications", EngineMenuProc},
721 {N_("ICS ..."), "ICS", IcsOptionsProc},
722 {N_("Match ..."), "Match", MatchOptionsProc},
723 {N_("Load Game ..."), "Load Game", LoadOptionsProc},
724 {N_("Save Game ..."), "Save Game", SaveOptionsProc},
725 // {N_(" ..."), "", OptionsProc},
726 {N_("Game List ..."), "Game List", GameListOptionsPopUp},
727 {N_("Sounds ..."), "Sounds", SoundOptionsProc},
728 {"----", NULL, NothingProc},
729 #ifndef OPTIONSDIALOG
730 {N_("Always Queen Ctrl+Shift+Q"), "Always Queen", AlwaysQueenProc},
731 {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
732 {N_("Animate Moving Ctrl+Shift+A"), "Animate Moving", AnimateMovingProc},
733 {N_("Auto Flag Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
734 {N_("Auto Flip View"), "Auto Flip View", AutoflipProc},
735 {N_("Blindfold"), "Blindfold", BlindfoldProc},
736 {N_("Flash Moves"), "Flash Moves", FlashMovesProc},
738 {N_("Highlight Dragging"), "Highlight Dragging", HighlightDraggingProc},
740 {N_("Highlight Last Move"), "Highlight Last Move", HighlightLastMoveProc},
741 {N_("Highlight With Arrow"), "Arrow", HighlightArrowProc},
742 {N_("Move Sound"), "Move Sound", MoveSoundProc},
743 // {N_("ICS Alarm"), "ICS Alarm", IcsAlarmProc},
744 {N_("One-Click Moving"), "OneClick", OneClickProc},
745 {N_("Periodic Updates"), "Periodic Updates", PeriodicUpdatesProc},
746 {N_("Ponder Next Move Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
747 {N_("Popup Exit Message"), "Popup Exit Message", PopupExitMessageProc},
748 {N_("Popup Move Errors"), "Popup Move Errors", PopupMoveErrorsProc},
749 // {N_("Premove"), "Premove", PremoveProc},
750 {N_("Show Coords"), "Show Coords", ShowCoordsProc},
751 {N_("Hide Thinking Ctrl+Shift+H"), "Hide Thinking", HideThinkingProc},
752 {N_("Test Legality Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
753 {"----", NULL, NothingProc},
755 {N_("Save Settings Now"), "Save Settings Now", SaveSettingsProc},
756 {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
760 MenuItem helpMenu[] = {
761 {N_("Info XBoard"), "Info XBoard", InfoProc},
762 {N_("Man XBoard F1"), "Man XBoard", ManProc},
763 {"----", NULL, NothingProc},
764 {N_("About XBoard"), "About XBoard", AboutProc},
769 {N_("File"), "File", fileMenu},
770 {N_("Edit"), "Edit", editMenu},
771 {N_("View"), "View", viewMenu},
772 {N_("Mode"), "Mode", modeMenu},
773 {N_("Action"), "Action", actionMenu},
774 {N_("Engine"), "Engine", engineMenu},
775 {N_("Options"), "Options", optionsMenu},
776 {N_("Help"), "Help", helpMenu},
780 #define PAUSE_BUTTON "P"
781 MenuItem buttonBar[] = {
782 {"<<", "<<", ToStartProc},
783 {"<", "<", BackwardProc},
784 {PAUSE_BUTTON, PAUSE_BUTTON, PauseProc},
785 {">", ">", ForwardProc},
786 {">>", ">>", ToEndProc},
790 #define PIECE_MENU_SIZE 18
791 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
792 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
793 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
794 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
795 N_("Empty square"), N_("Clear board") },
796 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
797 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
798 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
799 N_("Empty square"), N_("Clear board") }
801 /* must be in same order as PieceMenuStrings! */
802 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
803 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
804 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
805 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
806 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
807 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
808 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
809 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
810 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
813 #define DROP_MENU_SIZE 6
814 String dropMenuStrings[DROP_MENU_SIZE] = {
815 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
817 /* must be in same order as PieceMenuStrings! */
818 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
819 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
820 WhiteRook, WhiteQueen
828 DropMenuEnables dmEnables[] = {
846 { XtNborderWidth, 0 },
847 { XtNdefaultDistance, 0 },
851 { XtNborderWidth, 0 },
852 { XtNresizable, (XtArgVal) True },
856 { XtNborderWidth, 0 },
862 { XtNjustify, (XtArgVal) XtJustifyRight },
863 { XtNlabel, (XtArgVal) "..." },
864 { XtNresizable, (XtArgVal) True },
865 { XtNresize, (XtArgVal) False }
868 Arg messageArgs[] = {
869 { XtNjustify, (XtArgVal) XtJustifyLeft },
870 { XtNlabel, (XtArgVal) "..." },
871 { XtNresizable, (XtArgVal) True },
872 { XtNresize, (XtArgVal) False }
876 { XtNborderWidth, 0 },
877 { XtNjustify, (XtArgVal) XtJustifyLeft }
880 XtResource clientResources[] = {
881 { "flashCount", "flashCount", XtRInt, sizeof(int),
882 XtOffset(AppDataPtr, flashCount), XtRImmediate,
883 (XtPointer) FLASH_COUNT },
886 XrmOptionDescRec shellOptions[] = {
887 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
888 { "-flash", "flashCount", XrmoptionNoArg, "3" },
889 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
892 XtActionsRec boardActions[] = {
893 { "DrawPosition", DrawPositionProc },
894 { "HandleUserMove", HandleUserMove },
895 { "AnimateUserMove", AnimateUserMove },
896 { "HandlePV", HandlePV },
897 { "SelectPV", SelectPV },
898 { "StopPV", StopPV },
899 { "FileNameAction", FileNameAction },
900 { "AskQuestionProc", AskQuestionProc },
901 { "AskQuestionReplyAction", AskQuestionReplyAction },
902 { "PieceMenuPopup", PieceMenuPopup },
903 { "WhiteClock", WhiteClock },
904 { "BlackClock", BlackClock },
905 { "Iconify", Iconify },
906 { "ResetProc", ResetProc },
907 { "NewVariantProc", NewVariantProc },
908 { "LoadGameProc", LoadGameProc },
909 { "LoadNextGameProc", LoadNextGameProc },
910 { "LoadPrevGameProc", LoadPrevGameProc },
911 { "LoadSelectedProc", LoadSelectedProc },
912 { "SetFilterProc", SetFilterProc },
913 { "ReloadGameProc", ReloadGameProc },
914 { "LoadPositionProc", LoadPositionProc },
915 { "LoadNextPositionProc", LoadNextPositionProc },
916 { "LoadPrevPositionProc", LoadPrevPositionProc },
917 { "ReloadPositionProc", ReloadPositionProc },
918 { "CopyPositionProc", CopyPositionProc },
919 { "PastePositionProc", PastePositionProc },
920 { "CopyGameProc", CopyGameProc },
921 { "CopyGameListProc", CopyGameListProc },
922 { "PasteGameProc", PasteGameProc },
923 { "SaveGameProc", SaveGameProc },
924 { "SavePositionProc", SavePositionProc },
925 { "MailMoveProc", MailMoveProc },
926 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
927 { "QuitProc", QuitProc },
928 { "MachineWhiteProc", MachineWhiteProc },
929 { "MachineBlackProc", MachineBlackProc },
930 { "AnalysisModeProc", AnalyzeModeProc },
931 { "AnalyzeFileProc", AnalyzeFileProc },
932 { "TwoMachinesProc", TwoMachinesProc },
933 { "IcsClientProc", IcsClientProc },
934 { "EditGameProc", EditGameProc },
935 { "EditPositionProc", EditPositionProc },
936 { "TrainingProc", EditPositionProc },
937 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
938 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
939 { "ShowGameListProc", ShowGameListProc },
940 { "ShowMoveListProc", HistoryShowProc},
941 { "EditTagsProc", EditCommentProc },
942 { "EditCommentProc", EditCommentProc },
943 { "IcsInputBoxProc", IcsInputBoxProc },
944 { "PauseProc", PauseProc },
945 { "AcceptProc", AcceptProc },
946 { "DeclineProc", DeclineProc },
947 { "RematchProc", RematchProc },
948 { "CallFlagProc", CallFlagProc },
949 { "DrawProc", DrawProc },
950 { "AdjournProc", AdjournProc },
951 { "AbortProc", AbortProc },
952 { "ResignProc", ResignProc },
953 { "AdjuWhiteProc", AdjuWhiteProc },
954 { "AdjuBlackProc", AdjuBlackProc },
955 { "AdjuDrawProc", AdjuDrawProc },
956 { "TypeInProc", TypeInProc },
957 { "EnterKeyProc", EnterKeyProc },
958 { "UpKeyProc", UpKeyProc },
959 { "DownKeyProc", DownKeyProc },
960 { "StopObservingProc", StopObservingProc },
961 { "StopExaminingProc", StopExaminingProc },
962 { "UploadProc", UploadProc },
963 { "BackwardProc", BackwardProc },
964 { "ForwardProc", ForwardProc },
965 { "ToStartProc", ToStartProc },
966 { "ToEndProc", ToEndProc },
967 { "RevertProc", RevertProc },
968 { "AnnotateProc", AnnotateProc },
969 { "TruncateGameProc", TruncateGameProc },
970 { "MoveNowProc", MoveNowProc },
971 { "RetractMoveProc", RetractMoveProc },
972 { "EngineMenuProc", (XtActionProc) EngineMenuProc },
973 { "UciMenuProc", (XtActionProc) UciMenuProc },
974 { "TimeControlProc", (XtActionProc) TimeControlProc },
975 { "FlipViewProc", FlipViewProc },
976 { "PonderNextMoveProc", PonderNextMoveProc },
977 #ifndef OPTIONSDIALOG
978 { "AlwaysQueenProc", AlwaysQueenProc },
979 { "AnimateDraggingProc", AnimateDraggingProc },
980 { "AnimateMovingProc", AnimateMovingProc },
981 { "AutoflagProc", AutoflagProc },
982 { "AutoflipProc", AutoflipProc },
983 { "BlindfoldProc", BlindfoldProc },
984 { "FlashMovesProc", FlashMovesProc },
986 { "HighlightDraggingProc", HighlightDraggingProc },
988 { "HighlightLastMoveProc", HighlightLastMoveProc },
989 // { "IcsAlarmProc", IcsAlarmProc },
990 { "MoveSoundProc", MoveSoundProc },
991 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
992 { "PopupExitMessageProc", PopupExitMessageProc },
993 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
994 // { "PremoveProc", PremoveProc },
995 { "ShowCoordsProc", ShowCoordsProc },
996 { "ShowThinkingProc", ShowThinkingProc },
997 { "HideThinkingProc", HideThinkingProc },
998 { "TestLegalityProc", TestLegalityProc },
1000 { "SaveSettingsProc", SaveSettingsProc },
1001 { "SaveOnExitProc", SaveOnExitProc },
1002 { "InfoProc", InfoProc },
1003 { "ManProc", ManProc },
1004 { "HintProc", HintProc },
1005 { "BookProc", BookProc },
1006 { "AboutGameProc", AboutGameProc },
1007 { "AboutProc", AboutProc },
1008 { "DebugProc", DebugProc },
1009 { "NothingProc", NothingProc },
1010 { "CommentClick", (XtActionProc) CommentClick },
1011 { "CommentPopDown", (XtActionProc) CommentPopDown },
1012 { "TagsPopDown", (XtActionProc) TagsPopDown },
1013 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1014 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1015 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1016 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1017 { "GameListPopDown", (XtActionProc) GameListPopDown },
1018 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1019 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1020 { "HistoryPopDown", (XtActionProc) HistoryPopDown },
1021 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1022 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1023 { "ShufflePopDown", (XtActionProc) ShufflePopDown },
1024 { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
1025 { "GenericPopDown", (XtActionProc) GenericPopDown },
1026 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1029 char globalTranslations[] =
1030 ":<Key>F9: ResignProc() \n \
1031 :Ctrl<Key>n: ResetProc() \n \
1032 :Meta<Key>V: NewVariantProc() \n \
1033 :Ctrl<Key>o: LoadGameProc() \n \
1034 :Meta<Key>Next: LoadNextGameProc() \n \
1035 :Meta<Key>Prior: LoadPrevGameProc() \n \
1036 :Ctrl<Key>s: SaveGameProc() \n \
1037 :Ctrl<Key>c: CopyGameProc() \n \
1038 :Ctrl<Key>v: PasteGameProc() \n \
1039 :Ctrl<Key>O: LoadPositionProc() \n \
1040 :Shift<Key>Next: LoadNextPositionProc() \n \
1041 :Shift<Key>Prior: LoadPrevPositionProc() \n \
1042 :Ctrl<Key>S: SavePositionProc() \n \
1043 :Ctrl<Key>C: CopyPositionProc() \n \
1044 :Ctrl<Key>V: PastePositionProc() \n \
1045 :Ctrl<Key>q: QuitProc() \n \
1046 :Ctrl<Key>w: MachineWhiteProc() \n \
1047 :Ctrl<Key>b: MachineBlackProc() \n \
1048 :Ctrl<Key>t: TwoMachinesProc() \n \
1049 :Ctrl<Key>a: AnalysisModeProc() \n \
1050 :Ctrl<Key>f: AnalyzeFileProc() \n \
1051 :Ctrl<Key>e: EditGameProc() \n \
1052 :Ctrl<Key>E: EditPositionProc() \n \
1053 :Meta<Key>O: EngineOutputProc() \n \
1054 :Meta<Key>E: EvalGraphProc() \n \
1055 :Meta<Key>G: ShowGameListProc() \n \
1056 :Meta<Key>H: ShowMoveListProc() \n \
1057 :<Key>Pause: PauseProc() \n \
1058 :<Key>F3: AcceptProc() \n \
1059 :<Key>F4: DeclineProc() \n \
1060 :<Key>F12: RematchProc() \n \
1061 :<Key>F5: CallFlagProc() \n \
1062 :<Key>F6: DrawProc() \n \
1063 :<Key>F7: AdjournProc() \n \
1064 :<Key>F8: AbortProc() \n \
1065 :<Key>F10: StopObservingProc() \n \
1066 :<Key>F11: StopExaminingProc() \n \
1067 :Meta Ctrl<Key>F12: DebugProc() \n \
1068 :Meta<Key>End: ToEndProc() \n \
1069 :Meta<Key>Right: ForwardProc() \n \
1070 :Meta<Key>Home: ToStartProc() \n \
1071 :Meta<Key>Left: BackwardProc() \n \
1072 :<Key>Home: RevertProc() \n \
1073 :<Key>End: TruncateGameProc() \n \
1074 :Ctrl<Key>m: MoveNowProc() \n \
1075 :Ctrl<Key>x: RetractMoveProc() \n \
1076 :Meta<Key>J: EngineMenuProc() \n \
1077 :Meta<Key>U: UciMenuProc() \n \
1078 :Meta<Key>T: TimeControlProc() \n \
1079 :Ctrl<Key>P: PonderNextMoveProc() \n "
1080 #ifndef OPTIONSDIALOG
1082 :Ctrl<Key>Q: AlwaysQueenProc() \n \
1083 :Ctrl<Key>F: AutoflagProc() \n \
1084 :Ctrl<Key>A: AnimateMovingProc() \n \
1085 :Ctrl<Key>L: TestLegalityProc() \n \
1086 :Ctrl<Key>H: HideThinkingProc() \n "
1089 :<Key>-: Iconify() \n \
1090 :<Key>F1: ManProc() \n \
1091 :<Key>F2: FlipViewProc() \n \
1092 <KeyDown>.: BackwardProc() \n \
1093 <KeyUp>.: ForwardProc() \n \
1094 Shift<Key>1: AskQuestionProc(\"Direct command\",\
1095 \"Send to chess program:\",,1) \n \
1096 Shift<Key>2: AskQuestionProc(\"Direct command\",\
1097 \"Send to second chess program:\",,2) \n";
1099 char boardTranslations[] =
1100 "<Btn1Down>: HandleUserMove(0) \n \
1101 Shift<Btn1Up>: HandleUserMove(1) \n \
1102 <Btn1Up>: HandleUserMove(0) \n \
1103 <Btn1Motion>: AnimateUserMove() \n \
1104 <Btn3Motion>: HandlePV() \n \
1105 <Btn3Up>: PieceMenuPopup(menuB) \n \
1106 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1107 PieceMenuPopup(menuB) \n \
1108 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1109 PieceMenuPopup(menuW) \n \
1110 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1111 PieceMenuPopup(menuW) \n \
1112 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1113 PieceMenuPopup(menuB) \n";
1115 char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
1116 char blackTranslations[] = "<BtnDown>: BlackClock()\n";
1118 char ICSInputTranslations[] =
1119 "<Key>Up: UpKeyProc() \n "
1120 "<Key>Down: DownKeyProc() \n "
1121 "<Key>Return: EnterKeyProc() \n";
1123 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1124 // as the widget is destroyed before the up-click can call extend-end
1125 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1127 String xboardResources[] = {
1128 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1129 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1130 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1135 /* Max possible square size */
1136 #define MAXSQSIZE 256
1138 static int xpm_avail[MAXSQSIZE];
1140 #ifdef HAVE_DIR_STRUCT
1142 /* Extract piece size from filename */
1144 xpm_getsize(name, len, ext)
1155 if ((p=strchr(name, '.')) == NULL ||
1156 StrCaseCmp(p+1, ext) != 0)
1162 while (*p && isdigit(*p))
1169 /* Setup xpm_avail */
1171 xpm_getavail(dirname, ext)
1179 for (i=0; i<MAXSQSIZE; ++i)
1182 if (appData.debugMode)
1183 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1185 dir = opendir(dirname);
1188 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1189 programName, dirname);
1193 while ((ent=readdir(dir)) != NULL) {
1194 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1195 if (i > 0 && i < MAXSQSIZE)
1205 xpm_print_avail(fp, ext)
1211 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1212 for (i=1; i<MAXSQSIZE; ++i) {
1218 /* Return XPM piecesize closest to size */
1220 xpm_closest_to(dirname, size, ext)
1226 int sm_diff = MAXSQSIZE;
1230 xpm_getavail(dirname, ext);
1232 if (appData.debugMode)
1233 xpm_print_avail(stderr, ext);
1235 for (i=1; i<MAXSQSIZE; ++i) {
1238 diff = (diff<0) ? -diff : diff;
1239 if (diff < sm_diff) {
1247 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1253 #else /* !HAVE_DIR_STRUCT */
1254 /* If we are on a system without a DIR struct, we can't
1255 read the directory, so we can't collect a list of
1256 filenames, etc., so we can't do any size-fitting. */
1258 xpm_closest_to(dirname, size, ext)
1263 fprintf(stderr, _("\
1264 Warning: No DIR structure found on this system --\n\
1265 Unable to autosize for XPM/XIM pieces.\n\
1266 Please report this error to frankm@hiwaay.net.\n\
1267 Include system type & operating system in message.\n"));
1270 #endif /* HAVE_DIR_STRUCT */
1272 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1273 "magenta", "cyan", "white" };
1277 TextColors textColors[(int)NColorClasses];
1279 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1281 parse_color(str, which)
1285 char *p, buf[100], *d;
1288 if (strlen(str) > 99) /* watch bounds on buf */
1293 for (i=0; i<which; ++i) {
1300 /* Could be looking at something like:
1302 .. in which case we want to stop on a comma also */
1303 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1307 return -1; /* Use default for empty field */
1310 if (which == 2 || isdigit(*p))
1313 while (*p && isalpha(*p))
1318 for (i=0; i<8; ++i) {
1319 if (!StrCaseCmp(buf, cnames[i]))
1320 return which? (i+40) : (i+30);
1322 if (!StrCaseCmp(buf, "default")) return -1;
1324 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1329 parse_cpair(cc, str)
1333 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1334 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1339 /* bg and attr are optional */
1340 textColors[(int)cc].bg = parse_color(str, 1);
1341 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1342 textColors[(int)cc].attr = 0;
1348 /* Arrange to catch delete-window events */
1349 Atom wm_delete_window;
1351 CatchDeleteWindow(Widget w, String procname)
1354 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1355 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1356 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1363 XtSetArg(args[0], XtNiconic, False);
1364 XtSetValues(shellWidget, args, 1);
1366 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1369 //---------------------------------------------------------------------------------------------------------
1370 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1373 #define CW_USEDEFAULT (1<<31)
1374 #define ICS_TEXT_MENU_SIZE 90
1375 #define DEBUG_FILE "xboard.debug"
1376 #define SetCurrentDirectory chdir
1377 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1381 // these two must some day move to frontend.h, when they are implemented
1382 Boolean GameListIsUp();
1384 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1387 // front-end part of option handling
1389 // [HGM] This platform-dependent table provides the location for storing the color info
1390 extern char *crWhite, * crBlack;
1394 &appData.whitePieceColor,
1395 &appData.blackPieceColor,
1396 &appData.lightSquareColor,
1397 &appData.darkSquareColor,
1398 &appData.highlightSquareColor,
1399 &appData.premoveHighlightColor,
1400 &appData.lowTimeWarningColor,
1411 // [HGM] font: keep a font for each square size, even non-stndard ones
1412 #define NUM_SIZES 18
1413 #define MAX_SIZE 130
1414 Boolean fontSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1415 char *fontTable[NUM_FONTS][MAX_SIZE];
1418 ParseFont(char *name, int number)
1419 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1421 if(sscanf(name, "size%d:", &size)) {
1422 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1423 // defer processing it until we know if it matches our board size
1424 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1425 fontTable[number][size] = strdup(strchr(name, ':')+1);
1426 fontValid[number][size] = True;
1431 case 0: // CLOCK_FONT
1432 appData.clockFont = strdup(name);
1434 case 1: // MESSAGE_FONT
1435 appData.font = strdup(name);
1437 case 2: // COORD_FONT
1438 appData.coordFont = strdup(name);
1443 fontSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1448 { // only 2 fonts currently
1449 appData.clockFont = CLOCK_FONT_NAME;
1450 appData.coordFont = COORD_FONT_NAME;
1451 appData.font = DEFAULT_FONT_NAME;
1456 { // no-op, until we identify the code for this already in XBoard and move it here
1460 ParseColor(int n, char *name)
1461 { // in XBoard, just copy the color-name string
1462 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1466 ParseTextAttribs(ColorClass cc, char *s)
1468 (&appData.colorShout)[cc] = strdup(s);
1472 ParseBoardSize(void *addr, char *name)
1474 appData.boardSize = strdup(name);
1479 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1483 SetCommPortDefaults()
1484 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1487 // [HGM] args: these three cases taken out to stay in front-end
1489 SaveFontArg(FILE *f, ArgDescriptor *ad)
1492 int i, n = (int)(intptr_t)ad->argLoc;
1494 case 0: // CLOCK_FONT
1495 name = appData.clockFont;
1497 case 1: // MESSAGE_FONT
1498 name = appData.font;
1500 case 2: // COORD_FONT
1501 name = appData.coordFont;
1506 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1507 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1508 fontTable[n][squareSize] = strdup(name);
1509 fontValid[n][squareSize] = True;
1512 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1513 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1518 { // nothing to do, as the sounds are at all times represented by their text-string names already
1522 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1523 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1524 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1528 SaveColor(FILE *f, ArgDescriptor *ad)
1529 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1530 if(colorVariable[(int)(intptr_t)ad->argLoc])
1531 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1535 SaveBoardSize(FILE *f, char *name, void *addr)
1536 { // wrapper to shield back-end from BoardSize & sizeInfo
1537 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1541 ParseCommPortSettings(char *s)
1542 { // no such option in XBoard (yet)
1545 extern Widget engineOutputShell;
1548 GetActualPlacement(Widget wg, WindowPlacement *wp)
1558 XtSetArg(args[i], XtNx, &x); i++;
1559 XtSetArg(args[i], XtNy, &y); i++;
1560 XtSetArg(args[i], XtNwidth, &w); i++;
1561 XtSetArg(args[i], XtNheight, &h); i++;
1562 XtGetValues(wg, args, i);
1571 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1572 // In XBoard this will have to wait until awareness of window parameters is implemented
1573 GetActualPlacement(shellWidget, &wpMain);
1574 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1575 if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1576 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1577 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1578 if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1579 if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1583 PrintCommPortSettings(FILE *f, char *name)
1584 { // This option does not exist in XBoard
1588 MySearchPath(char *installDir, char *name, char *fullname)
1589 { // just append installDir and name. Perhaps ExpandPath should be used here?
1590 name = ExpandPathName(name);
1591 if(name && name[0] == '/')
1592 safeStrCpy(fullname, name, MSG_SIZ );
1594 sprintf(fullname, "%s%c%s", installDir, '/', name);
1600 MyGetFullPathName(char *name, char *fullname)
1601 { // should use ExpandPath?
1602 name = ExpandPathName(name);
1603 safeStrCpy(fullname, name, MSG_SIZ );
1608 EnsureOnScreen(int *x, int *y, int minX, int minY)
1615 { // [HGM] args: allows testing if main window is realized from back-end
1616 return xBoardWindow != 0;
1620 PopUpStartupDialog()
1621 { // start menu not implemented in XBoard
1625 ConvertToLine(int argc, char **argv)
1627 static char line[128*1024], buf[1024];
1631 for(i=1; i<argc; i++)
1633 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1634 && argv[i][0] != '{' )
1635 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1637 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1638 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1641 line[strlen(line)-1] = NULLCHAR;
1645 //--------------------------------------------------------------------------------------------
1647 extern Boolean twoBoards, partnerUp;
1650 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1652 #define BoardSize int
1653 void InitDrawingSizes(BoardSize boardSize, int flags)
1654 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1655 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1657 XtGeometryResult gres;
1660 if(!formWidget) return;
1663 * Enable shell resizing.
1665 shellArgs[0].value = (XtArgVal) &w;
1666 shellArgs[1].value = (XtArgVal) &h;
1667 XtGetValues(shellWidget, shellArgs, 2);
1669 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1670 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1671 XtSetValues(shellWidget, &shellArgs[2], 4);
1673 XtSetArg(args[0], XtNdefaultDistance, &sep);
1674 XtGetValues(formWidget, args, 1);
1676 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1677 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1678 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1680 hOffset = boardWidth + 10;
1681 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1682 secondSegments[i] = gridSegments[i];
1683 secondSegments[i].x1 += hOffset;
1684 secondSegments[i].x2 += hOffset;
1687 XtSetArg(args[0], XtNwidth, boardWidth);
1688 XtSetArg(args[1], XtNheight, boardHeight);
1689 XtSetValues(boardWidget, args, 2);
1691 timerWidth = (boardWidth - sep) / 2;
1692 XtSetArg(args[0], XtNwidth, timerWidth);
1693 XtSetValues(whiteTimerWidget, args, 1);
1694 XtSetValues(blackTimerWidget, args, 1);
1696 XawFormDoLayout(formWidget, False);
1698 if (appData.titleInWindow) {
1700 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1701 XtSetArg(args[i], XtNheight, &h); i++;
1702 XtGetValues(titleWidget, args, i);
1704 w = boardWidth - 2*bor;
1706 XtSetArg(args[0], XtNwidth, &w);
1707 XtGetValues(menuBarWidget, args, 1);
1708 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1711 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1712 if (gres != XtGeometryYes && appData.debugMode) {
1714 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1715 programName, gres, w, h, wr, hr);
1719 XawFormDoLayout(formWidget, True);
1722 * Inhibit shell resizing.
1724 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1725 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1726 shellArgs[4].value = shellArgs[2].value = w;
1727 shellArgs[5].value = shellArgs[3].value = h;
1728 XtSetValues(shellWidget, &shellArgs[0], 6);
1730 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1733 for(i=0; i<4; i++) {
1735 for(p=0; p<=(int)WhiteKing; p++)
1736 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1737 if(gameInfo.variant == VariantShogi) {
1738 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1739 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1740 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1741 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1742 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1745 if(gameInfo.variant == VariantGothic) {
1746 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1749 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1750 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1751 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1754 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1755 for(p=0; p<=(int)WhiteKing; p++)
1756 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1757 if(gameInfo.variant == VariantShogi) {
1758 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1759 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1760 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1761 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1762 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1765 if(gameInfo.variant == VariantGothic) {
1766 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1769 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1770 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1771 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1776 for(i=0; i<2; i++) {
1778 for(p=0; p<=(int)WhiteKing; p++)
1779 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1780 if(gameInfo.variant == VariantShogi) {
1781 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1782 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1783 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1784 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1785 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1788 if(gameInfo.variant == VariantGothic) {
1789 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1792 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1793 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1794 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1804 void ParseIcsTextColors()
1805 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1806 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1807 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1808 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1809 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1810 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1811 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1812 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1813 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1814 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1815 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1817 if (appData.colorize) {
1819 _("%s: can't parse color names; disabling colorization\n"),
1822 appData.colorize = FALSE;
1827 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1828 XrmValue vFrom, vTo;
1829 int forceMono = False;
1831 if (!appData.monoMode) {
1832 vFrom.addr = (caddr_t) appData.lightSquareColor;
1833 vFrom.size = strlen(appData.lightSquareColor);
1834 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1835 if (vTo.addr == NULL) {
1836 appData.monoMode = True;
1839 lightSquareColor = *(Pixel *) vTo.addr;
1842 if (!appData.monoMode) {
1843 vFrom.addr = (caddr_t) appData.darkSquareColor;
1844 vFrom.size = strlen(appData.darkSquareColor);
1845 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1846 if (vTo.addr == NULL) {
1847 appData.monoMode = True;
1850 darkSquareColor = *(Pixel *) vTo.addr;
1853 if (!appData.monoMode) {
1854 vFrom.addr = (caddr_t) appData.whitePieceColor;
1855 vFrom.size = strlen(appData.whitePieceColor);
1856 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1857 if (vTo.addr == NULL) {
1858 appData.monoMode = True;
1861 whitePieceColor = *(Pixel *) vTo.addr;
1864 if (!appData.monoMode) {
1865 vFrom.addr = (caddr_t) appData.blackPieceColor;
1866 vFrom.size = strlen(appData.blackPieceColor);
1867 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1868 if (vTo.addr == NULL) {
1869 appData.monoMode = True;
1872 blackPieceColor = *(Pixel *) vTo.addr;
1876 if (!appData.monoMode) {
1877 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1878 vFrom.size = strlen(appData.highlightSquareColor);
1879 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1880 if (vTo.addr == NULL) {
1881 appData.monoMode = True;
1884 highlightSquareColor = *(Pixel *) vTo.addr;
1888 if (!appData.monoMode) {
1889 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1890 vFrom.size = strlen(appData.premoveHighlightColor);
1891 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1892 if (vTo.addr == NULL) {
1893 appData.monoMode = True;
1896 premoveHighlightColor = *(Pixel *) vTo.addr;
1904 { // [HGM] taken out of main
1906 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1907 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1908 appData.bitmapDirectory = DEF_BITMAP_DIR;
1910 if (appData.bitmapDirectory[0] != NULLCHAR) {
1914 CreateXPMBoard(appData.liteBackTextureFile, 1);
1915 CreateXPMBoard(appData.darkBackTextureFile, 0);
1919 /* Create regular pieces */
1920 if (!useImages) CreatePieces();
1929 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1930 XSetWindowAttributes window_attributes;
1932 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1933 XrmValue vFrom, vTo;
1934 XtGeometryResult gres;
1937 int forceMono = False;
1939 srandom(time(0)); // [HGM] book: make random truly random
1941 setbuf(stdout, NULL);
1942 setbuf(stderr, NULL);
1945 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1946 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1950 programName = strrchr(argv[0], '/');
1951 if (programName == NULL)
1952 programName = argv[0];
1957 XtSetLanguageProc(NULL, NULL, NULL);
1958 bindtextdomain(PACKAGE, LOCALEDIR);
1959 textdomain(PACKAGE);
1963 XtAppInitialize(&appContext, "XBoard", shellOptions,
1964 XtNumber(shellOptions),
1965 &argc, argv, xboardResources, NULL, 0);
1966 appData.boardSize = "";
1967 InitAppData(ConvertToLine(argc, argv));
1969 if (p == NULL) p = "/tmp";
1970 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1971 gameCopyFilename = (char*) malloc(i);
1972 gamePasteFilename = (char*) malloc(i);
1973 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1974 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1976 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1977 clientResources, XtNumber(clientResources),
1980 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1981 static char buf[MSG_SIZ];
1982 EscapeExpand(buf, appData.initString);
1983 appData.initString = strdup(buf);
1984 EscapeExpand(buf, appData.secondInitString);
1985 appData.secondInitString = strdup(buf);
1986 EscapeExpand(buf, appData.firstComputerString);
1987 appData.firstComputerString = strdup(buf);
1988 EscapeExpand(buf, appData.secondComputerString);
1989 appData.secondComputerString = strdup(buf);
1992 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1995 if (chdir(chessDir) != 0) {
1996 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2002 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2003 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2004 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
2005 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2008 setbuf(debugFP, NULL);
2011 /* [HGM,HR] make sure board size is acceptable */
2012 if(appData.NrFiles > BOARD_FILES ||
2013 appData.NrRanks > BOARD_RANKS )
2014 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
2017 /* This feature does not work; animation needs a rewrite */
2018 appData.highlightDragging = FALSE;
2022 xDisplay = XtDisplay(shellWidget);
2023 xScreen = DefaultScreen(xDisplay);
2024 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2026 gameInfo.variant = StringToVariant(appData.variant);
2027 InitPosition(FALSE);
2030 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2032 if (isdigit(appData.boardSize[0])) {
2033 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2034 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2035 &fontPxlSize, &smallLayout, &tinyLayout);
2037 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2038 programName, appData.boardSize);
2042 /* Find some defaults; use the nearest known size */
2043 SizeDefaults *szd, *nearest;
2044 int distance = 99999;
2045 nearest = szd = sizeDefaults;
2046 while (szd->name != NULL) {
2047 if (abs(szd->squareSize - squareSize) < distance) {
2049 distance = abs(szd->squareSize - squareSize);
2050 if (distance == 0) break;
2054 if (i < 2) lineGap = nearest->lineGap;
2055 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2056 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2057 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2058 if (i < 6) smallLayout = nearest->smallLayout;
2059 if (i < 7) tinyLayout = nearest->tinyLayout;
2062 SizeDefaults *szd = sizeDefaults;
2063 if (*appData.boardSize == NULLCHAR) {
2064 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2065 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2068 if (szd->name == NULL) szd--;
2069 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2071 while (szd->name != NULL &&
2072 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2073 if (szd->name == NULL) {
2074 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2075 programName, appData.boardSize);
2079 squareSize = szd->squareSize;
2080 lineGap = szd->lineGap;
2081 clockFontPxlSize = szd->clockFontPxlSize;
2082 coordFontPxlSize = szd->coordFontPxlSize;
2083 fontPxlSize = szd->fontPxlSize;
2084 smallLayout = szd->smallLayout;
2085 tinyLayout = szd->tinyLayout;
2086 // [HGM] font: use defaults from settings file if available and not overruled
2088 if(!fontSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2089 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2090 if(!fontSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2091 appData.font = fontTable[MESSAGE_FONT][squareSize];
2092 if(!fontSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2093 appData.coordFont = fontTable[COORD_FONT][squareSize];
2095 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2096 if (strlen(appData.pixmapDirectory) > 0) {
2097 p = ExpandPathName(appData.pixmapDirectory);
2099 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2100 appData.pixmapDirectory);
2103 if (appData.debugMode) {
2104 fprintf(stderr, _("\
2105 XBoard square size (hint): %d\n\
2106 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2108 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2109 if (appData.debugMode) {
2110 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2113 defaultLineGap = lineGap;
2114 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2116 /* [HR] height treated separately (hacked) */
2117 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2118 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2119 if (appData.showJail == 1) {
2120 /* Jail on top and bottom */
2121 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2122 XtSetArg(boardArgs[2], XtNheight,
2123 boardHeight + 2*(lineGap + squareSize));
2124 } else if (appData.showJail == 2) {
2126 XtSetArg(boardArgs[1], XtNwidth,
2127 boardWidth + 2*(lineGap + squareSize));
2128 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2131 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2132 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2136 * Determine what fonts to use.
2138 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2139 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2140 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2141 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2142 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2143 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2144 appData.font = FindFont(appData.font, fontPxlSize);
2145 countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
2146 countFontStruct = XQueryFont(xDisplay, countFontID);
2147 // appData.font = FindFont(appData.font, fontPxlSize);
2149 xdb = XtDatabase(xDisplay);
2150 XrmPutStringResource(&xdb, "*font", appData.font);
2153 * Detect if there are not enough colors available and adapt.
2155 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2156 appData.monoMode = True;
2159 forceMono = MakeColors();
2162 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2164 appData.monoMode = True;
2167 if (appData.lowTimeWarning && !appData.monoMode) {
2168 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2169 vFrom.size = strlen(appData.lowTimeWarningColor);
2170 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2171 if (vTo.addr == NULL)
2172 appData.monoMode = True;
2174 lowTimeWarningColor = *(Pixel *) vTo.addr;
2177 if (appData.monoMode && appData.debugMode) {
2178 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2179 (unsigned long) XWhitePixel(xDisplay, xScreen),
2180 (unsigned long) XBlackPixel(xDisplay, xScreen));
2183 ParseIcsTextColors();
2184 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2185 textColors[ColorNone].attr = 0;
2187 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2193 layoutName = "tinyLayout";
2194 } else if (smallLayout) {
2195 layoutName = "smallLayout";
2197 layoutName = "normalLayout";
2199 /* Outer layoutWidget is there only to provide a name for use in
2200 resources that depend on the layout style */
2202 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2203 layoutArgs, XtNumber(layoutArgs));
2205 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2206 formArgs, XtNumber(formArgs));
2207 XtSetArg(args[0], XtNdefaultDistance, &sep);
2208 XtGetValues(formWidget, args, 1);
2211 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2212 XtSetArg(args[0], XtNtop, XtChainTop);
2213 XtSetArg(args[1], XtNbottom, XtChainTop);
2214 XtSetArg(args[2], XtNright, XtChainLeft);
2215 XtSetValues(menuBarWidget, args, 3);
2217 widgetList[j++] = whiteTimerWidget =
2218 XtCreateWidget("whiteTime", labelWidgetClass,
2219 formWidget, timerArgs, XtNumber(timerArgs));
2220 XtSetArg(args[0], XtNfont, clockFontStruct);
2221 XtSetArg(args[1], XtNtop, XtChainTop);
2222 XtSetArg(args[2], XtNbottom, XtChainTop);
2223 XtSetValues(whiteTimerWidget, args, 3);
2225 widgetList[j++] = blackTimerWidget =
2226 XtCreateWidget("blackTime", labelWidgetClass,
2227 formWidget, timerArgs, XtNumber(timerArgs));
2228 XtSetArg(args[0], XtNfont, clockFontStruct);
2229 XtSetArg(args[1], XtNtop, XtChainTop);
2230 XtSetArg(args[2], XtNbottom, XtChainTop);
2231 XtSetValues(blackTimerWidget, args, 3);
2233 if (appData.titleInWindow) {
2234 widgetList[j++] = titleWidget =
2235 XtCreateWidget("title", labelWidgetClass, formWidget,
2236 titleArgs, XtNumber(titleArgs));
2237 XtSetArg(args[0], XtNtop, XtChainTop);
2238 XtSetArg(args[1], XtNbottom, XtChainTop);
2239 XtSetValues(titleWidget, args, 2);
2242 if (appData.showButtonBar) {
2243 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2244 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2245 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2246 XtSetArg(args[2], XtNtop, XtChainTop);
2247 XtSetArg(args[3], XtNbottom, XtChainTop);
2248 XtSetValues(buttonBarWidget, args, 4);
2251 widgetList[j++] = messageWidget =
2252 XtCreateWidget("message", labelWidgetClass, formWidget,
2253 messageArgs, XtNumber(messageArgs));
2254 XtSetArg(args[0], XtNtop, XtChainTop);
2255 XtSetArg(args[1], XtNbottom, XtChainTop);
2256 XtSetValues(messageWidget, args, 2);
2258 widgetList[j++] = boardWidget =
2259 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2260 XtNumber(boardArgs));
2262 XtManageChildren(widgetList, j);
2264 timerWidth = (boardWidth - sep) / 2;
2265 XtSetArg(args[0], XtNwidth, timerWidth);
2266 XtSetValues(whiteTimerWidget, args, 1);
2267 XtSetValues(blackTimerWidget, args, 1);
2269 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2270 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2271 XtGetValues(whiteTimerWidget, args, 2);
2273 if (appData.showButtonBar) {
2274 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2275 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2276 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2280 * formWidget uses these constraints but they are stored
2284 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2285 XtSetValues(menuBarWidget, args, i);
2286 if (appData.titleInWindow) {
2289 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2290 XtSetValues(whiteTimerWidget, args, i);
2292 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2293 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2294 XtSetValues(blackTimerWidget, args, i);
2296 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2297 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2298 XtSetValues(titleWidget, args, i);
2300 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2301 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2302 XtSetValues(messageWidget, args, i);
2303 if (appData.showButtonBar) {
2305 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2306 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2307 XtSetValues(buttonBarWidget, args, i);
2311 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2312 XtSetValues(whiteTimerWidget, args, i);
2314 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2315 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2316 XtSetValues(blackTimerWidget, args, i);
2318 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2319 XtSetValues(titleWidget, args, i);
2321 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2322 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2323 XtSetValues(messageWidget, args, i);
2324 if (appData.showButtonBar) {
2326 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2327 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2328 XtSetValues(buttonBarWidget, args, i);
2333 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2334 XtSetValues(whiteTimerWidget, args, i);
2336 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2337 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2338 XtSetValues(blackTimerWidget, args, i);
2340 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2341 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2342 XtSetValues(messageWidget, args, i);
2343 if (appData.showButtonBar) {
2345 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2346 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2347 XtSetValues(buttonBarWidget, args, i);
2351 XtSetArg(args[0], XtNfromVert, messageWidget);
2352 XtSetArg(args[1], XtNtop, XtChainTop);
2353 XtSetArg(args[2], XtNbottom, XtChainBottom);
2354 XtSetArg(args[3], XtNleft, XtChainLeft);
2355 XtSetArg(args[4], XtNright, XtChainRight);
2356 XtSetValues(boardWidget, args, 5);
2358 XtRealizeWidget(shellWidget);
2361 XtSetArg(args[0], XtNx, wpMain.x);
2362 XtSetArg(args[1], XtNy, wpMain.y);
2363 XtSetValues(shellWidget, args, 2);
2367 * Correct the width of the message and title widgets.
2368 * It is not known why some systems need the extra fudge term.
2369 * The value "2" is probably larger than needed.
2371 XawFormDoLayout(formWidget, False);
2373 #define WIDTH_FUDGE 2
2375 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2376 XtSetArg(args[i], XtNheight, &h); i++;
2377 XtGetValues(messageWidget, args, i);
2378 if (appData.showButtonBar) {
2380 XtSetArg(args[i], XtNwidth, &w); i++;
2381 XtGetValues(buttonBarWidget, args, i);
2382 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2384 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2387 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2388 if (gres != XtGeometryYes && appData.debugMode) {
2389 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2390 programName, gres, w, h, wr, hr);
2393 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2394 /* The size used for the child widget in layout lags one resize behind
2395 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2397 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2398 if (gres != XtGeometryYes && appData.debugMode) {
2399 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2400 programName, gres, w, h, wr, hr);
2403 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2404 XtSetArg(args[1], XtNright, XtChainRight);
2405 XtSetValues(messageWidget, args, 2);
2407 if (appData.titleInWindow) {
2409 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2410 XtSetArg(args[i], XtNheight, &h); i++;
2411 XtGetValues(titleWidget, args, i);
2413 w = boardWidth - 2*bor;
2415 XtSetArg(args[0], XtNwidth, &w);
2416 XtGetValues(menuBarWidget, args, 1);
2417 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2420 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2421 if (gres != XtGeometryYes && appData.debugMode) {
2423 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2424 programName, gres, w, h, wr, hr);
2427 XawFormDoLayout(formWidget, True);
2429 xBoardWindow = XtWindow(boardWidget);
2431 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2432 // not need to go into InitDrawingSizes().
2436 * Create X checkmark bitmap and initialize option menu checks.
2438 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2439 checkmark_bits, checkmark_width, checkmark_height);
2440 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2441 #ifndef OPTIONSDIALOG
2442 if (appData.alwaysPromoteToQueen) {
2443 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2446 if (appData.animateDragging) {
2447 XtSetValues(XtNameToWidget(menuBarWidget,
2448 "menuOptions.Animate Dragging"),
2451 if (appData.animate) {
2452 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2455 if (appData.autoCallFlag) {
2456 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2459 if (appData.autoFlipView) {
2460 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2463 if (appData.blindfold) {
2464 XtSetValues(XtNameToWidget(menuBarWidget,
2465 "menuOptions.Blindfold"), args, 1);
2467 if (appData.flashCount > 0) {
2468 XtSetValues(XtNameToWidget(menuBarWidget,
2469 "menuOptions.Flash Moves"),
2473 if (appData.highlightDragging) {
2474 XtSetValues(XtNameToWidget(menuBarWidget,
2475 "menuOptions.Highlight Dragging"),
2479 if (appData.highlightLastMove) {
2480 XtSetValues(XtNameToWidget(menuBarWidget,
2481 "menuOptions.Highlight Last Move"),
2484 if (appData.highlightMoveWithArrow) {
2485 XtSetValues(XtNameToWidget(menuBarWidget,
2486 "menuOptions.Arrow"),
2489 // if (appData.icsAlarm) {
2490 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2493 if (appData.ringBellAfterMoves) {
2494 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2497 if (appData.oneClick) {
2498 XtSetValues(XtNameToWidget(menuBarWidget,
2499 "menuOptions.OneClick"), args, 1);
2501 if (appData.periodicUpdates) {
2502 XtSetValues(XtNameToWidget(menuBarWidget,
2503 "menuOptions.Periodic Updates"), args, 1);
2505 if (appData.ponderNextMove) {
2506 XtSetValues(XtNameToWidget(menuBarWidget,
2507 "menuOptions.Ponder Next Move"), args, 1);
2509 if (appData.popupExitMessage) {
2510 XtSetValues(XtNameToWidget(menuBarWidget,
2511 "menuOptions.Popup Exit Message"), args, 1);
2513 if (appData.popupMoveErrors) {
2514 XtSetValues(XtNameToWidget(menuBarWidget,
2515 "menuOptions.Popup Move Errors"), args, 1);
2517 // if (appData.premove) {
2518 // XtSetValues(XtNameToWidget(menuBarWidget,
2519 // "menuOptions.Premove"), args, 1);
2521 if (appData.showCoords) {
2522 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2525 if (appData.hideThinkingFromHuman) {
2526 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2529 if (appData.testLegality) {
2530 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2534 if (saveSettingsOnExit) {
2535 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2542 ReadBitmap(&wIconPixmap, "icon_white.bm",
2543 icon_white_bits, icon_white_width, icon_white_height);
2544 ReadBitmap(&bIconPixmap, "icon_black.bm",
2545 icon_black_bits, icon_black_width, icon_black_height);
2546 iconPixmap = wIconPixmap;
2548 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2549 XtSetValues(shellWidget, args, i);
2552 * Create a cursor for the board widget.
2554 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2555 XChangeWindowAttributes(xDisplay, xBoardWindow,
2556 CWCursor, &window_attributes);
2559 * Inhibit shell resizing.
2561 shellArgs[0].value = (XtArgVal) &w;
2562 shellArgs[1].value = (XtArgVal) &h;
2563 XtGetValues(shellWidget, shellArgs, 2);
2564 shellArgs[4].value = shellArgs[2].value = w;
2565 shellArgs[5].value = shellArgs[3].value = h;
2566 XtSetValues(shellWidget, &shellArgs[2], 4);
2567 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2568 marginH = h - boardHeight;
2570 CatchDeleteWindow(shellWidget, "QuitProc");
2578 if (appData.animate || appData.animateDragging)
2581 XtAugmentTranslations(formWidget,
2582 XtParseTranslationTable(globalTranslations));
2583 XtAugmentTranslations(boardWidget,
2584 XtParseTranslationTable(boardTranslations));
2585 XtAugmentTranslations(whiteTimerWidget,
2586 XtParseTranslationTable(whiteTranslations));
2587 XtAugmentTranslations(blackTimerWidget,
2588 XtParseTranslationTable(blackTranslations));
2590 /* Why is the following needed on some versions of X instead
2591 * of a translation? */
2592 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2593 (XtEventHandler) EventProc, NULL);
2595 XtAddEventHandler(formWidget, KeyPressMask, False,
2596 (XtEventHandler) MoveTypeInProc, NULL);
2598 /* [AS] Restore layout */
2599 if( wpMoveHistory.visible ) {
2603 if( wpEvalGraph.visible )
2608 if( wpEngineOutput.visible ) {
2609 EngineOutputPopUp();
2614 if (errorExitStatus == -1) {
2615 if (appData.icsActive) {
2616 /* We now wait until we see "login:" from the ICS before
2617 sending the logon script (problems with timestamp otherwise) */
2618 /*ICSInitScript();*/
2619 if (appData.icsInputBox) ICSInputBoxPopUp();
2623 signal(SIGWINCH, TermSizeSigHandler);
2625 signal(SIGINT, IntSigHandler);
2626 signal(SIGTERM, IntSigHandler);
2627 if (*appData.cmailGameName != NULLCHAR) {
2628 signal(SIGUSR1, CmailSigHandler);
2631 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2633 // XtSetKeyboardFocus(shellWidget, formWidget);
2634 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2636 XtAppMainLoop(appContext);
2637 if (appData.debugMode) fclose(debugFP); // [DM] debug
2644 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2645 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2647 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2648 unlink(gameCopyFilename);
2649 unlink(gamePasteFilename);
2652 RETSIGTYPE TermSizeSigHandler(int sig)
2665 CmailSigHandler(sig)
2671 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2673 /* Activate call-back function CmailSigHandlerCallBack() */
2674 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2676 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2680 CmailSigHandlerCallBack(isr, closure, message, count, error)
2688 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2690 /**** end signal code ****/
2696 /* try to open the icsLogon script, either in the location given
2697 * or in the users HOME directory
2704 f = fopen(appData.icsLogon, "r");
2707 homedir = getenv("HOME");
2708 if (homedir != NULL)
2710 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2711 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2712 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2713 f = fopen(buf, "r");
2718 ProcessICSInitScript(f);
2720 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2743 if (!menuBarWidget) return;
2744 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2746 DisplayError("menuEdit.Revert", 0);
2748 XtSetSensitive(w, !grey);
2750 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2752 DisplayError("menuEdit.Annotate", 0);
2754 XtSetSensitive(w, !grey);
2759 SetMenuEnables(enab)
2763 if (!menuBarWidget) return;
2764 while (enab->name != NULL) {
2765 w = XtNameToWidget(menuBarWidget, enab->name);
2767 DisplayError(enab->name, 0);
2769 XtSetSensitive(w, enab->value);
2775 Enables icsEnables[] = {
2776 { "menuFile.Mail Move", False },
2777 { "menuFile.Reload CMail Message", False },
2778 { "menuMode.Machine Black", False },
2779 { "menuMode.Machine White", False },
2780 { "menuMode.Analysis Mode", False },
2781 { "menuMode.Analyze File", False },
2782 { "menuMode.Two Machines", False },
2783 { "menuMode.Machine Match", False },
2785 { "menuEngine.Hint", False },
2786 { "menuEngine.Book", False },
2787 { "menuEngine.Move Now", False },
2788 #ifndef OPTIONSDIALOG
2789 { "menuOptions.Periodic Updates", False },
2790 { "menuOptions.Hide Thinking", False },
2791 { "menuOptions.Ponder Next Move", False },
2794 { "menuEngine.Engine #1 Settings", False },
2795 { "menuEngine.Engine #2 Settings", False },
2796 { "menuEdit.Annotate", False },
2800 Enables ncpEnables[] = {
2801 { "menuFile.Mail Move", False },
2802 { "menuFile.Reload CMail Message", False },
2803 { "menuMode.Machine White", False },
2804 { "menuMode.Machine Black", False },
2805 { "menuMode.Analysis Mode", False },
2806 { "menuMode.Analyze File", False },
2807 { "menuMode.Two Machines", False },
2808 { "menuMode.Machine Match", False },
2809 { "menuMode.ICS Client", False },
2810 { "menuView.ICStex", False },
2811 { "menuView.ICS Input Box", False },
2812 { "Action", False },
2813 { "menuEdit.Revert", False },
2814 { "menuEdit.Annotate", False },
2815 { "menuEngine.Engine #1 Settings", False },
2816 { "menuEngine.Engine #2 Settings", False },
2817 { "menuEngine.Move Now", False },
2818 { "menuEngine.Retract Move", False },
2819 { "menuOptions.ICS", False },
2820 #ifndef OPTIONSDIALOG
2821 { "menuOptions.Auto Flag", False },
2822 { "menuOptions.Auto Flip View", False },
2823 // { "menuOptions.ICS Alarm", False },
2824 { "menuOptions.Move Sound", False },
2825 { "menuOptions.Hide Thinking", False },
2826 { "menuOptions.Periodic Updates", False },
2827 { "menuOptions.Ponder Next Move", False },
2829 { "menuEngine.Hint", False },
2830 { "menuEngine.Book", False },
2834 Enables gnuEnables[] = {
2835 { "menuMode.ICS Client", False },
2836 { "menuView.ICStex", False },
2837 { "menuView.ICS Input Box", False },
2838 { "menuAction.Accept", False },
2839 { "menuAction.Decline", False },
2840 { "menuAction.Rematch", False },
2841 { "menuAction.Adjourn", False },
2842 { "menuAction.Stop Examining", False },
2843 { "menuAction.Stop Observing", False },
2844 { "menuAction.Upload to Examine", False },
2845 { "menuEdit.Revert", False },
2846 { "menuEdit.Annotate", False },
2847 { "menuOptions.ICS", False },
2849 /* The next two options rely on SetCmailMode being called *after* */
2850 /* SetGNUMode so that when GNU is being used to give hints these */
2851 /* menu options are still available */
2853 { "menuFile.Mail Move", False },
2854 { "menuFile.Reload CMail Message", False },
2858 Enables cmailEnables[] = {
2860 { "menuAction.Call Flag", False },
2861 { "menuAction.Draw", True },
2862 { "menuAction.Adjourn", False },
2863 { "menuAction.Abort", False },
2864 { "menuAction.Stop Observing", False },
2865 { "menuAction.Stop Examining", False },
2866 { "menuFile.Mail Move", True },
2867 { "menuFile.Reload CMail Message", True },
2871 Enables trainingOnEnables[] = {
2872 { "menuMode.Edit Comment", False },
2873 { "menuMode.Pause", False },
2874 { "menuEdit.Forward", False },
2875 { "menuEdit.Backward", False },
2876 { "menuEdit.Forward to End", False },
2877 { "menuEdit.Back to Start", False },
2878 { "menuEngine.Move Now", False },
2879 { "menuEdit.Truncate Game", False },
2883 Enables trainingOffEnables[] = {
2884 { "menuMode.Edit Comment", True },
2885 { "menuMode.Pause", True },
2886 { "menuEdit.Forward", True },
2887 { "menuEdit.Backward", True },
2888 { "menuEdit.Forward to End", True },
2889 { "menuEdit.Back to Start", True },
2890 { "menuEngine.Move Now", True },
2891 { "menuEdit.Truncate Game", True },
2895 Enables machineThinkingEnables[] = {
2896 { "menuFile.Load Game", False },
2897 // { "menuFile.Load Next Game", False },
2898 // { "menuFile.Load Previous Game", False },
2899 // { "menuFile.Reload Same Game", False },
2900 { "menuEdit.Paste Game", False },
2901 { "menuFile.Load Position", False },
2902 // { "menuFile.Load Next Position", False },
2903 // { "menuFile.Load Previous Position", False },
2904 // { "menuFile.Reload Same Position", False },
2905 { "menuEdit.Paste Position", False },
2906 { "menuMode.Machine White", False },
2907 { "menuMode.Machine Black", False },
2908 { "menuMode.Two Machines", False },
2909 { "menuMode.Machine Match", False },
2910 { "menuEngine.Retract Move", False },
2914 Enables userThinkingEnables[] = {
2915 { "menuFile.Load Game", True },
2916 // { "menuFile.Load Next Game", True },
2917 // { "menuFile.Load Previous Game", True },
2918 // { "menuFile.Reload Same Game", True },
2919 { "menuEdit.Paste Game", True },
2920 { "menuFile.Load Position", True },
2921 // { "menuFile.Load Next Position", True },
2922 // { "menuFile.Load Previous Position", True },
2923 // { "menuFile.Reload Same Position", True },
2924 { "menuEdit.Paste Position", True },
2925 { "menuMode.Machine White", True },
2926 { "menuMode.Machine Black", True },
2927 { "menuMode.Two Machines", True },
2928 { "menuMode.Machine Match", True },
2929 { "menuEngine.Retract Move", True },
2935 SetMenuEnables(icsEnables);
2938 if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
2939 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2940 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
2948 SetMenuEnables(ncpEnables);
2954 SetMenuEnables(gnuEnables);
2960 SetMenuEnables(cmailEnables);
2966 SetMenuEnables(trainingOnEnables);
2967 if (appData.showButtonBar) {
2968 XtSetSensitive(buttonBarWidget, False);
2974 SetTrainingModeOff()
2976 SetMenuEnables(trainingOffEnables);
2977 if (appData.showButtonBar) {
2978 XtSetSensitive(buttonBarWidget, True);
2983 SetUserThinkingEnables()
2985 if (appData.noChessProgram) return;
2986 SetMenuEnables(userThinkingEnables);
2990 SetMachineThinkingEnables()
2992 if (appData.noChessProgram) return;
2993 SetMenuEnables(machineThinkingEnables);
2995 case MachinePlaysBlack:
2996 case MachinePlaysWhite:
2997 case TwoMachinesPlay:
2998 XtSetSensitive(XtNameToWidget(menuBarWidget,
2999 ModeToWidgetName(gameMode)), True);
3006 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3007 #define HISTORY_SIZE 64
3008 static char *history[HISTORY_SIZE];
3009 int histIn = 0, histP = 0;
3012 SaveInHistory(char *cmd)
3014 if (history[histIn] != NULL) {
3015 free(history[histIn]);
3016 history[histIn] = NULL;
3018 if (*cmd == NULLCHAR) return;
3019 history[histIn] = StrSave(cmd);
3020 histIn = (histIn + 1) % HISTORY_SIZE;
3021 if (history[histIn] != NULL) {
3022 free(history[histIn]);
3023 history[histIn] = NULL;
3029 PrevInHistory(char *cmd)
3032 if (histP == histIn) {
3033 if (history[histIn] != NULL) free(history[histIn]);
3034 history[histIn] = StrSave(cmd);
3036 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3037 if (newhp == histIn || history[newhp] == NULL) return NULL;
3039 return history[histP];
3045 if (histP == histIn) return NULL;
3046 histP = (histP + 1) % HISTORY_SIZE;
3047 return history[histP];
3049 // end of borrowed code
3051 #define Abs(n) ((n)<0 ? -(n) : (n))
3054 * Find a font that matches "pattern" that is as close as
3055 * possible to the targetPxlSize. Prefer fonts that are k
3056 * pixels smaller to fonts that are k pixels larger. The
3057 * pattern must be in the X Consortium standard format,
3058 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3059 * The return value should be freed with XtFree when no
3063 FindFont(pattern, targetPxlSize)
3067 char **fonts, *p, *best, *scalable, *scalableTail;
3068 int i, j, nfonts, minerr, err, pxlSize;
3071 char **missing_list;
3073 char *def_string, *base_fnt_lst, strInt[3];
3075 XFontStruct **fnt_list;
3076 base_fnt_lst = calloc(1, strlen(pattern) + 3);
3077 snprintf(strInt, sizeof(strInt)/sizeof(strInt[0]), "%d", targetPxlSize);
3078 p = strstr(pattern, "--");
3079 strncpy(base_fnt_lst, pattern, p - pattern + 2);
3080 strcat(base_fnt_lst, strInt);
3081 strcat(base_fnt_lst, strchr(p + 2, '-'));
3083 if ((fntSet = XCreateFontSet(xDisplay,
3087 &def_string)) == NULL) {
3089 fprintf(stderr, _("Unable to create font set.\n"));
3093 nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
3095 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3097 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3098 programName, pattern);
3106 for (i=0; i<nfonts; i++) {
3109 if (*p != '-') continue;
3111 if (*p == NULLCHAR) break;
3112 if (*p++ == '-') j++;
3114 if (j < 7) continue;
3117 scalable = fonts[i];
3120 err = pxlSize - targetPxlSize;
3121 if (Abs(err) < Abs(minerr) ||
3122 (minerr > 0 && err < 0 && -err == minerr)) {
3128 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3129 /* If the error is too big and there is a scalable font,
3130 use the scalable font. */
3131 int headlen = scalableTail - scalable;
3132 p = (char *) XtMalloc(strlen(scalable) + 10);
3133 while (isdigit(*scalableTail)) scalableTail++;
3134 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3136 p = (char *) XtMalloc(strlen(best) + 2);
3137 safeStrCpy(p, best, strlen(best)+1 );
3139 if (appData.debugMode) {
3140 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3141 pattern, targetPxlSize, p);
3144 if (missing_count > 0)
3145 XFreeStringList(missing_list);
3146 XFreeFontSet(xDisplay, fntSet);
3148 XFreeFontNames(fonts);
3154 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3155 // must be called before all non-first callse to CreateGCs()
3156 XtReleaseGC(shellWidget, highlineGC);
3157 XtReleaseGC(shellWidget, lightSquareGC);
3158 XtReleaseGC(shellWidget, darkSquareGC);
3159 XtReleaseGC(shellWidget, lineGC);
3160 if (appData.monoMode) {
3161 if (DefaultDepth(xDisplay, xScreen) == 1) {
3162 XtReleaseGC(shellWidget, wbPieceGC);
3164 XtReleaseGC(shellWidget, bwPieceGC);
3167 XtReleaseGC(shellWidget, prelineGC);
3168 XtReleaseGC(shellWidget, jailSquareGC);
3169 XtReleaseGC(shellWidget, wdPieceGC);
3170 XtReleaseGC(shellWidget, wlPieceGC);
3171 XtReleaseGC(shellWidget, wjPieceGC);
3172 XtReleaseGC(shellWidget, bdPieceGC);
3173 XtReleaseGC(shellWidget, blPieceGC);
3174 XtReleaseGC(shellWidget, bjPieceGC);
3178 void CreateGCs(int redo)
3180 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3181 | GCBackground | GCFunction | GCPlaneMask;
3182 XGCValues gc_values;
3185 gc_values.plane_mask = AllPlanes;
3186 gc_values.line_width = lineGap;
3187 gc_values.line_style = LineSolid;
3188 gc_values.function = GXcopy;
3191 DeleteGCs(); // called a second time; clean up old GCs first
3192 } else { // [HGM] grid and font GCs created on first call only
3193 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3194 gc_values.background = XWhitePixel(xDisplay, xScreen);
3195 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3196 XSetFont(xDisplay, coordGC, coordFontID);
3198 // [HGM] make font for holdings counts (white on black)
3199 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3200 gc_values.background = XBlackPixel(xDisplay, xScreen);
3201 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3202 XSetFont(xDisplay, countGC, countFontID);
3204 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3205 gc_values.background = XBlackPixel(xDisplay, xScreen);
3206 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3208 if (appData.monoMode) {
3209 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3210 gc_values.background = XWhitePixel(xDisplay, xScreen);
3211 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3213 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3214 gc_values.background = XBlackPixel(xDisplay, xScreen);
3215 lightSquareGC = wbPieceGC
3216 = XtGetGC(shellWidget, value_mask, &gc_values);
3218 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3219 gc_values.background = XWhitePixel(xDisplay, xScreen);
3220 darkSquareGC = bwPieceGC
3221 = XtGetGC(shellWidget, value_mask, &gc_values);
3223 if (DefaultDepth(xDisplay, xScreen) == 1) {
3224 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3225 gc_values.function = GXcopyInverted;
3226 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3227 gc_values.function = GXcopy;
3228 if (XBlackPixel(xDisplay, xScreen) == 1) {
3229 bwPieceGC = darkSquareGC;
3230 wbPieceGC = copyInvertedGC;
3232 bwPieceGC = copyInvertedGC;
3233 wbPieceGC = lightSquareGC;
3237 gc_values.foreground = highlightSquareColor;
3238 gc_values.background = highlightSquareColor;
3239 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3241 gc_values.foreground = premoveHighlightColor;
3242 gc_values.background = premoveHighlightColor;
3243 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3245 gc_values.foreground = lightSquareColor;
3246 gc_values.background = darkSquareColor;
3247 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3249 gc_values.foreground = darkSquareColor;
3250 gc_values.background = lightSquareColor;
3251 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3253 gc_values.foreground = jailSquareColor;
3254 gc_values.background = jailSquareColor;
3255 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3257 gc_values.foreground = whitePieceColor;
3258 gc_values.background = darkSquareColor;
3259 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3261 gc_values.foreground = whitePieceColor;
3262 gc_values.background = lightSquareColor;
3263 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3265 gc_values.foreground = whitePieceColor;
3266 gc_values.background = jailSquareColor;
3267 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3269 gc_values.foreground = blackPieceColor;
3270 gc_values.background = darkSquareColor;
3271 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3273 gc_values.foreground = blackPieceColor;
3274 gc_values.background = lightSquareColor;
3275 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3277 gc_values.foreground = blackPieceColor;
3278 gc_values.background = jailSquareColor;
3279 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3283 void loadXIM(xim, xmask, filename, dest, mask)
3296 fp = fopen(filename, "rb");
3298 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3305 for (y=0; y<h; ++y) {
3306 for (x=0; x<h; ++x) {
3311 XPutPixel(xim, x, y, blackPieceColor);
3313 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3316 XPutPixel(xim, x, y, darkSquareColor);
3318 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3321 XPutPixel(xim, x, y, whitePieceColor);
3323 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3326 XPutPixel(xim, x, y, lightSquareColor);
3328 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3336 /* create Pixmap of piece */
3337 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3339 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3342 /* create Pixmap of clipmask
3343 Note: We assume the white/black pieces have the same
3344 outline, so we make only 6 masks. This is okay
3345 since the XPM clipmask routines do the same. */
3347 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3349 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3352 /* now create the 1-bit version */
3353 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3356 values.foreground = 1;
3357 values.background = 0;
3359 /* Don't use XtGetGC, not read only */
3360 maskGC = XCreateGC(xDisplay, *mask,
3361 GCForeground | GCBackground, &values);
3362 XCopyPlane(xDisplay, temp, *mask, maskGC,
3363 0, 0, squareSize, squareSize, 0, 0, 1);
3364 XFreePixmap(xDisplay, temp);
3369 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3371 void CreateXIMPieces()
3376 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3381 /* The XSynchronize calls were copied from CreatePieces.
3382 Not sure if needed, but can't hurt */
3383 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3386 /* temp needed by loadXIM() */
3387 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3388 0, 0, ss, ss, AllPlanes, XYPixmap);
3390 if (strlen(appData.pixmapDirectory) == 0) {
3394 if (appData.monoMode) {
3395 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3399 fprintf(stderr, _("\nLoading XIMs...\n"));
3401 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3402 fprintf(stderr, "%d", piece+1);
3403 for (kind=0; kind<4; kind++) {
3404 fprintf(stderr, ".");
3405 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3406 ExpandPathName(appData.pixmapDirectory),
3407 piece <= (int) WhiteKing ? "" : "w",
3408 pieceBitmapNames[piece],
3410 ximPieceBitmap[kind][piece] =
3411 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3412 0, 0, ss, ss, AllPlanes, XYPixmap);
3413 if (appData.debugMode)
3414 fprintf(stderr, _("(File:%s:) "), buf);
3415 loadXIM(ximPieceBitmap[kind][piece],
3417 &(xpmPieceBitmap2[kind][piece]),
3418 &(ximMaskPm2[piece]));
3419 if(piece <= (int)WhiteKing)
3420 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3422 fprintf(stderr," ");
3424 /* Load light and dark squares */
3425 /* If the LSQ and DSQ pieces don't exist, we will
3426 draw them with solid squares. */
3427 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3428 if (access(buf, 0) != 0) {
3432 fprintf(stderr, _("light square "));
3434 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3435 0, 0, ss, ss, AllPlanes, XYPixmap);
3436 if (appData.debugMode)
3437 fprintf(stderr, _("(File:%s:) "), buf);
3439 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3440 fprintf(stderr, _("dark square "));
3441 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3442 ExpandPathName(appData.pixmapDirectory), ss);
3443 if (appData.debugMode)
3444 fprintf(stderr, _("(File:%s:) "), buf);
3446 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3447 0, 0, ss, ss, AllPlanes, XYPixmap);
3448 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3449 xpmJailSquare = xpmLightSquare;
3451 fprintf(stderr, _("Done.\n"));
3453 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3456 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3459 void CreateXPMBoard(char *s, int kind)
3463 if(s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3464 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3465 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3469 void FreeXPMPieces()
3470 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3471 // thisroutine has to be called t free the old piece pixmaps
3473 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3474 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3476 XFreePixmap(xDisplay, xpmLightSquare);
3477 XFreePixmap(xDisplay, xpmDarkSquare);
3481 void CreateXPMPieces()
3485 u_int ss = squareSize;
3487 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3488 XpmColorSymbol symbols[4];
3489 static int redo = False;
3491 if(redo) FreeXPMPieces(); else redo = 1;
3493 /* The XSynchronize calls were copied from CreatePieces.
3494 Not sure if needed, but can't hurt */
3495 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3497 /* Setup translations so piece colors match square colors */
3498 symbols[0].name = "light_piece";
3499 symbols[0].value = appData.whitePieceColor;
3500 symbols[1].name = "dark_piece";
3501 symbols[1].value = appData.blackPieceColor;
3502 symbols[2].name = "light_square";
3503 symbols[2].value = appData.lightSquareColor;
3504 symbols[3].name = "dark_square";
3505 symbols[3].value = appData.darkSquareColor;
3507 attr.valuemask = XpmColorSymbols;
3508 attr.colorsymbols = symbols;
3509 attr.numsymbols = 4;
3511 if (appData.monoMode) {
3512 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3516 if (strlen(appData.pixmapDirectory) == 0) {
3517 XpmPieces* pieces = builtInXpms;
3520 while (pieces->size != squareSize && pieces->size) pieces++;
3521 if (!pieces->size) {
3522 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3525 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3526 for (kind=0; kind<4; kind++) {
3528 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3529 pieces->xpm[piece][kind],
3530 &(xpmPieceBitmap2[kind][piece]),
3531 NULL, &attr)) != 0) {
3532 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3536 if(piece <= (int) WhiteKing)
3537 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3541 xpmJailSquare = xpmLightSquare;
3545 fprintf(stderr, _("\nLoading XPMs...\n"));
3548 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3549 fprintf(stderr, "%d ", piece+1);
3550 for (kind=0; kind<4; kind++) {
3551 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3552 ExpandPathName(appData.pixmapDirectory),
3553 piece > (int) WhiteKing ? "w" : "",
3554 pieceBitmapNames[piece],
3556 if (appData.debugMode) {
3557 fprintf(stderr, _("(File:%s:) "), buf);
3559 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3560 &(xpmPieceBitmap2[kind][piece]),
3561 NULL, &attr)) != 0) {
3562 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3563 // [HGM] missing: read of unorthodox piece failed; substitute King.
3564 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3565 ExpandPathName(appData.pixmapDirectory),
3567 if (appData.debugMode) {
3568 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3570 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3571 &(xpmPieceBitmap2[kind][piece]),
3575 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3580 if(piece <= (int) WhiteKing)
3581 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3584 /* Load light and dark squares */
3585 /* If the LSQ and DSQ pieces don't exist, we will
3586 draw them with solid squares. */
3587 fprintf(stderr, _("light square "));
3588 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3589 if (access(buf, 0) != 0) {
3593 if (appData.debugMode)
3594 fprintf(stderr, _("(File:%s:) "), buf);
3596 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3597 &xpmLightSquare, NULL, &attr)) != 0) {
3598 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3601 fprintf(stderr, _("dark square "));
3602 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3603 ExpandPathName(appData.pixmapDirectory), ss);
3604 if (appData.debugMode) {
3605 fprintf(stderr, _("(File:%s:) "), buf);
3607 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3608 &xpmDarkSquare, NULL, &attr)) != 0) {
3609 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3613 xpmJailSquare = xpmLightSquare;
3614 fprintf(stderr, _("Done.\n"));
3616 oldVariant = -1; // kludge to force re-makig of animation masks
3617 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3620 #endif /* HAVE_LIBXPM */
3623 /* No built-in bitmaps */
3628 u_int ss = squareSize;
3630 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3633 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3634 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3635 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3636 pieceBitmapNames[piece],
3637 ss, kind == SOLID ? 's' : 'o');
3638 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3639 if(piece <= (int)WhiteKing)
3640 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3644 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3648 /* With built-in bitmaps */
3651 BuiltInBits* bib = builtInBits;
3654 u_int ss = squareSize;
3656 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3659 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3661 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3662 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3663 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3664 pieceBitmapNames[piece],
3665 ss, kind == SOLID ? 's' : 'o');
3666 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3667 bib->bits[kind][piece], ss, ss);
3668 if(piece <= (int)WhiteKing)
3669 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3673 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3678 void ReadBitmap(pm, name, bits, wreq, hreq)
3681 unsigned char bits[];
3687 char msg[MSG_SIZ], fullname[MSG_SIZ];
3689 if (*appData.bitmapDirectory != NULLCHAR) {
3690 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3691 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3692 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3693 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3694 &w, &h, pm, &x_hot, &y_hot);
3695 fprintf(stderr, "load %s\n", name);
3696 if (errcode != BitmapSuccess) {
3698 case BitmapOpenFailed:
3699 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3701 case BitmapFileInvalid:
3702 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3704 case BitmapNoMemory:
3705 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3709 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3713 fprintf(stderr, _("%s: %s...using built-in\n"),
3715 } else if (w != wreq || h != hreq) {
3717 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3718 programName, fullname, w, h, wreq, hreq);
3724 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3733 if (lineGap == 0) return;
3735 /* [HR] Split this into 2 loops for non-square boards. */
3737 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3738 gridSegments[i].x1 = 0;
3739 gridSegments[i].x2 =
3740 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3741 gridSegments[i].y1 = gridSegments[i].y2
3742 = lineGap / 2 + (i * (squareSize + lineGap));
3745 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3746 gridSegments[j + i].y1 = 0;
3747 gridSegments[j + i].y2 =
3748 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3749 gridSegments[j + i].x1 = gridSegments[j + i].x2
3750 = lineGap / 2 + (j * (squareSize + lineGap));
3754 static void MenuBarSelect(w, addr, index)
3759 XtActionProc proc = (XtActionProc) addr;
3761 (proc)(NULL, NULL, NULL, NULL);
3764 void CreateMenuBarPopup(parent, name, mb)
3774 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3777 XtSetArg(args[j], XtNleftMargin, 20); j++;
3778 XtSetArg(args[j], XtNrightMargin, 20); j++;
3780 while (mi->string != NULL) {
3781 if (strcmp(mi->string, "----") == 0) {
3782 entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
3785 XtSetArg(args[j], XtNlabel, XtNewString(mi->string));
3786 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3788 XtAddCallback(entry, XtNcallback,
3789 (XtCallbackProc) MenuBarSelect,
3790 (caddr_t) mi->proc);
3796 Widget CreateMenuBar(mb)
3800 Widget anchor, menuBar;
3802 char menuName[MSG_SIZ];
3805 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3806 XtSetArg(args[j], XtNvSpace, 0); j++;
3807 XtSetArg(args[j], XtNborderWidth, 0); j++;
3808 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3809 formWidget, args, j);
3811 while (mb->name != NULL) {
3812 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3813 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3815 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3818 shortName[0] = mb->name[0];
3819 shortName[1] = NULLCHAR;
3820 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3823 XtSetArg(args[j], XtNlabel, XtNewString(mb->name)); j++;
3826 XtSetArg(args[j], XtNborderWidth, 0); j++;
3827 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3829 CreateMenuBarPopup(menuBar, menuName, mb);
3835 Widget CreateButtonBar(mi)
3839 Widget button, buttonBar;
3843 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3845 XtSetArg(args[j], XtNhSpace, 0); j++;
3847 XtSetArg(args[j], XtNborderWidth, 0); j++;
3848 XtSetArg(args[j], XtNvSpace, 0); j++;
3849 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3850 formWidget, args, j);
3852 while (mi->string != NULL) {
3855 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3856 XtSetArg(args[j], XtNborderWidth, 0); j++;
3858 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3859 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3860 buttonBar, args, j);
3861 XtAddCallback(button, XtNcallback,
3862 (XtCallbackProc) MenuBarSelect,
3863 (caddr_t) mi->proc);
3870 CreatePieceMenu(name, color)
3877 ChessSquare selection;
3879 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3880 boardWidget, args, 0);
3882 for (i = 0; i < PIECE_MENU_SIZE; i++) {
3883 String item = pieceMenuStrings[color][i];
3885 if (strcmp(item, "----") == 0) {
3886 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3889 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3890 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3892 selection = pieceMenuTranslation[color][i];
3893 XtAddCallback(entry, XtNcallback,
3894 (XtCallbackProc) PieceMenuSelect,
3895 (caddr_t) selection);
3896 if (selection == WhitePawn || selection == BlackPawn) {
3897 XtSetArg(args[0], XtNpopupOnEntry, entry);
3898 XtSetValues(menu, args, 1);
3911 ChessSquare selection;
3913 whitePieceMenu = CreatePieceMenu("menuW", 0);
3914 blackPieceMenu = CreatePieceMenu("menuB", 1);
3916 XtRegisterGrabAction(PieceMenuPopup, True,
3917 (unsigned)(ButtonPressMask|ButtonReleaseMask),
3918 GrabModeAsync, GrabModeAsync);
3920 XtSetArg(args[0], XtNlabel, _("Drop"));
3921 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3922 boardWidget, args, 1);
3923 for (i = 0; i < DROP_MENU_SIZE; i++) {
3924 String item = dropMenuStrings[i];
3926 if (strcmp(item, "----") == 0) {
3927 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3930 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3931 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3933 selection = dropMenuTranslation[i];
3934 XtAddCallback(entry, XtNcallback,
3935 (XtCallbackProc) DropMenuSelect,
3936 (caddr_t) selection);
3941 void SetupDropMenu()
3949 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3950 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3951 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3952 dmEnables[i].piece);
3953 XtSetSensitive(entry, p != NULL || !appData.testLegality
3954 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3955 && !appData.icsActive));
3957 while (p && *p++ == dmEnables[i].piece) count++;
3958 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3960 XtSetArg(args[j], XtNlabel, label); j++;
3961 XtSetValues(entry, args, j);
3965 void PieceMenuPopup(w, event, params, num_params)
3969 Cardinal *num_params;
3971 String whichMenu; int menuNr;
3972 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3973 if (event->type == ButtonRelease)
3974 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3975 else if (event->type == ButtonPress)
3976 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3978 case 0: whichMenu = params[0]; break;
3979 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3981 case -1: if (errorUp) ErrorPopDown();
3984 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3987 static void PieceMenuSelect(w, piece, junk)
3992 if (pmFromX < 0 || pmFromY < 0) return;
3993 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3996 static void DropMenuSelect(w, piece, junk)
4001 if (pmFromX < 0 || pmFromY < 0) return;
4002 DropMenuEvent(piece, pmFromX, pmFromY);
4005 void WhiteClock(w, event, prms, nprms)
4014 void BlackClock(w, event, prms, nprms)
4025 * If the user selects on a border boundary, return -1; if off the board,
4026 * return -2. Otherwise map the event coordinate to the square.
4028 int EventToSquare(x, limit)
4036 if ((x % (squareSize + lineGap)) >= squareSize)
4038 x /= (squareSize + lineGap);
4044 static void do_flash_delay(msec)
4050 static void drawHighlight(file, rank, gc)
4056 if (lineGap == 0) return;
4059 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4060 (squareSize + lineGap);
4061 y = lineGap/2 + rank * (squareSize + lineGap);
4063 x = lineGap/2 + file * (squareSize + lineGap);
4064 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4065 (squareSize + lineGap);
4068 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4069 squareSize+lineGap, squareSize+lineGap);
4072 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4073 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4076 SetHighlights(fromX, fromY, toX, toY)
4077 int fromX, fromY, toX, toY;
4079 if (hi1X != fromX || hi1Y != fromY) {
4080 if (hi1X >= 0 && hi1Y >= 0) {
4081 drawHighlight(hi1X, hi1Y, lineGC);
4083 } // [HGM] first erase both, then draw new!
4084 if (hi2X != toX || hi2Y != toY) {
4085 if (hi2X >= 0 && hi2Y >= 0) {
4086 drawHighlight(hi2X, hi2Y, lineGC);
4089 if (hi1X != fromX || hi1Y != fromY) {
4090 if (fromX >= 0 && fromY >= 0) {
4091 drawHighlight(fromX, fromY, highlineGC);
4094 if (hi2X != toX || hi2Y != toY) {
4095 if (toX >= 0 && toY >= 0) {
4096 drawHighlight(toX, toY, highlineGC);
4108 SetHighlights(-1, -1, -1, -1);
4113 SetPremoveHighlights(fromX, fromY, toX, toY)
4114 int fromX, fromY, toX, toY;
4116 if (pm1X != fromX || pm1Y != fromY) {
4117 if (pm1X >= 0 && pm1Y >= 0) {
4118 drawHighlight(pm1X, pm1Y, lineGC);
4120 if (fromX >= 0 && fromY >= 0) {
4121 drawHighlight(fromX, fromY, prelineGC);
4124 if (pm2X != toX || pm2Y != toY) {
4125 if (pm2X >= 0 && pm2Y >= 0) {
4126 drawHighlight(pm2X, pm2Y, lineGC);
4128 if (toX >= 0 && toY >= 0) {
4129 drawHighlight(toX, toY, prelineGC);
4139 ClearPremoveHighlights()
4141 SetPremoveHighlights(-1, -1, -1, -1);
4144 static int CutOutSquare(x, y, x0, y0, kind)
4145 int x, y, *x0, *y0, kind;
4147 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4148 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4150 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4151 if(textureW[kind] < W*squareSize)
4152 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4154 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4155 if(textureH[kind] < H*squareSize)
4156 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4158 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4162 static void BlankSquare(x, y, color, piece, dest, fac)
4163 int x, y, color, fac;
4166 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4168 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4169 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4170 squareSize, squareSize, x*fac, y*fac);
4172 if (useImages && useImageSqs) {
4176 pm = xpmLightSquare;
4181 case 2: /* neutral */
4186 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4187 squareSize, squareSize, x*fac, y*fac);
4197 case 2: /* neutral */
4202 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4207 I split out the routines to draw a piece so that I could
4208 make a generic flash routine.
4210 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4212 int square_color, x, y;
4215 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4216 switch (square_color) {
4218 case 2: /* neutral */
4220 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4221 ? *pieceToOutline(piece)
4222 : *pieceToSolid(piece),
4223 dest, bwPieceGC, 0, 0,
4224 squareSize, squareSize, x, y);
4227 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4228 ? *pieceToSolid(piece)
4229 : *pieceToOutline(piece),
4230 dest, wbPieceGC, 0, 0,
4231 squareSize, squareSize, x, y);
4236 static void monoDrawPiece(piece, square_color, x, y, dest)
4238 int square_color, x, y;
4241 switch (square_color) {
4243 case 2: /* neutral */
4245 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4246 ? *pieceToOutline(piece)
4247 : *pieceToSolid(piece),
4248 dest, bwPieceGC, 0, 0,
4249 squareSize, squareSize, x, y, 1);
4252 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4253 ? *pieceToSolid(piece)
4254 : *pieceToOutline(piece),
4255 dest, wbPieceGC, 0, 0,
4256 squareSize, squareSize, x, y, 1);
4261 static void colorDrawPiece(piece, square_color, x, y, dest)
4263 int square_color, x, y;
4266 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4267 switch (square_color) {
4269 XCopyPlane(xDisplay, *pieceToSolid(piece),
4270 dest, (int) piece < (int) BlackPawn
4271 ? wlPieceGC : blPieceGC, 0, 0,
4272 squareSize, squareSize, x, y, 1);
4275 XCopyPlane(xDisplay, *pieceToSolid(piece),
4276 dest, (int) piece < (int) BlackPawn
4277 ? wdPieceGC : bdPieceGC, 0, 0,
4278 squareSize, squareSize, x, y, 1);
4280 case 2: /* neutral */
4282 XCopyPlane(xDisplay, *pieceToSolid(piece),
4283 dest, (int) piece < (int) BlackPawn
4284 ? wjPieceGC : bjPieceGC, 0, 0,
4285 squareSize, squareSize, x, y, 1);
4290 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4292 int square_color, x, y;
4295 int kind, p = piece;
4297 switch (square_color) {
4299 case 2: /* neutral */
4301 if ((int)piece < (int) BlackPawn) {
4309 if ((int)piece < (int) BlackPawn) {
4317 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4318 if(useTexture & square_color+1) {
4319 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4320 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4321 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4322 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4323 XSetClipMask(xDisplay, wlPieceGC, None);
4324 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4326 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4327 dest, wlPieceGC, 0, 0,
4328 squareSize, squareSize, x, y);
4331 typedef void (*DrawFunc)();
4333 DrawFunc ChooseDrawFunc()
4335 if (appData.monoMode) {
4336 if (DefaultDepth(xDisplay, xScreen) == 1) {
4337 return monoDrawPiece_1bit;
4339 return monoDrawPiece;
4343 return colorDrawPieceImage;
4345 return colorDrawPiece;
4349 /* [HR] determine square color depending on chess variant. */
4350 static int SquareColor(row, column)
4355 if (gameInfo.variant == VariantXiangqi) {
4356 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4358 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4360 } else if (row <= 4) {
4366 square_color = ((column + row) % 2) == 1;
4369 /* [hgm] holdings: next line makes all holdings squares light */
4370 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4372 return square_color;
4375 void DrawSquare(row, column, piece, do_flash)
4376 int row, column, do_flash;
4379 int square_color, x, y, direction, font_ascent, font_descent;
4382 XCharStruct overall;
4386 /* Calculate delay in milliseconds (2-delays per complete flash) */
4387 flash_delay = 500 / appData.flashRate;
4390 x = lineGap + ((BOARD_WIDTH-1)-column) *
4391 (squareSize + lineGap);
4392 y = lineGap + row * (squareSize + lineGap);
4394 x = lineGap + column * (squareSize + lineGap);
4395 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4396 (squareSize + lineGap);
4399 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4401 square_color = SquareColor(row, column);
4403 if ( // [HGM] holdings: blank out area between board and holdings
4404 column == BOARD_LEFT-1 || column == BOARD_RGHT
4405 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4406 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4407 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4409 // [HGM] print piece counts next to holdings
4410 string[1] = NULLCHAR;
4411 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4412 string[0] = '0' + piece;
4413 XTextExtents(countFontStruct, string, 1, &direction,
4414 &font_ascent, &font_descent, &overall);
4415 if (appData.monoMode) {
4416 XDrawImageString(xDisplay, xBoardWindow, countGC,
4417 x + squareSize - overall.width - 2,
4418 y + font_ascent + 1, string, 1);
4420 XDrawString(xDisplay, xBoardWindow, countGC,
4421 x + squareSize - overall.width - 2,
4422 y + font_ascent + 1, string, 1);
4425 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4426 string[0] = '0' + piece;
4427 XTextExtents(countFontStruct, string, 1, &direction,
4428 &font_ascent, &font_descent, &overall);
4429 if (appData.monoMode) {
4430 XDrawImageString(xDisplay, xBoardWindow, countGC,
4431 x + 2, y + font_ascent + 1, string, 1);
4433 XDrawString(xDisplay, xBoardWindow, countGC,
4434 x + 2, y + font_ascent + 1, string, 1);
4438 if (piece == EmptySquare || appData.blindfold) {
4439 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4441 drawfunc = ChooseDrawFunc();
4443 if (do_flash && appData.flashCount > 0) {
4444 for (i=0; i<appData.flashCount; ++i) {
4445 drawfunc(piece, square_color, x, y, xBoardWindow);
4446 XSync(xDisplay, False);
4447 do_flash_delay(flash_delay);
4449 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4450 XSync(xDisplay, False);
4451 do_flash_delay(flash_delay);
4454 drawfunc(piece, square_color, x, y, xBoardWindow);
4458 string[1] = NULLCHAR;
4459 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4460 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4461 string[0] = 'a' + column - BOARD_LEFT;
4462 XTextExtents(coordFontStruct, string, 1, &direction,
4463 &font_ascent, &font_descent, &overall);
4464 if (appData.monoMode) {
4465 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4466 x + squareSize - overall.width - 2,
4467 y + squareSize - font_descent - 1, string, 1);
4469 XDrawString(xDisplay, xBoardWindow, coordGC,
4470 x + squareSize - overall.width - 2,
4471 y + squareSize - font_descent - 1, string, 1);
4474 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4475 string[0] = ONE + row;
4476 XTextExtents(coordFontStruct, string, 1, &direction,
4477 &font_ascent, &font_descent, &overall);
4478 if (appData.monoMode) {
4479 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4480 x + 2, y + font_ascent + 1, string, 1);
4482 XDrawString(xDisplay, xBoardWindow, coordGC,
4483 x + 2, y + font_ascent + 1, string, 1);
4486 if(!partnerUp && marker[row][column]) {
4487 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4488 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4493 /* Why is this needed on some versions of X? */
4494 void EventProc(widget, unused, event)
4499 if (!XtIsRealized(widget))
4502 switch (event->type) {
4504 if (event->xexpose.count > 0) return; /* no clipping is done */
4505 XDrawPosition(widget, True, NULL);
4506 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4507 flipView = !flipView; partnerUp = !partnerUp;
4508 XDrawPosition(widget, True, NULL);
4509 flipView = !flipView; partnerUp = !partnerUp;
4513 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4520 void DrawPosition(fullRedraw, board)
4521 /*Boolean*/int fullRedraw;
4524 XDrawPosition(boardWidget, fullRedraw, board);
4527 /* Returns 1 if there are "too many" differences between b1 and b2
4528 (i.e. more than 1 move was made) */
4529 static int too_many_diffs(b1, b2)
4535 for (i=0; i<BOARD_HEIGHT; ++i) {
4536 for (j=0; j<BOARD_WIDTH; ++j) {
4537 if (b1[i][j] != b2[i][j]) {
4538 if (++c > 4) /* Castling causes 4 diffs */
4546 /* Matrix describing castling maneuvers */
4547 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4548 static int castling_matrix[4][5] = {
4549 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4550 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4551 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4552 { 7, 7, 4, 5, 6 } /* 0-0, black */
4555 /* Checks whether castling occurred. If it did, *rrow and *rcol
4556 are set to the destination (row,col) of the rook that moved.
4558 Returns 1 if castling occurred, 0 if not.
4560 Note: Only handles a max of 1 castling move, so be sure
4561 to call too_many_diffs() first.
4563 static int check_castle_draw(newb, oldb, rrow, rcol)
4570 /* For each type of castling... */
4571 for (i=0; i<4; ++i) {
4572 r = castling_matrix[i];
4574 /* Check the 4 squares involved in the castling move */
4576 for (j=1; j<=4; ++j) {
4577 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4584 /* All 4 changed, so it must be a castling move */
4593 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4594 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4596 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4599 void DrawSeekBackground( int left, int top, int right, int bottom )
4601 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4604 void DrawSeekText(char *buf, int x, int y)
4606 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4609 void DrawSeekDot(int x, int y, int colorNr)
4611 int square = colorNr & 0x80;
4614 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4616 XFillRectangle(xDisplay, xBoardWindow, color,
4617 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4619 XFillArc(xDisplay, xBoardWindow, color,
4620 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4623 static int damage[2][BOARD_RANKS][BOARD_FILES];
4626 * event handler for redrawing the board
4628 void XDrawPosition(w, repaint, board)
4630 /*Boolean*/int repaint;
4634 static int lastFlipView = 0;
4635 static int lastBoardValid[2] = {0, 0};
4636 static Board lastBoard[2];
4639 int nr = twoBoards*partnerUp;
4641 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4643 if (board == NULL) {
4644 if (!lastBoardValid[nr]) return;
4645 board = lastBoard[nr];
4647 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4648 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4649 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4654 * It would be simpler to clear the window with XClearWindow()
4655 * but this causes a very distracting flicker.
4658 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4660 if ( lineGap && IsDrawArrowEnabled())
4661 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4662 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4664 /* If too much changes (begin observing new game, etc.), don't
4666 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4668 /* Special check for castling so we don't flash both the king
4669 and the rook (just flash the king). */
4671 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4672 /* Draw rook with NO flashing. King will be drawn flashing later */
4673 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4674 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4678 /* First pass -- Draw (newly) empty squares and repair damage.
4679 This prevents you from having a piece show up twice while it
4680 is flashing on its new square */
4681 for (i = 0; i < BOARD_HEIGHT; i++)
4682 for (j = 0; j < BOARD_WIDTH; j++)
4683 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4684 || damage[nr][i][j]) {
4685 DrawSquare(i, j, board[i][j], 0);
4686 damage[nr][i][j] = False;
4689 /* Second pass -- Draw piece(s) in new position and flash them */
4690 for (i = 0; i < BOARD_HEIGHT; i++)
4691 for (j = 0; j < BOARD_WIDTH; j++)
4692 if (board[i][j] != lastBoard[nr][i][j]) {
4693 DrawSquare(i, j, board[i][j], do_flash);
4697 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4698 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4699 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4701 for (i = 0; i < BOARD_HEIGHT; i++)
4702 for (j = 0; j < BOARD_WIDTH; j++) {
4703 DrawSquare(i, j, board[i][j], 0);
4704 damage[nr][i][j] = False;
4708 CopyBoard(lastBoard[nr], board);
4709 lastBoardValid[nr] = 1;
4710 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4711 lastFlipView = flipView;
4713 /* Draw highlights */
4714 if (pm1X >= 0 && pm1Y >= 0) {
4715 drawHighlight(pm1X, pm1Y, prelineGC);
4717 if (pm2X >= 0 && pm2Y >= 0) {
4718 drawHighlight(pm2X, pm2Y, prelineGC);
4720 if (hi1X >= 0 && hi1Y >= 0) {
4721 drawHighlight(hi1X, hi1Y, highlineGC);
4723 if (hi2X >= 0 && hi2Y >= 0) {
4724 drawHighlight(hi2X, hi2Y, highlineGC);
4726 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4728 /* If piece being dragged around board, must redraw that too */
4731 XSync(xDisplay, False);
4736 * event handler for redrawing the board
4738 void DrawPositionProc(w, event, prms, nprms)
4744 XDrawPosition(w, True, NULL);
4749 * event handler for parsing user moves
4751 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4752 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4753 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4754 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4755 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4756 // and at the end FinishMove() to perform the move after optional promotion popups.
4757 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4758 void HandleUserMove(w, event, prms, nprms)
4764 if (w != boardWidget || errorExitStatus != -1) return;
4765 if(nprms) shiftKey = !strcmp(prms[0], "1");
4768 if (event->type == ButtonPress) {
4769 XtPopdown(promotionShell);
4770 XtDestroyWidget(promotionShell);
4771 promotionUp = False;
4779 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4780 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4781 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4784 void AnimateUserMove (Widget w, XEvent * event,
4785 String * params, Cardinal * nParams)
4787 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4788 DragPieceMove(event->xmotion.x, event->xmotion.y);
4791 void HandlePV (Widget w, XEvent * event,
4792 String * params, Cardinal * nParams)
4793 { // [HGM] pv: walk PV
4794 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4797 static int savedIndex; /* gross that this is global */
4799 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4802 XawTextPosition index, dummy;
4805 XawTextGetSelectionPos(w, &index, &dummy);
4806 XtSetArg(arg, XtNstring, &val);
4807 XtGetValues(w, &arg, 1);
4808 ReplaceComment(savedIndex, val);
4809 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4810 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4813 void EditCommentPopUp(index, title, text)
4818 if (text == NULL) text = "";
4819 NewCommentPopup(title, text, index);
4822 void ICSInputBoxPopUp()
4827 extern Option boxOptions[];
4829 void ICSInputSendText()
4836 edit = boxOptions[0].handle;
4838 XtSetArg(args[j], XtNstring, &val); j++;
4839 XtGetValues(edit, args, j);
4841 SendMultiLineToICS(val);
4842 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4843 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4846 void ICSInputBoxPopDown()
4851 void CommentPopUp(title, text)
4854 savedIndex = currentMove; // [HGM] vari
4855 NewCommentPopup(title, text, currentMove);
4858 void CommentPopDown()
4863 void FileNamePopUp(label, def, filter, proc, openMode)
4870 fileProc = proc; /* I can't see a way not */
4871 fileOpenMode = openMode; /* to use globals here */
4872 { // [HGM] use file-selector dialog stolen from Ghostview
4874 int index; // this is not supported yet
4876 if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
4877 (def[0] ? def : NULL), filter, openMode, NULL, &name))
4878 (void) (*fileProc)(f, index=0, name);
4882 void FileNamePopDown()
4884 if (!filenameUp) return;
4885 XtPopdown(fileNameShell);
4886 XtDestroyWidget(fileNameShell);
4891 void FileNameCallback(w, client_data, call_data)
4893 XtPointer client_data, call_data;
4898 XtSetArg(args[0], XtNlabel, &name);
4899 XtGetValues(w, args, 1);
4901 if (strcmp(name, _("cancel")) == 0) {
4906 FileNameAction(w, NULL, NULL, NULL);
4909 void FileNameAction(w, event, prms, nprms)
4921 name = XawDialogGetValueString(w = XtParent(w));
4923 if ((name != NULL) && (*name != NULLCHAR)) {
4924 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
4925 XtPopdown(w = XtParent(XtParent(w)));
4929 p = strrchr(buf, ' ');
4936 fullname = ExpandPathName(buf);
4938 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
4941 f = fopen(fullname, fileOpenMode);
4943 DisplayError(_("Failed to open file"), errno);
4945 (void) (*fileProc)(f, index, buf);
4952 XtPopdown(w = XtParent(XtParent(w)));
4958 void PromotionPopUp()
4961 Widget dialog, layout;
4963 Dimension bw_width, pw_width;
4967 XtSetArg(args[j], XtNwidth, &bw_width); j++;
4968 XtGetValues(boardWidget, args, j);
4971 XtSetArg(args[j], XtNresizable, True); j++;
4972 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
4974 XtCreatePopupShell("Promotion", transientShellWidgetClass,
4975 shellWidget, args, j);
4977 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
4978 layoutArgs, XtNumber(layoutArgs));
4981 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
4982 XtSetArg(args[j], XtNborderWidth, 0); j++;
4983 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
4986 if(gameInfo.variant != VariantShogi) {
4987 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
4988 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback,
4989 (XtPointer) dialog);
4990 XawDialogAddButton(dialog, _("General"), PromotionCallback,
4991 (XtPointer) dialog);
4992 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback,
4993 (XtPointer) dialog);
4994 XawDialogAddButton(dialog, _("Captain"), PromotionCallback,
4995 (XtPointer) dialog);
4997 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
4998 (XtPointer) dialog);
4999 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
5000 (XtPointer) dialog);
5001 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
5002 (XtPointer) dialog);
5003 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
5004 (XtPointer) dialog);
5006 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5007 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
5008 gameInfo.variant == VariantGiveaway) {
5009 XawDialogAddButton(dialog, _("King"), PromotionCallback,
5010 (XtPointer) dialog);
5012 if(gameInfo.variant == VariantCapablanca ||
5013 gameInfo.variant == VariantGothic ||
5014 gameInfo.variant == VariantCapaRandom) {
5015 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5016 (XtPointer) dialog);
5017 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5018 (XtPointer) dialog);
5020 } else // [HGM] shogi
5022 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5023 (XtPointer) dialog);
5024 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5025 (XtPointer) dialog);
5027 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5028 (XtPointer) dialog);
5030 XtRealizeWidget(promotionShell);
5031 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5034 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5035 XtGetValues(promotionShell, args, j);
5037 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5038 lineGap + squareSize/3 +
5039 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5040 0 : 6*(squareSize + lineGap)), &x, &y);
5043 XtSetArg(args[j], XtNx, x); j++;
5044 XtSetArg(args[j], XtNy, y); j++;
5045 XtSetValues(promotionShell, args, j);
5047 XtPopup(promotionShell, XtGrabNone);
5052 void PromotionPopDown()
5054 if (!promotionUp) return;
5055 XtPopdown(promotionShell);
5056 XtDestroyWidget(promotionShell);
5057 promotionUp = False;
5060 void PromotionCallback(w, client_data, call_data)
5062 XtPointer client_data, call_data;
5068 XtSetArg(args[0], XtNlabel, &name);
5069 XtGetValues(w, args, 1);
5073 if (fromX == -1) return;
5075 if (strcmp(name, _("cancel")) == 0) {
5079 } else if (strcmp(name, _("Knight")) == 0) {
5081 } else if (strcmp(name, _("Promote")) == 0) {
5083 } else if (strcmp(name, _("Defer")) == 0) {
5086 promoChar = ToLower(name[0]);
5089 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5091 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5092 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5097 void ErrorCallback(w, client_data, call_data)
5099 XtPointer client_data, call_data;
5102 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5104 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5110 if (!errorUp) return;
5112 XtPopdown(errorShell);
5113 XtDestroyWidget(errorShell);
5114 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5117 void ErrorPopUp(title, label, modal)
5118 char *title, *label;
5122 Widget dialog, layout;
5126 Dimension bw_width, pw_width;
5127 Dimension pw_height;
5131 XtSetArg(args[i], XtNresizable, True); i++;
5132 XtSetArg(args[i], XtNtitle, title); i++;
5134 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5135 shellWidget, args, i);
5137 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5138 layoutArgs, XtNumber(layoutArgs));
5141 XtSetArg(args[i], XtNlabel, label); i++;
5142 XtSetArg(args[i], XtNborderWidth, 0); i++;
5143 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5146 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5148 XtRealizeWidget(errorShell);
5149 CatchDeleteWindow(errorShell, "ErrorPopDown");
5152 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5153 XtGetValues(boardWidget, args, i);
5155 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5156 XtSetArg(args[i], XtNheight, &pw_height); i++;
5157 XtGetValues(errorShell, args, i);
5160 /* This code seems to tickle an X bug if it is executed too soon
5161 after xboard starts up. The coordinates get transformed as if
5162 the main window was positioned at (0, 0).
5164 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5165 0 - pw_height + squareSize / 3, &x, &y);
5167 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5168 RootWindowOfScreen(XtScreen(boardWidget)),
5169 (bw_width - pw_width) / 2,
5170 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5174 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5177 XtSetArg(args[i], XtNx, x); i++;
5178 XtSetArg(args[i], XtNy, y); i++;
5179 XtSetValues(errorShell, args, i);
5182 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5185 /* Disable all user input other than deleting the window */
5186 static int frozen = 0;
5190 /* Grab by a widget that doesn't accept input */
5191 XtAddGrab(messageWidget, TRUE, FALSE);
5195 /* Undo a FreezeUI */
5198 if (!frozen) return;
5199 XtRemoveGrab(messageWidget);
5203 char *ModeToWidgetName(mode)
5207 case BeginningOfGame:
5208 if (appData.icsActive)
5209 return "menuMode.ICS Client";
5210 else if (appData.noChessProgram ||
5211 *appData.cmailGameName != NULLCHAR)
5212 return "menuMode.Edit Game";
5214 return "menuMode.Machine Black";
5215 case MachinePlaysBlack:
5216 return "menuMode.Machine Black";
5217 case MachinePlaysWhite:
5218 return "menuMode.Machine White";
5220 return "menuMode.Analysis Mode";
5222 return "menuMode.Analyze File";
5223 case TwoMachinesPlay:
5224 return "menuMode.Two Machines";
5226 return "menuMode.Edit Game";
5227 case PlayFromGameFile:
5228 return "menuFile.Load Game";
5230 return "menuMode.Edit Position";
5232 return "menuMode.Training";
5233 case IcsPlayingWhite:
5234 case IcsPlayingBlack:
5238 return "menuMode.ICS Client";
5245 void ModeHighlight()
5248 static int oldPausing = FALSE;
5249 static GameMode oldmode = (GameMode) -1;
5252 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5254 if (pausing != oldPausing) {
5255 oldPausing = pausing;
5257 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5259 XtSetArg(args[0], XtNleftBitmap, None);
5261 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5264 if (appData.showButtonBar) {
5265 /* Always toggle, don't set. Previous code messes up when
5266 invoked while the button is pressed, as releasing it
5267 toggles the state again. */
5270 XtSetArg(args[0], XtNbackground, &oldbg);
5271 XtSetArg(args[1], XtNforeground, &oldfg);
5272 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5274 XtSetArg(args[0], XtNbackground, oldfg);
5275 XtSetArg(args[1], XtNforeground, oldbg);
5277 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5281 wname = ModeToWidgetName(oldmode);
5282 if (wname != NULL) {
5283 XtSetArg(args[0], XtNleftBitmap, None);
5284 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5286 wname = ModeToWidgetName(gameMode);
5287 if (wname != NULL) {
5288 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5289 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5293 /* Maybe all the enables should be handled here, not just this one */
5294 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5295 gameMode == Training || gameMode == PlayFromGameFile);
5300 * Button/menu procedures
5302 void ResetProc(w, event, prms, nprms)
5311 int LoadGamePopUp(f, gameNumber, title)
5316 cmailMsgLoaded = FALSE;
5317 if (gameNumber == 0) {
5318 int error = GameListBuild(f);
5320 DisplayError(_("Cannot build game list"), error);
5321 } else if (!ListEmpty(&gameList) &&
5322 ((ListGame *) gameList.tailPred)->number > 1) {
5323 GameListPopUp(f, title);
5329 return LoadGame(f, gameNumber, title, FALSE);
5332 void LoadGameProc(w, event, prms, nprms)
5338 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5341 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5344 void LoadNextGameProc(w, event, prms, nprms)
5353 void LoadPrevGameProc(w, event, prms, nprms)
5362 void ReloadGameProc(w, event, prms, nprms)
5371 void LoadNextPositionProc(w, event, prms, nprms)
5380 void LoadPrevPositionProc(w, event, prms, nprms)
5389 void ReloadPositionProc(w, event, prms, nprms)
5398 void LoadPositionProc(w, event, prms, nprms)
5404 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5407 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5410 void SaveGameProc(w, event, prms, nprms)
5416 FileNamePopUp(_("Save game file name?"),
5417 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5418 appData.oldSaveStyle ? ".game" : ".pgn",
5422 void SavePositionProc(w, event, prms, nprms)
5428 FileNamePopUp(_("Save position file name?"),
5429 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5430 appData.oldSaveStyle ? ".pos" : ".fen",
5434 void ReloadCmailMsgProc(w, event, prms, nprms)
5440 ReloadCmailMsgEvent(FALSE);
5443 void MailMoveProc(w, event, prms, nprms)
5452 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5453 char *selected_fen_position=NULL;
5456 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5457 Atom *type_return, XtPointer *value_return,
5458 unsigned long *length_return, int *format_return)
5460 char *selection_tmp;
5462 if (!selected_fen_position) return False; /* should never happen */
5463 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5464 /* note: since no XtSelectionDoneProc was registered, Xt will
5465 * automatically call XtFree on the value returned. So have to
5466 * make a copy of it allocated with XtMalloc */
5467 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5468 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5470 *value_return=selection_tmp;
5471 *length_return=strlen(selection_tmp);
5472 *type_return=*target;
5473 *format_return = 8; /* bits per byte */
5475 } else if (*target == XA_TARGETS(xDisplay)) {
5476 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5477 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5478 targets_tmp[1] = XA_STRING;
5479 *value_return = targets_tmp;
5480 *type_return = XA_ATOM;
5482 *format_return = 8 * sizeof(Atom);
5483 if (*format_return > 32) {
5484 *length_return *= *format_return / 32;
5485 *format_return = 32;
5493 /* note: when called from menu all parameters are NULL, so no clue what the
5494 * Widget which was clicked on was, or what the click event was
5496 void CopyPositionProc(w, event, prms, nprms)
5503 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5504 * have a notion of a position that is selected but not copied.
5505 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5507 if(gameMode == EditPosition) EditPositionDone(TRUE);
5508 if (selected_fen_position) free(selected_fen_position);
5509 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5510 if (!selected_fen_position) return;
5511 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5513 SendPositionSelection,
5514 NULL/* lose_ownership_proc */ ,
5515 NULL/* transfer_done_proc */);
5516 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5518 SendPositionSelection,
5519 NULL/* lose_ownership_proc */ ,
5520 NULL/* transfer_done_proc */);
5523 /* function called when the data to Paste is ready */
5525 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5526 Atom *type, XtPointer value, unsigned long *len, int *format)
5529 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5530 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5531 EditPositionPasteFEN(fenstr);
5535 /* called when Paste Position button is pressed,
5536 * all parameters will be NULL */
5537 void PastePositionProc(w, event, prms, nprms)
5543 XtGetSelectionValue(menuBarWidget,
5544 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5545 /* (XtSelectionCallbackProc) */ PastePositionCB,
5546 NULL, /* client_data passed to PastePositionCB */
5548 /* better to use the time field from the event that triggered the
5549 * call to this function, but that isn't trivial to get
5557 SendGameSelection(Widget w, Atom *selection, Atom *target,
5558 Atom *type_return, XtPointer *value_return,
5559 unsigned long *length_return, int *format_return)
5561 char *selection_tmp;
5563 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5564 FILE* f = fopen(gameCopyFilename, "r");
5567 if (f == NULL) return False;
5571 selection_tmp = XtMalloc(len + 1);
5572 count = fread(selection_tmp, 1, len, f);
5575 XtFree(selection_tmp);
5578 selection_tmp[len] = NULLCHAR;
5579 *value_return = selection_tmp;
5580 *length_return = len;
5581 *type_return = *target;
5582 *format_return = 8; /* bits per byte */
5584 } else if (*target == XA_TARGETS(xDisplay)) {
5585 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5586 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5587 targets_tmp[1] = XA_STRING;
5588 *value_return = targets_tmp;
5589 *type_return = XA_ATOM;
5591 *format_return = 8 * sizeof(Atom);
5592 if (*format_return > 32) {
5593 *length_return *= *format_return / 32;
5594 *format_return = 32;
5602 void CopySomething()
5607 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5608 * have a notion of a game that is selected but not copied.
5609 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5611 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5614 NULL/* lose_ownership_proc */ ,
5615 NULL/* transfer_done_proc */);
5616 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5619 NULL/* lose_ownership_proc */ ,
5620 NULL/* transfer_done_proc */);
5623 /* note: when called from menu all parameters are NULL, so no clue what the
5624 * Widget which was clicked on was, or what the click event was
5626 void CopyGameProc(w, event, prms, nprms)
5634 ret = SaveGameToFile(gameCopyFilename, FALSE);
5640 void CopyGameListProc(w, event, prms, nprms)
5646 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5650 /* function called when the data to Paste is ready */
5652 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5653 Atom *type, XtPointer value, unsigned long *len, int *format)
5656 if (value == NULL || *len == 0) {
5657 return; /* nothing had been selected to copy */
5659 f = fopen(gamePasteFilename, "w");
5661 DisplayError(_("Can't open temp file"), errno);
5664 fwrite(value, 1, *len, f);
5667 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5670 /* called when Paste Game button is pressed,
5671 * all parameters will be NULL */
5672 void PasteGameProc(w, event, prms, nprms)
5678 XtGetSelectionValue(menuBarWidget,
5679 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5680 /* (XtSelectionCallbackProc) */ PasteGameCB,
5681 NULL, /* client_data passed to PasteGameCB */
5683 /* better to use the time field from the event that triggered the
5684 * call to this function, but that isn't trivial to get
5694 SaveGameProc(NULL, NULL, NULL, NULL);
5698 void QuitProc(w, event, prms, nprms)
5707 void PauseProc(w, event, prms, nprms)
5717 void MachineBlackProc(w, event, prms, nprms)
5723 MachineBlackEvent();
5726 void MachineWhiteProc(w, event, prms, nprms)
5732 MachineWhiteEvent();
5735 void AnalyzeModeProc(w, event, prms, nprms)
5743 if (!first.analysisSupport) {
5744 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5745 DisplayError(buf, 0);
5748 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5749 if (appData.icsActive) {
5750 if (gameMode != IcsObserving) {
5751 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5752 DisplayError(buf, 0);
5754 if (appData.icsEngineAnalyze) {
5755 if (appData.debugMode)
5756 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5762 /* if enable, use want disable icsEngineAnalyze */
5763 if (appData.icsEngineAnalyze) {
5768 appData.icsEngineAnalyze = TRUE;
5769 if (appData.debugMode)
5770 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5772 #ifndef OPTIONSDIALOG
5773 if (!appData.showThinking)
5774 ShowThinkingProc(w,event,prms,nprms);
5780 void AnalyzeFileProc(w, event, prms, nprms)
5786 if (!first.analysisSupport) {
5788 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5789 DisplayError(buf, 0);
5793 #ifndef OPTIONSDIALOG
5794 if (!appData.showThinking)
5795 ShowThinkingProc(w,event,prms,nprms);
5798 FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5799 AnalysisPeriodicEvent(1);
5802 void TwoMachinesProc(w, event, prms, nprms)
5811 void MatchProc(w, event, prms, nprms)
5817 if(gameMode != BeginningOfGame) { DisplayError(_("You can only start a match from the initial position."), 0); return; }
5818 appData.matchGames = appData.defaultMatchGames;
5822 void IcsClientProc(w, event, prms, nprms)
5831 void EditGameProc(w, event, prms, nprms)
5840 void EditPositionProc(w, event, prms, nprms)
5846 EditPositionEvent();
5849 void TrainingProc(w, event, prms, nprms)
5858 void EditCommentProc(w, event, prms, nprms)
5866 if (PopDown(1)) { // popdown succesful
5868 XtSetArg(args[j], XtNleftBitmap, None); j++;
5869 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
5870 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
5871 } else // was not up
5875 void IcsInputBoxProc(w, event, prms, nprms)
5881 if (!PopDown(4)) ICSInputBoxPopUp();
5884 void AcceptProc(w, event, prms, nprms)
5893 void DeclineProc(w, event, prms, nprms)
5902 void RematchProc(w, event, prms, nprms)
5911 void CallFlagProc(w, event, prms, nprms)
5920 void DrawProc(w, event, prms, nprms)
5929 void AbortProc(w, event, prms, nprms)
5938 void AdjournProc(w, event, prms, nprms)
5947 void ResignProc(w, event, prms, nprms)
5956 void AdjuWhiteProc(w, event, prms, nprms)
5962 UserAdjudicationEvent(+1);
5965 void AdjuBlackProc(w, event, prms, nprms)
5971 UserAdjudicationEvent(-1);
5974 void AdjuDrawProc(w, event, prms, nprms)
5980 UserAdjudicationEvent(0);
5983 void EnterKeyProc(w, event, prms, nprms)
5989 if (shellUp[4] == True)
5993 void UpKeyProc(w, event, prms, nprms)
5998 { // [HGM] input: let up-arrow recall previous line from history
6005 if (!shellUp[4]) return;
6006 edit = boxOptions[0].handle;
6008 XtSetArg(args[j], XtNstring, &val); j++;
6009 XtGetValues(edit, args, j);
6010 val = PrevInHistory(val);
6011 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6012 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6014 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6015 XawTextReplace(edit, 0, 0, &t);
6016 XawTextSetInsertionPoint(edit, 9999);
6020 void DownKeyProc(w, event, prms, nprms)
6025 { // [HGM] input: let down-arrow recall next line from history
6030 if (!shellUp[4]) return;
6031 edit = boxOptions[0].handle;
6032 val = NextInHistory();
6033 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6034 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6036 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6037 XawTextReplace(edit, 0, 0, &t);
6038 XawTextSetInsertionPoint(edit, 9999);
6042 void StopObservingProc(w, event, prms, nprms)
6048 StopObservingEvent();
6051 void StopExaminingProc(w, event, prms, nprms)
6057 StopExaminingEvent();
6060 void UploadProc(w, event, prms, nprms)
6070 void ForwardProc(w, event, prms, nprms)
6080 void BackwardProc(w, event, prms, nprms)
6089 void ToStartProc(w, event, prms, nprms)
6098 void ToEndProc(w, event, prms, nprms)
6107 void RevertProc(w, event, prms, nprms)
6116 void AnnotateProc(w, event, prms, nprms)
6125 void TruncateGameProc(w, event, prms, nprms)
6131 TruncateGameEvent();
6133 void RetractMoveProc(w, event, prms, nprms)
6142 void MoveNowProc(w, event, prms, nprms)
6151 void FlipViewProc(w, event, prms, nprms)
6157 flipView = !flipView;
6158 DrawPosition(True, NULL);
6161 void PonderNextMoveProc(w, event, prms, nprms)
6169 PonderNextMoveEvent(!appData.ponderNextMove);
6170 #ifndef OPTIONSDIALOG
6171 if (appData.ponderNextMove) {
6172 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6174 XtSetArg(args[0], XtNleftBitmap, None);
6176 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6181 #ifndef OPTIONSDIALOG
6182 void AlwaysQueenProc(w, event, prms, nprms)
6190 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6192 if (appData.alwaysPromoteToQueen) {
6193 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6195 XtSetArg(args[0], XtNleftBitmap, None);
6197 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6201 void AnimateDraggingProc(w, event, prms, nprms)
6209 appData.animateDragging = !appData.animateDragging;
6211 if (appData.animateDragging) {
6212 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6215 XtSetArg(args[0], XtNleftBitmap, None);
6217 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6221 void AnimateMovingProc(w, event, prms, nprms)
6229 appData.animate = !appData.animate;
6231 if (appData.animate) {
6232 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6235 XtSetArg(args[0], XtNleftBitmap, None);
6237 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6241 void AutoflagProc(w, event, prms, nprms)
6249 appData.autoCallFlag = !appData.autoCallFlag;
6251 if (appData.autoCallFlag) {
6252 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6254 XtSetArg(args[0], XtNleftBitmap, None);
6256 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6260 void AutoflipProc(w, event, prms, nprms)
6268 appData.autoFlipView = !appData.autoFlipView;
6270 if (appData.autoFlipView) {
6271 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6273 XtSetArg(args[0], XtNleftBitmap, None);
6275 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6279 void BlindfoldProc(w, event, prms, nprms)
6287 appData.blindfold = !appData.blindfold;
6289 if (appData.blindfold) {
6290 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6292 XtSetArg(args[0], XtNleftBitmap, None);
6294 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6297 DrawPosition(True, NULL);
6300 void TestLegalityProc(w, event, prms, nprms)
6308 appData.testLegality = !appData.testLegality;
6310 if (appData.testLegality) {
6311 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6313 XtSetArg(args[0], XtNleftBitmap, None);
6315 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6320 void FlashMovesProc(w, event, prms, nprms)
6328 if (appData.flashCount == 0) {
6329 appData.flashCount = 3;
6331 appData.flashCount = -appData.flashCount;
6334 if (appData.flashCount > 0) {
6335 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6337 XtSetArg(args[0], XtNleftBitmap, None);
6339 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6344 void HighlightDraggingProc(w, event, prms, nprms)
6352 appData.highlightDragging = !appData.highlightDragging;
6354 if (appData.highlightDragging) {
6355 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6357 XtSetArg(args[0], XtNleftBitmap, None);
6359 XtSetValues(XtNameToWidget(menuBarWidget,
6360 "menuOptions.Highlight Dragging"), args, 1);
6364 void HighlightLastMoveProc(w, event, prms, nprms)
6372 appData.highlightLastMove = !appData.highlightLastMove;
6374 if (appData.highlightLastMove) {
6375 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6377 XtSetArg(args[0], XtNleftBitmap, None);
6379 XtSetValues(XtNameToWidget(menuBarWidget,
6380 "menuOptions.Highlight Last Move"), args, 1);
6383 void HighlightArrowProc(w, event, prms, nprms)
6391 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6393 if (appData.highlightMoveWithArrow) {
6394 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6396 XtSetArg(args[0], XtNleftBitmap, None);
6398 XtSetValues(XtNameToWidget(menuBarWidget,
6399 "menuOptions.Arrow"), args, 1);
6403 void IcsAlarmProc(w, event, prms, nprms)
6411 appData.icsAlarm = !appData.icsAlarm;
6413 if (appData.icsAlarm) {
6414 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6416 XtSetArg(args[0], XtNleftBitmap, None);
6418 XtSetValues(XtNameToWidget(menuBarWidget,
6419 "menuOptions.ICS Alarm"), args, 1);
6423 void MoveSoundProc(w, event, prms, nprms)
6431 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6433 if (appData.ringBellAfterMoves) {
6434 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6436 XtSetArg(args[0], XtNleftBitmap, None);
6438 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6442 void OneClickProc(w, event, prms, nprms)
6450 appData.oneClick = !appData.oneClick;
6452 if (appData.oneClick) {
6453 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6455 XtSetArg(args[0], XtNleftBitmap, None);
6457 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6461 void PeriodicUpdatesProc(w, event, prms, nprms)
6469 PeriodicUpdatesEvent(!appData.periodicUpdates);
6471 if (appData.periodicUpdates) {
6472 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6474 XtSetArg(args[0], XtNleftBitmap, None);
6476 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6480 void PopupExitMessageProc(w, event, prms, nprms)
6488 appData.popupExitMessage = !appData.popupExitMessage;
6490 if (appData.popupExitMessage) {
6491 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6493 XtSetArg(args[0], XtNleftBitmap, None);
6495 XtSetValues(XtNameToWidget(menuBarWidget,
6496 "menuOptions.Popup Exit Message"), args, 1);
6499 void PopupMoveErrorsProc(w, event, prms, nprms)
6507 appData.popupMoveErrors = !appData.popupMoveErrors;
6509 if (appData.popupMoveErrors) {
6510 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6512 XtSetArg(args[0], XtNleftBitmap, None);
6514 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6519 void PremoveProc(w, event, prms, nprms)
6527 appData.premove = !appData.premove;
6529 if (appData.premove) {
6530 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6532 XtSetArg(args[0], XtNleftBitmap, None);
6534 XtSetValues(XtNameToWidget(menuBarWidget,
6535 "menuOptions.Premove"), args, 1);
6539 void ShowCoordsProc(w, event, prms, nprms)
6547 appData.showCoords = !appData.showCoords;
6549 if (appData.showCoords) {
6550 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6552 XtSetArg(args[0], XtNleftBitmap, None);
6554 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6557 DrawPosition(True, NULL);
6560 void ShowThinkingProc(w, event, prms, nprms)
6566 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6567 ShowThinkingEvent();
6570 void HideThinkingProc(w, event, prms, nprms)
6578 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6579 ShowThinkingEvent();
6581 if (appData.hideThinkingFromHuman) {
6582 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6584 XtSetArg(args[0], XtNleftBitmap, None);
6586 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6591 void SaveOnExitProc(w, event, prms, nprms)
6599 saveSettingsOnExit = !saveSettingsOnExit;
6601 if (saveSettingsOnExit) {
6602 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6604 XtSetArg(args[0], XtNleftBitmap, None);
6606 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6610 void SaveSettingsProc(w, event, prms, nprms)
6616 SaveSettings(settingsFileName);
6619 void InfoProc(w, event, prms, nprms)
6626 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6631 void ManProc(w, event, prms, nprms)
6639 if (nprms && *nprms > 0)
6643 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6647 void HintProc(w, event, prms, nprms)
6656 void BookProc(w, event, prms, nprms)
6665 void AboutProc(w, event, prms, nprms)
6673 char *zippy = " (with Zippy code)";
6677 snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
6678 programVersion, zippy,
6679 "Copyright 1991 Digital Equipment Corporation",
6680 "Enhancements Copyright 1992-2009 Free Software Foundation",
6681 "Enhancements Copyright 2005 Alessandro Scotti",
6682 PACKAGE, " is free software and carries NO WARRANTY;",
6683 "see the file COPYING for more information.");
6684 ErrorPopUp(_("About XBoard"), buf, FALSE);
6687 void DebugProc(w, event, prms, nprms)
6693 appData.debugMode = !appData.debugMode;
6696 void AboutGameProc(w, event, prms, nprms)
6705 void NothingProc(w, event, prms, nprms)
6714 void Iconify(w, event, prms, nprms)
6723 XtSetArg(args[0], XtNiconic, True);
6724 XtSetValues(shellWidget, args, 1);
6727 void DisplayMessage(message, extMessage)
6728 char *message, *extMessage;
6730 /* display a message in the message widget */
6739 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6744 message = extMessage;
6748 /* need to test if messageWidget already exists, since this function
6749 can also be called during the startup, if for example a Xresource
6750 is not set up correctly */
6753 XtSetArg(arg, XtNlabel, message);
6754 XtSetValues(messageWidget, &arg, 1);
6760 void DisplayTitle(text)
6765 char title[MSG_SIZ];
6768 if (text == NULL) text = "";
6770 if (appData.titleInWindow) {
6772 XtSetArg(args[i], XtNlabel, text); i++;
6773 XtSetValues(titleWidget, args, i);
6776 if (*text != NULLCHAR) {
6777 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6778 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6779 } else if (appData.icsActive) {
6780 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6781 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6782 } else if (appData.cmailGameName[0] != NULLCHAR) {
6783 snprintf(icon, sizeof(icon), "%s", "CMail");
6784 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6786 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6787 } else if (gameInfo.variant == VariantGothic) {
6788 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6789 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6792 } else if (gameInfo.variant == VariantFalcon) {
6793 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6794 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6796 } else if (appData.noChessProgram) {
6797 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6798 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6800 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6801 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6804 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6805 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6806 XtSetValues(shellWidget, args, i);
6811 DisplayError(message, error)
6818 if (appData.debugMode || appData.matchMode) {
6819 fprintf(stderr, "%s: %s\n", programName, message);
6822 if (appData.debugMode || appData.matchMode) {
6823 fprintf(stderr, "%s: %s: %s\n",
6824 programName, message, strerror(error));
6826 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6829 ErrorPopUp(_("Error"), message, FALSE);
6833 void DisplayMoveError(message)
6838 DrawPosition(FALSE, NULL);
6839 if (appData.debugMode || appData.matchMode) {
6840 fprintf(stderr, "%s: %s\n", programName, message);
6842 if (appData.popupMoveErrors) {
6843 ErrorPopUp(_("Error"), message, FALSE);
6845 DisplayMessage(message, "");
6850 void DisplayFatalError(message, error, status)
6856 errorExitStatus = status;
6858 fprintf(stderr, "%s: %s\n", programName, message);
6860 fprintf(stderr, "%s: %s: %s\n",
6861 programName, message, strerror(error));
6862 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6865 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6866 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6872 void DisplayInformation(message)
6876 ErrorPopUp(_("Information"), message, TRUE);
6879 void DisplayNote(message)
6883 ErrorPopUp(_("Note"), message, FALSE);
6887 NullXErrorCheck(dpy, error_event)
6889 XErrorEvent *error_event;
6894 void DisplayIcsInteractionTitle(message)
6897 if (oldICSInteractionTitle == NULL) {
6898 /* Magic to find the old window title, adapted from vim */
6899 char *wina = getenv("WINDOWID");
6901 Window win = (Window) atoi(wina);
6902 Window root, parent, *children;
6903 unsigned int nchildren;
6904 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6906 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6907 if (!XQueryTree(xDisplay, win, &root, &parent,
6908 &children, &nchildren)) break;
6909 if (children) XFree((void *)children);
6910 if (parent == root || parent == 0) break;
6913 XSetErrorHandler(oldHandler);
6915 if (oldICSInteractionTitle == NULL) {
6916 oldICSInteractionTitle = "xterm";
6919 printf("\033]0;%s\007", message);
6923 char pendingReplyPrefix[MSG_SIZ];
6924 ProcRef pendingReplyPR;
6926 void AskQuestionProc(w, event, prms, nprms)
6933 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
6937 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6940 void AskQuestionPopDown()
6942 if (!askQuestionUp) return;
6943 XtPopdown(askQuestionShell);
6944 XtDestroyWidget(askQuestionShell);
6945 askQuestionUp = False;
6948 void AskQuestionReplyAction(w, event, prms, nprms)
6958 reply = XawDialogGetValueString(w = XtParent(w));
6959 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
6960 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
6961 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
6962 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
6963 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6964 AskQuestionPopDown();
6966 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
6969 void AskQuestionCallback(w, client_data, call_data)
6971 XtPointer client_data, call_data;
6976 XtSetArg(args[0], XtNlabel, &name);
6977 XtGetValues(w, args, 1);
6979 if (strcmp(name, _("cancel")) == 0) {
6980 AskQuestionPopDown();
6982 AskQuestionReplyAction(w, NULL, NULL, NULL);
6986 void AskQuestion(title, question, replyPrefix, pr)
6987 char *title, *question, *replyPrefix;
6991 Widget popup, layout, dialog, edit;
6997 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
6998 pendingReplyPR = pr;
7001 XtSetArg(args[i], XtNresizable, True); i++;
7002 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7003 askQuestionShell = popup =
7004 XtCreatePopupShell(title, transientShellWidgetClass,
7005 shellWidget, args, i);
7008 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7009 layoutArgs, XtNumber(layoutArgs));
7012 XtSetArg(args[i], XtNlabel, question); i++;
7013 XtSetArg(args[i], XtNvalue, ""); i++;
7014 XtSetArg(args[i], XtNborderWidth, 0); i++;
7015 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7018 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7019 (XtPointer) dialog);
7020 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7021 (XtPointer) dialog);
7023 XtRealizeWidget(popup);
7024 CatchDeleteWindow(popup, "AskQuestionPopDown");
7026 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7027 &x, &y, &win_x, &win_y, &mask);
7029 XtSetArg(args[0], XtNx, x - 10);
7030 XtSetArg(args[1], XtNy, y - 30);
7031 XtSetValues(popup, args, 2);
7033 XtPopup(popup, XtGrabExclusive);
7034 askQuestionUp = True;
7036 edit = XtNameToWidget(dialog, "*value");
7037 XtSetKeyboardFocus(popup, edit);
7045 if (*name == NULLCHAR) {
7047 } else if (strcmp(name, "$") == 0) {
7048 putc(BELLCHAR, stderr);
7051 char *prefix = "", *sep = "";
7052 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7053 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7061 PlaySound(appData.soundMove);
7067 PlaySound(appData.soundIcsWin);
7073 PlaySound(appData.soundIcsLoss);
7079 PlaySound(appData.soundIcsDraw);
7083 PlayIcsUnfinishedSound()
7085 PlaySound(appData.soundIcsUnfinished);
7091 PlaySound(appData.soundIcsAlarm);
7097 system("stty echo");
7103 system("stty -echo");
7107 Colorize(cc, continuation)
7112 int count, outCount, error;
7114 if (textColors[(int)cc].bg > 0) {
7115 if (textColors[(int)cc].fg > 0) {
7116 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7117 textColors[(int)cc].fg, textColors[(int)cc].bg);
7119 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7120 textColors[(int)cc].bg);
7123 if (textColors[(int)cc].fg > 0) {
7124 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7125 textColors[(int)cc].fg);
7127 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7130 count = strlen(buf);
7131 outCount = OutputToProcess(NoProc, buf, count, &error);
7132 if (outCount < count) {
7133 DisplayFatalError(_("Error writing to display"), error, 1);
7136 if (continuation) return;
7139 PlaySound(appData.soundShout);
7142 PlaySound(appData.soundSShout);
7145 PlaySound(appData.soundChannel1);
7148 PlaySound(appData.soundChannel);
7151 PlaySound(appData.soundKibitz);
7154 PlaySound(appData.soundTell);
7156 case ColorChallenge:
7157 PlaySound(appData.soundChallenge);
7160 PlaySound(appData.soundRequest);
7163 PlaySound(appData.soundSeek);
7174 return getpwuid(getuid())->pw_name;
7178 ExpandPathName(path)
7181 static char static_buf[4*MSG_SIZ];
7182 char *d, *s, buf[4*MSG_SIZ];
7188 while (*s && isspace(*s))
7197 if (*(s+1) == '/') {
7198 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7202 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7203 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7204 pwd = getpwnam(buf);
7207 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7211 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7212 strcat(d, strchr(s+1, '/'));
7216 safeStrCpy(d, s, 4*MSG_SIZ );
7223 static char host_name[MSG_SIZ];
7225 #if HAVE_GETHOSTNAME
7226 gethostname(host_name, MSG_SIZ);
7228 #else /* not HAVE_GETHOSTNAME */
7229 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7230 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7232 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7234 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7235 #endif /* not HAVE_GETHOSTNAME */
7238 XtIntervalId delayedEventTimerXID = 0;
7239 DelayedEventCallback delayedEventCallback = 0;
7244 delayedEventTimerXID = 0;
7245 delayedEventCallback();
7249 ScheduleDelayedEvent(cb, millisec)
7250 DelayedEventCallback cb; long millisec;
7252 if(delayedEventTimerXID && delayedEventCallback == cb)
7253 // [HGM] alive: replace, rather than add or flush identical event
7254 XtRemoveTimeOut(delayedEventTimerXID);
7255 delayedEventCallback = cb;
7256 delayedEventTimerXID =
7257 XtAppAddTimeOut(appContext, millisec,
7258 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7261 DelayedEventCallback
7264 if (delayedEventTimerXID) {
7265 return delayedEventCallback;
7272 CancelDelayedEvent()
7274 if (delayedEventTimerXID) {
7275 XtRemoveTimeOut(delayedEventTimerXID);
7276 delayedEventTimerXID = 0;
7280 XtIntervalId loadGameTimerXID = 0;
7282 int LoadGameTimerRunning()
7284 return loadGameTimerXID != 0;
7287 int StopLoadGameTimer()
7289 if (loadGameTimerXID != 0) {
7290 XtRemoveTimeOut(loadGameTimerXID);
7291 loadGameTimerXID = 0;
7299 LoadGameTimerCallback(arg, id)
7303 loadGameTimerXID = 0;
7308 StartLoadGameTimer(millisec)
7312 XtAppAddTimeOut(appContext, millisec,
7313 (XtTimerCallbackProc) LoadGameTimerCallback,
7317 XtIntervalId analysisClockXID = 0;
7320 AnalysisClockCallback(arg, id)
7324 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7325 || appData.icsEngineAnalyze) { // [DM]
7326 AnalysisPeriodicEvent(0);
7327 StartAnalysisClock();
7332 StartAnalysisClock()
7335 XtAppAddTimeOut(appContext, 2000,
7336 (XtTimerCallbackProc) AnalysisClockCallback,
7340 XtIntervalId clockTimerXID = 0;
7342 int ClockTimerRunning()
7344 return clockTimerXID != 0;
7347 int StopClockTimer()
7349 if (clockTimerXID != 0) {
7350 XtRemoveTimeOut(clockTimerXID);
7359 ClockTimerCallback(arg, id)
7368 StartClockTimer(millisec)
7372 XtAppAddTimeOut(appContext, millisec,
7373 (XtTimerCallbackProc) ClockTimerCallback,
7378 DisplayTimerLabel(w, color, timer, highlight)
7387 /* check for low time warning */
7388 Pixel foregroundOrWarningColor = timerForegroundPixel;
7391 appData.lowTimeWarning &&
7392 (timer / 1000) < appData.icsAlarmTime)
7393 foregroundOrWarningColor = lowTimeWarningColor;
7395 if (appData.clockMode) {
7396 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7397 XtSetArg(args[0], XtNlabel, buf);
7399 snprintf(buf, MSG_SIZ, "%s ", color);
7400 XtSetArg(args[0], XtNlabel, buf);
7405 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7406 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7408 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7409 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7412 XtSetValues(w, args, 3);
7416 DisplayWhiteClock(timeRemaining, highlight)
7422 if(appData.noGUI) return;
7423 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7424 if (highlight && iconPixmap == bIconPixmap) {
7425 iconPixmap = wIconPixmap;
7426 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7427 XtSetValues(shellWidget, args, 1);
7432 DisplayBlackClock(timeRemaining, highlight)
7438 if(appData.noGUI) return;
7439 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7440 if (highlight && iconPixmap == wIconPixmap) {
7441 iconPixmap = bIconPixmap;
7442 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7443 XtSetValues(shellWidget, args, 1);
7461 int StartChildProcess(cmdLine, dir, pr)
7468 int to_prog[2], from_prog[2];
7472 if (appData.debugMode) {
7473 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7476 /* We do NOT feed the cmdLine to the shell; we just
7477 parse it into blank-separated arguments in the
7478 most simple-minded way possible.
7481 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7484 while(*p == ' ') p++;
7486 if(*p == '"' || *p == '\'')
7487 p = strchr(++argv[i-1], *p);
7488 else p = strchr(p, ' ');
7489 if (p == NULL) break;
7494 SetUpChildIO(to_prog, from_prog);
7496 if ((pid = fork()) == 0) {
7498 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7499 close(to_prog[1]); // first close the unused pipe ends
7500 close(from_prog[0]);
7501 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7502 dup2(from_prog[1], 1);
7503 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7504 close(from_prog[1]); // and closing again loses one of the pipes!
7505 if(fileno(stderr) >= 2) // better safe than sorry...
7506 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7508 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7513 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7515 execvp(argv[0], argv);
7517 /* If we get here, exec failed */
7522 /* Parent process */
7524 close(from_prog[1]);
7526 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7529 cp->fdFrom = from_prog[0];
7530 cp->fdTo = to_prog[1];
7535 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7536 static RETSIGTYPE AlarmCallBack(int n)
7542 DestroyChildProcess(pr, signalType)
7546 ChildProc *cp = (ChildProc *) pr;
7548 if (cp->kind != CPReal) return;
7550 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7551 signal(SIGALRM, AlarmCallBack);
7553 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7554 kill(cp->pid, SIGKILL); // kill it forcefully
7555 wait((int *) 0); // and wait again
7559 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7561 /* Process is exiting either because of the kill or because of
7562 a quit command sent by the backend; either way, wait for it to die.
7571 InterruptChildProcess(pr)
7574 ChildProc *cp = (ChildProc *) pr;
7576 if (cp->kind != CPReal) return;
7577 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7580 int OpenTelnet(host, port, pr)
7585 char cmdLine[MSG_SIZ];
7587 if (port[0] == NULLCHAR) {
7588 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7590 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7592 return StartChildProcess(cmdLine, "", pr);
7595 int OpenTCP(host, port, pr)
7601 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7602 #else /* !OMIT_SOCKETS */
7604 struct sockaddr_in sa;
7606 unsigned short uport;
7609 if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
7613 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7614 sa.sin_family = AF_INET;
7615 sa.sin_addr.s_addr = INADDR_ANY;
7616 uport = (unsigned short) 0;
7617 sa.sin_port = htons(uport);
7618 if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
7622 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7623 if (!(hp = gethostbyname(host))) {
7625 if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
7626 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
7627 hp->h_addrtype = AF_INET;
7629 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
7630 hp->h_addr_list[0] = (char *) malloc(4);
7631 hp->h_addr_list[0][0] = b0;
7632 hp->h_addr_list[0][1] = b1;
7633 hp->h_addr_list[0][2] = b2;
7634 hp->h_addr_list[0][3] = b3;
7639 sa.sin_family = hp->h_addrtype;
7640 uport = (unsigned short) atoi(port);
7641 sa.sin_port = htons(uport);
7642 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
7644 if (connect(s, (struct sockaddr *) &sa,
7645 sizeof(struct sockaddr_in)) < 0) {
7649 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7656 #endif /* !OMIT_SOCKETS */
7661 int OpenCommPort(name, pr)
7668 fd = open(name, 2, 0);
7669 if (fd < 0) return errno;
7671 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7681 int OpenLoopback(pr)
7687 SetUpChildIO(to, from);
7689 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7692 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7699 int OpenRcmd(host, user, cmd, pr)
7700 char *host, *user, *cmd;
7703 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7707 #define INPUT_SOURCE_BUF_SIZE 8192
7716 char buf[INPUT_SOURCE_BUF_SIZE];
7721 DoInputCallback(closure, source, xid)
7726 InputSource *is = (InputSource *) closure;
7731 if (is->lineByLine) {
7732 count = read(is->fd, is->unused,
7733 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7735 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7738 is->unused += count;
7740 while (p < is->unused) {
7741 q = memchr(p, '\n', is->unused - p);
7742 if (q == NULL) break;
7744 (is->func)(is, is->closure, p, q - p, 0);
7748 while (p < is->unused) {
7753 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7758 (is->func)(is, is->closure, is->buf, count, error);
7762 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7769 ChildProc *cp = (ChildProc *) pr;
7771 is = (InputSource *) calloc(1, sizeof(InputSource));
7772 is->lineByLine = lineByLine;
7776 is->fd = fileno(stdin);
7778 is->kind = cp->kind;
7779 is->fd = cp->fdFrom;
7782 is->unused = is->buf;
7785 is->xid = XtAppAddInput(appContext, is->fd,
7786 (XtPointer) (XtInputReadMask),
7787 (XtInputCallbackProc) DoInputCallback,
7789 is->closure = closure;
7790 return (InputSourceRef) is;
7794 RemoveInputSource(isr)
7797 InputSource *is = (InputSource *) isr;
7799 if (is->xid == 0) return;
7800 XtRemoveInput(is->xid);
7804 int OutputToProcess(pr, message, count, outError)
7810 static int line = 0;
7811 ChildProc *cp = (ChildProc *) pr;
7816 if (appData.noJoin || !appData.useInternalWrap)
7817 outCount = fwrite(message, 1, count, stdout);
7820 int width = get_term_width();
7821 int len = wrap(NULL, message, count, width, &line);
7822 char *msg = malloc(len);
7826 outCount = fwrite(message, 1, count, stdout);
7829 dbgchk = wrap(msg, message, count, width, &line);
7830 if (dbgchk != len && appData.debugMode)
7831 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7832 outCount = fwrite(msg, 1, dbgchk, stdout);
7838 outCount = write(cp->fdTo, message, count);
7848 /* Output message to process, with "ms" milliseconds of delay
7849 between each character. This is needed when sending the logon
7850 script to ICC, which for some reason doesn't like the
7851 instantaneous send. */
7852 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
7859 ChildProc *cp = (ChildProc *) pr;
7864 r = write(cp->fdTo, message++, 1);
7877 /**** Animation code by Hugh Fisher, DCS, ANU.
7879 Known problem: if a window overlapping the board is
7880 moved away while a piece is being animated underneath,
7881 the newly exposed area won't be updated properly.
7882 I can live with this.
7884 Known problem: if you look carefully at the animation
7885 of pieces in mono mode, they are being drawn as solid
7886 shapes without interior detail while moving. Fixing
7887 this would be a major complication for minimal return.
7890 /* Masks for XPM pieces. Black and white pieces can have
7891 different shapes, but in the interest of retaining my
7892 sanity pieces must have the same outline on both light
7893 and dark squares, and all pieces must use the same
7894 background square colors/images. */
7896 static int xpmDone = 0;
7899 CreateAnimMasks (pieceDepth)
7906 unsigned long plane;
7909 /* Need a bitmap just to get a GC with right depth */
7910 buf = XCreatePixmap(xDisplay, xBoardWindow,
7912 values.foreground = 1;
7913 values.background = 0;
7914 /* Don't use XtGetGC, not read only */
7915 maskGC = XCreateGC(xDisplay, buf,
7916 GCForeground | GCBackground, &values);
7917 XFreePixmap(xDisplay, buf);
7919 buf = XCreatePixmap(xDisplay, xBoardWindow,
7920 squareSize, squareSize, pieceDepth);
7921 values.foreground = XBlackPixel(xDisplay, xScreen);
7922 values.background = XWhitePixel(xDisplay, xScreen);
7923 bufGC = XCreateGC(xDisplay, buf,
7924 GCForeground | GCBackground, &values);
7926 for (piece = WhitePawn; piece <= BlackKing; piece++) {
7927 /* Begin with empty mask */
7928 if(!xpmDone) // [HGM] pieces: keep using existing
7929 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7930 squareSize, squareSize, 1);
7931 XSetFunction(xDisplay, maskGC, GXclear);
7932 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7933 0, 0, squareSize, squareSize);
7935 /* Take a copy of the piece */
7940 XSetFunction(xDisplay, bufGC, GXcopy);
7941 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
7943 0, 0, squareSize, squareSize, 0, 0);
7945 /* XOR the background (light) over the piece */
7946 XSetFunction(xDisplay, bufGC, GXxor);
7948 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7949 0, 0, squareSize, squareSize, 0, 0);
7951 XSetForeground(xDisplay, bufGC, lightSquareColor);
7952 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7955 /* We now have an inverted piece image with the background
7956 erased. Construct mask by just selecting all the non-zero
7957 pixels - no need to reconstruct the original image. */
7958 XSetFunction(xDisplay, maskGC, GXor);
7960 /* Might be quicker to download an XImage and create bitmap
7961 data from it rather than this N copies per piece, but it
7962 only takes a fraction of a second and there is a much
7963 longer delay for loading the pieces. */
7964 for (n = 0; n < pieceDepth; n ++) {
7965 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7966 0, 0, squareSize, squareSize,
7972 XFreePixmap(xDisplay, buf);
7973 XFreeGC(xDisplay, bufGC);
7974 XFreeGC(xDisplay, maskGC);
7978 InitAnimState (anim, info)
7980 XWindowAttributes * info;
7985 /* Each buffer is square size, same depth as window */
7986 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
7987 squareSize, squareSize, info->depth);
7988 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
7989 squareSize, squareSize, info->depth);
7991 /* Create a plain GC for blitting */
7992 mask = GCForeground | GCBackground | GCFunction |
7993 GCPlaneMask | GCGraphicsExposures;
7994 values.foreground = XBlackPixel(xDisplay, xScreen);
7995 values.background = XWhitePixel(xDisplay, xScreen);
7996 values.function = GXcopy;
7997 values.plane_mask = AllPlanes;
7998 values.graphics_exposures = False;
7999 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8001 /* Piece will be copied from an existing context at
8002 the start of each new animation/drag. */
8003 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8005 /* Outline will be a read-only copy of an existing */
8006 anim->outlineGC = None;
8012 XWindowAttributes info;
8014 if (xpmDone && gameInfo.variant == oldVariant) return;
8015 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
8016 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8018 InitAnimState(&game, &info);
8019 InitAnimState(&player, &info);
8021 /* For XPM pieces, we need bitmaps to use as masks. */
8023 CreateAnimMasks(info.depth), xpmDone = 1;
8028 static Boolean frameWaiting;
8030 static RETSIGTYPE FrameAlarm (sig)
8033 frameWaiting = False;
8034 /* In case System-V style signals. Needed?? */
8035 signal(SIGALRM, FrameAlarm);
8042 struct itimerval delay;
8044 XSync(xDisplay, False);
8047 frameWaiting = True;
8048 signal(SIGALRM, FrameAlarm);
8049 delay.it_interval.tv_sec =
8050 delay.it_value.tv_sec = time / 1000;
8051 delay.it_interval.tv_usec =
8052 delay.it_value.tv_usec = (time % 1000) * 1000;
8053 setitimer(ITIMER_REAL, &delay, NULL);
8054 while (frameWaiting) pause();
8055 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8056 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8057 setitimer(ITIMER_REAL, &delay, NULL);
8067 XSync(xDisplay, False);
8069 usleep(time * 1000);
8074 /* Convert board position to corner of screen rect and color */
8077 ScreenSquare(column, row, pt, color)
8078 int column; int row; XPoint * pt; int * color;
8081 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8082 pt->y = lineGap + row * (squareSize + lineGap);
8084 pt->x = lineGap + column * (squareSize + lineGap);
8085 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8087 *color = SquareColor(row, column);
8090 /* Convert window coords to square */
8093 BoardSquare(x, y, column, row)
8094 int x; int y; int * column; int * row;
8096 *column = EventToSquare(x, BOARD_WIDTH);
8097 if (flipView && *column >= 0)
8098 *column = BOARD_WIDTH - 1 - *column;
8099 *row = EventToSquare(y, BOARD_HEIGHT);
8100 if (!flipView && *row >= 0)
8101 *row = BOARD_HEIGHT - 1 - *row;
8106 #undef Max /* just in case */
8108 #define Max(a, b) ((a) > (b) ? (a) : (b))
8109 #define Min(a, b) ((a) < (b) ? (a) : (b))
8112 SetRect(rect, x, y, width, height)
8113 XRectangle * rect; int x; int y; int width; int height;
8117 rect->width = width;
8118 rect->height = height;
8121 /* Test if two frames overlap. If they do, return
8122 intersection rect within old and location of
8123 that rect within new. */
8126 Intersect(old, new, size, area, pt)
8127 XPoint * old; XPoint * new;
8128 int size; XRectangle * area; XPoint * pt;
8130 if (old->x > new->x + size || new->x > old->x + size ||
8131 old->y > new->y + size || new->y > old->y + size) {
8134 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8135 size - abs(old->x - new->x), size - abs(old->y - new->y));
8136 pt->x = Max(old->x - new->x, 0);
8137 pt->y = Max(old->y - new->y, 0);
8142 /* For two overlapping frames, return the rect(s)
8143 in the old that do not intersect with the new. */
8146 CalcUpdateRects(old, new, size, update, nUpdates)
8147 XPoint * old; XPoint * new; int size;
8148 XRectangle update[]; int * nUpdates;
8152 /* If old = new (shouldn't happen) then nothing to draw */
8153 if (old->x == new->x && old->y == new->y) {
8157 /* Work out what bits overlap. Since we know the rects
8158 are the same size we don't need a full intersect calc. */
8160 /* Top or bottom edge? */
8161 if (new->y > old->y) {
8162 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8164 } else if (old->y > new->y) {
8165 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8166 size, old->y - new->y);
8169 /* Left or right edge - don't overlap any update calculated above. */
8170 if (new->x > old->x) {
8171 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8172 new->x - old->x, size - abs(new->y - old->y));
8174 } else if (old->x > new->x) {
8175 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8176 old->x - new->x, size - abs(new->y - old->y));
8183 /* Generate a series of frame coords from start->mid->finish.
8184 The movement rate doubles until the half way point is
8185 reached, then halves back down to the final destination,
8186 which gives a nice slow in/out effect. The algorithmn
8187 may seem to generate too many intermediates for short
8188 moves, but remember that the purpose is to attract the
8189 viewers attention to the piece about to be moved and
8190 then to where it ends up. Too few frames would be less
8194 Tween(start, mid, finish, factor, frames, nFrames)
8195 XPoint * start; XPoint * mid;
8196 XPoint * finish; int factor;
8197 XPoint frames[]; int * nFrames;
8199 int fraction, n, count;
8203 /* Slow in, stepping 1/16th, then 1/8th, ... */
8205 for (n = 0; n < factor; n++)
8207 for (n = 0; n < factor; n++) {
8208 frames[count].x = start->x + (mid->x - start->x) / fraction;
8209 frames[count].y = start->y + (mid->y - start->y) / fraction;
8211 fraction = fraction / 2;
8215 frames[count] = *mid;
8218 /* Slow out, stepping 1/2, then 1/4, ... */
8220 for (n = 0; n < factor; n++) {
8221 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8222 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8224 fraction = fraction * 2;
8229 /* Draw a piece on the screen without disturbing what's there */
8232 SelectGCMask(piece, clip, outline, mask)
8233 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8237 /* Bitmap for piece being moved. */
8238 if (appData.monoMode) {
8239 *mask = *pieceToSolid(piece);
8240 } else if (useImages) {
8242 *mask = xpmMask[piece];
8244 *mask = ximMaskPm[piece];
8247 *mask = *pieceToSolid(piece);
8250 /* GC for piece being moved. Square color doesn't matter, but
8251 since it gets modified we make a copy of the original. */
8253 if (appData.monoMode)
8258 if (appData.monoMode)
8263 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8265 /* Outline only used in mono mode and is not modified */
8267 *outline = bwPieceGC;
8269 *outline = wbPieceGC;
8273 OverlayPiece(piece, clip, outline, dest)
8274 ChessSquare piece; GC clip; GC outline; Drawable dest;
8279 /* Draw solid rectangle which will be clipped to shape of piece */
8280 XFillRectangle(xDisplay, dest, clip,
8281 0, 0, squareSize, squareSize);
8282 if (appData.monoMode)
8283 /* Also draw outline in contrasting color for black
8284 on black / white on white cases */
8285 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8286 0, 0, squareSize, squareSize, 0, 0, 1);
8288 /* Copy the piece */
8293 if(appData.upsideDown && flipView) kind ^= 2;
8294 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8296 0, 0, squareSize, squareSize,
8301 /* Animate the movement of a single piece */
8304 BeginAnimation(anim, piece, startColor, start)
8312 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8313 /* The old buffer is initialised with the start square (empty) */
8314 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8315 anim->prevFrame = *start;
8317 /* The piece will be drawn using its own bitmap as a matte */
8318 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8319 XSetClipMask(xDisplay, anim->pieceGC, mask);
8323 AnimationFrame(anim, frame, piece)
8328 XRectangle updates[4];
8333 /* Save what we are about to draw into the new buffer */
8334 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8335 frame->x, frame->y, squareSize, squareSize,
8338 /* Erase bits of the previous frame */
8339 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8340 /* Where the new frame overlapped the previous,
8341 the contents in newBuf are wrong. */
8342 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8343 overlap.x, overlap.y,
8344 overlap.width, overlap.height,
8346 /* Repaint the areas in the old that don't overlap new */
8347 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8348 for (i = 0; i < count; i++)
8349 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8350 updates[i].x - anim->prevFrame.x,
8351 updates[i].y - anim->prevFrame.y,
8352 updates[i].width, updates[i].height,
8353 updates[i].x, updates[i].y);
8355 /* Easy when no overlap */
8356 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8357 0, 0, squareSize, squareSize,
8358 anim->prevFrame.x, anim->prevFrame.y);
8361 /* Save this frame for next time round */
8362 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8363 0, 0, squareSize, squareSize,
8365 anim->prevFrame = *frame;
8367 /* Draw piece over original screen contents, not current,
8368 and copy entire rect. Wipes out overlapping piece images. */
8369 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8370 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8371 0, 0, squareSize, squareSize,
8372 frame->x, frame->y);
8376 EndAnimation (anim, finish)
8380 XRectangle updates[4];
8385 /* The main code will redraw the final square, so we
8386 only need to erase the bits that don't overlap. */
8387 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8388 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8389 for (i = 0; i < count; i++)
8390 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8391 updates[i].x - anim->prevFrame.x,
8392 updates[i].y - anim->prevFrame.y,
8393 updates[i].width, updates[i].height,
8394 updates[i].x, updates[i].y);
8396 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8397 0, 0, squareSize, squareSize,
8398 anim->prevFrame.x, anim->prevFrame.y);
8403 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8405 ChessSquare piece; int startColor;
8406 XPoint * start; XPoint * finish;
8407 XPoint frames[]; int nFrames;
8411 BeginAnimation(anim, piece, startColor, start);
8412 for (n = 0; n < nFrames; n++) {
8413 AnimationFrame(anim, &(frames[n]), piece);
8414 FrameDelay(appData.animSpeed);
8416 EndAnimation(anim, finish);
8420 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8423 ChessSquare piece = board[fromY][toY];
8424 board[fromY][toY] = EmptySquare;
8425 DrawPosition(FALSE, board);
8427 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8428 y = lineGap + toY * (squareSize + lineGap);
8430 x = lineGap + toX * (squareSize + lineGap);
8431 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8433 for(i=1; i<4*kFactor; i++) {
8434 int r = squareSize * 9 * i/(20*kFactor - 5);
8435 XFillArc(xDisplay, xBoardWindow, highlineGC,
8436 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8437 FrameDelay(appData.animSpeed);
8439 board[fromY][toY] = piece;
8442 /* Main control logic for deciding what to animate and how */
8445 AnimateMove(board, fromX, fromY, toX, toY)
8454 XPoint start, finish, mid;
8455 XPoint frames[kFactor * 2 + 1];
8456 int nFrames, startColor, endColor;
8458 /* Are we animating? */
8459 if (!appData.animate || appData.blindfold)
8462 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8463 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8464 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8466 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8467 piece = board[fromY][fromX];
8468 if (piece >= EmptySquare) return;
8473 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8476 if (appData.debugMode) {
8477 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8478 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8479 piece, fromX, fromY, toX, toY); }
8481 ScreenSquare(fromX, fromY, &start, &startColor);
8482 ScreenSquare(toX, toY, &finish, &endColor);
8485 /* Knight: make straight movement then diagonal */
8486 if (abs(toY - fromY) < abs(toX - fromX)) {
8487 mid.x = start.x + (finish.x - start.x) / 2;
8491 mid.y = start.y + (finish.y - start.y) / 2;
8494 mid.x = start.x + (finish.x - start.x) / 2;
8495 mid.y = start.y + (finish.y - start.y) / 2;
8498 /* Don't use as many frames for very short moves */
8499 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8500 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8502 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8503 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8504 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8506 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8507 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8510 /* Be sure end square is redrawn */
8511 damage[0][toY][toX] = True;
8515 DragPieceBegin(x, y)
8518 int boardX, boardY, color;
8521 /* Are we animating? */
8522 if (!appData.animateDragging || appData.blindfold)
8525 /* Figure out which square we start in and the
8526 mouse position relative to top left corner. */
8527 BoardSquare(x, y, &boardX, &boardY);
8528 player.startBoardX = boardX;
8529 player.startBoardY = boardY;
8530 ScreenSquare(boardX, boardY, &corner, &color);
8531 player.startSquare = corner;
8532 player.startColor = color;
8533 /* As soon as we start dragging, the piece will jump slightly to
8534 be centered over the mouse pointer. */
8535 player.mouseDelta.x = squareSize/2;
8536 player.mouseDelta.y = squareSize/2;
8537 /* Initialise animation */
8538 player.dragPiece = PieceForSquare(boardX, boardY);
8540 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8541 player.dragActive = True;
8542 BeginAnimation(&player, player.dragPiece, color, &corner);
8543 /* Mark this square as needing to be redrawn. Note that
8544 we don't remove the piece though, since logically (ie
8545 as seen by opponent) the move hasn't been made yet. */
8546 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8547 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8548 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8549 corner.x, corner.y, squareSize, squareSize,
8550 0, 0); // [HGM] zh: unstack in stead of grab
8551 if(gatingPiece != EmptySquare) {
8552 /* Kludge alert: When gating we want the introduced
8553 piece to appear on the from square. To generate an
8554 image of it, we draw it on the board, copy the image,
8555 and draw the original piece again. */
8556 ChessSquare piece = boards[currentMove][boardY][boardX];
8557 DrawSquare(boardY, boardX, gatingPiece, 0);
8558 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8559 corner.x, corner.y, squareSize, squareSize, 0, 0);
8560 DrawSquare(boardY, boardX, piece, 0);
8562 damage[0][boardY][boardX] = True;
8564 player.dragActive = False;
8569 ChangeDragPiece(ChessSquare piece)
8572 player.dragPiece = piece;
8573 /* The piece will be drawn using its own bitmap as a matte */
8574 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8575 XSetClipMask(xDisplay, player.pieceGC, mask);
8584 /* Are we animating? */
8585 if (!appData.animateDragging || appData.blindfold)
8589 if (! player.dragActive)
8591 /* Move piece, maintaining same relative position
8592 of mouse within square */
8593 corner.x = x - player.mouseDelta.x;
8594 corner.y = y - player.mouseDelta.y;
8595 AnimationFrame(&player, &corner, player.dragPiece);
8597 if (appData.highlightDragging) {
8599 BoardSquare(x, y, &boardX, &boardY);
8600 SetHighlights(fromX, fromY, boardX, boardY);
8609 int boardX, boardY, color;
8612 /* Are we animating? */
8613 if (!appData.animateDragging || appData.blindfold)
8617 if (! player.dragActive)
8619 /* Last frame in sequence is square piece is
8620 placed on, which may not match mouse exactly. */
8621 BoardSquare(x, y, &boardX, &boardY);
8622 ScreenSquare(boardX, boardY, &corner, &color);
8623 EndAnimation(&player, &corner);
8625 /* Be sure end square is redrawn */
8626 damage[0][boardY][boardX] = True;
8628 /* This prevents weird things happening with fast successive
8629 clicks which on my Sun at least can cause motion events
8630 without corresponding press/release. */
8631 player.dragActive = False;
8634 /* Handle expose event while piece being dragged */
8639 if (!player.dragActive || appData.blindfold)
8642 /* What we're doing: logically, the move hasn't been made yet,
8643 so the piece is still in it's original square. But visually
8644 it's being dragged around the board. So we erase the square
8645 that the piece is on and draw it at the last known drag point. */
8646 BlankSquare(player.startSquare.x, player.startSquare.y,
8647 player.startColor, EmptySquare, xBoardWindow, 1);
8648 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8649 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8652 #include <sys/ioctl.h>
8653 int get_term_width()
8655 int fd, default_width;
8658 default_width = 79; // this is FICS default anyway...
8660 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8662 if (!ioctl(fd, TIOCGSIZE, &win))
8663 default_width = win.ts_cols;
8664 #elif defined(TIOCGWINSZ)
8666 if (!ioctl(fd, TIOCGWINSZ, &win))
8667 default_width = win.ws_col;
8669 return default_width;
8675 static int old_width = 0;
8676 int new_width = get_term_width();
8678 if (old_width != new_width)
8679 ics_printf("set width %d\n", new_width);
8680 old_width = new_width;
8683 void NotifyFrontendLogin()
8688 /* [AS] Arrow highlighting support */
8690 static double A_WIDTH = 5; /* Width of arrow body */
8692 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8693 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8695 static double Sqr( double x )
8700 static int Round( double x )
8702 return (int) (x + 0.5);
8705 void SquareToPos(int rank, int file, int *x, int *y)
8708 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8709 *y = lineGap + rank * (squareSize + lineGap);
8711 *x = lineGap + file * (squareSize + lineGap);
8712 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8716 /* Draw an arrow between two points using current settings */
8717 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8720 double dx, dy, j, k, x, y;
8723 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8725 arrow[0].x = s_x + A_WIDTH + 0.5;
8728 arrow[1].x = s_x + A_WIDTH + 0.5;
8729 arrow[1].y = d_y - h;
8731 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8732 arrow[2].y = d_y - h;
8737 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8738 arrow[5].y = d_y - h;
8740 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8741 arrow[4].y = d_y - h;
8743 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8746 else if( d_y == s_y ) {
8747 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8750 arrow[0].y = s_y + A_WIDTH + 0.5;
8752 arrow[1].x = d_x - w;
8753 arrow[1].y = s_y + A_WIDTH + 0.5;
8755 arrow[2].x = d_x - w;
8756 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8761 arrow[5].x = d_x - w;
8762 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8764 arrow[4].x = d_x - w;
8765 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8768 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8771 /* [AS] Needed a lot of paper for this! :-) */
8772 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8773 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8775 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8777 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8782 arrow[0].x = Round(x - j);
8783 arrow[0].y = Round(y + j*dx);
8785 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8786 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8789 x = (double) d_x - k;
8790 y = (double) d_y - k*dy;
8793 x = (double) d_x + k;
8794 y = (double) d_y + k*dy;
8797 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8799 arrow[6].x = Round(x - j);
8800 arrow[6].y = Round(y + j*dx);
8802 arrow[2].x = Round(arrow[6].x + 2*j);
8803 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8805 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8806 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8811 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8812 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8815 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8816 // Polygon( hdc, arrow, 7 );
8819 /* [AS] Draw an arrow between two squares */
8820 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
8822 int s_x, s_y, d_x, d_y, hor, vert, i;
8824 if( s_col == d_col && s_row == d_row ) {
8828 /* Get source and destination points */
8829 SquareToPos( s_row, s_col, &s_x, &s_y);
8830 SquareToPos( d_row, d_col, &d_x, &d_y);
8833 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8835 else if( d_y < s_y ) {
8836 d_y += squareSize / 2 + squareSize / 4;
8839 d_y += squareSize / 2;
8843 d_x += squareSize / 2 - squareSize / 4;
8845 else if( d_x < s_x ) {
8846 d_x += squareSize / 2 + squareSize / 4;
8849 d_x += squareSize / 2;
8852 s_x += squareSize / 2;
8853 s_y += squareSize / 2;
8856 A_WIDTH = squareSize / 14.; //[HGM] make float
8858 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8860 hor = 64*s_col + 32; vert = 64*s_row + 32;
8861 for(i=0; i<= 64; i++) {
8862 damage[0][vert+6>>6][hor+6>>6] = True;
8863 damage[0][vert-6>>6][hor+6>>6] = True;
8864 damage[0][vert+6>>6][hor-6>>6] = True;
8865 damage[0][vert-6>>6][hor-6>>6] = True;
8866 hor += d_col - s_col; vert += d_row - s_row;
8870 Boolean IsDrawArrowEnabled()
8872 return appData.highlightMoveWithArrow && squareSize >= 32;
8875 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
8877 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8878 DrawArrowBetweenSquares(fromX, fromY, toX, toY);