2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
66 # if HAVE_SYS_SOCKET_H
67 # include <sys/socket.h>
68 # include <netinet/in.h>
70 # else /* not HAVE_SYS_SOCKET_H */
71 # if HAVE_LAN_SOCKET_H
72 # include <lan/socket.h>
74 # include <lan/netdb.h>
75 # else /* not HAVE_LAN_SOCKET_H */
76 # define OMIT_SOCKETS 1
77 # endif /* not HAVE_LAN_SOCKET_H */
78 # endif /* not HAVE_SYS_SOCKET_H */
79 #endif /* !OMIT_SOCKETS */
84 #else /* not STDC_HEADERS */
85 extern char *getenv();
88 # else /* not HAVE_STRING_H */
90 # endif /* not HAVE_STRING_H */
91 #endif /* not STDC_HEADERS */
94 # include <sys/fcntl.h>
95 #else /* not HAVE_SYS_FCNTL_H */
98 # endif /* HAVE_FCNTL_H */
99 #endif /* not HAVE_SYS_FCNTL_H */
101 #if HAVE_SYS_SYSTEMINFO_H
102 # include <sys/systeminfo.h>
103 #endif /* HAVE_SYS_SYSTEMINFO_H */
105 #if TIME_WITH_SYS_TIME
106 # include <sys/time.h>
110 # include <sys/time.h>
121 # include <sys/wait.h>
126 # define NAMLEN(dirent) strlen((dirent)->d_name)
127 # define HAVE_DIR_STRUCT
129 # define dirent direct
130 # define NAMLEN(dirent) (dirent)->d_namlen
132 # include <sys/ndir.h>
133 # define HAVE_DIR_STRUCT
136 # include <sys/dir.h>
137 # define HAVE_DIR_STRUCT
141 # define HAVE_DIR_STRUCT
145 #include <X11/Intrinsic.h>
146 #include <X11/StringDefs.h>
147 #include <X11/Shell.h>
148 #include <X11/cursorfont.h>
149 #include <X11/Xatom.h>
150 #include <X11/Xmu/Atoms.h>
152 #include <X11/Xaw3d/Dialog.h>
153 #include <X11/Xaw3d/Form.h>
154 #include <X11/Xaw3d/List.h>
155 #include <X11/Xaw3d/Label.h>
156 #include <X11/Xaw3d/SimpleMenu.h>
157 #include <X11/Xaw3d/SmeBSB.h>
158 #include <X11/Xaw3d/SmeLine.h>
159 #include <X11/Xaw3d/Box.h>
160 #include <X11/Xaw3d/MenuButton.h>
161 #include <X11/Xaw3d/Text.h>
162 #include <X11/Xaw3d/AsciiText.h>
164 #include <X11/Xaw/Dialog.h>
165 #include <X11/Xaw/Form.h>
166 #include <X11/Xaw/List.h>
167 #include <X11/Xaw/Label.h>
168 #include <X11/Xaw/SimpleMenu.h>
169 #include <X11/Xaw/SmeBSB.h>
170 #include <X11/Xaw/SmeLine.h>
171 #include <X11/Xaw/Box.h>
172 #include <X11/Xaw/MenuButton.h>
173 #include <X11/Xaw/Text.h>
174 #include <X11/Xaw/AsciiText.h>
177 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
182 #include "pixmaps/pixmaps.h"
183 #define IMAGE_EXT "xpm"
185 #define IMAGE_EXT "xim"
186 #include "bitmaps/bitmaps.h"
189 #include "bitmaps/icon_white.bm"
190 #include "bitmaps/icon_black.bm"
191 #include "bitmaps/checkmark.bm"
193 #include "frontend.h"
195 #include "backendz.h"
199 #include "xgamelist.h"
200 #include "xhistory.h"
201 #include "xedittags.h"
204 // must be moved to xengineoutput.h
206 void EngineOutputProc P((Widget w, XEvent *event,
207 String *prms, Cardinal *nprms));
208 void EvalGraphProc P((Widget w, XEvent *event,
209 String *prms, Cardinal *nprms));
216 #define usleep(t) _sleep2(((t)+500)/1000)
220 # define _(s) gettext (s)
221 # define N_(s) gettext_noop (s)
239 int main P((int argc, char **argv));
240 FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
241 char *init_path, char *filter, char *mode, int (*show_entry)(), char **name_return));
242 RETSIGTYPE CmailSigHandler P((int sig));
243 RETSIGTYPE IntSigHandler P((int sig));
244 RETSIGTYPE TermSizeSigHandler P((int sig));
245 void CreateGCs P((int redo));
246 void CreateXIMPieces P((void));
247 void CreateXPMPieces P((void));
248 void CreateXPMBoard P((char *s, int n));
249 void CreatePieces P((void));
250 void CreatePieceMenus P((void));
251 Widget CreateMenuBar P((Menu *mb));
252 Widget CreateButtonBar P ((MenuItem *mi));
253 char *FindFont P((char *pattern, int targetPxlSize));
254 void PieceMenuPopup P((Widget w, XEvent *event,
255 String *params, Cardinal *num_params));
256 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
257 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
258 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
259 u_int wreq, u_int hreq));
260 void CreateGrid P((void));
261 int EventToSquare P((int x, int limit));
262 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
263 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
264 void HandleUserMove P((Widget w, XEvent *event,
265 String *prms, Cardinal *nprms));
266 void AnimateUserMove P((Widget w, XEvent * event,
267 String * params, Cardinal * nParams));
268 void HandlePV P((Widget w, XEvent * event,
269 String * params, Cardinal * nParams));
270 void SelectPV P((Widget w, XEvent * event,
271 String * params, Cardinal * nParams));
272 void StopPV P((Widget w, XEvent * event,
273 String * params, Cardinal * nParams));
274 void WhiteClock P((Widget w, XEvent *event,
275 String *prms, Cardinal *nprms));
276 void BlackClock P((Widget w, XEvent *event,
277 String *prms, Cardinal *nprms));
278 void DrawPositionProc P((Widget w, XEvent *event,
279 String *prms, Cardinal *nprms));
280 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
282 void CommentClick P((Widget w, XEvent * event,
283 String * params, Cardinal * nParams));
284 void CommentPopUp P((char *title, char *label));
285 void CommentPopDown P((void));
286 void ICSInputBoxPopUp P((void));
287 void ICSInputBoxPopDown P((void));
288 void FileNamePopUp P((char *label, char *def, char *filter,
289 FileProc proc, char *openMode));
290 void FileNamePopDown P((void));
291 void FileNameCallback P((Widget w, XtPointer client_data,
292 XtPointer call_data));
293 void FileNameAction P((Widget w, XEvent *event,
294 String *prms, Cardinal *nprms));
295 void AskQuestionReplyAction P((Widget w, XEvent *event,
296 String *prms, Cardinal *nprms));
297 void AskQuestionProc P((Widget w, XEvent *event,
298 String *prms, Cardinal *nprms));
299 void AskQuestionPopDown P((void));
300 void PromotionPopDown P((void));
301 void PromotionCallback P((Widget w, XtPointer client_data,
302 XtPointer call_data));
303 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
304 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
305 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
306 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
308 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
310 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
312 void LoadPositionProc P((Widget w, XEvent *event,
313 String *prms, Cardinal *nprms));
314 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
316 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
318 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
320 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
322 void PastePositionProc P((Widget w, XEvent *event, String *prms,
324 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
325 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
326 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
327 void SavePositionProc P((Widget w, XEvent *event,
328 String *prms, Cardinal *nprms));
329 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
330 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
332 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
333 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
334 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
336 void MachineWhiteProc P((Widget w, XEvent *event,
337 String *prms, Cardinal *nprms));
338 void AnalyzeModeProc P((Widget w, XEvent *event,
339 String *prms, Cardinal *nprms));
340 void AnalyzeFileProc P((Widget w, XEvent *event,
341 String *prms, Cardinal *nprms));
342 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
344 void MatchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
345 void MatchOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
346 void IcsClientProc P((Widget w, XEvent *event, String *prms,
348 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
349 void EditPositionProc P((Widget w, XEvent *event,
350 String *prms, Cardinal *nprms));
351 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
352 void EditCommentProc P((Widget w, XEvent *event,
353 String *prms, Cardinal *nprms));
354 void IcsInputBoxProc P((Widget w, XEvent *event,
355 String *prms, Cardinal *nprms));
356 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
357 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
358 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
359 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
360 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
361 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
362 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
363 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
364 void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
365 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
366 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
367 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
368 void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
369 void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
370 void StopObservingProc P((Widget w, XEvent *event, String *prms,
372 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
374 void UploadProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
375 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
376 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
377 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
378 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
379 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
380 void AnnotateProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
381 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
383 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
385 void MoveNowProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
386 void AlwaysQueenProc P((Widget w, XEvent *event, String *prms,
388 void AnimateDraggingProc P((Widget w, XEvent *event, String *prms,
390 void AnimateMovingProc P((Widget w, XEvent *event, String *prms,
392 void AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
393 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
394 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
395 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
397 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
398 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
400 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
402 void HighlightArrowProc P((Widget w, XEvent *event, String *prms,
404 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
405 //void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
406 void OneClickProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
407 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
409 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
411 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
413 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
415 //void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
416 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
418 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
420 void HideThinkingProc P((Widget w, XEvent *event, String *prms,
422 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
424 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
425 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
426 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
427 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
428 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
429 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
430 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
431 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
432 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
433 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
434 void Iconify P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
435 void DisplayMove P((int moveNumber));
436 void DisplayTitle P((char *title));
437 void ICSInitScript P((void));
438 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
439 void ErrorPopUp P((char *title, char *text, int modal));
440 void ErrorPopDown P((void));
441 static char *ExpandPathName P((char *path));
442 static void CreateAnimVars P((void));
443 static void DragPieceMove P((int x, int y));
444 static void DrawDragPiece P((void));
445 char *ModeToWidgetName P((GameMode mode));
446 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
447 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
448 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
449 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
450 void OptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
451 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
452 void IcsTextProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
453 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
454 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
455 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
456 void IcsOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
457 void SoundOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
458 void BoardOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
459 void LoadOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
460 void SaveOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
461 void GameListOptionsPopDown P(());
462 void ShufflePopDown P(());
463 void TimeControlPopDown P(());
464 void GenericPopDown P(());
465 void update_ics_width P(());
466 int get_term_width P(());
467 int CopyMemoProc P(());
468 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
469 Boolean IsDrawArrowEnabled P(());
472 * XBoard depends on Xt R4 or higher
474 int xtVersion = XtSpecificationRelease;
479 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
480 jailSquareColor, highlightSquareColor, premoveHighlightColor;
481 Pixel lowTimeWarningColor;
482 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
483 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
484 wjPieceGC, bjPieceGC, prelineGC, countGC;
485 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
486 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
487 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
488 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
489 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
490 ICSInputShell, fileNameShell, askQuestionShell;
491 Widget historyShell, evalGraphShell, gameListShell;
492 int hOffset; // [HGM] dual
493 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
494 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
495 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
496 Font clockFontID, coordFontID, countFontID;
497 XFontStruct *clockFontStruct, *coordFontStruct, *countFontStruct;
498 XtAppContext appContext;
500 char *oldICSInteractionTitle;
504 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
506 Position commentX = -1, commentY = -1;
507 Dimension commentW, commentH;
508 typedef unsigned int BoardSize;
510 Boolean chessProgram;
512 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
513 int squareSize, smallLayout = 0, tinyLayout = 0,
514 marginW, marginH, // [HGM] for run-time resizing
515 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
516 ICSInputBoxUp = False, askQuestionUp = False,
517 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
518 errorUp = False, errorExitStatus = -1, lineGap, defaultLineGap;
519 Pixel timerForegroundPixel, timerBackgroundPixel;
520 Pixel buttonForegroundPixel, buttonBackgroundPixel;
521 char *chessDir, *programName, *programVersion,
522 *gameCopyFilename, *gamePasteFilename;
523 Boolean alwaysOnTop = False;
524 Boolean saveSettingsOnExit;
525 char *settingsFileName;
526 char *icsTextMenuString;
528 char *firstChessProgramNames;
529 char *secondChessProgramNames;
531 WindowPlacement wpMain;
532 WindowPlacement wpConsole;
533 WindowPlacement wpComment;
534 WindowPlacement wpMoveHistory;
535 WindowPlacement wpEvalGraph;
536 WindowPlacement wpEngineOutput;
537 WindowPlacement wpGameList;
538 WindowPlacement wpTags;
542 Pixmap pieceBitmap[2][(int)BlackPawn];
543 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
544 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
545 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
546 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
547 Pixmap xpmBoardBitmap[2];
548 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
549 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
550 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
551 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
552 XImage *ximLightSquare, *ximDarkSquare;
555 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
556 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
558 #define White(piece) ((int)(piece) < (int)BlackPawn)
560 /* Variables for doing smooth animation. This whole thing
561 would be much easier if the board was double-buffered,
562 but that would require a fairly major rewrite. */
567 GC blitGC, pieceGC, outlineGC;
568 XPoint startSquare, prevFrame, mouseDelta;
572 int startBoardX, startBoardY;
575 /* There can be two pieces being animated at once: a player
576 can begin dragging a piece before the remote opponent has moved. */
578 static AnimState game, player;
580 /* Bitmaps for use as masks when drawing XPM pieces.
581 Need one for each black and white piece. */
582 static Pixmap xpmMask[BlackKing + 1];
584 /* This magic number is the number of intermediate frames used
585 in each half of the animation. For short moves it's reduced
586 by 1. The total number of frames will be factor * 2 + 1. */
589 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
591 MenuItem fileMenu[] = {
592 {N_("New Game Ctrl+N"), "New Game", ResetProc},
593 {N_("New Shuffle Game ..."), "New Shuffle Game", ShuffleMenuProc},
594 {N_("New Variant ... Alt+Shift+V"), "New Variant", NewVariantProc}, // [HGM] variant: not functional yet
595 {"----", NULL, NothingProc},
596 {N_("Load Game Ctrl+O"), "Load Game", LoadGameProc},
597 {N_("Load Position Ctrl+Shift+O"), "Load Position", LoadPositionProc},
598 // {N_("Load Next Game"), "Load Next Game", LoadNextGameProc},
599 // {N_("Load Previous Game"), "Load Previous Game", LoadPrevGameProc},
600 // {N_("Reload Same Game"), "Reload Same Game", ReloadGameProc},
601 {N_("Next Position Shift+PgDn"), "Load Next Position", LoadNextPositionProc},
602 {N_("Prev Position Shift+PgUp"), "Load Previous Position", LoadPrevPositionProc},
603 {"----", NULL, NothingProc},
604 // {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
605 {N_("Save Game Ctrl+S"), "Save Game", SaveGameProc},
606 {N_("Save Position Ctrl+Shift+S"), "Save Position", SavePositionProc},
607 {"----", NULL, NothingProc},
608 {N_("Mail Move"), "Mail Move", MailMoveProc},
609 {N_("Reload CMail Message"), "Reload CMail Message", ReloadCmailMsgProc},
610 {"----", NULL, NothingProc},
611 {N_("Quit Ctr+Q"), "Exit", QuitProc},
615 MenuItem editMenu[] = {
616 {N_("Copy Game Ctrl+C"), "Copy Game", CopyGameProc},
617 {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
618 {"----", NULL, NothingProc},
619 {N_("Paste Game Ctrl+V"), "Paste Game", PasteGameProc},
620 {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
621 {"----", NULL, NothingProc},
622 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
623 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
624 {N_("Edit Tags"), "Edit Tags", EditTagsProc},
625 {N_("Edit Comment"), "Edit Comment", EditCommentProc},
626 {"----", NULL, NothingProc},
627 {N_("Revert Home"), "Revert", RevertProc},
628 {N_("Annotate"), "Annotate", AnnotateProc},
629 {N_("Truncate Game End"), "Truncate Game", TruncateGameProc},
630 {"----", NULL, NothingProc},
631 {N_("Backward Alt+Left"), "Backward", BackwardProc},
632 {N_("Forward Alt+Right"), "Forward", ForwardProc},
633 {N_("Back to Start Alt+Home"), "Back to Start", ToStartProc},
634 {N_("Forward to End Alt+End"), "Forward to End", ToEndProc},
638 MenuItem viewMenu[] = {
639 {N_("Flip View F2"), "Flip View", FlipViewProc},
640 {"----", NULL, NothingProc},
641 {N_("Engine Output Alt+Shift+O"), "Show Engine Output", EngineOutputProc},
642 {N_("Move History Alt+Shift+H"), "Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
643 {N_("Evaluation Graph Alt+Shift+E"), "Show Evaluation Graph", EvalGraphProc},
644 {N_("Game List Alt+Shift+G"), "Show Game List", ShowGameListProc},
645 {N_("ICS text menu"), "ICStex", IcsTextProc},
646 {"----", NULL, NothingProc},
647 {N_("Tags"), "Show Tags", EditTagsProc},
648 {N_("Comments"), "Show Comments", EditCommentProc},
649 {N_("ICS Input Box"), "ICS Input Box", IcsInputBoxProc},
650 {"----", NULL, NothingProc},
651 {N_("Board..."), "Board Options", BoardOptionsProc},
652 {N_("Game List Tags..."), "Game List", GameListOptionsPopUp},
656 MenuItem modeMenu[] = {
657 {N_("Machine White Ctrl+W"), "Machine White", MachineWhiteProc},
658 {N_("Machine Black Ctrl+B"), "Machine Black", MachineBlackProc},
659 {N_("Two Machines Ctrl+T"), "Two Machines", TwoMachinesProc},
660 {N_("Analysis Mode Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
661 {N_("Analyze File Ctrl+F"), "Analyze File", AnalyzeFileProc },
662 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
663 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
664 {N_("Training"), "Training", TrainingProc},
665 {N_("ICS Client"), "ICS Client", IcsClientProc},
666 {"----", NULL, NothingProc},
667 {N_("Machine Match"), "Machine Match", MatchProc},
668 {N_("Pause Pause"), "Pause", PauseProc},
672 MenuItem actionMenu[] = {
673 {N_("Accept F3"), "Accept", AcceptProc},
674 {N_("Decline F4"), "Decline", DeclineProc},
675 {N_("Rematch F12"), "Rematch", RematchProc},
676 {"----", NULL, NothingProc},
677 {N_("Call Flag F5"), "Call Flag", CallFlagProc},
678 {N_("Draw F6"), "Draw", DrawProc},
679 {N_("Adjourn F7"), "Adjourn", AdjournProc},
680 {N_("Abort F8"),"Abort", AbortProc},
681 {N_("Resign F9"), "Resign", ResignProc},
682 {"----", NULL, NothingProc},
683 {N_("Stop Observing F10"), "Stop Observing", StopObservingProc},
684 {N_("Stop Examining F11"), "Stop Examining", StopExaminingProc},
685 {N_("Upload to Examine"), "Upload to Examine", UploadProc},
686 {"----", NULL, NothingProc},
687 {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
688 {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
689 {N_("Adjudicate Draw"), "Adjudicate Draw", AdjuDrawProc},
693 MenuItem engineMenu[] = {
694 {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
695 {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
696 {"----", NULL, NothingProc},
697 {N_("Hint"), "Hint", HintProc},
698 {N_("Book"), "Book", BookProc},
699 {"----", NULL, NothingProc},
700 {N_("Move Now Ctrl+M"), "Move Now", MoveNowProc},
701 {N_("Retract Move Ctrl+X"), "Retract Move", RetractMoveProc},
705 MenuItem optionsMenu[] = {
706 #define OPTIONSDIALOG
708 {N_("General ..."), "General", OptionsProc},
710 {N_("Time Control ... Alt+Shift+T"), "Time Control", TimeControlProc},
711 {N_("Common Engine ... Alt+Shift+U"), "Common Engine", UciMenuProc},
712 {N_("Adjudications ... Alt+Shift+J"), "Adjudications", EngineMenuProc},
713 {N_("ICS ..."), "ICS", IcsOptionsProc},
714 {N_("Match ..."), "Match", MatchOptionsProc},
715 {N_("Load Game ..."), "Load Game", LoadOptionsProc},
716 {N_("Save Game ..."), "Save Game", SaveOptionsProc},
717 // {N_(" ..."), "", OptionsProc},
718 {N_("Game List ..."), "Game List", GameListOptionsPopUp},
719 {N_("Sounds ..."), "Sounds", SoundOptionsProc},
720 {"----", NULL, NothingProc},
721 #ifndef OPTIONSDIALOG
722 {N_("Always Queen Ctrl+Shift+Q"), "Always Queen", AlwaysQueenProc},
723 {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
724 {N_("Animate Moving Ctrl+Shift+A"), "Animate Moving", AnimateMovingProc},
725 {N_("Auto Flag Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
726 {N_("Auto Flip View"), "Auto Flip View", AutoflipProc},
727 {N_("Blindfold"), "Blindfold", BlindfoldProc},
728 {N_("Flash Moves"), "Flash Moves", FlashMovesProc},
730 {N_("Highlight Dragging"), "Highlight Dragging", HighlightDraggingProc},
732 {N_("Highlight Last Move"), "Highlight Last Move", HighlightLastMoveProc},
733 {N_("Highlight With Arrow"), "Arrow", HighlightArrowProc},
734 {N_("Move Sound"), "Move Sound", MoveSoundProc},
735 // {N_("ICS Alarm"), "ICS Alarm", IcsAlarmProc},
736 {N_("One-Click Moving"), "OneClick", OneClickProc},
737 {N_("Periodic Updates"), "Periodic Updates", PeriodicUpdatesProc},
738 {N_("Ponder Next Move Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
739 {N_("Popup Exit Message"), "Popup Exit Message", PopupExitMessageProc},
740 {N_("Popup Move Errors"), "Popup Move Errors", PopupMoveErrorsProc},
741 // {N_("Premove"), "Premove", PremoveProc},
742 {N_("Show Coords"), "Show Coords", ShowCoordsProc},
743 {N_("Hide Thinking Ctrl+Shift+H"), "Hide Thinking", HideThinkingProc},
744 {N_("Test Legality Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
745 {"----", NULL, NothingProc},
747 {N_("Save Settings Now"), "Save Settings Now", SaveSettingsProc},
748 {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
752 MenuItem helpMenu[] = {
753 {N_("Info XBoard"), "Info XBoard", InfoProc},
754 {N_("Man XBoard F1"), "Man XBoard", ManProc},
755 {"----", NULL, NothingProc},
756 {N_("About XBoard"), "About XBoard", AboutProc},
761 {N_("File"), "File", fileMenu},
762 {N_("Edit"), "Edit", editMenu},
763 {N_("View"), "View", viewMenu},
764 {N_("Mode"), "Mode", modeMenu},
765 {N_("Action"), "Action", actionMenu},
766 {N_("Engine"), "Engine", engineMenu},
767 {N_("Options"), "Options", optionsMenu},
768 {N_("Help"), "Help", helpMenu},
772 #define PAUSE_BUTTON "P"
773 MenuItem buttonBar[] = {
774 {"<<", "<<", ToStartProc},
775 {"<", "<", BackwardProc},
776 {PAUSE_BUTTON, PAUSE_BUTTON, PauseProc},
777 {">", ">", ForwardProc},
778 {">>", ">>", ToEndProc},
782 #define PIECE_MENU_SIZE 18
783 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
784 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
785 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
786 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
787 N_("Empty square"), N_("Clear board") },
788 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
789 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
790 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
791 N_("Empty square"), N_("Clear board") }
793 /* must be in same order as PieceMenuStrings! */
794 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
795 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
796 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
797 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
798 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
799 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
800 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
801 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
802 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
805 #define DROP_MENU_SIZE 6
806 String dropMenuStrings[DROP_MENU_SIZE] = {
807 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
809 /* must be in same order as PieceMenuStrings! */
810 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
811 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
812 WhiteRook, WhiteQueen
820 DropMenuEnables dmEnables[] = {
838 { XtNborderWidth, 0 },
839 { XtNdefaultDistance, 0 },
843 { XtNborderWidth, 0 },
844 { XtNresizable, (XtArgVal) True },
848 { XtNborderWidth, 0 },
854 { XtNjustify, (XtArgVal) XtJustifyRight },
855 { XtNlabel, (XtArgVal) "..." },
856 { XtNresizable, (XtArgVal) True },
857 { XtNresize, (XtArgVal) False }
860 Arg messageArgs[] = {
861 { XtNjustify, (XtArgVal) XtJustifyLeft },
862 { XtNlabel, (XtArgVal) "..." },
863 { XtNresizable, (XtArgVal) True },
864 { XtNresize, (XtArgVal) False }
868 { XtNborderWidth, 0 },
869 { XtNjustify, (XtArgVal) XtJustifyLeft }
872 XtResource clientResources[] = {
873 { "flashCount", "flashCount", XtRInt, sizeof(int),
874 XtOffset(AppDataPtr, flashCount), XtRImmediate,
875 (XtPointer) FLASH_COUNT },
878 XrmOptionDescRec shellOptions[] = {
879 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
880 { "-flash", "flashCount", XrmoptionNoArg, "3" },
881 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
884 XtActionsRec boardActions[] = {
885 { "DrawPosition", DrawPositionProc },
886 { "HandleUserMove", HandleUserMove },
887 { "AnimateUserMove", AnimateUserMove },
888 { "HandlePV", HandlePV },
889 { "SelectPV", SelectPV },
890 { "StopPV", StopPV },
891 { "FileNameAction", FileNameAction },
892 { "AskQuestionProc", AskQuestionProc },
893 { "AskQuestionReplyAction", AskQuestionReplyAction },
894 { "PieceMenuPopup", PieceMenuPopup },
895 { "WhiteClock", WhiteClock },
896 { "BlackClock", BlackClock },
897 { "Iconify", Iconify },
898 { "ResetProc", ResetProc },
899 { "NewVariantProc", NewVariantProc },
900 { "LoadGameProc", LoadGameProc },
901 { "LoadNextGameProc", LoadNextGameProc },
902 { "LoadPrevGameProc", LoadPrevGameProc },
903 { "LoadSelectedProc", LoadSelectedProc },
904 { "SetFilterProc", SetFilterProc },
905 { "ReloadGameProc", ReloadGameProc },
906 { "LoadPositionProc", LoadPositionProc },
907 { "LoadNextPositionProc", LoadNextPositionProc },
908 { "LoadPrevPositionProc", LoadPrevPositionProc },
909 { "ReloadPositionProc", ReloadPositionProc },
910 { "CopyPositionProc", CopyPositionProc },
911 { "PastePositionProc", PastePositionProc },
912 { "CopyGameProc", CopyGameProc },
913 { "PasteGameProc", PasteGameProc },
914 { "SaveGameProc", SaveGameProc },
915 { "SavePositionProc", SavePositionProc },
916 { "MailMoveProc", MailMoveProc },
917 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
918 { "QuitProc", QuitProc },
919 { "MachineWhiteProc", MachineWhiteProc },
920 { "MachineBlackProc", MachineBlackProc },
921 { "AnalysisModeProc", AnalyzeModeProc },
922 { "AnalyzeFileProc", AnalyzeFileProc },
923 { "TwoMachinesProc", TwoMachinesProc },
924 { "IcsClientProc", IcsClientProc },
925 { "EditGameProc", EditGameProc },
926 { "EditPositionProc", EditPositionProc },
927 { "TrainingProc", EditPositionProc },
928 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
929 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
930 { "ShowGameListProc", ShowGameListProc },
931 { "ShowMoveListProc", HistoryShowProc},
932 { "EditTagsProc", EditCommentProc },
933 { "EditCommentProc", EditCommentProc },
934 { "IcsInputBoxProc", IcsInputBoxProc },
935 { "PauseProc", PauseProc },
936 { "AcceptProc", AcceptProc },
937 { "DeclineProc", DeclineProc },
938 { "RematchProc", RematchProc },
939 { "CallFlagProc", CallFlagProc },
940 { "DrawProc", DrawProc },
941 { "AdjournProc", AdjournProc },
942 { "AbortProc", AbortProc },
943 { "ResignProc", ResignProc },
944 { "AdjuWhiteProc", AdjuWhiteProc },
945 { "AdjuBlackProc", AdjuBlackProc },
946 { "AdjuDrawProc", AdjuDrawProc },
947 { "EnterKeyProc", EnterKeyProc },
948 { "UpKeyProc", UpKeyProc },
949 { "DownKeyProc", DownKeyProc },
950 { "StopObservingProc", StopObservingProc },
951 { "StopExaminingProc", StopExaminingProc },
952 { "UploadProc", UploadProc },
953 { "BackwardProc", BackwardProc },
954 { "ForwardProc", ForwardProc },
955 { "ToStartProc", ToStartProc },
956 { "ToEndProc", ToEndProc },
957 { "RevertProc", RevertProc },
958 { "AnnotateProc", AnnotateProc },
959 { "TruncateGameProc", TruncateGameProc },
960 { "MoveNowProc", MoveNowProc },
961 { "RetractMoveProc", RetractMoveProc },
962 { "EngineMenuProc", (XtActionProc) EngineMenuProc },
963 { "UciMenuProc", (XtActionProc) UciMenuProc },
964 { "TimeControlProc", (XtActionProc) TimeControlProc },
965 { "FlipViewProc", FlipViewProc },
966 { "PonderNextMoveProc", PonderNextMoveProc },
967 #ifndef OPTIONSDIALOG
968 { "AlwaysQueenProc", AlwaysQueenProc },
969 { "AnimateDraggingProc", AnimateDraggingProc },
970 { "AnimateMovingProc", AnimateMovingProc },
971 { "AutoflagProc", AutoflagProc },
972 { "AutoflipProc", AutoflipProc },
973 { "BlindfoldProc", BlindfoldProc },
974 { "FlashMovesProc", FlashMovesProc },
976 { "HighlightDraggingProc", HighlightDraggingProc },
978 { "HighlightLastMoveProc", HighlightLastMoveProc },
979 // { "IcsAlarmProc", IcsAlarmProc },
980 { "MoveSoundProc", MoveSoundProc },
981 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
982 { "PopupExitMessageProc", PopupExitMessageProc },
983 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
984 // { "PremoveProc", PremoveProc },
985 { "ShowCoordsProc", ShowCoordsProc },
986 { "ShowThinkingProc", ShowThinkingProc },
987 { "HideThinkingProc", HideThinkingProc },
988 { "TestLegalityProc", TestLegalityProc },
990 { "SaveSettingsProc", SaveSettingsProc },
991 { "SaveOnExitProc", SaveOnExitProc },
992 { "InfoProc", InfoProc },
993 { "ManProc", ManProc },
994 { "HintProc", HintProc },
995 { "BookProc", BookProc },
996 { "AboutGameProc", AboutGameProc },
997 { "AboutProc", AboutProc },
998 { "DebugProc", DebugProc },
999 { "NothingProc", NothingProc },
1000 { "CommentClick", (XtActionProc) CommentClick },
1001 { "CommentPopDown", (XtActionProc) CommentPopDown },
1002 { "TagsPopDown", (XtActionProc) TagsPopDown },
1003 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1004 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1005 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1006 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1007 { "GameListPopDown", (XtActionProc) GameListPopDown },
1008 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1009 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1010 { "HistoryPopDown", (XtActionProc) HistoryPopDown },
1011 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1012 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1013 { "ShufflePopDown", (XtActionProc) ShufflePopDown },
1014 { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
1015 { "GenericPopDown", (XtActionProc) GenericPopDown },
1016 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1019 char globalTranslations[] =
1020 ":<Key>F9: ResignProc() \n \
1021 :Ctrl<Key>n: ResetProc() \n \
1022 :Meta<Key>V: NewVariantProc() \n \
1023 :Ctrl<Key>o: LoadGameProc() \n \
1024 :Meta<Key>Next: LoadNextGameProc() \n \
1025 :Meta<Key>Prior: LoadPrevGameProc() \n \
1026 :Ctrl<Key>s: SaveGameProc() \n \
1027 :Ctrl<Key>c: CopyGameProc() \n \
1028 :Ctrl<Key>v: PasteGameProc() \n \
1029 :Ctrl<Key>O: LoadPositionProc() \n \
1030 :Shift<Key>Next: LoadNextPositionProc() \n \
1031 :Shift<Key>Prior: LoadPrevPositionProc() \n \
1032 :Ctrl<Key>S: SavePositionProc() \n \
1033 :Ctrl<Key>C: CopyPositionProc() \n \
1034 :Ctrl<Key>V: PastePositionProc() \n \
1035 :Ctrl<Key>q: QuitProc() \n \
1036 :Ctrl<Key>w: MachineWhiteProc() \n \
1037 :Ctrl<Key>b: MachineBlackProc() \n \
1038 :Ctrl<Key>t: TwoMachinesProc() \n \
1039 :Ctrl<Key>a: AnalysisModeProc() \n \
1040 :Ctrl<Key>f: AnalyzeFileProc() \n \
1041 :Ctrl<Key>e: EditGameProc() \n \
1042 :Ctrl<Key>E: EditPositionProc() \n \
1043 :Meta<Key>O: EngineOutputProc() \n \
1044 :Meta<Key>E: EvalGraphProc() \n \
1045 :Meta<Key>G: ShowGameListProc() \n \
1046 :Meta<Key>H: ShowMoveListProc() \n \
1047 :<Key>Pause: PauseProc() \n \
1048 :<Key>F3: AcceptProc() \n \
1049 :<Key>F4: DeclineProc() \n \
1050 :<Key>F12: RematchProc() \n \
1051 :<Key>F5: CallFlagProc() \n \
1052 :<Key>F6: DrawProc() \n \
1053 :<Key>F7: AdjournProc() \n \
1054 :<Key>F8: AbortProc() \n \
1055 :<Key>F10: StopObservingProc() \n \
1056 :<Key>F11: StopExaminingProc() \n \
1057 :Meta Ctrl<Key>F12: DebugProc() \n \
1058 :Meta<Key>End: ToEndProc() \n \
1059 :Meta<Key>Right: ForwardProc() \n \
1060 :Meta<Key>Home: ToStartProc() \n \
1061 :Meta<Key>Left: BackwardProc() \n \
1062 :<Key>Home: RevertProc() \n \
1063 :<Key>End: TruncateGameProc() \n \
1064 :Ctrl<Key>m: MoveNowProc() \n \
1065 :Ctrl<Key>x: RetractMoveProc() \n \
1066 :Meta<Key>J: EngineMenuProc() \n \
1067 :Meta<Key>U: UciMenuProc() \n \
1068 :Meta<Key>T: TimeControlProc() \n \
1069 :Ctrl<Key>P: PonderNextMoveProc() \n "
1070 #ifndef OPTIONSDIALOG
1072 :Ctrl<Key>Q: AlwaysQueenProc() \n \
1073 :Ctrl<Key>F: AutoflagProc() \n \
1074 :Ctrl<Key>A: AnimateMovingProc() \n \
1075 :Ctrl<Key>L: TestLegalityProc() \n \
1076 :Ctrl<Key>H: HideThinkingProc() \n "
1079 :<Key>-: Iconify() \n \
1080 :<Key>F1: ManProc() \n \
1081 :<Key>F2: FlipViewProc() \n \
1082 <KeyDown>.: BackwardProc() \n \
1083 <KeyUp>.: ForwardProc() \n \
1084 Shift<Key>1: AskQuestionProc(\"Direct command\",\
1085 \"Send to chess program:\",,1) \n \
1086 Shift<Key>2: AskQuestionProc(\"Direct command\",\
1087 \"Send to second chess program:\",,2) \n";
1089 char boardTranslations[] =
1090 "<Btn1Down>: HandleUserMove(0) \n \
1091 Shift<Btn1Up>: HandleUserMove(1) \n \
1092 <Btn1Up>: HandleUserMove(0) \n \
1093 <Btn1Motion>: AnimateUserMove() \n \
1094 <Btn3Motion>: HandlePV() \n \
1095 <Btn3Up>: PieceMenuPopup(menuB) \n \
1096 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1097 PieceMenuPopup(menuB) \n \
1098 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1099 PieceMenuPopup(menuW) \n \
1100 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1101 PieceMenuPopup(menuW) \n \
1102 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1103 PieceMenuPopup(menuB) \n";
1105 char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
1106 char blackTranslations[] = "<BtnDown>: BlackClock()\n";
1108 char ICSInputTranslations[] =
1109 "<Key>Up: UpKeyProc() \n "
1110 "<Key>Down: DownKeyProc() \n "
1111 "<Key>Return: EnterKeyProc() \n";
1113 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1114 // as the widget is destroyed before the up-click can call extend-end
1115 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1117 String xboardResources[] = {
1118 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1119 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1120 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1125 /* Max possible square size */
1126 #define MAXSQSIZE 256
1128 static int xpm_avail[MAXSQSIZE];
1130 #ifdef HAVE_DIR_STRUCT
1132 /* Extract piece size from filename */
1134 xpm_getsize(name, len, ext)
1145 if ((p=strchr(name, '.')) == NULL ||
1146 StrCaseCmp(p+1, ext) != 0)
1152 while (*p && isdigit(*p))
1159 /* Setup xpm_avail */
1161 xpm_getavail(dirname, ext)
1169 for (i=0; i<MAXSQSIZE; ++i)
1172 if (appData.debugMode)
1173 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1175 dir = opendir(dirname);
1178 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1179 programName, dirname);
1183 while ((ent=readdir(dir)) != NULL) {
1184 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1185 if (i > 0 && i < MAXSQSIZE)
1195 xpm_print_avail(fp, ext)
1201 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1202 for (i=1; i<MAXSQSIZE; ++i) {
1208 /* Return XPM piecesize closest to size */
1210 xpm_closest_to(dirname, size, ext)
1216 int sm_diff = MAXSQSIZE;
1220 xpm_getavail(dirname, ext);
1222 if (appData.debugMode)
1223 xpm_print_avail(stderr, ext);
1225 for (i=1; i<MAXSQSIZE; ++i) {
1228 diff = (diff<0) ? -diff : diff;
1229 if (diff < sm_diff) {
1237 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1243 #else /* !HAVE_DIR_STRUCT */
1244 /* If we are on a system without a DIR struct, we can't
1245 read the directory, so we can't collect a list of
1246 filenames, etc., so we can't do any size-fitting. */
1248 xpm_closest_to(dirname, size, ext)
1253 fprintf(stderr, _("\
1254 Warning: No DIR structure found on this system --\n\
1255 Unable to autosize for XPM/XIM pieces.\n\
1256 Please report this error to frankm@hiwaay.net.\n\
1257 Include system type & operating system in message.\n"));
1260 #endif /* HAVE_DIR_STRUCT */
1262 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1263 "magenta", "cyan", "white" };
1267 TextColors textColors[(int)NColorClasses];
1269 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1271 parse_color(str, which)
1275 char *p, buf[100], *d;
1278 if (strlen(str) > 99) /* watch bounds on buf */
1283 for (i=0; i<which; ++i) {
1290 /* Could be looking at something like:
1292 .. in which case we want to stop on a comma also */
1293 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1297 return -1; /* Use default for empty field */
1300 if (which == 2 || isdigit(*p))
1303 while (*p && isalpha(*p))
1308 for (i=0; i<8; ++i) {
1309 if (!StrCaseCmp(buf, cnames[i]))
1310 return which? (i+40) : (i+30);
1312 if (!StrCaseCmp(buf, "default")) return -1;
1314 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1319 parse_cpair(cc, str)
1323 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1324 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1329 /* bg and attr are optional */
1330 textColors[(int)cc].bg = parse_color(str, 1);
1331 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1332 textColors[(int)cc].attr = 0;
1338 /* Arrange to catch delete-window events */
1339 Atom wm_delete_window;
1341 CatchDeleteWindow(Widget w, String procname)
1344 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1345 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1346 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1353 XtSetArg(args[0], XtNiconic, False);
1354 XtSetValues(shellWidget, args, 1);
1356 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1359 //---------------------------------------------------------------------------------------------------------
1360 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1363 #define CW_USEDEFAULT (1<<31)
1364 #define ICS_TEXT_MENU_SIZE 90
1365 #define DEBUG_FILE "xboard.debug"
1366 #define SetCurrentDirectory chdir
1367 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1371 // these two must some day move to frontend.h, when they are implemented
1372 Boolean GameListIsUp();
1374 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1377 // front-end part of option handling
1379 // [HGM] This platform-dependent table provides the location for storing the color info
1380 extern char *crWhite, * crBlack;
1384 &appData.whitePieceColor,
1385 &appData.blackPieceColor,
1386 &appData.lightSquareColor,
1387 &appData.darkSquareColor,
1388 &appData.highlightSquareColor,
1389 &appData.premoveHighlightColor,
1390 &appData.lowTimeWarningColor,
1401 // [HGM] font: keep a font for each square size, even non-stndard ones
1402 #define NUM_SIZES 18
1403 #define MAX_SIZE 130
1404 Boolean fontSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1405 char *fontTable[NUM_FONTS][MAX_SIZE];
1408 ParseFont(char *name, int number)
1409 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1411 if(sscanf(name, "size%d:", &size)) {
1412 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1413 // defer processing it until we know if it matches our board size
1414 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1415 fontTable[number][size] = strdup(strchr(name, ':')+1);
1416 fontValid[number][size] = True;
1421 case 0: // CLOCK_FONT
1422 appData.clockFont = strdup(name);
1424 case 1: // MESSAGE_FONT
1425 appData.font = strdup(name);
1427 case 2: // COORD_FONT
1428 appData.coordFont = strdup(name);
1433 fontSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1438 { // only 2 fonts currently
1439 appData.clockFont = CLOCK_FONT_NAME;
1440 appData.coordFont = COORD_FONT_NAME;
1441 appData.font = DEFAULT_FONT_NAME;
1446 { // no-op, until we identify the code for this already in XBoard and move it here
1450 ParseColor(int n, char *name)
1451 { // in XBoard, just copy the color-name string
1452 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1456 ParseTextAttribs(ColorClass cc, char *s)
1458 (&appData.colorShout)[cc] = strdup(s);
1462 ParseBoardSize(void *addr, char *name)
1464 appData.boardSize = strdup(name);
1469 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1473 SetCommPortDefaults()
1474 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1477 // [HGM] args: these three cases taken out to stay in front-end
1479 SaveFontArg(FILE *f, ArgDescriptor *ad)
1482 int i, n = (int)(intptr_t)ad->argLoc;
1484 case 0: // CLOCK_FONT
1485 name = appData.clockFont;
1487 case 1: // MESSAGE_FONT
1488 name = appData.font;
1490 case 2: // COORD_FONT
1491 name = appData.coordFont;
1496 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1497 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1498 fontTable[n][squareSize] = strdup(name);
1499 fontValid[n][squareSize] = True;
1502 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1503 fprintf(f, OPTCHAR "%s" SEPCHAR "size%d:%s\n", ad->argName, i, fontTable[n][i]);
1508 { // nothing to do, as the sounds are at all times represented by their text-string names already
1512 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1513 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1514 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1518 SaveColor(FILE *f, ArgDescriptor *ad)
1519 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1520 if(colorVariable[(int)(intptr_t)ad->argLoc])
1521 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1525 SaveBoardSize(FILE *f, char *name, void *addr)
1526 { // wrapper to shield back-end from BoardSize & sizeInfo
1527 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1531 ParseCommPortSettings(char *s)
1532 { // no such option in XBoard (yet)
1535 extern Widget engineOutputShell;
1538 GetActualPlacement(Widget wg, WindowPlacement *wp)
1548 XtSetArg(args[i], XtNx, &x); i++;
1549 XtSetArg(args[i], XtNy, &y); i++;
1550 XtSetArg(args[i], XtNwidth, &w); i++;
1551 XtSetArg(args[i], XtNheight, &h); i++;
1552 XtGetValues(wg, args, i);
1561 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1562 // In XBoard this will have to wait until awareness of window parameters is implemented
1563 GetActualPlacement(shellWidget, &wpMain);
1564 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1565 if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1566 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1567 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1571 PrintCommPortSettings(FILE *f, char *name)
1572 { // This option does not exist in XBoard
1576 MySearchPath(char *installDir, char *name, char *fullname)
1577 { // just append installDir and name. Perhaps ExpandPath should be used here?
1578 name = ExpandPathName(name);
1579 if(name && name[0] == '/')
1580 safeStrCpy(fullname, name, MSG_SIZ );
1582 sprintf(fullname, "%s%c%s", installDir, '/', name);
1588 MyGetFullPathName(char *name, char *fullname)
1589 { // should use ExpandPath?
1590 name = ExpandPathName(name);
1591 safeStrCpy(fullname, name, MSG_SIZ );
1596 EnsureOnScreen(int *x, int *y, int minX, int minY)
1603 { // [HGM] args: allows testing if main window is realized from back-end
1604 return xBoardWindow != 0;
1608 PopUpStartupDialog()
1609 { // start menu not implemented in XBoard
1613 ConvertToLine(int argc, char **argv)
1615 static char line[128*1024], buf[1024];
1619 for(i=1; i<argc; i++)
1621 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1622 && argv[i][0] != '{' )
1623 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1625 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1626 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1629 line[strlen(line)-1] = NULLCHAR;
1633 //--------------------------------------------------------------------------------------------
1635 extern Boolean twoBoards, partnerUp;
1638 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1640 #define BoardSize int
1641 void InitDrawingSizes(BoardSize boardSize, int flags)
1642 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1643 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1645 XtGeometryResult gres;
1648 if(!formWidget) return;
1651 * Enable shell resizing.
1653 shellArgs[0].value = (XtArgVal) &w;
1654 shellArgs[1].value = (XtArgVal) &h;
1655 XtGetValues(shellWidget, shellArgs, 2);
1657 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1658 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1659 XtSetValues(shellWidget, &shellArgs[2], 4);
1661 XtSetArg(args[0], XtNdefaultDistance, &sep);
1662 XtGetValues(formWidget, args, 1);
1664 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1665 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1666 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1668 hOffset = boardWidth + 10;
1669 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1670 secondSegments[i] = gridSegments[i];
1671 secondSegments[i].x1 += hOffset;
1672 secondSegments[i].x2 += hOffset;
1675 XtSetArg(args[0], XtNwidth, boardWidth);
1676 XtSetArg(args[1], XtNheight, boardHeight);
1677 XtSetValues(boardWidget, args, 2);
1679 timerWidth = (boardWidth - sep) / 2;
1680 XtSetArg(args[0], XtNwidth, timerWidth);
1681 XtSetValues(whiteTimerWidget, args, 1);
1682 XtSetValues(blackTimerWidget, args, 1);
1684 XawFormDoLayout(formWidget, False);
1686 if (appData.titleInWindow) {
1688 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1689 XtSetArg(args[i], XtNheight, &h); i++;
1690 XtGetValues(titleWidget, args, i);
1692 w = boardWidth - 2*bor;
1694 XtSetArg(args[0], XtNwidth, &w);
1695 XtGetValues(menuBarWidget, args, 1);
1696 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1699 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1700 if (gres != XtGeometryYes && appData.debugMode) {
1702 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1703 programName, gres, w, h, wr, hr);
1707 XawFormDoLayout(formWidget, True);
1710 * Inhibit shell resizing.
1712 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1713 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1714 shellArgs[4].value = shellArgs[2].value = w;
1715 shellArgs[5].value = shellArgs[3].value = h;
1716 XtSetValues(shellWidget, &shellArgs[0], 6);
1718 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1721 for(i=0; i<4; i++) {
1723 for(p=0; p<=(int)WhiteKing; p++)
1724 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1725 if(gameInfo.variant == VariantShogi) {
1726 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1727 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1728 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1729 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1730 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1733 if(gameInfo.variant == VariantGothic) {
1734 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1737 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1738 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1739 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1742 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1743 for(p=0; p<=(int)WhiteKing; p++)
1744 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1745 if(gameInfo.variant == VariantShogi) {
1746 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1747 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1748 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1749 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1750 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1753 if(gameInfo.variant == VariantGothic) {
1754 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1757 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1758 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1759 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1764 for(i=0; i<2; i++) {
1766 for(p=0; p<=(int)WhiteKing; p++)
1767 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1768 if(gameInfo.variant == VariantShogi) {
1769 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1770 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1771 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1772 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1773 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1776 if(gameInfo.variant == VariantGothic) {
1777 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1780 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1781 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1782 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1792 void ParseIcsTextColors()
1793 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1794 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1795 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1796 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1797 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1798 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1799 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1800 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1801 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1802 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1803 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1805 if (appData.colorize) {
1807 _("%s: can't parse color names; disabling colorization\n"),
1810 appData.colorize = FALSE;
1815 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1816 XrmValue vFrom, vTo;
1817 int forceMono = False;
1819 if (!appData.monoMode) {
1820 vFrom.addr = (caddr_t) appData.lightSquareColor;
1821 vFrom.size = strlen(appData.lightSquareColor);
1822 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1823 if (vTo.addr == NULL) {
1824 appData.monoMode = True;
1827 lightSquareColor = *(Pixel *) vTo.addr;
1830 if (!appData.monoMode) {
1831 vFrom.addr = (caddr_t) appData.darkSquareColor;
1832 vFrom.size = strlen(appData.darkSquareColor);
1833 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1834 if (vTo.addr == NULL) {
1835 appData.monoMode = True;
1838 darkSquareColor = *(Pixel *) vTo.addr;
1841 if (!appData.monoMode) {
1842 vFrom.addr = (caddr_t) appData.whitePieceColor;
1843 vFrom.size = strlen(appData.whitePieceColor);
1844 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1845 if (vTo.addr == NULL) {
1846 appData.monoMode = True;
1849 whitePieceColor = *(Pixel *) vTo.addr;
1852 if (!appData.monoMode) {
1853 vFrom.addr = (caddr_t) appData.blackPieceColor;
1854 vFrom.size = strlen(appData.blackPieceColor);
1855 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1856 if (vTo.addr == NULL) {
1857 appData.monoMode = True;
1860 blackPieceColor = *(Pixel *) vTo.addr;
1864 if (!appData.monoMode) {
1865 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1866 vFrom.size = strlen(appData.highlightSquareColor);
1867 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1868 if (vTo.addr == NULL) {
1869 appData.monoMode = True;
1872 highlightSquareColor = *(Pixel *) vTo.addr;
1876 if (!appData.monoMode) {
1877 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1878 vFrom.size = strlen(appData.premoveHighlightColor);
1879 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1880 if (vTo.addr == NULL) {
1881 appData.monoMode = True;
1884 premoveHighlightColor = *(Pixel *) vTo.addr;
1895 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1896 XSetWindowAttributes window_attributes;
1898 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1899 XrmValue vFrom, vTo;
1900 XtGeometryResult gres;
1903 int forceMono = False;
1905 srandom(time(0)); // [HGM] book: make random truly random
1907 setbuf(stdout, NULL);
1908 setbuf(stderr, NULL);
1911 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1912 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1916 programName = strrchr(argv[0], '/');
1917 if (programName == NULL)
1918 programName = argv[0];
1923 XtSetLanguageProc(NULL, NULL, NULL);
1924 bindtextdomain(PACKAGE, LOCALEDIR);
1925 textdomain(PACKAGE);
1929 XtAppInitialize(&appContext, "XBoard", shellOptions,
1930 XtNumber(shellOptions),
1931 &argc, argv, xboardResources, NULL, 0);
1932 appData.boardSize = "";
1933 InitAppData(ConvertToLine(argc, argv));
1935 if (p == NULL) p = "/tmp";
1936 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1937 gameCopyFilename = (char*) malloc(i);
1938 gamePasteFilename = (char*) malloc(i);
1939 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1940 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1942 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1943 clientResources, XtNumber(clientResources),
1946 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1947 static char buf[MSG_SIZ];
1948 EscapeExpand(buf, appData.initString);
1949 appData.initString = strdup(buf);
1950 EscapeExpand(buf, appData.secondInitString);
1951 appData.secondInitString = strdup(buf);
1952 EscapeExpand(buf, appData.firstComputerString);
1953 appData.firstComputerString = strdup(buf);
1954 EscapeExpand(buf, appData.secondComputerString);
1955 appData.secondComputerString = strdup(buf);
1958 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1961 if (chdir(chessDir) != 0) {
1962 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1968 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1969 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1970 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1971 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1974 setbuf(debugFP, NULL);
1977 /* [HGM,HR] make sure board size is acceptable */
1978 if(appData.NrFiles > BOARD_FILES ||
1979 appData.NrRanks > BOARD_RANKS )
1980 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1983 /* This feature does not work; animation needs a rewrite */
1984 appData.highlightDragging = FALSE;
1988 xDisplay = XtDisplay(shellWidget);
1989 xScreen = DefaultScreen(xDisplay);
1990 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1992 gameInfo.variant = StringToVariant(appData.variant);
1993 InitPosition(FALSE);
1996 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1998 if (isdigit(appData.boardSize[0])) {
1999 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2000 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2001 &fontPxlSize, &smallLayout, &tinyLayout);
2003 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2004 programName, appData.boardSize);
2008 /* Find some defaults; use the nearest known size */
2009 SizeDefaults *szd, *nearest;
2010 int distance = 99999;
2011 nearest = szd = sizeDefaults;
2012 while (szd->name != NULL) {
2013 if (abs(szd->squareSize - squareSize) < distance) {
2015 distance = abs(szd->squareSize - squareSize);
2016 if (distance == 0) break;
2020 if (i < 2) lineGap = nearest->lineGap;
2021 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2022 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2023 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2024 if (i < 6) smallLayout = nearest->smallLayout;
2025 if (i < 7) tinyLayout = nearest->tinyLayout;
2028 SizeDefaults *szd = sizeDefaults;
2029 if (*appData.boardSize == NULLCHAR) {
2030 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2031 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2034 if (szd->name == NULL) szd--;
2035 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2037 while (szd->name != NULL &&
2038 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2039 if (szd->name == NULL) {
2040 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2041 programName, appData.boardSize);
2045 squareSize = szd->squareSize;
2046 lineGap = szd->lineGap;
2047 clockFontPxlSize = szd->clockFontPxlSize;
2048 coordFontPxlSize = szd->coordFontPxlSize;
2049 fontPxlSize = szd->fontPxlSize;
2050 smallLayout = szd->smallLayout;
2051 tinyLayout = szd->tinyLayout;
2052 // [HGM] font: use defaults from settings file if available and not overruled
2054 if(!fontSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2055 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2056 if(!fontSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2057 appData.font = fontTable[MESSAGE_FONT][squareSize];
2058 if(!fontSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2059 appData.coordFont = fontTable[COORD_FONT][squareSize];
2061 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2062 if (strlen(appData.pixmapDirectory) > 0) {
2063 p = ExpandPathName(appData.pixmapDirectory);
2065 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2066 appData.pixmapDirectory);
2069 if (appData.debugMode) {
2070 fprintf(stderr, _("\
2071 XBoard square size (hint): %d\n\
2072 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2074 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2075 if (appData.debugMode) {
2076 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2079 defaultLineGap = lineGap;
2080 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2082 /* [HR] height treated separately (hacked) */
2083 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2084 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2085 if (appData.showJail == 1) {
2086 /* Jail on top and bottom */
2087 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2088 XtSetArg(boardArgs[2], XtNheight,
2089 boardHeight + 2*(lineGap + squareSize));
2090 } else if (appData.showJail == 2) {
2092 XtSetArg(boardArgs[1], XtNwidth,
2093 boardWidth + 2*(lineGap + squareSize));
2094 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2097 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2098 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2102 * Determine what fonts to use.
2104 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2105 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2106 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2107 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2108 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2109 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2110 appData.font = FindFont(appData.font, fontPxlSize);
2111 countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
2112 countFontStruct = XQueryFont(xDisplay, countFontID);
2113 // appData.font = FindFont(appData.font, fontPxlSize);
2115 xdb = XtDatabase(xDisplay);
2116 XrmPutStringResource(&xdb, "*font", appData.font);
2119 * Detect if there are not enough colors available and adapt.
2121 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2122 appData.monoMode = True;
2125 forceMono = MakeColors();
2128 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2131 if (appData.bitmapDirectory == NULL ||
2132 appData.bitmapDirectory[0] == NULLCHAR)
2133 appData.bitmapDirectory = DEF_BITMAP_DIR;
2136 if (appData.lowTimeWarning && !appData.monoMode) {
2137 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2138 vFrom.size = strlen(appData.lowTimeWarningColor);
2139 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2140 if (vTo.addr == NULL)
2141 appData.monoMode = True;
2143 lowTimeWarningColor = *(Pixel *) vTo.addr;
2146 if (appData.monoMode && appData.debugMode) {
2147 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2148 (unsigned long) XWhitePixel(xDisplay, xScreen),
2149 (unsigned long) XBlackPixel(xDisplay, xScreen));
2152 ParseIcsTextColors();
2153 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2154 textColors[ColorNone].attr = 0;
2156 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2162 layoutName = "tinyLayout";
2163 } else if (smallLayout) {
2164 layoutName = "smallLayout";
2166 layoutName = "normalLayout";
2168 /* Outer layoutWidget is there only to provide a name for use in
2169 resources that depend on the layout style */
2171 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2172 layoutArgs, XtNumber(layoutArgs));
2174 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2175 formArgs, XtNumber(formArgs));
2176 XtSetArg(args[0], XtNdefaultDistance, &sep);
2177 XtGetValues(formWidget, args, 1);
2180 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2181 XtSetArg(args[0], XtNtop, XtChainTop);
2182 XtSetArg(args[1], XtNbottom, XtChainTop);
2183 XtSetArg(args[2], XtNright, XtChainLeft);
2184 XtSetValues(menuBarWidget, args, 3);
2186 widgetList[j++] = whiteTimerWidget =
2187 XtCreateWidget("whiteTime", labelWidgetClass,
2188 formWidget, timerArgs, XtNumber(timerArgs));
2189 XtSetArg(args[0], XtNfont, clockFontStruct);
2190 XtSetArg(args[1], XtNtop, XtChainTop);
2191 XtSetArg(args[2], XtNbottom, XtChainTop);
2192 XtSetValues(whiteTimerWidget, args, 3);
2194 widgetList[j++] = blackTimerWidget =
2195 XtCreateWidget("blackTime", labelWidgetClass,
2196 formWidget, timerArgs, XtNumber(timerArgs));
2197 XtSetArg(args[0], XtNfont, clockFontStruct);
2198 XtSetArg(args[1], XtNtop, XtChainTop);
2199 XtSetArg(args[2], XtNbottom, XtChainTop);
2200 XtSetValues(blackTimerWidget, args, 3);
2202 if (appData.titleInWindow) {
2203 widgetList[j++] = titleWidget =
2204 XtCreateWidget("title", labelWidgetClass, formWidget,
2205 titleArgs, XtNumber(titleArgs));
2206 XtSetArg(args[0], XtNtop, XtChainTop);
2207 XtSetArg(args[1], XtNbottom, XtChainTop);
2208 XtSetValues(titleWidget, args, 2);
2211 if (appData.showButtonBar) {
2212 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2213 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2214 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2215 XtSetArg(args[2], XtNtop, XtChainTop);
2216 XtSetArg(args[3], XtNbottom, XtChainTop);
2217 XtSetValues(buttonBarWidget, args, 4);
2220 widgetList[j++] = messageWidget =
2221 XtCreateWidget("message", labelWidgetClass, formWidget,
2222 messageArgs, XtNumber(messageArgs));
2223 XtSetArg(args[0], XtNtop, XtChainTop);
2224 XtSetArg(args[1], XtNbottom, XtChainTop);
2225 XtSetValues(messageWidget, args, 2);
2227 widgetList[j++] = boardWidget =
2228 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2229 XtNumber(boardArgs));
2231 XtManageChildren(widgetList, j);
2233 timerWidth = (boardWidth - sep) / 2;
2234 XtSetArg(args[0], XtNwidth, timerWidth);
2235 XtSetValues(whiteTimerWidget, args, 1);
2236 XtSetValues(blackTimerWidget, args, 1);
2238 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2239 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2240 XtGetValues(whiteTimerWidget, args, 2);
2242 if (appData.showButtonBar) {
2243 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2244 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2245 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2249 * formWidget uses these constraints but they are stored
2253 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2254 XtSetValues(menuBarWidget, args, i);
2255 if (appData.titleInWindow) {
2258 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2259 XtSetValues(whiteTimerWidget, args, i);
2261 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2262 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2263 XtSetValues(blackTimerWidget, args, i);
2265 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2266 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2267 XtSetValues(titleWidget, args, i);
2269 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2270 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2271 XtSetValues(messageWidget, args, i);
2272 if (appData.showButtonBar) {
2274 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2275 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2276 XtSetValues(buttonBarWidget, args, i);
2280 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2281 XtSetValues(whiteTimerWidget, args, i);
2283 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2284 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2285 XtSetValues(blackTimerWidget, args, i);
2287 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2288 XtSetValues(titleWidget, args, i);
2290 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2291 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2292 XtSetValues(messageWidget, args, i);
2293 if (appData.showButtonBar) {
2295 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2296 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2297 XtSetValues(buttonBarWidget, args, i);
2302 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2303 XtSetValues(whiteTimerWidget, args, i);
2305 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2306 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2307 XtSetValues(blackTimerWidget, args, i);
2309 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2310 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2311 XtSetValues(messageWidget, args, i);
2312 if (appData.showButtonBar) {
2314 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2315 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2316 XtSetValues(buttonBarWidget, args, i);
2320 XtSetArg(args[0], XtNfromVert, messageWidget);
2321 XtSetArg(args[1], XtNtop, XtChainTop);
2322 XtSetArg(args[2], XtNbottom, XtChainBottom);
2323 XtSetArg(args[3], XtNleft, XtChainLeft);
2324 XtSetArg(args[4], XtNright, XtChainRight);
2325 XtSetValues(boardWidget, args, 5);
2327 XtRealizeWidget(shellWidget);
2330 XtSetArg(args[0], XtNx, wpMain.x);
2331 XtSetArg(args[1], XtNy, wpMain.y);
2332 XtSetValues(shellWidget, args, 2);
2336 * Correct the width of the message and title widgets.
2337 * It is not known why some systems need the extra fudge term.
2338 * The value "2" is probably larger than needed.
2340 XawFormDoLayout(formWidget, False);
2342 #define WIDTH_FUDGE 2
2344 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2345 XtSetArg(args[i], XtNheight, &h); i++;
2346 XtGetValues(messageWidget, args, i);
2347 if (appData.showButtonBar) {
2349 XtSetArg(args[i], XtNwidth, &w); i++;
2350 XtGetValues(buttonBarWidget, args, i);
2351 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2353 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2356 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2357 if (gres != XtGeometryYes && appData.debugMode) {
2358 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2359 programName, gres, w, h, wr, hr);
2362 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2363 /* The size used for the child widget in layout lags one resize behind
2364 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2366 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2367 if (gres != XtGeometryYes && appData.debugMode) {
2368 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2369 programName, gres, w, h, wr, hr);
2372 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2373 XtSetArg(args[1], XtNright, XtChainRight);
2374 XtSetValues(messageWidget, args, 2);
2376 if (appData.titleInWindow) {
2378 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2379 XtSetArg(args[i], XtNheight, &h); i++;
2380 XtGetValues(titleWidget, args, i);
2382 w = boardWidth - 2*bor;
2384 XtSetArg(args[0], XtNwidth, &w);
2385 XtGetValues(menuBarWidget, args, 1);
2386 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2389 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2390 if (gres != XtGeometryYes && appData.debugMode) {
2392 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2393 programName, gres, w, h, wr, hr);
2396 XawFormDoLayout(formWidget, True);
2398 xBoardWindow = XtWindow(boardWidget);
2400 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2401 // not need to go into InitDrawingSizes().
2405 * Create X checkmark bitmap and initialize option menu checks.
2407 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2408 checkmark_bits, checkmark_width, checkmark_height);
2409 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2410 #ifndef OPTIONSDIALOG
2411 if (appData.alwaysPromoteToQueen) {
2412 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2415 if (appData.animateDragging) {
2416 XtSetValues(XtNameToWidget(menuBarWidget,
2417 "menuOptions.Animate Dragging"),
2420 if (appData.animate) {
2421 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2424 if (appData.autoCallFlag) {
2425 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2428 if (appData.autoFlipView) {
2429 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2432 if (appData.blindfold) {
2433 XtSetValues(XtNameToWidget(menuBarWidget,
2434 "menuOptions.Blindfold"), args, 1);
2436 if (appData.flashCount > 0) {
2437 XtSetValues(XtNameToWidget(menuBarWidget,
2438 "menuOptions.Flash Moves"),
2442 if (appData.highlightDragging) {
2443 XtSetValues(XtNameToWidget(menuBarWidget,
2444 "menuOptions.Highlight Dragging"),
2448 if (appData.highlightLastMove) {
2449 XtSetValues(XtNameToWidget(menuBarWidget,
2450 "menuOptions.Highlight Last Move"),
2453 if (appData.highlightMoveWithArrow) {
2454 XtSetValues(XtNameToWidget(menuBarWidget,
2455 "menuOptions.Arrow"),
2458 // if (appData.icsAlarm) {
2459 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2462 if (appData.ringBellAfterMoves) {
2463 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2466 if (appData.oneClick) {
2467 XtSetValues(XtNameToWidget(menuBarWidget,
2468 "menuOptions.OneClick"), args, 1);
2470 if (appData.periodicUpdates) {
2471 XtSetValues(XtNameToWidget(menuBarWidget,
2472 "menuOptions.Periodic Updates"), args, 1);
2474 if (appData.ponderNextMove) {
2475 XtSetValues(XtNameToWidget(menuBarWidget,
2476 "menuOptions.Ponder Next Move"), args, 1);
2478 if (appData.popupExitMessage) {
2479 XtSetValues(XtNameToWidget(menuBarWidget,
2480 "menuOptions.Popup Exit Message"), args, 1);
2482 if (appData.popupMoveErrors) {
2483 XtSetValues(XtNameToWidget(menuBarWidget,
2484 "menuOptions.Popup Move Errors"), args, 1);
2486 // if (appData.premove) {
2487 // XtSetValues(XtNameToWidget(menuBarWidget,
2488 // "menuOptions.Premove"), args, 1);
2490 if (appData.showCoords) {
2491 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2494 if (appData.hideThinkingFromHuman) {
2495 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2498 if (appData.testLegality) {
2499 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2503 if (saveSettingsOnExit) {
2504 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2511 ReadBitmap(&wIconPixmap, "icon_white.bm",
2512 icon_white_bits, icon_white_width, icon_white_height);
2513 ReadBitmap(&bIconPixmap, "icon_black.bm",
2514 icon_black_bits, icon_black_width, icon_black_height);
2515 iconPixmap = wIconPixmap;
2517 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2518 XtSetValues(shellWidget, args, i);
2521 * Create a cursor for the board widget.
2523 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2524 XChangeWindowAttributes(xDisplay, xBoardWindow,
2525 CWCursor, &window_attributes);
2528 * Inhibit shell resizing.
2530 shellArgs[0].value = (XtArgVal) &w;
2531 shellArgs[1].value = (XtArgVal) &h;
2532 XtGetValues(shellWidget, shellArgs, 2);
2533 shellArgs[4].value = shellArgs[2].value = w;
2534 shellArgs[5].value = shellArgs[3].value = h;
2535 XtSetValues(shellWidget, &shellArgs[2], 4);
2536 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2537 marginH = h - boardHeight;
2539 CatchDeleteWindow(shellWidget, "QuitProc");
2544 if (appData.bitmapDirectory[0] != NULLCHAR) {
2548 CreateXPMBoard(appData.liteBackTextureFile, 1);
2549 CreateXPMBoard(appData.darkBackTextureFile, 0);
2553 /* Create regular pieces */
2554 if (!useImages) CreatePieces();
2559 if (appData.animate || appData.animateDragging)
2562 XtAugmentTranslations(formWidget,
2563 XtParseTranslationTable(globalTranslations));
2564 XtAugmentTranslations(boardWidget,
2565 XtParseTranslationTable(boardTranslations));
2566 XtAugmentTranslations(whiteTimerWidget,
2567 XtParseTranslationTable(whiteTranslations));
2568 XtAugmentTranslations(blackTimerWidget,
2569 XtParseTranslationTable(blackTranslations));
2571 /* Why is the following needed on some versions of X instead
2572 * of a translation? */
2573 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2574 (XtEventHandler) EventProc, NULL);
2577 /* [AS] Restore layout */
2578 if( wpMoveHistory.visible ) {
2582 if( wpEvalGraph.visible )
2587 if( wpEngineOutput.visible ) {
2588 EngineOutputPopUp();
2593 if (errorExitStatus == -1) {
2594 if (appData.icsActive) {
2595 /* We now wait until we see "login:" from the ICS before
2596 sending the logon script (problems with timestamp otherwise) */
2597 /*ICSInitScript();*/
2598 if (appData.icsInputBox) ICSInputBoxPopUp();
2602 signal(SIGWINCH, TermSizeSigHandler);
2604 signal(SIGINT, IntSigHandler);
2605 signal(SIGTERM, IntSigHandler);
2606 if (*appData.cmailGameName != NULLCHAR) {
2607 signal(SIGUSR1, CmailSigHandler);
2610 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2612 XtSetKeyboardFocus(shellWidget, formWidget);
2614 XtAppMainLoop(appContext);
2615 if (appData.debugMode) fclose(debugFP); // [DM] debug
2622 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2623 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2625 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2626 unlink(gameCopyFilename);
2627 unlink(gamePasteFilename);
2630 RETSIGTYPE TermSizeSigHandler(int sig)
2643 CmailSigHandler(sig)
2649 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2651 /* Activate call-back function CmailSigHandlerCallBack() */
2652 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2654 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2658 CmailSigHandlerCallBack(isr, closure, message, count, error)
2666 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2668 /**** end signal code ****/
2674 /* try to open the icsLogon script, either in the location given
2675 * or in the users HOME directory
2682 f = fopen(appData.icsLogon, "r");
2685 homedir = getenv("HOME");
2686 if (homedir != NULL)
2688 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2689 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2690 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2691 f = fopen(buf, "r");
2696 ProcessICSInitScript(f);
2698 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2721 if (!menuBarWidget) return;
2722 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2724 DisplayError("menuEdit.Revert", 0);
2726 XtSetSensitive(w, !grey);
2728 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2730 DisplayError("menuEdit.Annotate", 0);
2732 XtSetSensitive(w, !grey);
2737 SetMenuEnables(enab)
2741 if (!menuBarWidget) return;
2742 while (enab->name != NULL) {
2743 w = XtNameToWidget(menuBarWidget, enab->name);
2745 DisplayError(enab->name, 0);
2747 XtSetSensitive(w, enab->value);
2753 Enables icsEnables[] = {
2754 { "menuFile.Mail Move", False },
2755 { "menuFile.Reload CMail Message", False },
2756 { "menuMode.Machine Black", False },
2757 { "menuMode.Machine White", False },
2758 { "menuMode.Analysis Mode", False },
2759 { "menuMode.Analyze File", False },
2760 { "menuMode.Two Machines", False },
2761 { "menuMode.Machine Match", False },
2763 { "menuEngine.Hint", False },
2764 { "menuEngine.Book", False },
2765 { "menuEngine.Move Now", False },
2766 #ifndef OPTIONSDIALOG
2767 { "menuOptions.Periodic Updates", False },
2768 { "menuOptions.Hide Thinking", False },
2769 { "menuOptions.Ponder Next Move", False },
2771 { "menuEngine.Engine #1 Settings", False },
2773 { "menuEngine.Engine #2 Settings", False },
2774 { "menuEdit.Annotate", False },
2778 Enables ncpEnables[] = {
2779 { "menuFile.Mail Move", False },
2780 { "menuFile.Reload CMail Message", False },
2781 { "menuMode.Machine White", False },
2782 { "menuMode.Machine Black", False },
2783 { "menuMode.Analysis Mode", False },
2784 { "menuMode.Analyze File", False },
2785 { "menuMode.Two Machines", False },
2786 { "menuMode.Machine Match", False },
2787 { "menuMode.ICS Client", False },
2788 { "menuView.ICStex", False },
2789 { "menuView.ICS Input Box", False },
2790 { "Action", False },
2791 { "menuEdit.Revert", False },
2792 { "menuEdit.Annotate", False },
2793 { "menuEngine.Engine #1 Settings", False },
2794 { "menuEngine.Engine #2 Settings", False },
2795 { "menuEngine.Move Now", False },
2796 { "menuEngine.Retract Move", False },
2797 { "menuOptions.ICS", False },
2798 #ifndef OPTIONSDIALOG
2799 { "menuOptions.Auto Flag", False },
2800 { "menuOptions.Auto Flip View", False },
2801 // { "menuOptions.ICS Alarm", False },
2802 { "menuOptions.Move Sound", False },
2803 { "menuOptions.Hide Thinking", False },
2804 { "menuOptions.Periodic Updates", False },
2805 { "menuOptions.Ponder Next Move", False },
2807 { "menuEngine.Hint", False },
2808 { "menuEngine.Book", False },
2812 Enables gnuEnables[] = {
2813 { "menuMode.ICS Client", False },
2814 { "menuView.ICStex", False },
2815 { "menuView.ICS Input Box", False },
2816 { "menuAction.Accept", False },
2817 { "menuAction.Decline", False },
2818 { "menuAction.Rematch", False },
2819 { "menuAction.Adjourn", False },
2820 { "menuAction.Stop Examining", False },
2821 { "menuAction.Stop Observing", False },
2822 { "menuAction.Upload to Examine", False },
2823 { "menuEdit.Revert", False },
2824 { "menuEdit.Annotate", False },
2825 { "menuOptions.ICS", False },
2827 /* The next two options rely on SetCmailMode being called *after* */
2828 /* SetGNUMode so that when GNU is being used to give hints these */
2829 /* menu options are still available */
2831 { "menuFile.Mail Move", False },
2832 { "menuFile.Reload CMail Message", False },
2836 Enables cmailEnables[] = {
2838 { "menuAction.Call Flag", False },
2839 { "menuAction.Draw", True },
2840 { "menuAction.Adjourn", False },
2841 { "menuAction.Abort", False },
2842 { "menuAction.Stop Observing", False },
2843 { "menuAction.Stop Examining", False },
2844 { "menuFile.Mail Move", True },
2845 { "menuFile.Reload CMail Message", True },
2849 Enables trainingOnEnables[] = {
2850 { "menuMode.Edit Comment", False },
2851 { "menuMode.Pause", False },
2852 { "menuEdit.Forward", False },
2853 { "menuEdit.Backward", False },
2854 { "menuEdit.Forward to End", False },
2855 { "menuEdit.Back to Start", False },
2856 { "menuEngine.Move Now", False },
2857 { "menuEdit.Truncate Game", False },
2861 Enables trainingOffEnables[] = {
2862 { "menuMode.Edit Comment", True },
2863 { "menuMode.Pause", True },
2864 { "menuEdit.Forward", True },
2865 { "menuEdit.Backward", True },
2866 { "menuEdit.Forward to End", True },
2867 { "menuEdit.Back to Start", True },
2868 { "menuEngine.Move Now", True },
2869 { "menuEdit.Truncate Game", True },
2873 Enables machineThinkingEnables[] = {
2874 { "menuFile.Load Game", False },
2875 // { "menuFile.Load Next Game", False },
2876 // { "menuFile.Load Previous Game", False },
2877 // { "menuFile.Reload Same Game", False },
2878 { "menuEdit.Paste Game", False },
2879 { "menuFile.Load Position", False },
2880 // { "menuFile.Load Next Position", False },
2881 // { "menuFile.Load Previous Position", False },
2882 // { "menuFile.Reload Same Position", False },
2883 { "menuEdit.Paste Position", False },
2884 { "menuMode.Machine White", False },
2885 { "menuMode.Machine Black", False },
2886 { "menuMode.Two Machines", False },
2887 { "menuMode.Machine Match", False },
2888 { "menuEngine.Retract Move", False },
2892 Enables userThinkingEnables[] = {
2893 { "menuFile.Load Game", True },
2894 // { "menuFile.Load Next Game", True },
2895 // { "menuFile.Load Previous Game", True },
2896 // { "menuFile.Reload Same Game", True },
2897 { "menuEdit.Paste Game", True },
2898 { "menuFile.Load Position", True },
2899 // { "menuFile.Load Next Position", True },
2900 // { "menuFile.Load Previous Position", True },
2901 // { "menuFile.Reload Same Position", True },
2902 { "menuEdit.Paste Position", True },
2903 { "menuMode.Machine White", True },
2904 { "menuMode.Machine Black", True },
2905 { "menuMode.Two Machines", True },
2906 { "menuMode.Machine Match", True },
2907 { "menuEngine.Retract Move", True },
2913 SetMenuEnables(icsEnables);
2916 if (appData.zippyPlay && !appData.noChessProgram) /* [DM] icsEngineAnalyze */
2917 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2924 SetMenuEnables(ncpEnables);
2930 SetMenuEnables(gnuEnables);
2936 SetMenuEnables(cmailEnables);
2942 SetMenuEnables(trainingOnEnables);
2943 if (appData.showButtonBar) {
2944 XtSetSensitive(buttonBarWidget, False);
2950 SetTrainingModeOff()
2952 SetMenuEnables(trainingOffEnables);
2953 if (appData.showButtonBar) {
2954 XtSetSensitive(buttonBarWidget, True);
2959 SetUserThinkingEnables()
2961 if (appData.noChessProgram) return;
2962 SetMenuEnables(userThinkingEnables);
2966 SetMachineThinkingEnables()
2968 if (appData.noChessProgram) return;
2969 SetMenuEnables(machineThinkingEnables);
2971 case MachinePlaysBlack:
2972 case MachinePlaysWhite:
2973 case TwoMachinesPlay:
2974 XtSetSensitive(XtNameToWidget(menuBarWidget,
2975 ModeToWidgetName(gameMode)), True);
2982 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
2983 #define HISTORY_SIZE 64
2984 static char *history[HISTORY_SIZE];
2985 int histIn = 0, histP = 0;
2988 SaveInHistory(char *cmd)
2990 if (history[histIn] != NULL) {
2991 free(history[histIn]);
2992 history[histIn] = NULL;
2994 if (*cmd == NULLCHAR) return;
2995 history[histIn] = StrSave(cmd);
2996 histIn = (histIn + 1) % HISTORY_SIZE;
2997 if (history[histIn] != NULL) {
2998 free(history[histIn]);
2999 history[histIn] = NULL;
3005 PrevInHistory(char *cmd)
3008 if (histP == histIn) {
3009 if (history[histIn] != NULL) free(history[histIn]);
3010 history[histIn] = StrSave(cmd);
3012 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3013 if (newhp == histIn || history[newhp] == NULL) return NULL;
3015 return history[histP];
3021 if (histP == histIn) return NULL;
3022 histP = (histP + 1) % HISTORY_SIZE;
3023 return history[histP];
3025 // end of borrowed code
3027 #define Abs(n) ((n)<0 ? -(n) : (n))
3030 * Find a font that matches "pattern" that is as close as
3031 * possible to the targetPxlSize. Prefer fonts that are k
3032 * pixels smaller to fonts that are k pixels larger. The
3033 * pattern must be in the X Consortium standard format,
3034 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3035 * The return value should be freed with XtFree when no
3039 FindFont(pattern, targetPxlSize)
3043 char **fonts, *p, *best, *scalable, *scalableTail;
3044 int i, j, nfonts, minerr, err, pxlSize;
3047 char **missing_list;
3049 char *def_string, *base_fnt_lst, strInt[3];
3051 XFontStruct **fnt_list;
3053 base_fnt_lst = calloc(1, strlen(pattern) + 3);
3054 snprintf(strInt, sizeof(strInt)/sizeof(strInt[0]), "%d", targetPxlSize);
3055 p = strstr(pattern, "--");
3056 strncpy(base_fnt_lst, pattern, p - pattern + 2);
3057 strcat(base_fnt_lst, strInt);
3058 strcat(base_fnt_lst, strchr(p + 2, '-'));
3060 if ((fntSet = XCreateFontSet(xDisplay,
3064 &def_string)) == NULL) {
3066 fprintf(stderr, _("Unable to create font set.\n"));
3070 nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
3072 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3074 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3075 programName, pattern);
3083 for (i=0; i<nfonts; i++) {
3086 if (*p != '-') continue;
3088 if (*p == NULLCHAR) break;
3089 if (*p++ == '-') j++;
3091 if (j < 7) continue;
3094 scalable = fonts[i];
3097 err = pxlSize - targetPxlSize;
3098 if (Abs(err) < Abs(minerr) ||
3099 (minerr > 0 && err < 0 && -err == minerr)) {
3105 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3106 /* If the error is too big and there is a scalable font,
3107 use the scalable font. */
3108 int headlen = scalableTail - scalable;
3109 p = (char *) XtMalloc(strlen(scalable) + 10);
3110 while (isdigit(*scalableTail)) scalableTail++;
3111 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3113 p = (char *) XtMalloc(strlen(best) + 2);
3114 safeStrCpy(p, best, strlen(best)+1 );
3116 if (appData.debugMode) {
3117 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3118 pattern, targetPxlSize, p);
3121 if (missing_count > 0)
3122 XFreeStringList(missing_list);
3123 XFreeFontSet(xDisplay, fntSet);
3125 XFreeFontNames(fonts);
3131 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3132 // must be called before all non-first callse to CreateGCs()
3133 XtReleaseGC(shellWidget, highlineGC);
3134 XtReleaseGC(shellWidget, lightSquareGC);
3135 XtReleaseGC(shellWidget, darkSquareGC);
3136 XtReleaseGC(shellWidget, lineGC);
3137 if (appData.monoMode) {
3138 if (DefaultDepth(xDisplay, xScreen) == 1) {
3139 XtReleaseGC(shellWidget, wbPieceGC);
3141 XtReleaseGC(shellWidget, bwPieceGC);
3144 XtReleaseGC(shellWidget, prelineGC);
3145 XtReleaseGC(shellWidget, jailSquareGC);
3146 XtReleaseGC(shellWidget, wdPieceGC);
3147 XtReleaseGC(shellWidget, wlPieceGC);
3148 XtReleaseGC(shellWidget, wjPieceGC);
3149 XtReleaseGC(shellWidget, bdPieceGC);
3150 XtReleaseGC(shellWidget, blPieceGC);
3151 XtReleaseGC(shellWidget, bjPieceGC);
3155 void CreateGCs(int redo)
3157 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3158 | GCBackground | GCFunction | GCPlaneMask;
3159 XGCValues gc_values;
3162 gc_values.plane_mask = AllPlanes;
3163 gc_values.line_width = lineGap;
3164 gc_values.line_style = LineSolid;
3165 gc_values.function = GXcopy;
3168 DeleteGCs(); // called a second time; clean up old GCs first
3169 } else { // [HGM] grid and font GCs created on first call only
3170 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3171 gc_values.background = XWhitePixel(xDisplay, xScreen);
3172 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3173 XSetFont(xDisplay, coordGC, coordFontID);
3175 // [HGM] make font for holdings counts (white on black)
3176 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3177 gc_values.background = XBlackPixel(xDisplay, xScreen);
3178 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3179 XSetFont(xDisplay, countGC, countFontID);
3181 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3182 gc_values.background = XBlackPixel(xDisplay, xScreen);
3183 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3185 if (appData.monoMode) {
3186 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3187 gc_values.background = XWhitePixel(xDisplay, xScreen);
3188 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3190 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3191 gc_values.background = XBlackPixel(xDisplay, xScreen);
3192 lightSquareGC = wbPieceGC
3193 = XtGetGC(shellWidget, value_mask, &gc_values);
3195 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3196 gc_values.background = XWhitePixel(xDisplay, xScreen);
3197 darkSquareGC = bwPieceGC
3198 = XtGetGC(shellWidget, value_mask, &gc_values);
3200 if (DefaultDepth(xDisplay, xScreen) == 1) {
3201 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3202 gc_values.function = GXcopyInverted;
3203 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3204 gc_values.function = GXcopy;
3205 if (XBlackPixel(xDisplay, xScreen) == 1) {
3206 bwPieceGC = darkSquareGC;
3207 wbPieceGC = copyInvertedGC;
3209 bwPieceGC = copyInvertedGC;
3210 wbPieceGC = lightSquareGC;
3214 gc_values.foreground = highlightSquareColor;
3215 gc_values.background = highlightSquareColor;
3216 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3218 gc_values.foreground = premoveHighlightColor;
3219 gc_values.background = premoveHighlightColor;
3220 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3222 gc_values.foreground = lightSquareColor;
3223 gc_values.background = darkSquareColor;
3224 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3226 gc_values.foreground = darkSquareColor;
3227 gc_values.background = lightSquareColor;
3228 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3230 gc_values.foreground = jailSquareColor;
3231 gc_values.background = jailSquareColor;
3232 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3234 gc_values.foreground = whitePieceColor;
3235 gc_values.background = darkSquareColor;
3236 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3238 gc_values.foreground = whitePieceColor;
3239 gc_values.background = lightSquareColor;
3240 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3242 gc_values.foreground = whitePieceColor;
3243 gc_values.background = jailSquareColor;
3244 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3246 gc_values.foreground = blackPieceColor;
3247 gc_values.background = darkSquareColor;
3248 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3250 gc_values.foreground = blackPieceColor;
3251 gc_values.background = lightSquareColor;
3252 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3254 gc_values.foreground = blackPieceColor;
3255 gc_values.background = jailSquareColor;
3256 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3260 void loadXIM(xim, xmask, filename, dest, mask)
3273 fp = fopen(filename, "rb");
3275 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3282 for (y=0; y<h; ++y) {
3283 for (x=0; x<h; ++x) {
3288 XPutPixel(xim, x, y, blackPieceColor);
3290 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3293 XPutPixel(xim, x, y, darkSquareColor);
3295 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3298 XPutPixel(xim, x, y, whitePieceColor);
3300 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3303 XPutPixel(xim, x, y, lightSquareColor);
3305 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3313 /* create Pixmap of piece */
3314 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3316 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3319 /* create Pixmap of clipmask
3320 Note: We assume the white/black pieces have the same
3321 outline, so we make only 6 masks. This is okay
3322 since the XPM clipmask routines do the same. */
3324 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3326 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3329 /* now create the 1-bit version */
3330 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3333 values.foreground = 1;
3334 values.background = 0;
3336 /* Don't use XtGetGC, not read only */
3337 maskGC = XCreateGC(xDisplay, *mask,
3338 GCForeground | GCBackground, &values);
3339 XCopyPlane(xDisplay, temp, *mask, maskGC,
3340 0, 0, squareSize, squareSize, 0, 0, 1);
3341 XFreePixmap(xDisplay, temp);
3346 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3348 void CreateXIMPieces()
3353 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3358 /* The XSynchronize calls were copied from CreatePieces.
3359 Not sure if needed, but can't hurt */
3360 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3363 /* temp needed by loadXIM() */
3364 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3365 0, 0, ss, ss, AllPlanes, XYPixmap);
3367 if (strlen(appData.pixmapDirectory) == 0) {
3371 if (appData.monoMode) {
3372 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3376 fprintf(stderr, _("\nLoading XIMs...\n"));
3378 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3379 fprintf(stderr, "%d", piece+1);
3380 for (kind=0; kind<4; kind++) {
3381 fprintf(stderr, ".");
3382 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3383 ExpandPathName(appData.pixmapDirectory),
3384 piece <= (int) WhiteKing ? "" : "w",
3385 pieceBitmapNames[piece],
3387 ximPieceBitmap[kind][piece] =
3388 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3389 0, 0, ss, ss, AllPlanes, XYPixmap);
3390 if (appData.debugMode)
3391 fprintf(stderr, _("(File:%s:) "), buf);
3392 loadXIM(ximPieceBitmap[kind][piece],
3394 &(xpmPieceBitmap2[kind][piece]),
3395 &(ximMaskPm2[piece]));
3396 if(piece <= (int)WhiteKing)
3397 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3399 fprintf(stderr," ");
3401 /* Load light and dark squares */
3402 /* If the LSQ and DSQ pieces don't exist, we will
3403 draw them with solid squares. */
3404 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3405 if (access(buf, 0) != 0) {
3409 fprintf(stderr, _("light square "));
3411 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3412 0, 0, ss, ss, AllPlanes, XYPixmap);
3413 if (appData.debugMode)
3414 fprintf(stderr, _("(File:%s:) "), buf);
3416 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3417 fprintf(stderr, _("dark square "));
3418 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3419 ExpandPathName(appData.pixmapDirectory), ss);
3420 if (appData.debugMode)
3421 fprintf(stderr, _("(File:%s:) "), buf);
3423 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3424 0, 0, ss, ss, AllPlanes, XYPixmap);
3425 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3426 xpmJailSquare = xpmLightSquare;
3428 fprintf(stderr, _("Done.\n"));
3430 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3433 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3436 void CreateXPMBoard(char *s, int kind)
3440 if(s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3441 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3442 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3446 void FreeXPMPieces()
3447 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3448 // thisroutine has to be called t free the old piece pixmaps
3450 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3451 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3453 XFreePixmap(xDisplay, xpmLightSquare);
3454 XFreePixmap(xDisplay, xpmDarkSquare);
3458 void CreateXPMPieces()
3462 u_int ss = squareSize;
3464 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3465 XpmColorSymbol symbols[4];
3466 static int redo = False;
3468 if(redo) FreeXPMPieces(); else redo = 1;
3470 /* The XSynchronize calls were copied from CreatePieces.
3471 Not sure if needed, but can't hurt */
3472 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3474 /* Setup translations so piece colors match square colors */
3475 symbols[0].name = "light_piece";
3476 symbols[0].value = appData.whitePieceColor;
3477 symbols[1].name = "dark_piece";
3478 symbols[1].value = appData.blackPieceColor;
3479 symbols[2].name = "light_square";
3480 symbols[2].value = appData.lightSquareColor;
3481 symbols[3].name = "dark_square";
3482 symbols[3].value = appData.darkSquareColor;
3484 attr.valuemask = XpmColorSymbols;
3485 attr.colorsymbols = symbols;
3486 attr.numsymbols = 4;
3488 if (appData.monoMode) {
3489 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3493 if (strlen(appData.pixmapDirectory) == 0) {
3494 XpmPieces* pieces = builtInXpms;
3497 while (pieces->size != squareSize && pieces->size) pieces++;
3498 if (!pieces->size) {
3499 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3502 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3503 for (kind=0; kind<4; kind++) {
3505 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3506 pieces->xpm[piece][kind],
3507 &(xpmPieceBitmap2[kind][piece]),
3508 NULL, &attr)) != 0) {
3509 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3513 if(piece <= (int) WhiteKing)
3514 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3518 xpmJailSquare = xpmLightSquare;
3522 fprintf(stderr, _("\nLoading XPMs...\n"));
3525 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3526 fprintf(stderr, "%d ", piece+1);
3527 for (kind=0; kind<4; kind++) {
3528 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3529 ExpandPathName(appData.pixmapDirectory),
3530 piece > (int) WhiteKing ? "w" : "",
3531 pieceBitmapNames[piece],
3533 if (appData.debugMode) {
3534 fprintf(stderr, _("(File:%s:) "), buf);
3536 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3537 &(xpmPieceBitmap2[kind][piece]),
3538 NULL, &attr)) != 0) {
3539 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3540 // [HGM] missing: read of unorthodox piece failed; substitute King.
3541 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3542 ExpandPathName(appData.pixmapDirectory),
3544 if (appData.debugMode) {
3545 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3547 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3548 &(xpmPieceBitmap2[kind][piece]),
3552 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3557 if(piece <= (int) WhiteKing)
3558 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3561 /* Load light and dark squares */
3562 /* If the LSQ and DSQ pieces don't exist, we will
3563 draw them with solid squares. */
3564 fprintf(stderr, _("light square "));
3565 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3566 if (access(buf, 0) != 0) {
3570 if (appData.debugMode)
3571 fprintf(stderr, _("(File:%s:) "), buf);
3573 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3574 &xpmLightSquare, NULL, &attr)) != 0) {
3575 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3578 fprintf(stderr, _("dark square "));
3579 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3580 ExpandPathName(appData.pixmapDirectory), ss);
3581 if (appData.debugMode) {
3582 fprintf(stderr, _("(File:%s:) "), buf);
3584 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3585 &xpmDarkSquare, NULL, &attr)) != 0) {
3586 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3590 xpmJailSquare = xpmLightSquare;
3591 fprintf(stderr, _("Done.\n"));
3593 oldVariant = -1; // kludge to force re-makig of animation masks
3594 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3597 #endif /* HAVE_LIBXPM */
3600 /* No built-in bitmaps */
3605 u_int ss = squareSize;
3607 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3610 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3611 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3612 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3613 pieceBitmapNames[piece],
3614 ss, kind == SOLID ? 's' : 'o');
3615 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3616 if(piece <= (int)WhiteKing)
3617 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3621 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3625 /* With built-in bitmaps */
3628 BuiltInBits* bib = builtInBits;
3631 u_int ss = squareSize;
3633 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3636 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3638 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3639 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3640 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3641 pieceBitmapNames[piece],
3642 ss, kind == SOLID ? 's' : 'o');
3643 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3644 bib->bits[kind][piece], ss, ss);
3645 if(piece <= (int)WhiteKing)
3646 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3650 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3655 void ReadBitmap(pm, name, bits, wreq, hreq)
3658 unsigned char bits[];
3664 char msg[MSG_SIZ], fullname[MSG_SIZ];
3666 if (*appData.bitmapDirectory != NULLCHAR) {
3667 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3668 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3669 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3670 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3671 &w, &h, pm, &x_hot, &y_hot);
3672 fprintf(stderr, "load %s\n", name);
3673 if (errcode != BitmapSuccess) {
3675 case BitmapOpenFailed:
3676 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3678 case BitmapFileInvalid:
3679 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3681 case BitmapNoMemory:
3682 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3686 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3690 fprintf(stderr, _("%s: %s...using built-in\n"),
3692 } else if (w != wreq || h != hreq) {
3694 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3695 programName, fullname, w, h, wreq, hreq);
3701 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3710 if (lineGap == 0) return;
3712 /* [HR] Split this into 2 loops for non-square boards. */
3714 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3715 gridSegments[i].x1 = 0;
3716 gridSegments[i].x2 =
3717 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3718 gridSegments[i].y1 = gridSegments[i].y2
3719 = lineGap / 2 + (i * (squareSize + lineGap));
3722 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3723 gridSegments[j + i].y1 = 0;
3724 gridSegments[j + i].y2 =
3725 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3726 gridSegments[j + i].x1 = gridSegments[j + i].x2
3727 = lineGap / 2 + (j * (squareSize + lineGap));
3731 static void MenuBarSelect(w, addr, index)
3736 XtActionProc proc = (XtActionProc) addr;
3738 (proc)(NULL, NULL, NULL, NULL);
3741 void CreateMenuBarPopup(parent, name, mb)
3751 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3754 XtSetArg(args[j], XtNleftMargin, 20); j++;
3755 XtSetArg(args[j], XtNrightMargin, 20); j++;
3757 while (mi->string != NULL) {
3758 if (strcmp(mi->string, "----") == 0) {
3759 entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
3762 XtSetArg(args[j], XtNlabel, XtNewString(mi->string));
3763 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3765 XtAddCallback(entry, XtNcallback,
3766 (XtCallbackProc) MenuBarSelect,
3767 (caddr_t) mi->proc);
3773 Widget CreateMenuBar(mb)
3777 Widget anchor, menuBar;
3779 char menuName[MSG_SIZ];
3782 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3783 XtSetArg(args[j], XtNvSpace, 0); j++;
3784 XtSetArg(args[j], XtNborderWidth, 0); j++;
3785 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3786 formWidget, args, j);
3788 while (mb->name != NULL) {
3789 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3790 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3792 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3795 shortName[0] = mb->name[0];
3796 shortName[1] = NULLCHAR;
3797 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3800 XtSetArg(args[j], XtNlabel, XtNewString(mb->name)); j++;
3803 XtSetArg(args[j], XtNborderWidth, 0); j++;
3804 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3806 CreateMenuBarPopup(menuBar, menuName, mb);
3812 Widget CreateButtonBar(mi)
3816 Widget button, buttonBar;
3820 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3822 XtSetArg(args[j], XtNhSpace, 0); j++;
3824 XtSetArg(args[j], XtNborderWidth, 0); j++;
3825 XtSetArg(args[j], XtNvSpace, 0); j++;
3826 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3827 formWidget, args, j);
3829 while (mi->string != NULL) {
3832 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3833 XtSetArg(args[j], XtNborderWidth, 0); j++;
3835 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3836 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3837 buttonBar, args, j);
3838 XtAddCallback(button, XtNcallback,
3839 (XtCallbackProc) MenuBarSelect,
3840 (caddr_t) mi->proc);
3847 CreatePieceMenu(name, color)
3854 ChessSquare selection;
3856 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3857 boardWidget, args, 0);
3859 for (i = 0; i < PIECE_MENU_SIZE; i++) {
3860 String item = pieceMenuStrings[color][i];
3862 if (strcmp(item, "----") == 0) {
3863 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3866 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3867 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3869 selection = pieceMenuTranslation[color][i];
3870 XtAddCallback(entry, XtNcallback,
3871 (XtCallbackProc) PieceMenuSelect,
3872 (caddr_t) selection);
3873 if (selection == WhitePawn || selection == BlackPawn) {
3874 XtSetArg(args[0], XtNpopupOnEntry, entry);
3875 XtSetValues(menu, args, 1);
3888 ChessSquare selection;
3890 whitePieceMenu = CreatePieceMenu("menuW", 0);
3891 blackPieceMenu = CreatePieceMenu("menuB", 1);
3893 XtRegisterGrabAction(PieceMenuPopup, True,
3894 (unsigned)(ButtonPressMask|ButtonReleaseMask),
3895 GrabModeAsync, GrabModeAsync);
3897 XtSetArg(args[0], XtNlabel, _("Drop"));
3898 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3899 boardWidget, args, 1);
3900 for (i = 0; i < DROP_MENU_SIZE; i++) {
3901 String item = dropMenuStrings[i];
3903 if (strcmp(item, "----") == 0) {
3904 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3907 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3908 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3910 selection = dropMenuTranslation[i];
3911 XtAddCallback(entry, XtNcallback,
3912 (XtCallbackProc) DropMenuSelect,
3913 (caddr_t) selection);
3918 void SetupDropMenu()
3926 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3927 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3928 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3929 dmEnables[i].piece);
3930 XtSetSensitive(entry, p != NULL || !appData.testLegality
3931 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3932 && !appData.icsActive));
3934 while (p && *p++ == dmEnables[i].piece) count++;
3935 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3937 XtSetArg(args[j], XtNlabel, label); j++;
3938 XtSetValues(entry, args, j);
3942 void PieceMenuPopup(w, event, params, num_params)
3946 Cardinal *num_params;
3948 String whichMenu; int menuNr;
3949 if (event->type == ButtonRelease)
3950 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3951 else if (event->type == ButtonPress)
3952 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3954 case 0: whichMenu = params[0]; break;
3955 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3957 case -1: if (errorUp) ErrorPopDown();
3960 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3963 static void PieceMenuSelect(w, piece, junk)
3968 if (pmFromX < 0 || pmFromY < 0) return;
3969 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3972 static void DropMenuSelect(w, piece, junk)
3977 if (pmFromX < 0 || pmFromY < 0) return;
3978 DropMenuEvent(piece, pmFromX, pmFromY);
3981 void WhiteClock(w, event, prms, nprms)
3990 void BlackClock(w, event, prms, nprms)
4001 * If the user selects on a border boundary, return -1; if off the board,
4002 * return -2. Otherwise map the event coordinate to the square.
4004 int EventToSquare(x, limit)
4012 if ((x % (squareSize + lineGap)) >= squareSize)
4014 x /= (squareSize + lineGap);
4020 static void do_flash_delay(msec)
4026 static void drawHighlight(file, rank, gc)
4032 if (lineGap == 0) return;
4035 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4036 (squareSize + lineGap);
4037 y = lineGap/2 + rank * (squareSize + lineGap);
4039 x = lineGap/2 + file * (squareSize + lineGap);
4040 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4041 (squareSize + lineGap);
4044 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4045 squareSize+lineGap, squareSize+lineGap);
4048 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4049 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4052 SetHighlights(fromX, fromY, toX, toY)
4053 int fromX, fromY, toX, toY;
4055 if (hi1X != fromX || hi1Y != fromY) {
4056 if (hi1X >= 0 && hi1Y >= 0) {
4057 drawHighlight(hi1X, hi1Y, lineGC);
4059 } // [HGM] first erase both, then draw new!
4060 if (hi2X != toX || hi2Y != toY) {
4061 if (hi2X >= 0 && hi2Y >= 0) {
4062 drawHighlight(hi2X, hi2Y, lineGC);
4065 if (hi1X != fromX || hi1Y != fromY) {
4066 if (fromX >= 0 && fromY >= 0) {
4067 drawHighlight(fromX, fromY, highlineGC);
4070 if (hi2X != toX || hi2Y != toY) {
4071 if (toX >= 0 && toY >= 0) {
4072 drawHighlight(toX, toY, highlineGC);
4084 SetHighlights(-1, -1, -1, -1);
4089 SetPremoveHighlights(fromX, fromY, toX, toY)
4090 int fromX, fromY, toX, toY;
4092 if (pm1X != fromX || pm1Y != fromY) {
4093 if (pm1X >= 0 && pm1Y >= 0) {
4094 drawHighlight(pm1X, pm1Y, lineGC);
4096 if (fromX >= 0 && fromY >= 0) {
4097 drawHighlight(fromX, fromY, prelineGC);
4100 if (pm2X != toX || pm2Y != toY) {
4101 if (pm2X >= 0 && pm2Y >= 0) {
4102 drawHighlight(pm2X, pm2Y, lineGC);
4104 if (toX >= 0 && toY >= 0) {
4105 drawHighlight(toX, toY, prelineGC);
4115 ClearPremoveHighlights()
4117 SetPremoveHighlights(-1, -1, -1, -1);
4120 static int CutOutSquare(x, y, x0, y0, kind)
4121 int x, y, *x0, *y0, kind;
4123 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4124 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4126 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4127 if(textureW[kind] < W*squareSize)
4128 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4130 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4131 if(textureH[kind] < H*squareSize)
4132 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4134 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4138 static void BlankSquare(x, y, color, piece, dest, fac)
4139 int x, y, color, fac;
4142 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4144 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4145 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4146 squareSize, squareSize, x*fac, y*fac);
4148 if (useImages && useImageSqs) {
4152 pm = xpmLightSquare;
4157 case 2: /* neutral */
4162 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4163 squareSize, squareSize, x*fac, y*fac);
4173 case 2: /* neutral */
4178 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4183 I split out the routines to draw a piece so that I could
4184 make a generic flash routine.
4186 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4188 int square_color, x, y;
4191 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4192 switch (square_color) {
4194 case 2: /* neutral */
4196 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4197 ? *pieceToOutline(piece)
4198 : *pieceToSolid(piece),
4199 dest, bwPieceGC, 0, 0,
4200 squareSize, squareSize, x, y);
4203 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4204 ? *pieceToSolid(piece)
4205 : *pieceToOutline(piece),
4206 dest, wbPieceGC, 0, 0,
4207 squareSize, squareSize, x, y);
4212 static void monoDrawPiece(piece, square_color, x, y, dest)
4214 int square_color, x, y;
4217 switch (square_color) {
4219 case 2: /* neutral */
4221 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4222 ? *pieceToOutline(piece)
4223 : *pieceToSolid(piece),
4224 dest, bwPieceGC, 0, 0,
4225 squareSize, squareSize, x, y, 1);
4228 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4229 ? *pieceToSolid(piece)
4230 : *pieceToOutline(piece),
4231 dest, wbPieceGC, 0, 0,
4232 squareSize, squareSize, x, y, 1);
4237 static void colorDrawPiece(piece, square_color, x, y, dest)
4239 int square_color, x, y;
4242 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4243 switch (square_color) {
4245 XCopyPlane(xDisplay, *pieceToSolid(piece),
4246 dest, (int) piece < (int) BlackPawn
4247 ? wlPieceGC : blPieceGC, 0, 0,
4248 squareSize, squareSize, x, y, 1);
4251 XCopyPlane(xDisplay, *pieceToSolid(piece),
4252 dest, (int) piece < (int) BlackPawn
4253 ? wdPieceGC : bdPieceGC, 0, 0,
4254 squareSize, squareSize, x, y, 1);
4256 case 2: /* neutral */
4258 XCopyPlane(xDisplay, *pieceToSolid(piece),
4259 dest, (int) piece < (int) BlackPawn
4260 ? wjPieceGC : bjPieceGC, 0, 0,
4261 squareSize, squareSize, x, y, 1);
4266 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4268 int square_color, x, y;
4271 int kind, p = piece;
4273 switch (square_color) {
4275 case 2: /* neutral */
4277 if ((int)piece < (int) BlackPawn) {
4285 if ((int)piece < (int) BlackPawn) {
4293 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4294 if(useTexture & square_color+1) {
4295 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4296 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4297 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4298 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4299 XSetClipMask(xDisplay, wlPieceGC, None);
4300 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4302 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4303 dest, wlPieceGC, 0, 0,
4304 squareSize, squareSize, x, y);
4307 typedef void (*DrawFunc)();
4309 DrawFunc ChooseDrawFunc()
4311 if (appData.monoMode) {
4312 if (DefaultDepth(xDisplay, xScreen) == 1) {
4313 return monoDrawPiece_1bit;
4315 return monoDrawPiece;
4319 return colorDrawPieceImage;
4321 return colorDrawPiece;
4325 /* [HR] determine square color depending on chess variant. */
4326 static int SquareColor(row, column)
4331 if (gameInfo.variant == VariantXiangqi) {
4332 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4334 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4336 } else if (row <= 4) {
4342 square_color = ((column + row) % 2) == 1;
4345 /* [hgm] holdings: next line makes all holdings squares light */
4346 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4348 return square_color;
4351 void DrawSquare(row, column, piece, do_flash)
4352 int row, column, do_flash;
4355 int square_color, x, y, direction, font_ascent, font_descent;
4358 XCharStruct overall;
4362 /* Calculate delay in milliseconds (2-delays per complete flash) */
4363 flash_delay = 500 / appData.flashRate;
4366 x = lineGap + ((BOARD_WIDTH-1)-column) *
4367 (squareSize + lineGap);
4368 y = lineGap + row * (squareSize + lineGap);
4370 x = lineGap + column * (squareSize + lineGap);
4371 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4372 (squareSize + lineGap);
4375 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4377 square_color = SquareColor(row, column);
4379 if ( // [HGM] holdings: blank out area between board and holdings
4380 column == BOARD_LEFT-1 || column == BOARD_RGHT
4381 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4382 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4383 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4385 // [HGM] print piece counts next to holdings
4386 string[1] = NULLCHAR;
4387 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4388 string[0] = '0' + piece;
4389 XTextExtents(countFontStruct, string, 1, &direction,
4390 &font_ascent, &font_descent, &overall);
4391 if (appData.monoMode) {
4392 XDrawImageString(xDisplay, xBoardWindow, countGC,
4393 x + squareSize - overall.width - 2,
4394 y + font_ascent + 1, string, 1);
4396 XDrawString(xDisplay, xBoardWindow, countGC,
4397 x + squareSize - overall.width - 2,
4398 y + font_ascent + 1, string, 1);
4401 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4402 string[0] = '0' + piece;
4403 XTextExtents(countFontStruct, string, 1, &direction,
4404 &font_ascent, &font_descent, &overall);
4405 if (appData.monoMode) {
4406 XDrawImageString(xDisplay, xBoardWindow, countGC,
4407 x + 2, y + font_ascent + 1, string, 1);
4409 XDrawString(xDisplay, xBoardWindow, countGC,
4410 x + 2, y + font_ascent + 1, string, 1);
4414 if (piece == EmptySquare || appData.blindfold) {
4415 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4417 drawfunc = ChooseDrawFunc();
4419 if (do_flash && appData.flashCount > 0) {
4420 for (i=0; i<appData.flashCount; ++i) {
4421 drawfunc(piece, square_color, x, y, xBoardWindow);
4422 XSync(xDisplay, False);
4423 do_flash_delay(flash_delay);
4425 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4426 XSync(xDisplay, False);
4427 do_flash_delay(flash_delay);
4430 drawfunc(piece, square_color, x, y, xBoardWindow);
4434 string[1] = NULLCHAR;
4435 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4436 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4437 string[0] = 'a' + column - BOARD_LEFT;
4438 XTextExtents(coordFontStruct, string, 1, &direction,
4439 &font_ascent, &font_descent, &overall);
4440 if (appData.monoMode) {
4441 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4442 x + squareSize - overall.width - 2,
4443 y + squareSize - font_descent - 1, string, 1);
4445 XDrawString(xDisplay, xBoardWindow, coordGC,
4446 x + squareSize - overall.width - 2,
4447 y + squareSize - font_descent - 1, string, 1);
4450 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4451 string[0] = ONE + row;
4452 XTextExtents(coordFontStruct, string, 1, &direction,
4453 &font_ascent, &font_descent, &overall);
4454 if (appData.monoMode) {
4455 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4456 x + 2, y + font_ascent + 1, string, 1);
4458 XDrawString(xDisplay, xBoardWindow, coordGC,
4459 x + 2, y + font_ascent + 1, string, 1);
4462 if(!partnerUp && marker[row][column]) {
4463 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4464 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4469 /* Why is this needed on some versions of X? */
4470 void EventProc(widget, unused, event)
4475 if (!XtIsRealized(widget))
4478 switch (event->type) {
4480 if (event->xexpose.count > 0) return; /* no clipping is done */
4481 XDrawPosition(widget, True, NULL);
4482 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4483 flipView = !flipView; partnerUp = !partnerUp;
4484 XDrawPosition(widget, True, NULL);
4485 flipView = !flipView; partnerUp = !partnerUp;
4489 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4496 void DrawPosition(fullRedraw, board)
4497 /*Boolean*/int fullRedraw;
4500 XDrawPosition(boardWidget, fullRedraw, board);
4503 /* Returns 1 if there are "too many" differences between b1 and b2
4504 (i.e. more than 1 move was made) */
4505 static int too_many_diffs(b1, b2)
4511 for (i=0; i<BOARD_HEIGHT; ++i) {
4512 for (j=0; j<BOARD_WIDTH; ++j) {
4513 if (b1[i][j] != b2[i][j]) {
4514 if (++c > 4) /* Castling causes 4 diffs */
4522 /* Matrix describing castling maneuvers */
4523 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4524 static int castling_matrix[4][5] = {
4525 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4526 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4527 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4528 { 7, 7, 4, 5, 6 } /* 0-0, black */
4531 /* Checks whether castling occurred. If it did, *rrow and *rcol
4532 are set to the destination (row,col) of the rook that moved.
4534 Returns 1 if castling occurred, 0 if not.
4536 Note: Only handles a max of 1 castling move, so be sure
4537 to call too_many_diffs() first.
4539 static int check_castle_draw(newb, oldb, rrow, rcol)
4546 /* For each type of castling... */
4547 for (i=0; i<4; ++i) {
4548 r = castling_matrix[i];
4550 /* Check the 4 squares involved in the castling move */
4552 for (j=1; j<=4; ++j) {
4553 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4560 /* All 4 changed, so it must be a castling move */
4569 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4570 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4572 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4575 void DrawSeekBackground( int left, int top, int right, int bottom )
4577 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4580 void DrawSeekText(char *buf, int x, int y)
4582 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4585 void DrawSeekDot(int x, int y, int colorNr)
4587 int square = colorNr & 0x80;
4590 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4592 XFillRectangle(xDisplay, xBoardWindow, color,
4593 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4595 XFillArc(xDisplay, xBoardWindow, color,
4596 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4599 static int damage[2][BOARD_RANKS][BOARD_FILES];
4602 * event handler for redrawing the board
4604 void XDrawPosition(w, repaint, board)
4606 /*Boolean*/int repaint;
4610 static int lastFlipView = 0;
4611 static int lastBoardValid[2] = {0, 0};
4612 static Board lastBoard[2];
4615 int nr = twoBoards*partnerUp;
4617 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4619 if (board == NULL) {
4620 if (!lastBoardValid[nr]) return;
4621 board = lastBoard[nr];
4623 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4624 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4625 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4630 * It would be simpler to clear the window with XClearWindow()
4631 * but this causes a very distracting flicker.
4634 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4636 if ( lineGap && IsDrawArrowEnabled())
4637 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4638 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4640 /* If too much changes (begin observing new game, etc.), don't
4642 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4644 /* Special check for castling so we don't flash both the king
4645 and the rook (just flash the king). */
4647 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4648 /* Draw rook with NO flashing. King will be drawn flashing later */
4649 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4650 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4654 /* First pass -- Draw (newly) empty squares and repair damage.
4655 This prevents you from having a piece show up twice while it
4656 is flashing on its new square */
4657 for (i = 0; i < BOARD_HEIGHT; i++)
4658 for (j = 0; j < BOARD_WIDTH; j++)
4659 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4660 || damage[nr][i][j]) {
4661 DrawSquare(i, j, board[i][j], 0);
4662 damage[nr][i][j] = False;
4665 /* Second pass -- Draw piece(s) in new position and flash them */
4666 for (i = 0; i < BOARD_HEIGHT; i++)
4667 for (j = 0; j < BOARD_WIDTH; j++)
4668 if (board[i][j] != lastBoard[nr][i][j]) {
4669 DrawSquare(i, j, board[i][j], do_flash);
4673 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4674 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4675 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4677 for (i = 0; i < BOARD_HEIGHT; i++)
4678 for (j = 0; j < BOARD_WIDTH; j++) {
4679 DrawSquare(i, j, board[i][j], 0);
4680 damage[nr][i][j] = False;
4684 CopyBoard(lastBoard[nr], board);
4685 lastBoardValid[nr] = 1;
4686 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4687 lastFlipView = flipView;
4689 /* Draw highlights */
4690 if (pm1X >= 0 && pm1Y >= 0) {
4691 drawHighlight(pm1X, pm1Y, prelineGC);
4693 if (pm2X >= 0 && pm2Y >= 0) {
4694 drawHighlight(pm2X, pm2Y, prelineGC);
4696 if (hi1X >= 0 && hi1Y >= 0) {
4697 drawHighlight(hi1X, hi1Y, highlineGC);
4699 if (hi2X >= 0 && hi2Y >= 0) {
4700 drawHighlight(hi2X, hi2Y, highlineGC);
4702 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4704 /* If piece being dragged around board, must redraw that too */
4707 XSync(xDisplay, False);
4712 * event handler for redrawing the board
4714 void DrawPositionProc(w, event, prms, nprms)
4720 XDrawPosition(w, True, NULL);
4725 * event handler for parsing user moves
4727 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4728 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4729 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4730 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4731 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4732 // and at the end FinishMove() to perform the move after optional promotion popups.
4733 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4734 void HandleUserMove(w, event, prms, nprms)
4740 if (w != boardWidget || errorExitStatus != -1) return;
4741 if(nprms) shiftKey = !strcmp(prms[0], "1");
4744 if (event->type == ButtonPress) {
4745 XtPopdown(promotionShell);
4746 XtDestroyWidget(promotionShell);
4747 promotionUp = False;
4755 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4756 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4757 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4760 void AnimateUserMove (Widget w, XEvent * event,
4761 String * params, Cardinal * nParams)
4763 DragPieceMove(event->xmotion.x, event->xmotion.y);
4766 void HandlePV (Widget w, XEvent * event,
4767 String * params, Cardinal * nParams)
4768 { // [HGM] pv: walk PV
4769 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4772 static int savedIndex; /* gross that this is global */
4774 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4777 XawTextPosition index, dummy;
4780 XawTextGetSelectionPos(w, &index, &dummy);
4781 XtSetArg(arg, XtNstring, &val);
4782 XtGetValues(w, &arg, 1);
4783 ReplaceComment(savedIndex, val);
4784 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4785 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4788 void EditCommentPopUp(index, title, text)
4793 if (text == NULL) text = "";
4794 NewCommentPopup(title, text, index);
4797 void ICSInputBoxPopUp()
4802 extern Option boxOptions[];
4804 void ICSInputSendText()
4811 edit = boxOptions[0].handle;
4813 XtSetArg(args[j], XtNstring, &val); j++;
4814 XtGetValues(edit, args, j);
4816 SendMultiLineToICS(val);
4817 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4818 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4821 void ICSInputBoxPopDown()
4826 void CommentPopUp(title, text)
4829 savedIndex = currentMove; // [HGM] vari
4830 NewCommentPopup(title, text, currentMove);
4833 void CommentPopDown()
4838 void FileNamePopUp(label, def, filter, proc, openMode)
4845 fileProc = proc; /* I can't see a way not */
4846 fileOpenMode = openMode; /* to use globals here */
4847 { // [HGM] use file-selector dialog stolen from Ghostview
4849 int index; // this is not supported yet
4851 if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
4852 (def[0] ? def : NULL), filter, openMode, NULL, &name))
4853 (void) (*fileProc)(f, index=0, name);
4857 void FileNamePopDown()
4859 if (!filenameUp) return;
4860 XtPopdown(fileNameShell);
4861 XtDestroyWidget(fileNameShell);
4866 void FileNameCallback(w, client_data, call_data)
4868 XtPointer client_data, call_data;
4873 XtSetArg(args[0], XtNlabel, &name);
4874 XtGetValues(w, args, 1);
4876 if (strcmp(name, _("cancel")) == 0) {
4881 FileNameAction(w, NULL, NULL, NULL);
4884 void FileNameAction(w, event, prms, nprms)
4896 name = XawDialogGetValueString(w = XtParent(w));
4898 if ((name != NULL) && (*name != NULLCHAR)) {
4899 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
4900 XtPopdown(w = XtParent(XtParent(w)));
4904 p = strrchr(buf, ' ');
4911 fullname = ExpandPathName(buf);
4913 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
4916 f = fopen(fullname, fileOpenMode);
4918 DisplayError(_("Failed to open file"), errno);
4920 (void) (*fileProc)(f, index, buf);
4927 XtPopdown(w = XtParent(XtParent(w)));
4933 void PromotionPopUp()
4936 Widget dialog, layout;
4938 Dimension bw_width, pw_width;
4942 XtSetArg(args[j], XtNwidth, &bw_width); j++;
4943 XtGetValues(boardWidget, args, j);
4946 XtSetArg(args[j], XtNresizable, True); j++;
4947 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
4949 XtCreatePopupShell("Promotion", transientShellWidgetClass,
4950 shellWidget, args, j);
4952 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
4953 layoutArgs, XtNumber(layoutArgs));
4956 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
4957 XtSetArg(args[j], XtNborderWidth, 0); j++;
4958 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
4961 if(gameInfo.variant != VariantShogi) {
4962 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
4963 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback,
4964 (XtPointer) dialog);
4965 XawDialogAddButton(dialog, _("General"), PromotionCallback,
4966 (XtPointer) dialog);
4967 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback,
4968 (XtPointer) dialog);
4969 XawDialogAddButton(dialog, _("Captain"), PromotionCallback,
4970 (XtPointer) dialog);
4972 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
4973 (XtPointer) dialog);
4974 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
4975 (XtPointer) dialog);
4976 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
4977 (XtPointer) dialog);
4978 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
4979 (XtPointer) dialog);
4981 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
4982 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
4983 gameInfo.variant == VariantGiveaway) {
4984 XawDialogAddButton(dialog, _("King"), PromotionCallback,
4985 (XtPointer) dialog);
4987 if(gameInfo.variant == VariantCapablanca ||
4988 gameInfo.variant == VariantGothic ||
4989 gameInfo.variant == VariantCapaRandom) {
4990 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
4991 (XtPointer) dialog);
4992 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
4993 (XtPointer) dialog);
4995 } else // [HGM] shogi
4997 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
4998 (XtPointer) dialog);
4999 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5000 (XtPointer) dialog);
5002 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5003 (XtPointer) dialog);
5005 XtRealizeWidget(promotionShell);
5006 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5009 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5010 XtGetValues(promotionShell, args, j);
5012 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5013 lineGap + squareSize/3 +
5014 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5015 0 : 6*(squareSize + lineGap)), &x, &y);
5018 XtSetArg(args[j], XtNx, x); j++;
5019 XtSetArg(args[j], XtNy, y); j++;
5020 XtSetValues(promotionShell, args, j);
5022 XtPopup(promotionShell, XtGrabNone);
5027 void PromotionPopDown()
5029 if (!promotionUp) return;
5030 XtPopdown(promotionShell);
5031 XtDestroyWidget(promotionShell);
5032 promotionUp = False;
5035 void PromotionCallback(w, client_data, call_data)
5037 XtPointer client_data, call_data;
5043 XtSetArg(args[0], XtNlabel, &name);
5044 XtGetValues(w, args, 1);
5048 if (fromX == -1) return;
5050 if (strcmp(name, _("cancel")) == 0) {
5054 } else if (strcmp(name, _("Knight")) == 0) {
5056 } else if (strcmp(name, _("Promote")) == 0) {
5058 } else if (strcmp(name, _("Defer")) == 0) {
5061 promoChar = ToLower(name[0]);
5064 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5066 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5067 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5072 void ErrorCallback(w, client_data, call_data)
5074 XtPointer client_data, call_data;
5077 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5079 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5085 if (!errorUp) return;
5087 XtPopdown(errorShell);
5088 XtDestroyWidget(errorShell);
5089 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5092 void ErrorPopUp(title, label, modal)
5093 char *title, *label;
5097 Widget dialog, layout;
5101 Dimension bw_width, pw_width;
5102 Dimension pw_height;
5106 XtSetArg(args[i], XtNresizable, True); i++;
5107 XtSetArg(args[i], XtNtitle, title); i++;
5109 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5110 shellWidget, args, i);
5112 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5113 layoutArgs, XtNumber(layoutArgs));
5116 XtSetArg(args[i], XtNlabel, label); i++;
5117 XtSetArg(args[i], XtNborderWidth, 0); i++;
5118 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5121 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5123 XtRealizeWidget(errorShell);
5124 CatchDeleteWindow(errorShell, "ErrorPopDown");
5127 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5128 XtGetValues(boardWidget, args, i);
5130 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5131 XtSetArg(args[i], XtNheight, &pw_height); i++;
5132 XtGetValues(errorShell, args, i);
5135 /* This code seems to tickle an X bug if it is executed too soon
5136 after xboard starts up. The coordinates get transformed as if
5137 the main window was positioned at (0, 0).
5139 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5140 0 - pw_height + squareSize / 3, &x, &y);
5142 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5143 RootWindowOfScreen(XtScreen(boardWidget)),
5144 (bw_width - pw_width) / 2,
5145 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5149 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5152 XtSetArg(args[i], XtNx, x); i++;
5153 XtSetArg(args[i], XtNy, y); i++;
5154 XtSetValues(errorShell, args, i);
5157 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5160 /* Disable all user input other than deleting the window */
5161 static int frozen = 0;
5165 /* Grab by a widget that doesn't accept input */
5166 XtAddGrab(messageWidget, TRUE, FALSE);
5170 /* Undo a FreezeUI */
5173 if (!frozen) return;
5174 XtRemoveGrab(messageWidget);
5178 char *ModeToWidgetName(mode)
5182 case BeginningOfGame:
5183 if (appData.icsActive)
5184 return "menuMode.ICS Client";
5185 else if (appData.noChessProgram ||
5186 *appData.cmailGameName != NULLCHAR)
5187 return "menuMode.Edit Game";
5189 return "menuMode.Machine Black";
5190 case MachinePlaysBlack:
5191 return "menuMode.Machine Black";
5192 case MachinePlaysWhite:
5193 return "menuMode.Machine White";
5195 return "menuMode.Analysis Mode";
5197 return "menuMode.Analyze File";
5198 case TwoMachinesPlay:
5199 return "menuMode.Two Machines";
5201 return "menuMode.Edit Game";
5202 case PlayFromGameFile:
5203 return "menuFile.Load Game";
5205 return "menuMode.Edit Position";
5207 return "menuMode.Training";
5208 case IcsPlayingWhite:
5209 case IcsPlayingBlack:
5213 return "menuMode.ICS Client";
5220 void ModeHighlight()
5223 static int oldPausing = FALSE;
5224 static GameMode oldmode = (GameMode) -1;
5227 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5229 if (pausing != oldPausing) {
5230 oldPausing = pausing;
5232 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5234 XtSetArg(args[0], XtNleftBitmap, None);
5236 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5239 if (appData.showButtonBar) {
5240 /* Always toggle, don't set. Previous code messes up when
5241 invoked while the button is pressed, as releasing it
5242 toggles the state again. */
5245 XtSetArg(args[0], XtNbackground, &oldbg);
5246 XtSetArg(args[1], XtNforeground, &oldfg);
5247 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5249 XtSetArg(args[0], XtNbackground, oldfg);
5250 XtSetArg(args[1], XtNforeground, oldbg);
5252 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5256 wname = ModeToWidgetName(oldmode);
5257 if (wname != NULL) {
5258 XtSetArg(args[0], XtNleftBitmap, None);
5259 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5261 wname = ModeToWidgetName(gameMode);
5262 if (wname != NULL) {
5263 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5264 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5268 /* Maybe all the enables should be handled here, not just this one */
5269 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5270 gameMode == Training || gameMode == PlayFromGameFile);
5275 * Button/menu procedures
5277 void ResetProc(w, event, prms, nprms)
5286 int LoadGamePopUp(f, gameNumber, title)
5291 cmailMsgLoaded = FALSE;
5292 if (gameNumber == 0) {
5293 int error = GameListBuild(f);
5295 DisplayError(_("Cannot build game list"), error);
5296 } else if (!ListEmpty(&gameList) &&
5297 ((ListGame *) gameList.tailPred)->number > 1) {
5298 GameListPopUp(f, title);
5304 return LoadGame(f, gameNumber, title, FALSE);
5307 void LoadGameProc(w, event, prms, nprms)
5313 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5316 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5319 void LoadNextGameProc(w, event, prms, nprms)
5328 void LoadPrevGameProc(w, event, prms, nprms)
5337 void ReloadGameProc(w, event, prms, nprms)
5346 void LoadNextPositionProc(w, event, prms, nprms)
5355 void LoadPrevPositionProc(w, event, prms, nprms)
5364 void ReloadPositionProc(w, event, prms, nprms)
5373 void LoadPositionProc(w, event, prms, nprms)
5379 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5382 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5385 void SaveGameProc(w, event, prms, nprms)
5391 FileNamePopUp(_("Save game file name?"),
5392 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5393 appData.oldSaveStyle ? ".game" : ".pgn",
5397 void SavePositionProc(w, event, prms, nprms)
5403 FileNamePopUp(_("Save position file name?"),
5404 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5405 appData.oldSaveStyle ? ".pos" : ".fen",
5409 void ReloadCmailMsgProc(w, event, prms, nprms)
5415 ReloadCmailMsgEvent(FALSE);
5418 void MailMoveProc(w, event, prms, nprms)
5427 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5428 char *selected_fen_position=NULL;
5431 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5432 Atom *type_return, XtPointer *value_return,
5433 unsigned long *length_return, int *format_return)
5435 char *selection_tmp;
5437 if (!selected_fen_position) return False; /* should never happen */
5438 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5439 /* note: since no XtSelectionDoneProc was registered, Xt will
5440 * automatically call XtFree on the value returned. So have to
5441 * make a copy of it allocated with XtMalloc */
5442 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5443 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5445 *value_return=selection_tmp;
5446 *length_return=strlen(selection_tmp);
5447 *type_return=*target;
5448 *format_return = 8; /* bits per byte */
5450 } else if (*target == XA_TARGETS(xDisplay)) {
5451 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5452 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5453 targets_tmp[1] = XA_STRING;
5454 *value_return = targets_tmp;
5455 *type_return = XA_ATOM;
5457 *format_return = 8 * sizeof(Atom);
5458 if (*format_return > 32) {
5459 *length_return *= *format_return / 32;
5460 *format_return = 32;
5468 /* note: when called from menu all parameters are NULL, so no clue what the
5469 * Widget which was clicked on was, or what the click event was
5471 void CopyPositionProc(w, event, prms, nprms)
5478 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5479 * have a notion of a position that is selected but not copied.
5480 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5482 if(gameMode == EditPosition) EditPositionDone(TRUE);
5483 if (selected_fen_position) free(selected_fen_position);
5484 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5485 if (!selected_fen_position) return;
5486 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5488 SendPositionSelection,
5489 NULL/* lose_ownership_proc */ ,
5490 NULL/* transfer_done_proc */);
5491 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5493 SendPositionSelection,
5494 NULL/* lose_ownership_proc */ ,
5495 NULL/* transfer_done_proc */);
5498 /* function called when the data to Paste is ready */
5500 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5501 Atom *type, XtPointer value, unsigned long *len, int *format)
5504 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5505 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5506 EditPositionPasteFEN(fenstr);
5510 /* called when Paste Position button is pressed,
5511 * all parameters will be NULL */
5512 void PastePositionProc(w, event, prms, nprms)
5518 XtGetSelectionValue(menuBarWidget,
5519 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5520 /* (XtSelectionCallbackProc) */ PastePositionCB,
5521 NULL, /* client_data passed to PastePositionCB */
5523 /* better to use the time field from the event that triggered the
5524 * call to this function, but that isn't trivial to get
5532 SendGameSelection(Widget w, Atom *selection, Atom *target,
5533 Atom *type_return, XtPointer *value_return,
5534 unsigned long *length_return, int *format_return)
5536 char *selection_tmp;
5538 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5539 FILE* f = fopen(gameCopyFilename, "r");
5542 if (f == NULL) return False;
5546 selection_tmp = XtMalloc(len + 1);
5547 count = fread(selection_tmp, 1, len, f);
5550 XtFree(selection_tmp);
5553 selection_tmp[len] = NULLCHAR;
5554 *value_return = selection_tmp;
5555 *length_return = len;
5556 *type_return = *target;
5557 *format_return = 8; /* bits per byte */
5559 } else if (*target == XA_TARGETS(xDisplay)) {
5560 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5561 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5562 targets_tmp[1] = XA_STRING;
5563 *value_return = targets_tmp;
5564 *type_return = XA_ATOM;
5566 *format_return = 8 * sizeof(Atom);
5567 if (*format_return > 32) {
5568 *length_return *= *format_return / 32;
5569 *format_return = 32;
5577 /* note: when called from menu all parameters are NULL, so no clue what the
5578 * Widget which was clicked on was, or what the click event was
5580 void CopyGameProc(w, event, prms, nprms)
5588 ret = SaveGameToFile(gameCopyFilename, FALSE);
5592 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5593 * have a notion of a game that is selected but not copied.
5594 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5596 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5599 NULL/* lose_ownership_proc */ ,
5600 NULL/* transfer_done_proc */);
5601 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5604 NULL/* lose_ownership_proc */ ,
5605 NULL/* transfer_done_proc */);
5608 /* function called when the data to Paste is ready */
5610 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5611 Atom *type, XtPointer value, unsigned long *len, int *format)
5614 if (value == NULL || *len == 0) {
5615 return; /* nothing had been selected to copy */
5617 f = fopen(gamePasteFilename, "w");
5619 DisplayError(_("Can't open temp file"), errno);
5622 fwrite(value, 1, *len, f);
5625 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5628 /* called when Paste Game button is pressed,
5629 * all parameters will be NULL */
5630 void PasteGameProc(w, event, prms, nprms)
5636 XtGetSelectionValue(menuBarWidget,
5637 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5638 /* (XtSelectionCallbackProc) */ PasteGameCB,
5639 NULL, /* client_data passed to PasteGameCB */
5641 /* better to use the time field from the event that triggered the
5642 * call to this function, but that isn't trivial to get
5652 SaveGameProc(NULL, NULL, NULL, NULL);
5656 void QuitProc(w, event, prms, nprms)
5665 void PauseProc(w, event, prms, nprms)
5675 void MachineBlackProc(w, event, prms, nprms)
5681 MachineBlackEvent();
5684 void MachineWhiteProc(w, event, prms, nprms)
5690 MachineWhiteEvent();
5693 void AnalyzeModeProc(w, event, prms, nprms)
5701 if (!first.analysisSupport) {
5702 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5703 DisplayError(buf, 0);
5706 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5707 if (appData.icsActive) {
5708 if (gameMode != IcsObserving) {
5709 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5710 DisplayError(buf, 0);
5712 if (appData.icsEngineAnalyze) {
5713 if (appData.debugMode)
5714 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5720 /* if enable, use want disable icsEngineAnalyze */
5721 if (appData.icsEngineAnalyze) {
5726 appData.icsEngineAnalyze = TRUE;
5727 if (appData.debugMode)
5728 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5730 #ifndef OPTIONSDIALOG
5731 if (!appData.showThinking)
5732 ShowThinkingProc(w,event,prms,nprms);
5738 void AnalyzeFileProc(w, event, prms, nprms)
5744 if (!first.analysisSupport) {
5746 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5747 DisplayError(buf, 0);
5751 #ifndef OPTIONSDIALOG
5752 if (!appData.showThinking)
5753 ShowThinkingProc(w,event,prms,nprms);
5756 FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5757 AnalysisPeriodicEvent(1);
5760 void TwoMachinesProc(w, event, prms, nprms)
5769 void MatchProc(w, event, prms, nprms)
5775 if(gameMode != BeginningOfGame) { DisplayError(_("You can only start a match from the initial position."), 0); return; }
5776 matchMode = 2; // This is back-end, really
\r
5777 appData.matchGames = appData.defaultMatchGames;
\r
5779 first.matchWins = second.matchWins = 0;
\r
5783 void IcsClientProc(w, event, prms, nprms)
5792 void EditGameProc(w, event, prms, nprms)
5801 void EditPositionProc(w, event, prms, nprms)
5807 EditPositionEvent();
5810 void TrainingProc(w, event, prms, nprms)
5819 void EditCommentProc(w, event, prms, nprms)
5827 if (PopDown(1)) { // popdown succesful
5829 XtSetArg(args[j], XtNleftBitmap, None); j++;
5830 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
5831 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
5832 } else // was not up
5836 void IcsInputBoxProc(w, event, prms, nprms)
5842 if (!PopDown(4)) ICSInputBoxPopUp();
5845 void AcceptProc(w, event, prms, nprms)
5854 void DeclineProc(w, event, prms, nprms)
5863 void RematchProc(w, event, prms, nprms)
5872 void CallFlagProc(w, event, prms, nprms)
5881 void DrawProc(w, event, prms, nprms)
5890 void AbortProc(w, event, prms, nprms)
5899 void AdjournProc(w, event, prms, nprms)
5908 void ResignProc(w, event, prms, nprms)
5917 void AdjuWhiteProc(w, event, prms, nprms)
5923 UserAdjudicationEvent(+1);
5926 void AdjuBlackProc(w, event, prms, nprms)
5932 UserAdjudicationEvent(-1);
5935 void AdjuDrawProc(w, event, prms, nprms)
5941 UserAdjudicationEvent(0);
5944 void EnterKeyProc(w, event, prms, nprms)
5950 if (shellUp[4] == True)
5954 void UpKeyProc(w, event, prms, nprms)
5959 { // [HGM] input: let up-arrow recall previous line from history
5966 if (!shellUp[4]) return;
5967 edit = boxOptions[0].handle;
5969 XtSetArg(args[j], XtNstring, &val); j++;
5970 XtGetValues(edit, args, j);
5971 val = PrevInHistory(val);
5972 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5973 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5975 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
5976 XawTextReplace(edit, 0, 0, &t);
5977 XawTextSetInsertionPoint(edit, 9999);
5981 void DownKeyProc(w, event, prms, nprms)
5986 { // [HGM] input: let down-arrow recall next line from history
5991 if (!shellUp[4]) return;
5992 edit = boxOptions[0].handle;
5993 val = NextInHistory();
5994 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5995 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5997 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
5998 XawTextReplace(edit, 0, 0, &t);
5999 XawTextSetInsertionPoint(edit, 9999);
6003 void StopObservingProc(w, event, prms, nprms)
6009 StopObservingEvent();
6012 void StopExaminingProc(w, event, prms, nprms)
6018 StopExaminingEvent();
6021 void UploadProc(w, event, prms, nprms)
6031 void ForwardProc(w, event, prms, nprms)
6041 void BackwardProc(w, event, prms, nprms)
6050 void ToStartProc(w, event, prms, nprms)
6059 void ToEndProc(w, event, prms, nprms)
6068 void RevertProc(w, event, prms, nprms)
6077 void AnnotateProc(w, event, prms, nprms)
6086 void TruncateGameProc(w, event, prms, nprms)
6092 TruncateGameEvent();
6094 void RetractMoveProc(w, event, prms, nprms)
6103 void MoveNowProc(w, event, prms, nprms)
6112 void FlipViewProc(w, event, prms, nprms)
6118 flipView = !flipView;
6119 DrawPosition(True, NULL);
6122 void PonderNextMoveProc(w, event, prms, nprms)
6130 PonderNextMoveEvent(!appData.ponderNextMove);
6131 #ifndef OPTIONSDIALOG
6132 if (appData.ponderNextMove) {
6133 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6135 XtSetArg(args[0], XtNleftBitmap, None);
6137 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6142 #ifndef OPTIONSDIALOG
6143 void AlwaysQueenProc(w, event, prms, nprms)
6151 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6153 if (appData.alwaysPromoteToQueen) {
6154 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6156 XtSetArg(args[0], XtNleftBitmap, None);
6158 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6162 void AnimateDraggingProc(w, event, prms, nprms)
6170 appData.animateDragging = !appData.animateDragging;
6172 if (appData.animateDragging) {
6173 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6176 XtSetArg(args[0], XtNleftBitmap, None);
6178 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6182 void AnimateMovingProc(w, event, prms, nprms)
6190 appData.animate = !appData.animate;
6192 if (appData.animate) {
6193 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6196 XtSetArg(args[0], XtNleftBitmap, None);
6198 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6202 void AutoflagProc(w, event, prms, nprms)
6210 appData.autoCallFlag = !appData.autoCallFlag;
6212 if (appData.autoCallFlag) {
6213 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6215 XtSetArg(args[0], XtNleftBitmap, None);
6217 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6221 void AutoflipProc(w, event, prms, nprms)
6229 appData.autoFlipView = !appData.autoFlipView;
6231 if (appData.autoFlipView) {
6232 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6234 XtSetArg(args[0], XtNleftBitmap, None);
6236 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6240 void BlindfoldProc(w, event, prms, nprms)
6248 appData.blindfold = !appData.blindfold;
6250 if (appData.blindfold) {
6251 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6253 XtSetArg(args[0], XtNleftBitmap, None);
6255 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6258 DrawPosition(True, NULL);
6261 void TestLegalityProc(w, event, prms, nprms)
6269 appData.testLegality = !appData.testLegality;
6271 if (appData.testLegality) {
6272 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6274 XtSetArg(args[0], XtNleftBitmap, None);
6276 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6281 void FlashMovesProc(w, event, prms, nprms)
6289 if (appData.flashCount == 0) {
6290 appData.flashCount = 3;
6292 appData.flashCount = -appData.flashCount;
6295 if (appData.flashCount > 0) {
6296 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6298 XtSetArg(args[0], XtNleftBitmap, None);
6300 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6305 void HighlightDraggingProc(w, event, prms, nprms)
6313 appData.highlightDragging = !appData.highlightDragging;
6315 if (appData.highlightDragging) {
6316 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6318 XtSetArg(args[0], XtNleftBitmap, None);
6320 XtSetValues(XtNameToWidget(menuBarWidget,
6321 "menuOptions.Highlight Dragging"), args, 1);
6325 void HighlightLastMoveProc(w, event, prms, nprms)
6333 appData.highlightLastMove = !appData.highlightLastMove;
6335 if (appData.highlightLastMove) {
6336 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6338 XtSetArg(args[0], XtNleftBitmap, None);
6340 XtSetValues(XtNameToWidget(menuBarWidget,
6341 "menuOptions.Highlight Last Move"), args, 1);
6344 void HighlightArrowProc(w, event, prms, nprms)
6352 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6354 if (appData.highlightMoveWithArrow) {
6355 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6357 XtSetArg(args[0], XtNleftBitmap, None);
6359 XtSetValues(XtNameToWidget(menuBarWidget,
6360 "menuOptions.Arrow"), args, 1);
6364 void IcsAlarmProc(w, event, prms, nprms)
6372 appData.icsAlarm = !appData.icsAlarm;
6374 if (appData.icsAlarm) {
6375 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6377 XtSetArg(args[0], XtNleftBitmap, None);
6379 XtSetValues(XtNameToWidget(menuBarWidget,
6380 "menuOptions.ICS Alarm"), args, 1);
6384 void MoveSoundProc(w, event, prms, nprms)
6392 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6394 if (appData.ringBellAfterMoves) {
6395 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6397 XtSetArg(args[0], XtNleftBitmap, None);
6399 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6403 void OneClickProc(w, event, prms, nprms)
6411 appData.oneClick = !appData.oneClick;
6413 if (appData.oneClick) {
6414 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6416 XtSetArg(args[0], XtNleftBitmap, None);
6418 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6422 void PeriodicUpdatesProc(w, event, prms, nprms)
6430 PeriodicUpdatesEvent(!appData.periodicUpdates);
6432 if (appData.periodicUpdates) {
6433 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6435 XtSetArg(args[0], XtNleftBitmap, None);
6437 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6441 void PopupExitMessageProc(w, event, prms, nprms)
6449 appData.popupExitMessage = !appData.popupExitMessage;
6451 if (appData.popupExitMessage) {
6452 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6454 XtSetArg(args[0], XtNleftBitmap, None);
6456 XtSetValues(XtNameToWidget(menuBarWidget,
6457 "menuOptions.Popup Exit Message"), args, 1);
6460 void PopupMoveErrorsProc(w, event, prms, nprms)
6468 appData.popupMoveErrors = !appData.popupMoveErrors;
6470 if (appData.popupMoveErrors) {
6471 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6473 XtSetArg(args[0], XtNleftBitmap, None);
6475 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6480 void PremoveProc(w, event, prms, nprms)
6488 appData.premove = !appData.premove;
6490 if (appData.premove) {
6491 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6493 XtSetArg(args[0], XtNleftBitmap, None);
6495 XtSetValues(XtNameToWidget(menuBarWidget,
6496 "menuOptions.Premove"), args, 1);
6500 void ShowCoordsProc(w, event, prms, nprms)
6508 appData.showCoords = !appData.showCoords;
6510 if (appData.showCoords) {
6511 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6513 XtSetArg(args[0], XtNleftBitmap, None);
6515 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6518 DrawPosition(True, NULL);
6521 void ShowThinkingProc(w, event, prms, nprms)
6527 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6528 ShowThinkingEvent();
6531 void HideThinkingProc(w, event, prms, nprms)
6539 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6540 ShowThinkingEvent();
6542 if (appData.hideThinkingFromHuman) {
6543 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6545 XtSetArg(args[0], XtNleftBitmap, None);
6547 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6552 void SaveOnExitProc(w, event, prms, nprms)
6560 saveSettingsOnExit = !saveSettingsOnExit;
6562 if (saveSettingsOnExit) {
6563 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6565 XtSetArg(args[0], XtNleftBitmap, None);
6567 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6571 void SaveSettingsProc(w, event, prms, nprms)
6577 SaveSettings(settingsFileName);
6580 void InfoProc(w, event, prms, nprms)
6587 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6592 void ManProc(w, event, prms, nprms)
6600 if (nprms && *nprms > 0)
6604 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6608 void HintProc(w, event, prms, nprms)
6617 void BookProc(w, event, prms, nprms)
6626 void AboutProc(w, event, prms, nprms)
6634 char *zippy = " (with Zippy code)";
6638 snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
6639 programVersion, zippy,
6640 "Copyright 1991 Digital Equipment Corporation",
6641 "Enhancements Copyright 1992-2009 Free Software Foundation",
6642 "Enhancements Copyright 2005 Alessandro Scotti",
6643 PACKAGE, " is free software and carries NO WARRANTY;",
6644 "see the file COPYING for more information.");
6645 ErrorPopUp(_("About XBoard"), buf, FALSE);
6648 void DebugProc(w, event, prms, nprms)
6654 appData.debugMode = !appData.debugMode;
6657 void AboutGameProc(w, event, prms, nprms)
6666 void NothingProc(w, event, prms, nprms)
6675 void Iconify(w, event, prms, nprms)
6684 XtSetArg(args[0], XtNiconic, True);
6685 XtSetValues(shellWidget, args, 1);
6688 void DisplayMessage(message, extMessage)
6689 char *message, *extMessage;
6691 /* display a message in the message widget */
6700 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6705 message = extMessage;
6709 /* need to test if messageWidget already exists, since this function
6710 can also be called during the startup, if for example a Xresource
6711 is not set up correctly */
6714 XtSetArg(arg, XtNlabel, message);
6715 XtSetValues(messageWidget, &arg, 1);
6721 void DisplayTitle(text)
6726 char title[MSG_SIZ];
6729 if (text == NULL) text = "";
6731 if (appData.titleInWindow) {
6733 XtSetArg(args[i], XtNlabel, text); i++;
6734 XtSetValues(titleWidget, args, i);
6737 if (*text != NULLCHAR) {
6738 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6739 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6740 } else if (appData.icsActive) {
6741 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6742 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6743 } else if (appData.cmailGameName[0] != NULLCHAR) {
6744 snprintf(icon, sizeof(icon), "%s", "CMail");
6745 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6747 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6748 } else if (gameInfo.variant == VariantGothic) {
6749 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6750 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6753 } else if (gameInfo.variant == VariantFalcon) {
6754 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6755 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6757 } else if (appData.noChessProgram) {
6758 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6759 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6761 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6762 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6765 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6766 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6767 XtSetValues(shellWidget, args, i);
6772 DisplayError(message, error)
6779 if (appData.debugMode || appData.matchMode) {
6780 fprintf(stderr, "%s: %s\n", programName, message);
6783 if (appData.debugMode || appData.matchMode) {
6784 fprintf(stderr, "%s: %s: %s\n",
6785 programName, message, strerror(error));
6787 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6790 ErrorPopUp(_("Error"), message, FALSE);
6794 void DisplayMoveError(message)
6799 DrawPosition(FALSE, NULL);
6800 if (appData.debugMode || appData.matchMode) {
6801 fprintf(stderr, "%s: %s\n", programName, message);
6803 if (appData.popupMoveErrors) {
6804 ErrorPopUp(_("Error"), message, FALSE);
6806 DisplayMessage(message, "");
6811 void DisplayFatalError(message, error, status)
6817 errorExitStatus = status;
6819 fprintf(stderr, "%s: %s\n", programName, message);
6821 fprintf(stderr, "%s: %s: %s\n",
6822 programName, message, strerror(error));
6823 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6826 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6827 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6833 void DisplayInformation(message)
6837 ErrorPopUp(_("Information"), message, TRUE);
6840 void DisplayNote(message)
6844 ErrorPopUp(_("Note"), message, FALSE);
6848 NullXErrorCheck(dpy, error_event)
6850 XErrorEvent *error_event;
6855 void DisplayIcsInteractionTitle(message)
6858 if (oldICSInteractionTitle == NULL) {
6859 /* Magic to find the old window title, adapted from vim */
6860 char *wina = getenv("WINDOWID");
6862 Window win = (Window) atoi(wina);
6863 Window root, parent, *children;
6864 unsigned int nchildren;
6865 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6867 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6868 if (!XQueryTree(xDisplay, win, &root, &parent,
6869 &children, &nchildren)) break;
6870 if (children) XFree((void *)children);
6871 if (parent == root || parent == 0) break;
6874 XSetErrorHandler(oldHandler);
6876 if (oldICSInteractionTitle == NULL) {
6877 oldICSInteractionTitle = "xterm";
6880 printf("\033]0;%s\007", message);
6884 char pendingReplyPrefix[MSG_SIZ];
6885 ProcRef pendingReplyPR;
6887 void AskQuestionProc(w, event, prms, nprms)
6894 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
6898 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6901 void AskQuestionPopDown()
6903 if (!askQuestionUp) return;
6904 XtPopdown(askQuestionShell);
6905 XtDestroyWidget(askQuestionShell);
6906 askQuestionUp = False;
6909 void AskQuestionReplyAction(w, event, prms, nprms)
6919 reply = XawDialogGetValueString(w = XtParent(w));
6920 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
6921 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
6922 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
6923 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
6924 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6925 AskQuestionPopDown();
6927 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
6930 void AskQuestionCallback(w, client_data, call_data)
6932 XtPointer client_data, call_data;
6937 XtSetArg(args[0], XtNlabel, &name);
6938 XtGetValues(w, args, 1);
6940 if (strcmp(name, _("cancel")) == 0) {
6941 AskQuestionPopDown();
6943 AskQuestionReplyAction(w, NULL, NULL, NULL);
6947 void AskQuestion(title, question, replyPrefix, pr)
6948 char *title, *question, *replyPrefix;
6952 Widget popup, layout, dialog, edit;
6958 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
6959 pendingReplyPR = pr;
6962 XtSetArg(args[i], XtNresizable, True); i++;
6963 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
6964 askQuestionShell = popup =
6965 XtCreatePopupShell(title, transientShellWidgetClass,
6966 shellWidget, args, i);
6969 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
6970 layoutArgs, XtNumber(layoutArgs));
6973 XtSetArg(args[i], XtNlabel, question); i++;
6974 XtSetArg(args[i], XtNvalue, ""); i++;
6975 XtSetArg(args[i], XtNborderWidth, 0); i++;
6976 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
6979 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
6980 (XtPointer) dialog);
6981 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
6982 (XtPointer) dialog);
6984 XtRealizeWidget(popup);
6985 CatchDeleteWindow(popup, "AskQuestionPopDown");
6987 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
6988 &x, &y, &win_x, &win_y, &mask);
6990 XtSetArg(args[0], XtNx, x - 10);
6991 XtSetArg(args[1], XtNy, y - 30);
6992 XtSetValues(popup, args, 2);
6994 XtPopup(popup, XtGrabExclusive);
6995 askQuestionUp = True;
6997 edit = XtNameToWidget(dialog, "*value");
6998 XtSetKeyboardFocus(popup, edit);
7006 if (*name == NULLCHAR) {
7008 } else if (strcmp(name, "$") == 0) {
7009 putc(BELLCHAR, stderr);
7012 char *prefix = "", *sep = "";
7013 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7014 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7022 PlaySound(appData.soundMove);
7028 PlaySound(appData.soundIcsWin);
7034 PlaySound(appData.soundIcsLoss);
7040 PlaySound(appData.soundIcsDraw);
7044 PlayIcsUnfinishedSound()
7046 PlaySound(appData.soundIcsUnfinished);
7052 PlaySound(appData.soundIcsAlarm);
7058 system("stty echo");
7064 system("stty -echo");
7068 Colorize(cc, continuation)
7073 int count, outCount, error;
7075 if (textColors[(int)cc].bg > 0) {
7076 if (textColors[(int)cc].fg > 0) {
7077 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7078 textColors[(int)cc].fg, textColors[(int)cc].bg);
7080 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7081 textColors[(int)cc].bg);
7084 if (textColors[(int)cc].fg > 0) {
7085 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7086 textColors[(int)cc].fg);
7088 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7091 count = strlen(buf);
7092 outCount = OutputToProcess(NoProc, buf, count, &error);
7093 if (outCount < count) {
7094 DisplayFatalError(_("Error writing to display"), error, 1);
7097 if (continuation) return;
7100 PlaySound(appData.soundShout);
7103 PlaySound(appData.soundSShout);
7106 PlaySound(appData.soundChannel1);
7109 PlaySound(appData.soundChannel);
7112 PlaySound(appData.soundKibitz);
7115 PlaySound(appData.soundTell);
7117 case ColorChallenge:
7118 PlaySound(appData.soundChallenge);
7121 PlaySound(appData.soundRequest);
7124 PlaySound(appData.soundSeek);
7135 return getpwuid(getuid())->pw_name;
7139 ExpandPathName(path)
7142 static char static_buf[4*MSG_SIZ];
7143 char *d, *s, buf[4*MSG_SIZ];
7149 while (*s && isspace(*s))
7158 if (*(s+1) == '/') {
7159 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7163 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7164 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7165 pwd = getpwnam(buf);
7168 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7172 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7173 strcat(d, strchr(s+1, '/'));
7177 safeStrCpy(d, s, 4*MSG_SIZ );
7184 static char host_name[MSG_SIZ];
7186 #if HAVE_GETHOSTNAME
7187 gethostname(host_name, MSG_SIZ);
7189 #else /* not HAVE_GETHOSTNAME */
7190 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7191 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7193 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7195 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7196 #endif /* not HAVE_GETHOSTNAME */
7199 XtIntervalId delayedEventTimerXID = 0;
7200 DelayedEventCallback delayedEventCallback = 0;
7205 delayedEventTimerXID = 0;
7206 delayedEventCallback();
7210 ScheduleDelayedEvent(cb, millisec)
7211 DelayedEventCallback cb; long millisec;
7213 if(delayedEventTimerXID && delayedEventCallback == cb)
7214 // [HGM] alive: replace, rather than add or flush identical event
7215 XtRemoveTimeOut(delayedEventTimerXID);
7216 delayedEventCallback = cb;
7217 delayedEventTimerXID =
7218 XtAppAddTimeOut(appContext, millisec,
7219 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7222 DelayedEventCallback
7225 if (delayedEventTimerXID) {
7226 return delayedEventCallback;
7233 CancelDelayedEvent()
7235 if (delayedEventTimerXID) {
7236 XtRemoveTimeOut(delayedEventTimerXID);
7237 delayedEventTimerXID = 0;
7241 XtIntervalId loadGameTimerXID = 0;
7243 int LoadGameTimerRunning()
7245 return loadGameTimerXID != 0;
7248 int StopLoadGameTimer()
7250 if (loadGameTimerXID != 0) {
7251 XtRemoveTimeOut(loadGameTimerXID);
7252 loadGameTimerXID = 0;
7260 LoadGameTimerCallback(arg, id)
7264 loadGameTimerXID = 0;
7269 StartLoadGameTimer(millisec)
7273 XtAppAddTimeOut(appContext, millisec,
7274 (XtTimerCallbackProc) LoadGameTimerCallback,
7278 XtIntervalId analysisClockXID = 0;
7281 AnalysisClockCallback(arg, id)
7285 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7286 || appData.icsEngineAnalyze) { // [DM]
7287 AnalysisPeriodicEvent(0);
7288 StartAnalysisClock();
7293 StartAnalysisClock()
7296 XtAppAddTimeOut(appContext, 2000,
7297 (XtTimerCallbackProc) AnalysisClockCallback,
7301 XtIntervalId clockTimerXID = 0;
7303 int ClockTimerRunning()
7305 return clockTimerXID != 0;
7308 int StopClockTimer()
7310 if (clockTimerXID != 0) {
7311 XtRemoveTimeOut(clockTimerXID);
7320 ClockTimerCallback(arg, id)
7329 StartClockTimer(millisec)
7333 XtAppAddTimeOut(appContext, millisec,
7334 (XtTimerCallbackProc) ClockTimerCallback,
7339 DisplayTimerLabel(w, color, timer, highlight)
7348 /* check for low time warning */
7349 Pixel foregroundOrWarningColor = timerForegroundPixel;
7352 appData.lowTimeWarning &&
7353 (timer / 1000) < appData.icsAlarmTime)
7354 foregroundOrWarningColor = lowTimeWarningColor;
7356 if (appData.clockMode) {
7357 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7358 XtSetArg(args[0], XtNlabel, buf);
7360 snprintf(buf, MSG_SIZ, "%s ", color);
7361 XtSetArg(args[0], XtNlabel, buf);
7366 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7367 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7369 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7370 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7373 XtSetValues(w, args, 3);
7377 DisplayWhiteClock(timeRemaining, highlight)
7383 if(appData.noGUI) return;
7384 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7385 if (highlight && iconPixmap == bIconPixmap) {
7386 iconPixmap = wIconPixmap;
7387 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7388 XtSetValues(shellWidget, args, 1);
7393 DisplayBlackClock(timeRemaining, highlight)
7399 if(appData.noGUI) return;
7400 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7401 if (highlight && iconPixmap == wIconPixmap) {
7402 iconPixmap = bIconPixmap;
7403 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7404 XtSetValues(shellWidget, args, 1);
7422 int StartChildProcess(cmdLine, dir, pr)
7429 int to_prog[2], from_prog[2];
7433 if (appData.debugMode) {
7434 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7437 /* We do NOT feed the cmdLine to the shell; we just
7438 parse it into blank-separated arguments in the
7439 most simple-minded way possible.
7442 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7445 while(*p == ' ') p++;
7447 if(*p == '"' || *p == '\'')
7448 p = strchr(++argv[i-1], *p);
7449 else p = strchr(p, ' ');
7450 if (p == NULL) break;
7455 SetUpChildIO(to_prog, from_prog);
7457 if ((pid = fork()) == 0) {
7459 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7460 close(to_prog[1]); // first close the unused pipe ends
7461 close(from_prog[0]);
7462 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7463 dup2(from_prog[1], 1);
7464 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7465 close(from_prog[1]); // and closing again loses one of the pipes!
7466 if(fileno(stderr) >= 2) // better safe than sorry...
7467 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7469 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7474 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7476 execvp(argv[0], argv);
7478 /* If we get here, exec failed */
7483 /* Parent process */
7485 close(from_prog[1]);
7487 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7490 cp->fdFrom = from_prog[0];
7491 cp->fdTo = to_prog[1];
7496 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7497 static RETSIGTYPE AlarmCallBack(int n)
7503 DestroyChildProcess(pr, signalType)
7507 ChildProc *cp = (ChildProc *) pr;
7509 if (cp->kind != CPReal) return;
7511 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7512 signal(SIGALRM, AlarmCallBack);
7514 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7515 kill(cp->pid, SIGKILL); // kill it forcefully
7516 wait((int *) 0); // and wait again
7520 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7522 /* Process is exiting either because of the kill or because of
7523 a quit command sent by the backend; either way, wait for it to die.
7532 InterruptChildProcess(pr)
7535 ChildProc *cp = (ChildProc *) pr;
7537 if (cp->kind != CPReal) return;
7538 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7541 int OpenTelnet(host, port, pr)
7546 char cmdLine[MSG_SIZ];
7548 if (port[0] == NULLCHAR) {
7549 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7551 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7553 return StartChildProcess(cmdLine, "", pr);
7556 int OpenTCP(host, port, pr)
7562 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7563 #else /* !OMIT_SOCKETS */
7565 struct sockaddr_in sa;
7567 unsigned short uport;
7570 if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
7574 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7575 sa.sin_family = AF_INET;
7576 sa.sin_addr.s_addr = INADDR_ANY;
7577 uport = (unsigned short) 0;
7578 sa.sin_port = htons(uport);
7579 if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
7583 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7584 if (!(hp = gethostbyname(host))) {
7586 if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
7587 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
7588 hp->h_addrtype = AF_INET;
7590 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
7591 hp->h_addr_list[0] = (char *) malloc(4);
7592 hp->h_addr_list[0][0] = b0;
7593 hp->h_addr_list[0][1] = b1;
7594 hp->h_addr_list[0][2] = b2;
7595 hp->h_addr_list[0][3] = b3;
7600 sa.sin_family = hp->h_addrtype;
7601 uport = (unsigned short) atoi(port);
7602 sa.sin_port = htons(uport);
7603 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
7605 if (connect(s, (struct sockaddr *) &sa,
7606 sizeof(struct sockaddr_in)) < 0) {
7610 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7617 #endif /* !OMIT_SOCKETS */
7622 int OpenCommPort(name, pr)
7629 fd = open(name, 2, 0);
7630 if (fd < 0) return errno;
7632 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7642 int OpenLoopback(pr)
7648 SetUpChildIO(to, from);
7650 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7653 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7660 int OpenRcmd(host, user, cmd, pr)
7661 char *host, *user, *cmd;
7664 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7668 #define INPUT_SOURCE_BUF_SIZE 8192
7677 char buf[INPUT_SOURCE_BUF_SIZE];
7682 DoInputCallback(closure, source, xid)
7687 InputSource *is = (InputSource *) closure;
7692 if (is->lineByLine) {
7693 count = read(is->fd, is->unused,
7694 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7696 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7699 is->unused += count;
7701 while (p < is->unused) {
7702 q = memchr(p, '\n', is->unused - p);
7703 if (q == NULL) break;
7705 (is->func)(is, is->closure, p, q - p, 0);
7709 while (p < is->unused) {
7714 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7719 (is->func)(is, is->closure, is->buf, count, error);
7723 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7730 ChildProc *cp = (ChildProc *) pr;
7732 is = (InputSource *) calloc(1, sizeof(InputSource));
7733 is->lineByLine = lineByLine;
7737 is->fd = fileno(stdin);
7739 is->kind = cp->kind;
7740 is->fd = cp->fdFrom;
7743 is->unused = is->buf;
7746 is->xid = XtAppAddInput(appContext, is->fd,
7747 (XtPointer) (XtInputReadMask),
7748 (XtInputCallbackProc) DoInputCallback,
7750 is->closure = closure;
7751 return (InputSourceRef) is;
7755 RemoveInputSource(isr)
7758 InputSource *is = (InputSource *) isr;
7760 if (is->xid == 0) return;
7761 XtRemoveInput(is->xid);
7765 int OutputToProcess(pr, message, count, outError)
7771 static int line = 0;
7772 ChildProc *cp = (ChildProc *) pr;
7777 if (appData.noJoin || !appData.useInternalWrap)
7778 outCount = fwrite(message, 1, count, stdout);
7781 int width = get_term_width();
7782 int len = wrap(NULL, message, count, width, &line);
7783 char *msg = malloc(len);
7787 outCount = fwrite(message, 1, count, stdout);
7790 dbgchk = wrap(msg, message, count, width, &line);
7791 if (dbgchk != len && appData.debugMode)
7792 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7793 outCount = fwrite(msg, 1, dbgchk, stdout);
7799 outCount = write(cp->fdTo, message, count);
7809 /* Output message to process, with "ms" milliseconds of delay
7810 between each character. This is needed when sending the logon
7811 script to ICC, which for some reason doesn't like the
7812 instantaneous send. */
7813 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
7820 ChildProc *cp = (ChildProc *) pr;
7825 r = write(cp->fdTo, message++, 1);
7838 /**** Animation code by Hugh Fisher, DCS, ANU.
7840 Known problem: if a window overlapping the board is
7841 moved away while a piece is being animated underneath,
7842 the newly exposed area won't be updated properly.
7843 I can live with this.
7845 Known problem: if you look carefully at the animation
7846 of pieces in mono mode, they are being drawn as solid
7847 shapes without interior detail while moving. Fixing
7848 this would be a major complication for minimal return.
7851 /* Masks for XPM pieces. Black and white pieces can have
7852 different shapes, but in the interest of retaining my
7853 sanity pieces must have the same outline on both light
7854 and dark squares, and all pieces must use the same
7855 background square colors/images. */
7857 static int xpmDone = 0;
7860 CreateAnimMasks (pieceDepth)
7867 unsigned long plane;
7870 /* Need a bitmap just to get a GC with right depth */
7871 buf = XCreatePixmap(xDisplay, xBoardWindow,
7873 values.foreground = 1;
7874 values.background = 0;
7875 /* Don't use XtGetGC, not read only */
7876 maskGC = XCreateGC(xDisplay, buf,
7877 GCForeground | GCBackground, &values);
7878 XFreePixmap(xDisplay, buf);
7880 buf = XCreatePixmap(xDisplay, xBoardWindow,
7881 squareSize, squareSize, pieceDepth);
7882 values.foreground = XBlackPixel(xDisplay, xScreen);
7883 values.background = XWhitePixel(xDisplay, xScreen);
7884 bufGC = XCreateGC(xDisplay, buf,
7885 GCForeground | GCBackground, &values);
7887 for (piece = WhitePawn; piece <= BlackKing; piece++) {
7888 /* Begin with empty mask */
7889 if(!xpmDone) // [HGM] pieces: keep using existing
7890 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7891 squareSize, squareSize, 1);
7892 XSetFunction(xDisplay, maskGC, GXclear);
7893 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7894 0, 0, squareSize, squareSize);
7896 /* Take a copy of the piece */
7901 XSetFunction(xDisplay, bufGC, GXcopy);
7902 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
7904 0, 0, squareSize, squareSize, 0, 0);
7906 /* XOR the background (light) over the piece */
7907 XSetFunction(xDisplay, bufGC, GXxor);
7909 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7910 0, 0, squareSize, squareSize, 0, 0);
7912 XSetForeground(xDisplay, bufGC, lightSquareColor);
7913 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7916 /* We now have an inverted piece image with the background
7917 erased. Construct mask by just selecting all the non-zero
7918 pixels - no need to reconstruct the original image. */
7919 XSetFunction(xDisplay, maskGC, GXor);
7921 /* Might be quicker to download an XImage and create bitmap
7922 data from it rather than this N copies per piece, but it
7923 only takes a fraction of a second and there is a much
7924 longer delay for loading the pieces. */
7925 for (n = 0; n < pieceDepth; n ++) {
7926 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7927 0, 0, squareSize, squareSize,
7933 XFreePixmap(xDisplay, buf);
7934 XFreeGC(xDisplay, bufGC);
7935 XFreeGC(xDisplay, maskGC);
7939 InitAnimState (anim, info)
7941 XWindowAttributes * info;
7946 /* Each buffer is square size, same depth as window */
7947 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
7948 squareSize, squareSize, info->depth);
7949 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
7950 squareSize, squareSize, info->depth);
7952 /* Create a plain GC for blitting */
7953 mask = GCForeground | GCBackground | GCFunction |
7954 GCPlaneMask | GCGraphicsExposures;
7955 values.foreground = XBlackPixel(xDisplay, xScreen);
7956 values.background = XWhitePixel(xDisplay, xScreen);
7957 values.function = GXcopy;
7958 values.plane_mask = AllPlanes;
7959 values.graphics_exposures = False;
7960 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
7962 /* Piece will be copied from an existing context at
7963 the start of each new animation/drag. */
7964 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
7966 /* Outline will be a read-only copy of an existing */
7967 anim->outlineGC = None;
7973 XWindowAttributes info;
7975 if (xpmDone && gameInfo.variant == oldVariant) return;
7976 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
7977 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
7979 InitAnimState(&game, &info);
7980 InitAnimState(&player, &info);
7982 /* For XPM pieces, we need bitmaps to use as masks. */
7984 CreateAnimMasks(info.depth);
7990 static Boolean frameWaiting;
7992 static RETSIGTYPE FrameAlarm (sig)
7995 frameWaiting = False;
7996 /* In case System-V style signals. Needed?? */
7997 signal(SIGALRM, FrameAlarm);
8004 struct itimerval delay;
8006 XSync(xDisplay, False);
8009 frameWaiting = True;
8010 signal(SIGALRM, FrameAlarm);
8011 delay.it_interval.tv_sec =
8012 delay.it_value.tv_sec = time / 1000;
8013 delay.it_interval.tv_usec =
8014 delay.it_value.tv_usec = (time % 1000) * 1000;
8015 setitimer(ITIMER_REAL, &delay, NULL);
8016 while (frameWaiting) pause();
8017 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8018 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8019 setitimer(ITIMER_REAL, &delay, NULL);
8029 XSync(xDisplay, False);
8031 usleep(time * 1000);
8036 /* Convert board position to corner of screen rect and color */
8039 ScreenSquare(column, row, pt, color)
8040 int column; int row; XPoint * pt; int * color;
8043 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8044 pt->y = lineGap + row * (squareSize + lineGap);
8046 pt->x = lineGap + column * (squareSize + lineGap);
8047 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8049 *color = SquareColor(row, column);
8052 /* Convert window coords to square */
8055 BoardSquare(x, y, column, row)
8056 int x; int y; int * column; int * row;
8058 *column = EventToSquare(x, BOARD_WIDTH);
8059 if (flipView && *column >= 0)
8060 *column = BOARD_WIDTH - 1 - *column;
8061 *row = EventToSquare(y, BOARD_HEIGHT);
8062 if (!flipView && *row >= 0)
8063 *row = BOARD_HEIGHT - 1 - *row;
8068 #undef Max /* just in case */
8070 #define Max(a, b) ((a) > (b) ? (a) : (b))
8071 #define Min(a, b) ((a) < (b) ? (a) : (b))
8074 SetRect(rect, x, y, width, height)
8075 XRectangle * rect; int x; int y; int width; int height;
8079 rect->width = width;
8080 rect->height = height;
8083 /* Test if two frames overlap. If they do, return
8084 intersection rect within old and location of
8085 that rect within new. */
8088 Intersect(old, new, size, area, pt)
8089 XPoint * old; XPoint * new;
8090 int size; XRectangle * area; XPoint * pt;
8092 if (old->x > new->x + size || new->x > old->x + size ||
8093 old->y > new->y + size || new->y > old->y + size) {
8096 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8097 size - abs(old->x - new->x), size - abs(old->y - new->y));
8098 pt->x = Max(old->x - new->x, 0);
8099 pt->y = Max(old->y - new->y, 0);
8104 /* For two overlapping frames, return the rect(s)
8105 in the old that do not intersect with the new. */
8108 CalcUpdateRects(old, new, size, update, nUpdates)
8109 XPoint * old; XPoint * new; int size;
8110 XRectangle update[]; int * nUpdates;
8114 /* If old = new (shouldn't happen) then nothing to draw */
8115 if (old->x == new->x && old->y == new->y) {
8119 /* Work out what bits overlap. Since we know the rects
8120 are the same size we don't need a full intersect calc. */
8122 /* Top or bottom edge? */
8123 if (new->y > old->y) {
8124 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8126 } else if (old->y > new->y) {
8127 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8128 size, old->y - new->y);
8131 /* Left or right edge - don't overlap any update calculated above. */
8132 if (new->x > old->x) {
8133 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8134 new->x - old->x, size - abs(new->y - old->y));
8136 } else if (old->x > new->x) {
8137 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8138 old->x - new->x, size - abs(new->y - old->y));
8145 /* Generate a series of frame coords from start->mid->finish.
8146 The movement rate doubles until the half way point is
8147 reached, then halves back down to the final destination,
8148 which gives a nice slow in/out effect. The algorithmn
8149 may seem to generate too many intermediates for short
8150 moves, but remember that the purpose is to attract the
8151 viewers attention to the piece about to be moved and
8152 then to where it ends up. Too few frames would be less
8156 Tween(start, mid, finish, factor, frames, nFrames)
8157 XPoint * start; XPoint * mid;
8158 XPoint * finish; int factor;
8159 XPoint frames[]; int * nFrames;
8161 int fraction, n, count;
8165 /* Slow in, stepping 1/16th, then 1/8th, ... */
8167 for (n = 0; n < factor; n++)
8169 for (n = 0; n < factor; n++) {
8170 frames[count].x = start->x + (mid->x - start->x) / fraction;
8171 frames[count].y = start->y + (mid->y - start->y) / fraction;
8173 fraction = fraction / 2;
8177 frames[count] = *mid;
8180 /* Slow out, stepping 1/2, then 1/4, ... */
8182 for (n = 0; n < factor; n++) {
8183 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8184 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8186 fraction = fraction * 2;
8191 /* Draw a piece on the screen without disturbing what's there */
8194 SelectGCMask(piece, clip, outline, mask)
8195 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8199 /* Bitmap for piece being moved. */
8200 if (appData.monoMode) {
8201 *mask = *pieceToSolid(piece);
8202 } else if (useImages) {
8204 *mask = xpmMask[piece];
8206 *mask = ximMaskPm[piece];
8209 *mask = *pieceToSolid(piece);
8212 /* GC for piece being moved. Square color doesn't matter, but
8213 since it gets modified we make a copy of the original. */
8215 if (appData.monoMode)
8220 if (appData.monoMode)
8225 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8227 /* Outline only used in mono mode and is not modified */
8229 *outline = bwPieceGC;
8231 *outline = wbPieceGC;
8235 OverlayPiece(piece, clip, outline, dest)
8236 ChessSquare piece; GC clip; GC outline; Drawable dest;
8241 /* Draw solid rectangle which will be clipped to shape of piece */
8242 XFillRectangle(xDisplay, dest, clip,
8243 0, 0, squareSize, squareSize);
8244 if (appData.monoMode)
8245 /* Also draw outline in contrasting color for black
8246 on black / white on white cases */
8247 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8248 0, 0, squareSize, squareSize, 0, 0, 1);
8250 /* Copy the piece */
8255 if(appData.upsideDown && flipView) kind ^= 2;
8256 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8258 0, 0, squareSize, squareSize,
8263 /* Animate the movement of a single piece */
8266 BeginAnimation(anim, piece, startColor, start)
8274 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8275 /* The old buffer is initialised with the start square (empty) */
8276 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8277 anim->prevFrame = *start;
8279 /* The piece will be drawn using its own bitmap as a matte */
8280 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8281 XSetClipMask(xDisplay, anim->pieceGC, mask);
8285 AnimationFrame(anim, frame, piece)
8290 XRectangle updates[4];
8295 /* Save what we are about to draw into the new buffer */
8296 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8297 frame->x, frame->y, squareSize, squareSize,
8300 /* Erase bits of the previous frame */
8301 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8302 /* Where the new frame overlapped the previous,
8303 the contents in newBuf are wrong. */
8304 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8305 overlap.x, overlap.y,
8306 overlap.width, overlap.height,
8308 /* Repaint the areas in the old that don't overlap new */
8309 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8310 for (i = 0; i < count; i++)
8311 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8312 updates[i].x - anim->prevFrame.x,
8313 updates[i].y - anim->prevFrame.y,
8314 updates[i].width, updates[i].height,
8315 updates[i].x, updates[i].y);
8317 /* Easy when no overlap */
8318 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8319 0, 0, squareSize, squareSize,
8320 anim->prevFrame.x, anim->prevFrame.y);
8323 /* Save this frame for next time round */
8324 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8325 0, 0, squareSize, squareSize,
8327 anim->prevFrame = *frame;
8329 /* Draw piece over original screen contents, not current,
8330 and copy entire rect. Wipes out overlapping piece images. */
8331 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8332 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8333 0, 0, squareSize, squareSize,
8334 frame->x, frame->y);
8338 EndAnimation (anim, finish)
8342 XRectangle updates[4];
8347 /* The main code will redraw the final square, so we
8348 only need to erase the bits that don't overlap. */
8349 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8350 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8351 for (i = 0; i < count; i++)
8352 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8353 updates[i].x - anim->prevFrame.x,
8354 updates[i].y - anim->prevFrame.y,
8355 updates[i].width, updates[i].height,
8356 updates[i].x, updates[i].y);
8358 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8359 0, 0, squareSize, squareSize,
8360 anim->prevFrame.x, anim->prevFrame.y);
8365 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8367 ChessSquare piece; int startColor;
8368 XPoint * start; XPoint * finish;
8369 XPoint frames[]; int nFrames;
8373 BeginAnimation(anim, piece, startColor, start);
8374 for (n = 0; n < nFrames; n++) {
8375 AnimationFrame(anim, &(frames[n]), piece);
8376 FrameDelay(appData.animSpeed);
8378 EndAnimation(anim, finish);
8382 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8385 ChessSquare piece = board[fromY][toY];
8386 board[fromY][toY] = EmptySquare;
8387 DrawPosition(FALSE, board);
8389 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8390 y = lineGap + toY * (squareSize + lineGap);
8392 x = lineGap + toX * (squareSize + lineGap);
8393 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8395 for(i=1; i<4*kFactor; i++) {
8396 int r = squareSize * 9 * i/(20*kFactor - 5);
8397 XFillArc(xDisplay, xBoardWindow, highlineGC,
8398 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8399 FrameDelay(appData.animSpeed);
8401 board[fromY][toY] = piece;
8404 /* Main control logic for deciding what to animate and how */
8407 AnimateMove(board, fromX, fromY, toX, toY)
8416 XPoint start, finish, mid;
8417 XPoint frames[kFactor * 2 + 1];
8418 int nFrames, startColor, endColor;
8420 /* Are we animating? */
8421 if (!appData.animate || appData.blindfold)
8424 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8425 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8426 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8428 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8429 piece = board[fromY][fromX];
8430 if (piece >= EmptySquare) return;
8435 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8438 if (appData.debugMode) {
8439 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8440 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8441 piece, fromX, fromY, toX, toY); }
8443 ScreenSquare(fromX, fromY, &start, &startColor);
8444 ScreenSquare(toX, toY, &finish, &endColor);
8447 /* Knight: make straight movement then diagonal */
8448 if (abs(toY - fromY) < abs(toX - fromX)) {
8449 mid.x = start.x + (finish.x - start.x) / 2;
8453 mid.y = start.y + (finish.y - start.y) / 2;
8456 mid.x = start.x + (finish.x - start.x) / 2;
8457 mid.y = start.y + (finish.y - start.y) / 2;
8460 /* Don't use as many frames for very short moves */
8461 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8462 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8464 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8465 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8466 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8468 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8469 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8472 /* Be sure end square is redrawn */
8473 damage[0][toY][toX] = True;
8477 DragPieceBegin(x, y)
8480 int boardX, boardY, color;
8483 /* Are we animating? */
8484 if (!appData.animateDragging || appData.blindfold)
8487 /* Figure out which square we start in and the
8488 mouse position relative to top left corner. */
8489 BoardSquare(x, y, &boardX, &boardY);
8490 player.startBoardX = boardX;
8491 player.startBoardY = boardY;
8492 ScreenSquare(boardX, boardY, &corner, &color);
8493 player.startSquare = corner;
8494 player.startColor = color;
8495 /* As soon as we start dragging, the piece will jump slightly to
8496 be centered over the mouse pointer. */
8497 player.mouseDelta.x = squareSize/2;
8498 player.mouseDelta.y = squareSize/2;
8499 /* Initialise animation */
8500 player.dragPiece = PieceForSquare(boardX, boardY);
8502 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8503 player.dragActive = True;
8504 BeginAnimation(&player, player.dragPiece, color, &corner);
8505 /* Mark this square as needing to be redrawn. Note that
8506 we don't remove the piece though, since logically (ie
8507 as seen by opponent) the move hasn't been made yet. */
8508 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8509 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8510 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8511 corner.x, corner.y, squareSize, squareSize,
8512 0, 0); // [HGM] zh: unstack in stead of grab
8513 if(gatingPiece != EmptySquare) {
8514 /* Kludge alert: When gating we want the introduced
8515 piece to appear on the from square. To generate an
8516 image of it, we draw it on the board, copy the image,
8517 and draw the original piece again. */
8518 ChessSquare piece = boards[currentMove][boardY][boardX];
8519 DrawSquare(boardY, boardX, gatingPiece, 0);
8520 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8521 corner.x, corner.y, squareSize, squareSize, 0, 0);
8522 DrawSquare(boardY, boardX, piece, 0);
8524 damage[0][boardY][boardX] = True;
8526 player.dragActive = False;
8536 /* Are we animating? */
8537 if (!appData.animateDragging || appData.blindfold)
8541 if (! player.dragActive)
8543 /* Move piece, maintaining same relative position
8544 of mouse within square */
8545 corner.x = x - player.mouseDelta.x;
8546 corner.y = y - player.mouseDelta.y;
8547 AnimationFrame(&player, &corner, player.dragPiece);
8549 if (appData.highlightDragging) {
8551 BoardSquare(x, y, &boardX, &boardY);
8552 SetHighlights(fromX, fromY, boardX, boardY);
8561 int boardX, boardY, color;
8564 /* Are we animating? */
8565 if (!appData.animateDragging || appData.blindfold)
8569 if (! player.dragActive)
8571 /* Last frame in sequence is square piece is
8572 placed on, which may not match mouse exactly. */
8573 BoardSquare(x, y, &boardX, &boardY);
8574 ScreenSquare(boardX, boardY, &corner, &color);
8575 EndAnimation(&player, &corner);
8577 /* Be sure end square is redrawn */
8578 damage[0][boardY][boardX] = True;
8580 /* This prevents weird things happening with fast successive
8581 clicks which on my Sun at least can cause motion events
8582 without corresponding press/release. */
8583 player.dragActive = False;
8586 /* Handle expose event while piece being dragged */
8591 if (!player.dragActive || appData.blindfold)
8594 /* What we're doing: logically, the move hasn't been made yet,
8595 so the piece is still in it's original square. But visually
8596 it's being dragged around the board. So we erase the square
8597 that the piece is on and draw it at the last known drag point. */
8598 BlankSquare(player.startSquare.x, player.startSquare.y,
8599 player.startColor, EmptySquare, xBoardWindow, 1);
8600 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8601 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8604 #include <sys/ioctl.h>
8605 int get_term_width()
8607 int fd, default_width;
8610 default_width = 79; // this is FICS default anyway...
8612 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8614 if (!ioctl(fd, TIOCGSIZE, &win))
8615 default_width = win.ts_cols;
8616 #elif defined(TIOCGWINSZ)
8618 if (!ioctl(fd, TIOCGWINSZ, &win))
8619 default_width = win.ws_col;
8621 return default_width;
8627 static int old_width = 0;
8628 int new_width = get_term_width();
8630 if (old_width != new_width)
8631 ics_printf("set width %d\n", new_width);
8632 old_width = new_width;
8635 void NotifyFrontendLogin()
8640 /* [AS] Arrow highlighting support */
8642 static double A_WIDTH = 5; /* Width of arrow body */
8644 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8645 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8647 static double Sqr( double x )
8652 static int Round( double x )
8654 return (int) (x + 0.5);
8657 void SquareToPos(int rank, int file, int *x, int *y)
8660 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8661 *y = lineGap + rank * (squareSize + lineGap);
8663 *x = lineGap + file * (squareSize + lineGap);
8664 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8668 /* Draw an arrow between two points using current settings */
8669 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8672 double dx, dy, j, k, x, y;
8675 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8677 arrow[0].x = s_x + A_WIDTH + 0.5;
8680 arrow[1].x = s_x + A_WIDTH + 0.5;
8681 arrow[1].y = d_y - h;
8683 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8684 arrow[2].y = d_y - h;
8689 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8690 arrow[5].y = d_y - h;
8692 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8693 arrow[4].y = d_y - h;
8695 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8698 else if( d_y == s_y ) {
8699 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8702 arrow[0].y = s_y + A_WIDTH + 0.5;
8704 arrow[1].x = d_x - w;
8705 arrow[1].y = s_y + A_WIDTH + 0.5;
8707 arrow[2].x = d_x - w;
8708 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8713 arrow[5].x = d_x - w;
8714 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8716 arrow[4].x = d_x - w;
8717 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8720 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8723 /* [AS] Needed a lot of paper for this! :-) */
8724 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8725 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8727 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8729 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8734 arrow[0].x = Round(x - j);
8735 arrow[0].y = Round(y + j*dx);
8737 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8738 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8741 x = (double) d_x - k;
8742 y = (double) d_y - k*dy;
8745 x = (double) d_x + k;
8746 y = (double) d_y + k*dy;
8749 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8751 arrow[6].x = Round(x - j);
8752 arrow[6].y = Round(y + j*dx);
8754 arrow[2].x = Round(arrow[6].x + 2*j);
8755 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8757 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8758 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8763 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8764 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8767 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8768 // Polygon( hdc, arrow, 7 );
8771 /* [AS] Draw an arrow between two squares */
8772 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
8774 int s_x, s_y, d_x, d_y, hor, vert, i;
8776 if( s_col == d_col && s_row == d_row ) {
8780 /* Get source and destination points */
8781 SquareToPos( s_row, s_col, &s_x, &s_y);
8782 SquareToPos( d_row, d_col, &d_x, &d_y);
8785 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8787 else if( d_y < s_y ) {
8788 d_y += squareSize / 2 + squareSize / 4;
8791 d_y += squareSize / 2;
8795 d_x += squareSize / 2 - squareSize / 4;
8797 else if( d_x < s_x ) {
8798 d_x += squareSize / 2 + squareSize / 4;
8801 d_x += squareSize / 2;
8804 s_x += squareSize / 2;
8805 s_y += squareSize / 2;
8808 A_WIDTH = squareSize / 14.; //[HGM] make float
8810 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8812 hor = 64*s_col + 32; vert = 64*s_row + 32;
8813 for(i=0; i<= 64; i++) {
8814 damage[0][vert+6>>6][hor+6>>6] = True;
8815 damage[0][vert-6>>6][hor+6>>6] = True;
8816 damage[0][vert+6>>6][hor-6>>6] = True;
8817 damage[0][vert-6>>6][hor-6>>6] = True;
8818 hor += d_col - s_col; vert += d_row - s_row;
8822 Boolean IsDrawArrowEnabled()
8824 return appData.highlightMoveWithArrow && squareSize >= 32;
8827 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
8829 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8830 DrawArrowBetweenSquares(fromX, fromY, toX, toY);