2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
66 # if HAVE_SYS_SOCKET_H
67 # include <sys/socket.h>
68 # include <netinet/in.h>
70 # else /* not HAVE_SYS_SOCKET_H */
71 # if HAVE_LAN_SOCKET_H
72 # include <lan/socket.h>
74 # include <lan/netdb.h>
75 # else /* not HAVE_LAN_SOCKET_H */
76 # define OMIT_SOCKETS 1
77 # endif /* not HAVE_LAN_SOCKET_H */
78 # endif /* not HAVE_SYS_SOCKET_H */
79 #endif /* !OMIT_SOCKETS */
84 #else /* not STDC_HEADERS */
85 extern char *getenv();
88 # else /* not HAVE_STRING_H */
90 # endif /* not HAVE_STRING_H */
91 #endif /* not STDC_HEADERS */
94 # include <sys/fcntl.h>
95 #else /* not HAVE_SYS_FCNTL_H */
98 # endif /* HAVE_FCNTL_H */
99 #endif /* not HAVE_SYS_FCNTL_H */
101 #if HAVE_SYS_SYSTEMINFO_H
102 # include <sys/systeminfo.h>
103 #endif /* HAVE_SYS_SYSTEMINFO_H */
105 #if TIME_WITH_SYS_TIME
106 # include <sys/time.h>
110 # include <sys/time.h>
121 # include <sys/wait.h>
126 # define NAMLEN(dirent) strlen((dirent)->d_name)
127 # define HAVE_DIR_STRUCT
129 # define dirent direct
130 # define NAMLEN(dirent) (dirent)->d_namlen
132 # include <sys/ndir.h>
133 # define HAVE_DIR_STRUCT
136 # include <sys/dir.h>
137 # define HAVE_DIR_STRUCT
141 # define HAVE_DIR_STRUCT
145 #include <X11/Intrinsic.h>
146 #include <X11/StringDefs.h>
147 #include <X11/Shell.h>
148 #include <X11/cursorfont.h>
149 #include <X11/Xatom.h>
150 #include <X11/Xmu/Atoms.h>
152 #include <X11/Xaw3d/Dialog.h>
153 #include <X11/Xaw3d/Form.h>
154 #include <X11/Xaw3d/List.h>
155 #include <X11/Xaw3d/Label.h>
156 #include <X11/Xaw3d/SimpleMenu.h>
157 #include <X11/Xaw3d/SmeBSB.h>
158 #include <X11/Xaw3d/SmeLine.h>
159 #include <X11/Xaw3d/Box.h>
160 #include <X11/Xaw3d/MenuButton.h>
161 #include <X11/Xaw3d/Text.h>
162 #include <X11/Xaw3d/AsciiText.h>
164 #include <X11/Xaw/Dialog.h>
165 #include <X11/Xaw/Form.h>
166 #include <X11/Xaw/List.h>
167 #include <X11/Xaw/Label.h>
168 #include <X11/Xaw/SimpleMenu.h>
169 #include <X11/Xaw/SmeBSB.h>
170 #include <X11/Xaw/SmeLine.h>
171 #include <X11/Xaw/Box.h>
172 #include <X11/Xaw/MenuButton.h>
173 #include <X11/Xaw/Text.h>
174 #include <X11/Xaw/AsciiText.h>
177 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
182 #include "pixmaps/pixmaps.h"
183 #define IMAGE_EXT "xpm"
185 #define IMAGE_EXT "xim"
186 #include "bitmaps/bitmaps.h"
189 #include "bitmaps/icon_white.bm"
190 #include "bitmaps/icon_black.bm"
191 #include "bitmaps/checkmark.bm"
193 #include "frontend.h"
195 #include "backendz.h"
199 #include "xgamelist.h"
200 #include "xhistory.h"
201 #include "xedittags.h"
204 // must be moved to xengineoutput.h
206 void EngineOutputProc P((Widget w, XEvent *event,
207 String *prms, Cardinal *nprms));
208 void EvalGraphProc P((Widget w, XEvent *event,
209 String *prms, Cardinal *nprms));
216 #define usleep(t) _sleep2(((t)+500)/1000)
220 # define _(s) gettext (s)
221 # define N_(s) gettext_noop (s)
239 int main P((int argc, char **argv));
240 FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
241 char *init_path, char *filter, char *mode, int (*show_entry)(), char **name_return));
242 RETSIGTYPE CmailSigHandler P((int sig));
243 RETSIGTYPE IntSigHandler P((int sig));
244 RETSIGTYPE TermSizeSigHandler P((int sig));
245 void CreateGCs P((int redo));
246 void CreateAnyPieces P((void));
247 void CreateXIMPieces P((void));
248 void CreateXPMPieces P((void));
249 void CreateXPMBoard P((char *s, int n));
250 void CreatePieces P((void));
251 void CreatePieceMenus P((void));
252 Widget CreateMenuBar P((Menu *mb));
253 Widget CreateButtonBar P ((MenuItem *mi));
254 char *FindFont P((char *pattern, int targetPxlSize));
255 void PieceMenuPopup P((Widget w, XEvent *event,
256 String *params, Cardinal *num_params));
257 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
258 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
259 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
260 u_int wreq, u_int hreq));
261 void CreateGrid P((void));
262 int EventToSquare P((int x, int limit));
263 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
264 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
265 void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
266 void HandleUserMove P((Widget w, XEvent *event,
267 String *prms, Cardinal *nprms));
268 void AnimateUserMove P((Widget w, XEvent * event,
269 String * params, Cardinal * nParams));
270 void HandlePV P((Widget w, XEvent * event,
271 String * params, Cardinal * nParams));
272 void SelectPV P((Widget w, XEvent * event,
273 String * params, Cardinal * nParams));
274 void StopPV P((Widget w, XEvent * event,
275 String * params, Cardinal * nParams));
276 void WhiteClock P((Widget w, XEvent *event,
277 String *prms, Cardinal *nprms));
278 void BlackClock P((Widget w, XEvent *event,
279 String *prms, Cardinal *nprms));
280 void DrawPositionProc P((Widget w, XEvent *event,
281 String *prms, Cardinal *nprms));
282 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
284 void CommentClick P((Widget w, XEvent * event,
285 String * params, Cardinal * nParams));
286 void CommentPopUp P((char *title, char *label));
287 void CommentPopDown P((void));
288 void ICSInputBoxPopUp P((void));
289 void ICSInputBoxPopDown P((void));
290 void FileNamePopUp P((char *label, char *def, char *filter,
291 FileProc proc, char *openMode));
292 void FileNamePopDown P((void));
293 void FileNameCallback P((Widget w, XtPointer client_data,
294 XtPointer call_data));
295 void FileNameAction P((Widget w, XEvent *event,
296 String *prms, Cardinal *nprms));
297 void AskQuestionReplyAction P((Widget w, XEvent *event,
298 String *prms, Cardinal *nprms));
299 void AskQuestionProc P((Widget w, XEvent *event,
300 String *prms, Cardinal *nprms));
301 void AskQuestionPopDown P((void));
302 void PromotionPopDown P((void));
303 void PromotionCallback P((Widget w, XtPointer client_data,
304 XtPointer call_data));
305 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
306 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
307 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
308 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
310 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
312 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
314 void LoadPositionProc P((Widget w, XEvent *event,
315 String *prms, Cardinal *nprms));
316 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
318 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
320 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
322 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
324 void PastePositionProc P((Widget w, XEvent *event, String *prms,
326 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
327 void CopyGameListProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
328 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
329 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
330 void SavePositionProc P((Widget w, XEvent *event,
331 String *prms, Cardinal *nprms));
332 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
333 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
335 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
336 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
337 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
339 void MachineWhiteProc P((Widget w, XEvent *event,
340 String *prms, Cardinal *nprms));
341 void AnalyzeModeProc P((Widget w, XEvent *event,
342 String *prms, Cardinal *nprms));
343 void AnalyzeFileProc P((Widget w, XEvent *event,
344 String *prms, Cardinal *nprms));
345 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
347 void MatchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
348 void MatchOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
349 void IcsClientProc P((Widget w, XEvent *event, String *prms,
351 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
352 void EditPositionProc P((Widget w, XEvent *event,
353 String *prms, Cardinal *nprms));
354 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
355 void EditCommentProc P((Widget w, XEvent *event,
356 String *prms, Cardinal *nprms));
357 void IcsInputBoxProc P((Widget w, XEvent *event,
358 String *prms, Cardinal *nprms));
359 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
360 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
361 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
362 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
363 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
364 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
365 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
366 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
367 void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
368 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
369 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
370 void TypeInProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
371 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
372 void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
373 void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
374 void StopObservingProc P((Widget w, XEvent *event, String *prms,
376 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
378 void UploadProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
379 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
380 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
381 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
382 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
383 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
384 void AnnotateProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
385 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
387 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
389 void MoveNowProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
390 void AlwaysQueenProc P((Widget w, XEvent *event, String *prms,
392 void AnimateDraggingProc P((Widget w, XEvent *event, String *prms,
394 void AnimateMovingProc P((Widget w, XEvent *event, String *prms,
396 void AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
397 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
398 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
399 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
401 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
402 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
404 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
406 void HighlightArrowProc P((Widget w, XEvent *event, String *prms,
408 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
409 //void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
410 void OneClickProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
411 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
413 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
415 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
417 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
419 //void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
420 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
422 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
424 void HideThinkingProc P((Widget w, XEvent *event, String *prms,
426 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
428 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
429 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
430 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
431 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
432 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
433 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
434 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
435 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
436 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
437 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
438 void Iconify P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
439 void DisplayMove P((int moveNumber));
440 void DisplayTitle P((char *title));
441 void ICSInitScript P((void));
442 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
443 void ErrorPopUp P((char *title, char *text, int modal));
444 void ErrorPopDown P((void));
445 static char *ExpandPathName P((char *path));
446 static void CreateAnimVars P((void));
447 static void DragPieceMove P((int x, int y));
448 static void DrawDragPiece P((void));
449 char *ModeToWidgetName P((GameMode mode));
450 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
451 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
452 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
453 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
454 void OptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
455 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
456 void IcsTextProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
457 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
458 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
459 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
460 void IcsOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
461 void SoundOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
462 void BoardOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
463 void LoadOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
464 void SaveOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
465 void GameListOptionsPopDown P(());
466 void ShufflePopDown P(());
467 void TimeControlPopDown P(());
468 void GenericPopDown P(());
469 void update_ics_width P(());
470 int get_term_width P(());
471 int CopyMemoProc P(());
472 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
473 Boolean IsDrawArrowEnabled P(());
476 * XBoard depends on Xt R4 or higher
478 int xtVersion = XtSpecificationRelease;
483 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
484 jailSquareColor, highlightSquareColor, premoveHighlightColor;
485 Pixel lowTimeWarningColor;
486 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
487 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
488 wjPieceGC, bjPieceGC, prelineGC, countGC;
489 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
490 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
491 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
492 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
493 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
494 ICSInputShell, fileNameShell, askQuestionShell;
495 Widget historyShell, evalGraphShell, gameListShell;
496 int hOffset; // [HGM] dual
497 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
498 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
499 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
500 Font clockFontID, coordFontID, countFontID;
501 XFontStruct *clockFontStruct, *coordFontStruct, *countFontStruct;
502 XtAppContext appContext;
504 char *oldICSInteractionTitle;
508 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
510 Position commentX = -1, commentY = -1;
511 Dimension commentW, commentH;
512 typedef unsigned int BoardSize;
514 Boolean chessProgram;
516 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
517 int squareSize, smallLayout = 0, tinyLayout = 0,
518 marginW, marginH, // [HGM] for run-time resizing
519 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
520 ICSInputBoxUp = False, askQuestionUp = False,
521 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
522 errorUp = False, errorExitStatus = -1, lineGap, defaultLineGap;
523 Pixel timerForegroundPixel, timerBackgroundPixel;
524 Pixel buttonForegroundPixel, buttonBackgroundPixel;
525 char *chessDir, *programName, *programVersion,
526 *gameCopyFilename, *gamePasteFilename;
527 Boolean alwaysOnTop = False;
528 Boolean saveSettingsOnExit;
529 char *settingsFileName;
530 char *icsTextMenuString;
532 char *firstChessProgramNames;
533 char *secondChessProgramNames;
535 WindowPlacement wpMain;
536 WindowPlacement wpConsole;
537 WindowPlacement wpComment;
538 WindowPlacement wpMoveHistory;
539 WindowPlacement wpEvalGraph;
540 WindowPlacement wpEngineOutput;
541 WindowPlacement wpGameList;
542 WindowPlacement wpTags;
544 extern Widget shells[];
545 extern Boolean shellUp[];
549 Pixmap pieceBitmap[2][(int)BlackPawn];
550 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
551 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
552 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
553 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
554 Pixmap xpmBoardBitmap[2];
555 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
556 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
557 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
558 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
559 XImage *ximLightSquare, *ximDarkSquare;
562 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
563 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
565 #define White(piece) ((int)(piece) < (int)BlackPawn)
567 /* Variables for doing smooth animation. This whole thing
568 would be much easier if the board was double-buffered,
569 but that would require a fairly major rewrite. */
574 GC blitGC, pieceGC, outlineGC;
575 XPoint startSquare, prevFrame, mouseDelta;
579 int startBoardX, startBoardY;
582 /* There can be two pieces being animated at once: a player
583 can begin dragging a piece before the remote opponent has moved. */
585 static AnimState game, player;
587 /* Bitmaps for use as masks when drawing XPM pieces.
588 Need one for each black and white piece. */
589 static Pixmap xpmMask[BlackKing + 1];
591 /* This magic number is the number of intermediate frames used
592 in each half of the animation. For short moves it's reduced
593 by 1. The total number of frames will be factor * 2 + 1. */
596 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
598 MenuItem fileMenu[] = {
599 {N_("New Game Ctrl+N"), "New Game", ResetProc},
600 {N_("New Shuffle Game ..."), "New Shuffle Game", ShuffleMenuProc},
601 {N_("New Variant ... Alt+Shift+V"), "New Variant", NewVariantProc}, // [HGM] variant: not functional yet
602 {"----", NULL, NothingProc},
603 {N_("Load Game Ctrl+O"), "Load Game", LoadGameProc},
604 {N_("Load Position Ctrl+Shift+O"), "Load Position", LoadPositionProc},
605 // {N_("Load Next Game"), "Load Next Game", LoadNextGameProc},
606 // {N_("Load Previous Game"), "Load Previous Game", LoadPrevGameProc},
607 // {N_("Reload Same Game"), "Reload Same Game", ReloadGameProc},
608 {N_("Next Position Shift+PgDn"), "Load Next Position", LoadNextPositionProc},
609 {N_("Prev Position Shift+PgUp"), "Load Previous Position", LoadPrevPositionProc},
610 {"----", NULL, NothingProc},
611 // {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
612 {N_("Save Game Ctrl+S"), "Save Game", SaveGameProc},
613 {N_("Save Position Ctrl+Shift+S"), "Save Position", SavePositionProc},
614 {"----", NULL, NothingProc},
615 {N_("Mail Move"), "Mail Move", MailMoveProc},
616 {N_("Reload CMail Message"), "Reload CMail Message", ReloadCmailMsgProc},
617 {"----", NULL, NothingProc},
618 {N_("Quit Ctr+Q"), "Exit", QuitProc},
622 MenuItem editMenu[] = {
623 {N_("Copy Game Ctrl+C"), "Copy Game", CopyGameProc},
624 {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
625 {N_("Copy Game List"), "Copy Game List", CopyGameListProc},
626 {"----", NULL, NothingProc},
627 {N_("Paste Game Ctrl+V"), "Paste Game", PasteGameProc},
628 {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
629 {"----", NULL, NothingProc},
630 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
631 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
632 {N_("Edit Tags"), "Edit Tags", EditTagsProc},
633 {N_("Edit Comment"), "Edit Comment", EditCommentProc},
634 {"----", NULL, NothingProc},
635 {N_("Revert Home"), "Revert", RevertProc},
636 {N_("Annotate"), "Annotate", AnnotateProc},
637 {N_("Truncate Game End"), "Truncate Game", TruncateGameProc},
638 {"----", NULL, NothingProc},
639 {N_("Backward Alt+Left"), "Backward", BackwardProc},
640 {N_("Forward Alt+Right"), "Forward", ForwardProc},
641 {N_("Back to Start Alt+Home"), "Back to Start", ToStartProc},
642 {N_("Forward to End Alt+End"), "Forward to End", ToEndProc},
646 MenuItem viewMenu[] = {
647 {N_("Flip View F2"), "Flip View", FlipViewProc},
648 {"----", NULL, NothingProc},
649 {N_("Engine Output Alt+Shift+O"), "Show Engine Output", EngineOutputProc},
650 {N_("Move History Alt+Shift+H"), "Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
651 {N_("Evaluation Graph Alt+Shift+E"), "Show Evaluation Graph", EvalGraphProc},
652 {N_("Game List Alt+Shift+G"), "Show Game List", ShowGameListProc},
653 {N_("ICS text menu"), "ICStex", IcsTextProc},
654 {"----", NULL, NothingProc},
655 {N_("Tags"), "Show Tags", EditTagsProc},
656 {N_("Comments"), "Show Comments", EditCommentProc},
657 {N_("ICS Input Box"), "ICS Input Box", IcsInputBoxProc},
658 {"----", NULL, NothingProc},
659 {N_("Board..."), "Board Options", BoardOptionsProc},
660 {N_("Game List Tags..."), "Game List", GameListOptionsPopUp},
664 MenuItem modeMenu[] = {
665 {N_("Machine White Ctrl+W"), "Machine White", MachineWhiteProc},
666 {N_("Machine Black Ctrl+B"), "Machine Black", MachineBlackProc},
667 {N_("Two Machines Ctrl+T"), "Two Machines", TwoMachinesProc},
668 {N_("Analysis Mode Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
669 {N_("Analyze File Ctrl+F"), "Analyze File", AnalyzeFileProc },
670 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
671 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
672 {N_("Training"), "Training", TrainingProc},
673 {N_("ICS Client"), "ICS Client", IcsClientProc},
674 {"----", NULL, NothingProc},
675 {N_("Machine Match"), "Machine Match", MatchProc},
676 {N_("Pause Pause"), "Pause", PauseProc},
680 MenuItem actionMenu[] = {
681 {N_("Accept F3"), "Accept", AcceptProc},
682 {N_("Decline F4"), "Decline", DeclineProc},
683 {N_("Rematch F12"), "Rematch", RematchProc},
684 {"----", NULL, NothingProc},
685 {N_("Call Flag F5"), "Call Flag", CallFlagProc},
686 {N_("Draw F6"), "Draw", DrawProc},
687 {N_("Adjourn F7"), "Adjourn", AdjournProc},
688 {N_("Abort F8"),"Abort", AbortProc},
689 {N_("Resign F9"), "Resign", ResignProc},
690 {"----", NULL, NothingProc},
691 {N_("Stop Observing F10"), "Stop Observing", StopObservingProc},
692 {N_("Stop Examining F11"), "Stop Examining", StopExaminingProc},
693 {N_("Upload to Examine"), "Upload to Examine", UploadProc},
694 {"----", NULL, NothingProc},
695 {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
696 {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
697 {N_("Adjudicate Draw"), "Adjudicate Draw", AdjuDrawProc},
701 MenuItem engineMenu[] = {
702 {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
703 {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
704 {"----", NULL, NothingProc},
705 {N_("Hint"), "Hint", HintProc},
706 {N_("Book"), "Book", BookProc},
707 {"----", NULL, NothingProc},
708 {N_("Move Now Ctrl+M"), "Move Now", MoveNowProc},
709 {N_("Retract Move Ctrl+X"), "Retract Move", RetractMoveProc},
713 MenuItem optionsMenu[] = {
714 #define OPTIONSDIALOG
716 {N_("General ..."), "General", OptionsProc},
718 {N_("Time Control ... Alt+Shift+T"), "Time Control", TimeControlProc},
719 {N_("Common Engine ... Alt+Shift+U"), "Common Engine", UciMenuProc},
720 {N_("Adjudications ... Alt+Shift+J"), "Adjudications", EngineMenuProc},
721 {N_("ICS ..."), "ICS", IcsOptionsProc},
722 {N_("Match ..."), "Match", MatchOptionsProc},
723 {N_("Load Game ..."), "Load Game", LoadOptionsProc},
724 {N_("Save Game ..."), "Save Game", SaveOptionsProc},
725 // {N_(" ..."), "", OptionsProc},
726 {N_("Game List ..."), "Game List", GameListOptionsPopUp},
727 {N_("Sounds ..."), "Sounds", SoundOptionsProc},
728 {"----", NULL, NothingProc},
729 #ifndef OPTIONSDIALOG
730 {N_("Always Queen Ctrl+Shift+Q"), "Always Queen", AlwaysQueenProc},
731 {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
732 {N_("Animate Moving Ctrl+Shift+A"), "Animate Moving", AnimateMovingProc},
733 {N_("Auto Flag Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
734 {N_("Auto Flip View"), "Auto Flip View", AutoflipProc},
735 {N_("Blindfold"), "Blindfold", BlindfoldProc},
736 {N_("Flash Moves"), "Flash Moves", FlashMovesProc},
738 {N_("Highlight Dragging"), "Highlight Dragging", HighlightDraggingProc},
740 {N_("Highlight Last Move"), "Highlight Last Move", HighlightLastMoveProc},
741 {N_("Highlight With Arrow"), "Arrow", HighlightArrowProc},
742 {N_("Move Sound"), "Move Sound", MoveSoundProc},
743 // {N_("ICS Alarm"), "ICS Alarm", IcsAlarmProc},
744 {N_("One-Click Moving"), "OneClick", OneClickProc},
745 {N_("Periodic Updates"), "Periodic Updates", PeriodicUpdatesProc},
746 {N_("Ponder Next Move Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
747 {N_("Popup Exit Message"), "Popup Exit Message", PopupExitMessageProc},
748 {N_("Popup Move Errors"), "Popup Move Errors", PopupMoveErrorsProc},
749 // {N_("Premove"), "Premove", PremoveProc},
750 {N_("Show Coords"), "Show Coords", ShowCoordsProc},
751 {N_("Hide Thinking Ctrl+Shift+H"), "Hide Thinking", HideThinkingProc},
752 {N_("Test Legality Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
753 {"----", NULL, NothingProc},
755 {N_("Save Settings Now"), "Save Settings Now", SaveSettingsProc},
756 {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
760 MenuItem helpMenu[] = {
761 {N_("Info XBoard"), "Info XBoard", InfoProc},
762 {N_("Man XBoard F1"), "Man XBoard", ManProc},
763 {"----", NULL, NothingProc},
764 {N_("About XBoard"), "About XBoard", AboutProc},
769 {N_("File"), "File", fileMenu},
770 {N_("Edit"), "Edit", editMenu},
771 {N_("View"), "View", viewMenu},
772 {N_("Mode"), "Mode", modeMenu},
773 {N_("Action"), "Action", actionMenu},
774 {N_("Engine"), "Engine", engineMenu},
775 {N_("Options"), "Options", optionsMenu},
776 {N_("Help"), "Help", helpMenu},
780 #define PAUSE_BUTTON "P"
781 MenuItem buttonBar[] = {
782 {"<<", "<<", ToStartProc},
783 {"<", "<", BackwardProc},
784 {PAUSE_BUTTON, PAUSE_BUTTON, PauseProc},
785 {">", ">", ForwardProc},
786 {">>", ">>", ToEndProc},
790 #define PIECE_MENU_SIZE 18
791 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
792 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
793 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
794 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
795 N_("Empty square"), N_("Clear board") },
796 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
797 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
798 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
799 N_("Empty square"), N_("Clear board") }
801 /* must be in same order as PieceMenuStrings! */
802 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
803 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
804 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
805 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
806 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
807 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
808 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
809 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
810 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
813 #define DROP_MENU_SIZE 6
814 String dropMenuStrings[DROP_MENU_SIZE] = {
815 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
817 /* must be in same order as PieceMenuStrings! */
818 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
819 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
820 WhiteRook, WhiteQueen
828 DropMenuEnables dmEnables[] = {
846 { XtNborderWidth, 0 },
847 { XtNdefaultDistance, 0 },
851 { XtNborderWidth, 0 },
852 { XtNresizable, (XtArgVal) True },
856 { XtNborderWidth, 0 },
862 { XtNjustify, (XtArgVal) XtJustifyRight },
863 { XtNlabel, (XtArgVal) "..." },
864 { XtNresizable, (XtArgVal) True },
865 { XtNresize, (XtArgVal) False }
868 Arg messageArgs[] = {
869 { XtNjustify, (XtArgVal) XtJustifyLeft },
870 { XtNlabel, (XtArgVal) "..." },
871 { XtNresizable, (XtArgVal) True },
872 { XtNresize, (XtArgVal) False }
876 { XtNborderWidth, 0 },
877 { XtNjustify, (XtArgVal) XtJustifyLeft }
880 XtResource clientResources[] = {
881 { "flashCount", "flashCount", XtRInt, sizeof(int),
882 XtOffset(AppDataPtr, flashCount), XtRImmediate,
883 (XtPointer) FLASH_COUNT },
886 XrmOptionDescRec shellOptions[] = {
887 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
888 { "-flash", "flashCount", XrmoptionNoArg, "3" },
889 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
892 XtActionsRec boardActions[] = {
893 { "DrawPosition", DrawPositionProc },
894 { "HandleUserMove", HandleUserMove },
895 { "AnimateUserMove", AnimateUserMove },
896 { "HandlePV", HandlePV },
897 { "SelectPV", SelectPV },
898 { "StopPV", StopPV },
899 { "FileNameAction", FileNameAction },
900 { "AskQuestionProc", AskQuestionProc },
901 { "AskQuestionReplyAction", AskQuestionReplyAction },
902 { "PieceMenuPopup", PieceMenuPopup },
903 { "WhiteClock", WhiteClock },
904 { "BlackClock", BlackClock },
905 { "Iconify", Iconify },
906 { "ResetProc", ResetProc },
907 { "NewVariantProc", NewVariantProc },
908 { "LoadGameProc", LoadGameProc },
909 { "LoadNextGameProc", LoadNextGameProc },
910 { "LoadPrevGameProc", LoadPrevGameProc },
911 { "LoadSelectedProc", LoadSelectedProc },
912 { "SetFilterProc", SetFilterProc },
913 { "ReloadGameProc", ReloadGameProc },
914 { "LoadPositionProc", LoadPositionProc },
915 { "LoadNextPositionProc", LoadNextPositionProc },
916 { "LoadPrevPositionProc", LoadPrevPositionProc },
917 { "ReloadPositionProc", ReloadPositionProc },
918 { "CopyPositionProc", CopyPositionProc },
919 { "PastePositionProc", PastePositionProc },
920 { "CopyGameProc", CopyGameProc },
921 { "CopyGameListProc", CopyGameListProc },
922 { "PasteGameProc", PasteGameProc },
923 { "SaveGameProc", SaveGameProc },
924 { "SavePositionProc", SavePositionProc },
925 { "MailMoveProc", MailMoveProc },
926 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
927 { "QuitProc", QuitProc },
928 { "MachineWhiteProc", MachineWhiteProc },
929 { "MachineBlackProc", MachineBlackProc },
930 { "AnalysisModeProc", AnalyzeModeProc },
931 { "AnalyzeFileProc", AnalyzeFileProc },
932 { "TwoMachinesProc", TwoMachinesProc },
933 { "IcsClientProc", IcsClientProc },
934 { "EditGameProc", EditGameProc },
935 { "EditPositionProc", EditPositionProc },
936 { "TrainingProc", EditPositionProc },
937 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
938 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
939 { "ShowGameListProc", ShowGameListProc },
940 { "ShowMoveListProc", HistoryShowProc},
941 { "EditTagsProc", EditCommentProc },
942 { "EditCommentProc", EditCommentProc },
943 { "IcsInputBoxProc", IcsInputBoxProc },
944 { "PauseProc", PauseProc },
945 { "AcceptProc", AcceptProc },
946 { "DeclineProc", DeclineProc },
947 { "RematchProc", RematchProc },
948 { "CallFlagProc", CallFlagProc },
949 { "DrawProc", DrawProc },
950 { "AdjournProc", AdjournProc },
951 { "AbortProc", AbortProc },
952 { "ResignProc", ResignProc },
953 { "AdjuWhiteProc", AdjuWhiteProc },
954 { "AdjuBlackProc", AdjuBlackProc },
955 { "AdjuDrawProc", AdjuDrawProc },
956 { "TypeInProc", TypeInProc },
957 { "EnterKeyProc", EnterKeyProc },
958 { "UpKeyProc", UpKeyProc },
959 { "DownKeyProc", DownKeyProc },
960 { "StopObservingProc", StopObservingProc },
961 { "StopExaminingProc", StopExaminingProc },
962 { "UploadProc", UploadProc },
963 { "BackwardProc", BackwardProc },
964 { "ForwardProc", ForwardProc },
965 { "ToStartProc", ToStartProc },
966 { "ToEndProc", ToEndProc },
967 { "RevertProc", RevertProc },
968 { "AnnotateProc", AnnotateProc },
969 { "TruncateGameProc", TruncateGameProc },
970 { "MoveNowProc", MoveNowProc },
971 { "RetractMoveProc", RetractMoveProc },
972 { "EngineMenuProc", (XtActionProc) EngineMenuProc },
973 { "UciMenuProc", (XtActionProc) UciMenuProc },
974 { "TimeControlProc", (XtActionProc) TimeControlProc },
975 { "FlipViewProc", FlipViewProc },
976 { "PonderNextMoveProc", PonderNextMoveProc },
977 #ifndef OPTIONSDIALOG
978 { "AlwaysQueenProc", AlwaysQueenProc },
979 { "AnimateDraggingProc", AnimateDraggingProc },
980 { "AnimateMovingProc", AnimateMovingProc },
981 { "AutoflagProc", AutoflagProc },
982 { "AutoflipProc", AutoflipProc },
983 { "BlindfoldProc", BlindfoldProc },
984 { "FlashMovesProc", FlashMovesProc },
986 { "HighlightDraggingProc", HighlightDraggingProc },
988 { "HighlightLastMoveProc", HighlightLastMoveProc },
989 // { "IcsAlarmProc", IcsAlarmProc },
990 { "MoveSoundProc", MoveSoundProc },
991 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
992 { "PopupExitMessageProc", PopupExitMessageProc },
993 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
994 // { "PremoveProc", PremoveProc },
995 { "ShowCoordsProc", ShowCoordsProc },
996 { "ShowThinkingProc", ShowThinkingProc },
997 { "HideThinkingProc", HideThinkingProc },
998 { "TestLegalityProc", TestLegalityProc },
1000 { "SaveSettingsProc", SaveSettingsProc },
1001 { "SaveOnExitProc", SaveOnExitProc },
1002 { "InfoProc", InfoProc },
1003 { "ManProc", ManProc },
1004 { "HintProc", HintProc },
1005 { "BookProc", BookProc },
1006 { "AboutGameProc", AboutGameProc },
1007 { "AboutProc", AboutProc },
1008 { "DebugProc", DebugProc },
1009 { "NothingProc", NothingProc },
1010 { "CommentClick", (XtActionProc) CommentClick },
1011 { "CommentPopDown", (XtActionProc) CommentPopDown },
1012 { "TagsPopDown", (XtActionProc) TagsPopDown },
1013 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1014 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1015 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1016 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1017 { "GameListPopDown", (XtActionProc) GameListPopDown },
1018 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1019 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1020 { "HistoryPopDown", (XtActionProc) HistoryPopDown },
1021 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1022 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1023 { "ShufflePopDown", (XtActionProc) ShufflePopDown },
1024 { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
1025 { "GenericPopDown", (XtActionProc) GenericPopDown },
1026 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1029 char globalTranslations[] =
1030 ":<Key>F9: ResignProc() \n \
1031 :Ctrl<Key>n: ResetProc() \n \
1032 :Meta<Key>V: NewVariantProc() \n \
1033 :Ctrl<Key>o: LoadGameProc() \n \
1034 :Meta<Key>Next: LoadNextGameProc() \n \
1035 :Meta<Key>Prior: LoadPrevGameProc() \n \
1036 :Ctrl<Key>s: SaveGameProc() \n \
1037 :Ctrl<Key>c: CopyGameProc() \n \
1038 :Ctrl<Key>v: PasteGameProc() \n \
1039 :Ctrl<Key>O: LoadPositionProc() \n \
1040 :Shift<Key>Next: LoadNextPositionProc() \n \
1041 :Shift<Key>Prior: LoadPrevPositionProc() \n \
1042 :Ctrl<Key>S: SavePositionProc() \n \
1043 :Ctrl<Key>C: CopyPositionProc() \n \
1044 :Ctrl<Key>V: PastePositionProc() \n \
1045 :Ctrl<Key>q: QuitProc() \n \
1046 :Ctrl<Key>w: MachineWhiteProc() \n \
1047 :Ctrl<Key>b: MachineBlackProc() \n \
1048 :Ctrl<Key>t: TwoMachinesProc() \n \
1049 :Ctrl<Key>a: AnalysisModeProc() \n \
1050 :Ctrl<Key>f: AnalyzeFileProc() \n \
1051 :Ctrl<Key>e: EditGameProc() \n \
1052 :Ctrl<Key>E: EditPositionProc() \n \
1053 :Meta<Key>O: EngineOutputProc() \n \
1054 :Meta<Key>E: EvalGraphProc() \n \
1055 :Meta<Key>G: ShowGameListProc() \n \
1056 :Meta<Key>H: ShowMoveListProc() \n \
1057 :<Key>Pause: PauseProc() \n \
1058 :<Key>F3: AcceptProc() \n \
1059 :<Key>F4: DeclineProc() \n \
1060 :<Key>F12: RematchProc() \n \
1061 :<Key>F5: CallFlagProc() \n \
1062 :<Key>F6: DrawProc() \n \
1063 :<Key>F7: AdjournProc() \n \
1064 :<Key>F8: AbortProc() \n \
1065 :<Key>F10: StopObservingProc() \n \
1066 :<Key>F11: StopExaminingProc() \n \
1067 :Meta Ctrl<Key>F12: DebugProc() \n \
1068 :Meta<Key>End: ToEndProc() \n \
1069 :Meta<Key>Right: ForwardProc() \n \
1070 :Meta<Key>Home: ToStartProc() \n \
1071 :Meta<Key>Left: BackwardProc() \n \
1072 :<Key>Home: RevertProc() \n \
1073 :<Key>End: TruncateGameProc() \n \
1074 :Ctrl<Key>m: MoveNowProc() \n \
1075 :Ctrl<Key>x: RetractMoveProc() \n \
1076 :Meta<Key>J: EngineMenuProc() \n \
1077 :Meta<Key>U: UciMenuProc() \n \
1078 :Meta<Key>T: TimeControlProc() \n \
1079 :Ctrl<Key>P: PonderNextMoveProc() \n "
1080 #ifndef OPTIONSDIALOG
1082 :Ctrl<Key>Q: AlwaysQueenProc() \n \
1083 :Ctrl<Key>F: AutoflagProc() \n \
1084 :Ctrl<Key>A: AnimateMovingProc() \n \
1085 :Ctrl<Key>L: TestLegalityProc() \n \
1086 :Ctrl<Key>H: HideThinkingProc() \n "
1089 :<Key>-: Iconify() \n \
1090 :<Key>F1: ManProc() \n \
1091 :<Key>F2: FlipViewProc() \n \
1092 <KeyDown>.: BackwardProc() \n \
1093 <KeyUp>.: ForwardProc() \n \
1094 Shift<Key>1: AskQuestionProc(\"Direct command\",\
1095 \"Send to chess program:\",,1) \n \
1096 Shift<Key>2: AskQuestionProc(\"Direct command\",\
1097 \"Send to second chess program:\",,2) \n";
1099 char boardTranslations[] =
1100 "<Btn1Down>: HandleUserMove(0) \n \
1101 Shift<Btn1Up>: HandleUserMove(1) \n \
1102 <Btn1Up>: HandleUserMove(0) \n \
1103 <Btn1Motion>: AnimateUserMove() \n \
1104 <Btn3Motion>: HandlePV() \n \
1105 <Btn3Up>: PieceMenuPopup(menuB) \n \
1106 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1107 PieceMenuPopup(menuB) \n \
1108 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1109 PieceMenuPopup(menuW) \n \
1110 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1111 PieceMenuPopup(menuW) \n \
1112 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1113 PieceMenuPopup(menuB) \n";
1115 char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
1116 char blackTranslations[] = "<BtnDown>: BlackClock()\n";
1118 char ICSInputTranslations[] =
1119 "<Key>Up: UpKeyProc() \n "
1120 "<Key>Down: DownKeyProc() \n "
1121 "<Key>Return: EnterKeyProc() \n";
1123 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1124 // as the widget is destroyed before the up-click can call extend-end
1125 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1127 String xboardResources[] = {
1128 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1129 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1130 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1135 /* Max possible square size */
1136 #define MAXSQSIZE 256
1138 static int xpm_avail[MAXSQSIZE];
1140 #ifdef HAVE_DIR_STRUCT
1142 /* Extract piece size from filename */
1144 xpm_getsize(name, len, ext)
1155 if ((p=strchr(name, '.')) == NULL ||
1156 StrCaseCmp(p+1, ext) != 0)
1162 while (*p && isdigit(*p))
1169 /* Setup xpm_avail */
1171 xpm_getavail(dirname, ext)
1179 for (i=0; i<MAXSQSIZE; ++i)
1182 if (appData.debugMode)
1183 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1185 dir = opendir(dirname);
1188 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1189 programName, dirname);
1193 while ((ent=readdir(dir)) != NULL) {
1194 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1195 if (i > 0 && i < MAXSQSIZE)
1205 xpm_print_avail(fp, ext)
1211 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1212 for (i=1; i<MAXSQSIZE; ++i) {
1218 /* Return XPM piecesize closest to size */
1220 xpm_closest_to(dirname, size, ext)
1226 int sm_diff = MAXSQSIZE;
1230 xpm_getavail(dirname, ext);
1232 if (appData.debugMode)
1233 xpm_print_avail(stderr, ext);
1235 for (i=1; i<MAXSQSIZE; ++i) {
1238 diff = (diff<0) ? -diff : diff;
1239 if (diff < sm_diff) {
1247 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1253 #else /* !HAVE_DIR_STRUCT */
1254 /* If we are on a system without a DIR struct, we can't
1255 read the directory, so we can't collect a list of
1256 filenames, etc., so we can't do any size-fitting. */
1258 xpm_closest_to(dirname, size, ext)
1263 fprintf(stderr, _("\
1264 Warning: No DIR structure found on this system --\n\
1265 Unable to autosize for XPM/XIM pieces.\n\
1266 Please report this error to frankm@hiwaay.net.\n\
1267 Include system type & operating system in message.\n"));
1270 #endif /* HAVE_DIR_STRUCT */
1272 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1273 "magenta", "cyan", "white" };
1277 TextColors textColors[(int)NColorClasses];
1279 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1281 parse_color(str, which)
1285 char *p, buf[100], *d;
1288 if (strlen(str) > 99) /* watch bounds on buf */
1293 for (i=0; i<which; ++i) {
1300 /* Could be looking at something like:
1302 .. in which case we want to stop on a comma also */
1303 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1307 return -1; /* Use default for empty field */
1310 if (which == 2 || isdigit(*p))
1313 while (*p && isalpha(*p))
1318 for (i=0; i<8; ++i) {
1319 if (!StrCaseCmp(buf, cnames[i]))
1320 return which? (i+40) : (i+30);
1322 if (!StrCaseCmp(buf, "default")) return -1;
1324 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1329 parse_cpair(cc, str)
1333 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1334 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1339 /* bg and attr are optional */
1340 textColors[(int)cc].bg = parse_color(str, 1);
1341 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1342 textColors[(int)cc].attr = 0;
1348 /* Arrange to catch delete-window events */
1349 Atom wm_delete_window;
1351 CatchDeleteWindow(Widget w, String procname)
1354 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1355 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1356 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1363 XtSetArg(args[0], XtNiconic, False);
1364 XtSetValues(shellWidget, args, 1);
1366 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1369 //---------------------------------------------------------------------------------------------------------
1370 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1373 #define CW_USEDEFAULT (1<<31)
1374 #define ICS_TEXT_MENU_SIZE 90
1375 #define DEBUG_FILE "xboard.debug"
1376 #define SetCurrentDirectory chdir
1377 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1381 // these two must some day move to frontend.h, when they are implemented
1382 Boolean GameListIsUp();
1384 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1387 // front-end part of option handling
1389 // [HGM] This platform-dependent table provides the location for storing the color info
1390 extern char *crWhite, * crBlack;
1394 &appData.whitePieceColor,
1395 &appData.blackPieceColor,
1396 &appData.lightSquareColor,
1397 &appData.darkSquareColor,
1398 &appData.highlightSquareColor,
1399 &appData.premoveHighlightColor,
1400 &appData.lowTimeWarningColor,
1411 // [HGM] font: keep a font for each square size, even non-stndard ones
1412 #define NUM_SIZES 18
1413 #define MAX_SIZE 130
1414 Boolean fontSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1415 char *fontTable[NUM_FONTS][MAX_SIZE];
1418 ParseFont(char *name, int number)
1419 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1421 if(sscanf(name, "size%d:", &size)) {
1422 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1423 // defer processing it until we know if it matches our board size
1424 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1425 fontTable[number][size] = strdup(strchr(name, ':')+1);
1426 fontValid[number][size] = True;
1431 case 0: // CLOCK_FONT
1432 appData.clockFont = strdup(name);
1434 case 1: // MESSAGE_FONT
1435 appData.font = strdup(name);
1437 case 2: // COORD_FONT
1438 appData.coordFont = strdup(name);
1443 fontSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1448 { // only 2 fonts currently
1449 appData.clockFont = CLOCK_FONT_NAME;
1450 appData.coordFont = COORD_FONT_NAME;
1451 appData.font = DEFAULT_FONT_NAME;
1456 { // no-op, until we identify the code for this already in XBoard and move it here
1460 ParseColor(int n, char *name)
1461 { // in XBoard, just copy the color-name string
1462 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1466 ParseTextAttribs(ColorClass cc, char *s)
1468 (&appData.colorShout)[cc] = strdup(s);
1472 ParseBoardSize(void *addr, char *name)
1474 appData.boardSize = strdup(name);
1479 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1483 SetCommPortDefaults()
1484 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1487 // [HGM] args: these three cases taken out to stay in front-end
1489 SaveFontArg(FILE *f, ArgDescriptor *ad)
1492 int i, n = (int)(intptr_t)ad->argLoc;
1494 case 0: // CLOCK_FONT
1495 name = appData.clockFont;
1497 case 1: // MESSAGE_FONT
1498 name = appData.font;
1500 case 2: // COORD_FONT
1501 name = appData.coordFont;
1506 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1507 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1508 fontTable[n][squareSize] = strdup(name);
1509 fontValid[n][squareSize] = True;
1512 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1513 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1518 { // nothing to do, as the sounds are at all times represented by their text-string names already
1522 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1523 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1524 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1528 SaveColor(FILE *f, ArgDescriptor *ad)
1529 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1530 if(colorVariable[(int)(intptr_t)ad->argLoc])
1531 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1535 SaveBoardSize(FILE *f, char *name, void *addr)
1536 { // wrapper to shield back-end from BoardSize & sizeInfo
1537 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1541 ParseCommPortSettings(char *s)
1542 { // no such option in XBoard (yet)
1545 extern Widget engineOutputShell;
1548 GetActualPlacement(Widget wg, WindowPlacement *wp)
1558 XtSetArg(args[i], XtNx, &x); i++;
1559 XtSetArg(args[i], XtNy, &y); i++;
1560 XtSetArg(args[i], XtNwidth, &w); i++;
1561 XtSetArg(args[i], XtNheight, &h); i++;
1562 XtGetValues(wg, args, i);
1571 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1572 // In XBoard this will have to wait until awareness of window parameters is implemented
1573 GetActualPlacement(shellWidget, &wpMain);
1574 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1575 if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1576 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1577 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1578 if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1579 if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1583 PrintCommPortSettings(FILE *f, char *name)
1584 { // This option does not exist in XBoard
1588 MySearchPath(char *installDir, char *name, char *fullname)
1589 { // just append installDir and name. Perhaps ExpandPath should be used here?
1590 name = ExpandPathName(name);
1591 if(name && name[0] == '/')
1592 safeStrCpy(fullname, name, MSG_SIZ );
1594 sprintf(fullname, "%s%c%s", installDir, '/', name);
1600 MyGetFullPathName(char *name, char *fullname)
1601 { // should use ExpandPath?
1602 name = ExpandPathName(name);
1603 safeStrCpy(fullname, name, MSG_SIZ );
1608 EnsureOnScreen(int *x, int *y, int minX, int minY)
1615 { // [HGM] args: allows testing if main window is realized from back-end
1616 return xBoardWindow != 0;
1620 PopUpStartupDialog()
1621 { // start menu not implemented in XBoard
1625 ConvertToLine(int argc, char **argv)
1627 static char line[128*1024], buf[1024];
1631 for(i=1; i<argc; i++)
1633 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1634 && argv[i][0] != '{' )
1635 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1637 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1638 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1641 line[strlen(line)-1] = NULLCHAR;
1645 //--------------------------------------------------------------------------------------------
1647 extern Boolean twoBoards, partnerUp;
1650 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1652 #define BoardSize int
1653 void InitDrawingSizes(BoardSize boardSize, int flags)
1654 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1655 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1657 XtGeometryResult gres;
1660 if(!formWidget) return;
1663 * Enable shell resizing.
1665 shellArgs[0].value = (XtArgVal) &w;
1666 shellArgs[1].value = (XtArgVal) &h;
1667 XtGetValues(shellWidget, shellArgs, 2);
1669 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1670 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1671 XtSetValues(shellWidget, &shellArgs[2], 4);
1673 XtSetArg(args[0], XtNdefaultDistance, &sep);
1674 XtGetValues(formWidget, args, 1);
1676 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1677 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1678 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1680 hOffset = boardWidth + 10;
1681 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1682 secondSegments[i] = gridSegments[i];
1683 secondSegments[i].x1 += hOffset;
1684 secondSegments[i].x2 += hOffset;
1687 XtSetArg(args[0], XtNwidth, boardWidth);
1688 XtSetArg(args[1], XtNheight, boardHeight);
1689 XtSetValues(boardWidget, args, 2);
1691 timerWidth = (boardWidth - sep) / 2;
1692 XtSetArg(args[0], XtNwidth, timerWidth);
1693 XtSetValues(whiteTimerWidget, args, 1);
1694 XtSetValues(blackTimerWidget, args, 1);
1696 XawFormDoLayout(formWidget, False);
1698 if (appData.titleInWindow) {
1700 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1701 XtSetArg(args[i], XtNheight, &h); i++;
1702 XtGetValues(titleWidget, args, i);
1704 w = boardWidth - 2*bor;
1706 XtSetArg(args[0], XtNwidth, &w);
1707 XtGetValues(menuBarWidget, args, 1);
1708 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1711 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1712 if (gres != XtGeometryYes && appData.debugMode) {
1714 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1715 programName, gres, w, h, wr, hr);
1719 XawFormDoLayout(formWidget, True);
1722 * Inhibit shell resizing.
1724 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1725 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1726 shellArgs[4].value = shellArgs[2].value = w;
1727 shellArgs[5].value = shellArgs[3].value = h;
1728 XtSetValues(shellWidget, &shellArgs[0], 6);
1730 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1733 for(i=0; i<4; i++) {
1735 for(p=0; p<=(int)WhiteKing; p++)
1736 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1737 if(gameInfo.variant == VariantShogi) {
1738 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1739 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1740 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1741 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1742 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1745 if(gameInfo.variant == VariantGothic) {
1746 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1749 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1750 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1751 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1754 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1755 for(p=0; p<=(int)WhiteKing; p++)
1756 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1757 if(gameInfo.variant == VariantShogi) {
1758 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1759 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1760 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1761 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1762 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1765 if(gameInfo.variant == VariantGothic) {
1766 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1769 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1770 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1771 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1776 for(i=0; i<2; i++) {
1778 for(p=0; p<=(int)WhiteKing; p++)
1779 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1780 if(gameInfo.variant == VariantShogi) {
1781 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1782 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1783 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1784 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1785 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1788 if(gameInfo.variant == VariantGothic) {
1789 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1792 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1793 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1794 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1804 void ParseIcsTextColors()
1805 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1806 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1807 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1808 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1809 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1810 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1811 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1812 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1813 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1814 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1815 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1817 if (appData.colorize) {
1819 _("%s: can't parse color names; disabling colorization\n"),
1822 appData.colorize = FALSE;
1827 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1828 XrmValue vFrom, vTo;
1829 int forceMono = False;
1831 if (!appData.monoMode) {
1832 vFrom.addr = (caddr_t) appData.lightSquareColor;
1833 vFrom.size = strlen(appData.lightSquareColor);
1834 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1835 if (vTo.addr == NULL) {
1836 appData.monoMode = True;
1839 lightSquareColor = *(Pixel *) vTo.addr;
1842 if (!appData.monoMode) {
1843 vFrom.addr = (caddr_t) appData.darkSquareColor;
1844 vFrom.size = strlen(appData.darkSquareColor);
1845 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1846 if (vTo.addr == NULL) {
1847 appData.monoMode = True;
1850 darkSquareColor = *(Pixel *) vTo.addr;
1853 if (!appData.monoMode) {
1854 vFrom.addr = (caddr_t) appData.whitePieceColor;
1855 vFrom.size = strlen(appData.whitePieceColor);
1856 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1857 if (vTo.addr == NULL) {
1858 appData.monoMode = True;
1861 whitePieceColor = *(Pixel *) vTo.addr;
1864 if (!appData.monoMode) {
1865 vFrom.addr = (caddr_t) appData.blackPieceColor;
1866 vFrom.size = strlen(appData.blackPieceColor);
1867 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1868 if (vTo.addr == NULL) {
1869 appData.monoMode = True;
1872 blackPieceColor = *(Pixel *) vTo.addr;
1876 if (!appData.monoMode) {
1877 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1878 vFrom.size = strlen(appData.highlightSquareColor);
1879 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1880 if (vTo.addr == NULL) {
1881 appData.monoMode = True;
1884 highlightSquareColor = *(Pixel *) vTo.addr;
1888 if (!appData.monoMode) {
1889 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1890 vFrom.size = strlen(appData.premoveHighlightColor);
1891 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1892 if (vTo.addr == NULL) {
1893 appData.monoMode = True;
1896 premoveHighlightColor = *(Pixel *) vTo.addr;
1904 { // [HGM] taken out of main
1906 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1907 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1908 appData.bitmapDirectory = DEF_BITMAP_DIR;
1910 if (appData.bitmapDirectory[0] != NULLCHAR) {
1914 CreateXPMBoard(appData.liteBackTextureFile, 1);
1915 CreateXPMBoard(appData.darkBackTextureFile, 0);
1919 /* Create regular pieces */
1920 if (!useImages) CreatePieces();
1929 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1930 XSetWindowAttributes window_attributes;
1932 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1933 XrmValue vFrom, vTo;
1934 XtGeometryResult gres;
1937 int forceMono = False;
1939 srandom(time(0)); // [HGM] book: make random truly random
1941 setbuf(stdout, NULL);
1942 setbuf(stderr, NULL);
1945 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1946 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1950 programName = strrchr(argv[0], '/');
1951 if (programName == NULL)
1952 programName = argv[0];
1957 XtSetLanguageProc(NULL, NULL, NULL);
1958 bindtextdomain(PACKAGE, LOCALEDIR);
1959 textdomain(PACKAGE);
1963 XtAppInitialize(&appContext, "XBoard", shellOptions,
1964 XtNumber(shellOptions),
1965 &argc, argv, xboardResources, NULL, 0);
1966 appData.boardSize = "";
1967 InitAppData(ConvertToLine(argc, argv));
1969 if (p == NULL) p = "/tmp";
1970 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1971 gameCopyFilename = (char*) malloc(i);
1972 gamePasteFilename = (char*) malloc(i);
1973 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1974 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1976 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1977 clientResources, XtNumber(clientResources),
1980 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1981 static char buf[MSG_SIZ];
1982 EscapeExpand(buf, appData.initString);
1983 appData.initString = strdup(buf);
1984 EscapeExpand(buf, appData.secondInitString);
1985 appData.secondInitString = strdup(buf);
1986 EscapeExpand(buf, appData.firstComputerString);
1987 appData.firstComputerString = strdup(buf);
1988 EscapeExpand(buf, appData.secondComputerString);
1989 appData.secondComputerString = strdup(buf);
1992 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1995 if (chdir(chessDir) != 0) {
1996 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2002 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2003 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2004 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
2005 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2008 setbuf(debugFP, NULL);
2011 /* [HGM,HR] make sure board size is acceptable */
2012 if(appData.NrFiles > BOARD_FILES ||
2013 appData.NrRanks > BOARD_RANKS )
2014 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
2017 /* This feature does not work; animation needs a rewrite */
2018 appData.highlightDragging = FALSE;
2022 xDisplay = XtDisplay(shellWidget);
2023 xScreen = DefaultScreen(xDisplay);
2024 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2026 gameInfo.variant = StringToVariant(appData.variant);
2027 InitPosition(FALSE);
2030 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2032 if (isdigit(appData.boardSize[0])) {
2033 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2034 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2035 &fontPxlSize, &smallLayout, &tinyLayout);
2037 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2038 programName, appData.boardSize);
2042 /* Find some defaults; use the nearest known size */
2043 SizeDefaults *szd, *nearest;
2044 int distance = 99999;
2045 nearest = szd = sizeDefaults;
2046 while (szd->name != NULL) {
2047 if (abs(szd->squareSize - squareSize) < distance) {
2049 distance = abs(szd->squareSize - squareSize);
2050 if (distance == 0) break;
2054 if (i < 2) lineGap = nearest->lineGap;
2055 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2056 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2057 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2058 if (i < 6) smallLayout = nearest->smallLayout;
2059 if (i < 7) tinyLayout = nearest->tinyLayout;
2062 SizeDefaults *szd = sizeDefaults;
2063 if (*appData.boardSize == NULLCHAR) {
2064 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2065 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2068 if (szd->name == NULL) szd--;
2069 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2071 while (szd->name != NULL &&
2072 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2073 if (szd->name == NULL) {
2074 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2075 programName, appData.boardSize);
2079 squareSize = szd->squareSize;
2080 lineGap = szd->lineGap;
2081 clockFontPxlSize = szd->clockFontPxlSize;
2082 coordFontPxlSize = szd->coordFontPxlSize;
2083 fontPxlSize = szd->fontPxlSize;
2084 smallLayout = szd->smallLayout;
2085 tinyLayout = szd->tinyLayout;
2086 // [HGM] font: use defaults from settings file if available and not overruled
2088 if(!fontSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2089 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2090 if(!fontSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2091 appData.font = fontTable[MESSAGE_FONT][squareSize];
2092 if(!fontSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2093 appData.coordFont = fontTable[COORD_FONT][squareSize];
2095 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2096 if (strlen(appData.pixmapDirectory) > 0) {
2097 p = ExpandPathName(appData.pixmapDirectory);
2099 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2100 appData.pixmapDirectory);
2103 if (appData.debugMode) {
2104 fprintf(stderr, _("\
2105 XBoard square size (hint): %d\n\
2106 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2108 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2109 if (appData.debugMode) {
2110 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2113 defaultLineGap = lineGap;
2114 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2116 /* [HR] height treated separately (hacked) */
2117 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2118 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2119 if (appData.showJail == 1) {
2120 /* Jail on top and bottom */
2121 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2122 XtSetArg(boardArgs[2], XtNheight,
2123 boardHeight + 2*(lineGap + squareSize));
2124 } else if (appData.showJail == 2) {
2126 XtSetArg(boardArgs[1], XtNwidth,
2127 boardWidth + 2*(lineGap + squareSize));
2128 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2131 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2132 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2136 * Determine what fonts to use.
2138 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2139 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2140 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2141 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2142 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2143 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2144 appData.font = FindFont(appData.font, fontPxlSize);
2145 countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
2146 countFontStruct = XQueryFont(xDisplay, countFontID);
2147 // appData.font = FindFont(appData.font, fontPxlSize);
2149 xdb = XtDatabase(xDisplay);
2150 XrmPutStringResource(&xdb, "*font", appData.font);
2153 * Detect if there are not enough colors available and adapt.
2155 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2156 appData.monoMode = True;
2159 forceMono = MakeColors();
2162 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2164 appData.monoMode = True;
2167 if (appData.lowTimeWarning && !appData.monoMode) {
2168 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2169 vFrom.size = strlen(appData.lowTimeWarningColor);
2170 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2171 if (vTo.addr == NULL)
2172 appData.monoMode = True;
2174 lowTimeWarningColor = *(Pixel *) vTo.addr;
2177 if (appData.monoMode && appData.debugMode) {
2178 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2179 (unsigned long) XWhitePixel(xDisplay, xScreen),
2180 (unsigned long) XBlackPixel(xDisplay, xScreen));
2183 ParseIcsTextColors();
2184 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2185 textColors[ColorNone].attr = 0;
2187 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2193 layoutName = "tinyLayout";
2194 } else if (smallLayout) {
2195 layoutName = "smallLayout";
2197 layoutName = "normalLayout";
2199 /* Outer layoutWidget is there only to provide a name for use in
2200 resources that depend on the layout style */
2202 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2203 layoutArgs, XtNumber(layoutArgs));
2205 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2206 formArgs, XtNumber(formArgs));
2207 XtSetArg(args[0], XtNdefaultDistance, &sep);
2208 XtGetValues(formWidget, args, 1);
2211 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2212 XtSetArg(args[0], XtNtop, XtChainTop);
2213 XtSetArg(args[1], XtNbottom, XtChainTop);
2214 XtSetArg(args[2], XtNright, XtChainLeft);
2215 XtSetValues(menuBarWidget, args, 3);
2217 widgetList[j++] = whiteTimerWidget =
2218 XtCreateWidget("whiteTime", labelWidgetClass,
2219 formWidget, timerArgs, XtNumber(timerArgs));
2220 XtSetArg(args[0], XtNfont, clockFontStruct);
2221 XtSetArg(args[1], XtNtop, XtChainTop);
2222 XtSetArg(args[2], XtNbottom, XtChainTop);
2223 XtSetValues(whiteTimerWidget, args, 3);
2225 widgetList[j++] = blackTimerWidget =
2226 XtCreateWidget("blackTime", labelWidgetClass,
2227 formWidget, timerArgs, XtNumber(timerArgs));
2228 XtSetArg(args[0], XtNfont, clockFontStruct);
2229 XtSetArg(args[1], XtNtop, XtChainTop);
2230 XtSetArg(args[2], XtNbottom, XtChainTop);
2231 XtSetValues(blackTimerWidget, args, 3);
2233 if (appData.titleInWindow) {
2234 widgetList[j++] = titleWidget =
2235 XtCreateWidget("title", labelWidgetClass, formWidget,
2236 titleArgs, XtNumber(titleArgs));
2237 XtSetArg(args[0], XtNtop, XtChainTop);
2238 XtSetArg(args[1], XtNbottom, XtChainTop);
2239 XtSetValues(titleWidget, args, 2);
2242 if (appData.showButtonBar) {
2243 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2244 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2245 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2246 XtSetArg(args[2], XtNtop, XtChainTop);
2247 XtSetArg(args[3], XtNbottom, XtChainTop);
2248 XtSetValues(buttonBarWidget, args, 4);
2251 widgetList[j++] = messageWidget =
2252 XtCreateWidget("message", labelWidgetClass, formWidget,
2253 messageArgs, XtNumber(messageArgs));
2254 XtSetArg(args[0], XtNtop, XtChainTop);
2255 XtSetArg(args[1], XtNbottom, XtChainTop);
2256 XtSetValues(messageWidget, args, 2);
2258 widgetList[j++] = boardWidget =
2259 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2260 XtNumber(boardArgs));
2262 XtManageChildren(widgetList, j);
2264 timerWidth = (boardWidth - sep) / 2;
2265 XtSetArg(args[0], XtNwidth, timerWidth);
2266 XtSetValues(whiteTimerWidget, args, 1);
2267 XtSetValues(blackTimerWidget, args, 1);
2269 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2270 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2271 XtGetValues(whiteTimerWidget, args, 2);
2273 if (appData.showButtonBar) {
2274 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2275 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2276 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2280 * formWidget uses these constraints but they are stored
2284 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2285 XtSetValues(menuBarWidget, args, i);
2286 if (appData.titleInWindow) {
2289 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2290 XtSetValues(whiteTimerWidget, args, i);
2292 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2293 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2294 XtSetValues(blackTimerWidget, args, i);
2296 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2297 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2298 XtSetValues(titleWidget, args, i);
2300 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2301 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2302 XtSetValues(messageWidget, args, i);
2303 if (appData.showButtonBar) {
2305 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2306 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2307 XtSetValues(buttonBarWidget, args, i);
2311 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2312 XtSetValues(whiteTimerWidget, args, i);
2314 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2315 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2316 XtSetValues(blackTimerWidget, args, i);
2318 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2319 XtSetValues(titleWidget, args, i);
2321 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2322 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2323 XtSetValues(messageWidget, args, i);
2324 if (appData.showButtonBar) {
2326 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2327 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2328 XtSetValues(buttonBarWidget, args, i);
2333 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2334 XtSetValues(whiteTimerWidget, args, i);
2336 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2337 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2338 XtSetValues(blackTimerWidget, args, i);
2340 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2341 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2342 XtSetValues(messageWidget, args, i);
2343 if (appData.showButtonBar) {
2345 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2346 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2347 XtSetValues(buttonBarWidget, args, i);
2351 XtSetArg(args[0], XtNfromVert, messageWidget);
2352 XtSetArg(args[1], XtNtop, XtChainTop);
2353 XtSetArg(args[2], XtNbottom, XtChainBottom);
2354 XtSetArg(args[3], XtNleft, XtChainLeft);
2355 XtSetArg(args[4], XtNright, XtChainRight);
2356 XtSetValues(boardWidget, args, 5);
2358 XtRealizeWidget(shellWidget);
2361 XtSetArg(args[0], XtNx, wpMain.x);
2362 XtSetArg(args[1], XtNy, wpMain.y);
2363 XtSetValues(shellWidget, args, 2);
2367 * Correct the width of the message and title widgets.
2368 * It is not known why some systems need the extra fudge term.
2369 * The value "2" is probably larger than needed.
2371 XawFormDoLayout(formWidget, False);
2373 #define WIDTH_FUDGE 2
2375 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2376 XtSetArg(args[i], XtNheight, &h); i++;
2377 XtGetValues(messageWidget, args, i);
2378 if (appData.showButtonBar) {
2380 XtSetArg(args[i], XtNwidth, &w); i++;
2381 XtGetValues(buttonBarWidget, args, i);
2382 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2384 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2387 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2388 if (gres != XtGeometryYes && appData.debugMode) {
2389 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2390 programName, gres, w, h, wr, hr);
2393 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2394 /* The size used for the child widget in layout lags one resize behind
2395 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2397 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2398 if (gres != XtGeometryYes && appData.debugMode) {
2399 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2400 programName, gres, w, h, wr, hr);
2403 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2404 XtSetArg(args[1], XtNright, XtChainRight);
2405 XtSetValues(messageWidget, args, 2);
2407 if (appData.titleInWindow) {
2409 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2410 XtSetArg(args[i], XtNheight, &h); i++;
2411 XtGetValues(titleWidget, args, i);
2413 w = boardWidth - 2*bor;
2415 XtSetArg(args[0], XtNwidth, &w);
2416 XtGetValues(menuBarWidget, args, 1);
2417 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2420 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2421 if (gres != XtGeometryYes && appData.debugMode) {
2423 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2424 programName, gres, w, h, wr, hr);
2427 XawFormDoLayout(formWidget, True);
2429 xBoardWindow = XtWindow(boardWidget);
2431 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2432 // not need to go into InitDrawingSizes().
2436 * Create X checkmark bitmap and initialize option menu checks.
2438 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2439 checkmark_bits, checkmark_width, checkmark_height);
2440 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2441 #ifndef OPTIONSDIALOG
2442 if (appData.alwaysPromoteToQueen) {
2443 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2446 if (appData.animateDragging) {
2447 XtSetValues(XtNameToWidget(menuBarWidget,
2448 "menuOptions.Animate Dragging"),
2451 if (appData.animate) {
2452 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2455 if (appData.autoCallFlag) {
2456 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2459 if (appData.autoFlipView) {
2460 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2463 if (appData.blindfold) {
2464 XtSetValues(XtNameToWidget(menuBarWidget,
2465 "menuOptions.Blindfold"), args, 1);
2467 if (appData.flashCount > 0) {
2468 XtSetValues(XtNameToWidget(menuBarWidget,
2469 "menuOptions.Flash Moves"),
2473 if (appData.highlightDragging) {
2474 XtSetValues(XtNameToWidget(menuBarWidget,
2475 "menuOptions.Highlight Dragging"),
2479 if (appData.highlightLastMove) {
2480 XtSetValues(XtNameToWidget(menuBarWidget,
2481 "menuOptions.Highlight Last Move"),
2484 if (appData.highlightMoveWithArrow) {
2485 XtSetValues(XtNameToWidget(menuBarWidget,
2486 "menuOptions.Arrow"),
2489 // if (appData.icsAlarm) {
2490 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2493 if (appData.ringBellAfterMoves) {
2494 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2497 if (appData.oneClick) {
2498 XtSetValues(XtNameToWidget(menuBarWidget,
2499 "menuOptions.OneClick"), args, 1);
2501 if (appData.periodicUpdates) {
2502 XtSetValues(XtNameToWidget(menuBarWidget,
2503 "menuOptions.Periodic Updates"), args, 1);
2505 if (appData.ponderNextMove) {
2506 XtSetValues(XtNameToWidget(menuBarWidget,
2507 "menuOptions.Ponder Next Move"), args, 1);
2509 if (appData.popupExitMessage) {
2510 XtSetValues(XtNameToWidget(menuBarWidget,
2511 "menuOptions.Popup Exit Message"), args, 1);
2513 if (appData.popupMoveErrors) {
2514 XtSetValues(XtNameToWidget(menuBarWidget,
2515 "menuOptions.Popup Move Errors"), args, 1);
2517 // if (appData.premove) {
2518 // XtSetValues(XtNameToWidget(menuBarWidget,
2519 // "menuOptions.Premove"), args, 1);
2521 if (appData.showCoords) {
2522 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2525 if (appData.hideThinkingFromHuman) {
2526 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2529 if (appData.testLegality) {
2530 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2534 if (saveSettingsOnExit) {
2535 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2542 ReadBitmap(&wIconPixmap, "icon_white.bm",
2543 icon_white_bits, icon_white_width, icon_white_height);
2544 ReadBitmap(&bIconPixmap, "icon_black.bm",
2545 icon_black_bits, icon_black_width, icon_black_height);
2546 iconPixmap = wIconPixmap;
2548 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2549 XtSetValues(shellWidget, args, i);
2552 * Create a cursor for the board widget.
2554 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2555 XChangeWindowAttributes(xDisplay, xBoardWindow,
2556 CWCursor, &window_attributes);
2559 * Inhibit shell resizing.
2561 shellArgs[0].value = (XtArgVal) &w;
2562 shellArgs[1].value = (XtArgVal) &h;
2563 XtGetValues(shellWidget, shellArgs, 2);
2564 shellArgs[4].value = shellArgs[2].value = w;
2565 shellArgs[5].value = shellArgs[3].value = h;
2566 XtSetValues(shellWidget, &shellArgs[2], 4);
2567 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2568 marginH = h - boardHeight;
2570 CatchDeleteWindow(shellWidget, "QuitProc");
2578 if (appData.animate || appData.animateDragging)
2581 XtAugmentTranslations(formWidget,
2582 XtParseTranslationTable(globalTranslations));
2583 XtAugmentTranslations(boardWidget,
2584 XtParseTranslationTable(boardTranslations));
2585 XtAugmentTranslations(whiteTimerWidget,
2586 XtParseTranslationTable(whiteTranslations));
2587 XtAugmentTranslations(blackTimerWidget,
2588 XtParseTranslationTable(blackTranslations));
2590 /* Why is the following needed on some versions of X instead
2591 * of a translation? */
2592 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2593 (XtEventHandler) EventProc, NULL);
2595 XtAddEventHandler(formWidget, KeyPressMask, False,
2596 (XtEventHandler) MoveTypeInProc, NULL);
2598 /* [AS] Restore layout */
2599 if( wpMoveHistory.visible ) {
2603 if( wpEvalGraph.visible )
2608 if( wpEngineOutput.visible ) {
2609 EngineOutputPopUp();
2614 if (errorExitStatus == -1) {
2615 if (appData.icsActive) {
2616 /* We now wait until we see "login:" from the ICS before
2617 sending the logon script (problems with timestamp otherwise) */
2618 /*ICSInitScript();*/
2619 if (appData.icsInputBox) ICSInputBoxPopUp();
2623 signal(SIGWINCH, TermSizeSigHandler);
2625 signal(SIGINT, IntSigHandler);
2626 signal(SIGTERM, IntSigHandler);
2627 if (*appData.cmailGameName != NULLCHAR) {
2628 signal(SIGUSR1, CmailSigHandler);
2631 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2633 // XtSetKeyboardFocus(shellWidget, formWidget);
2634 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2636 XtAppMainLoop(appContext);
2637 if (appData.debugMode) fclose(debugFP); // [DM] debug
2644 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2645 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2647 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2648 unlink(gameCopyFilename);
2649 unlink(gamePasteFilename);
2652 RETSIGTYPE TermSizeSigHandler(int sig)
2665 CmailSigHandler(sig)
2671 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2673 /* Activate call-back function CmailSigHandlerCallBack() */
2674 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2676 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2680 CmailSigHandlerCallBack(isr, closure, message, count, error)
2688 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2690 /**** end signal code ****/
2696 /* try to open the icsLogon script, either in the location given
2697 * or in the users HOME directory
2704 f = fopen(appData.icsLogon, "r");
2707 homedir = getenv("HOME");
2708 if (homedir != NULL)
2710 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2711 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2712 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2713 f = fopen(buf, "r");
2718 ProcessICSInitScript(f);
2720 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2743 if (!menuBarWidget) return;
2744 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2746 DisplayError("menuEdit.Revert", 0);
2748 XtSetSensitive(w, !grey);
2750 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2752 DisplayError("menuEdit.Annotate", 0);
2754 XtSetSensitive(w, !grey);
2759 SetMenuEnables(enab)
2763 if (!menuBarWidget) return;
2764 while (enab->name != NULL) {
2765 w = XtNameToWidget(menuBarWidget, enab->name);
2767 DisplayError(enab->name, 0);
2769 XtSetSensitive(w, enab->value);
2775 Enables icsEnables[] = {
2776 { "menuFile.Mail Move", False },
2777 { "menuFile.Reload CMail Message", False },
2778 { "menuMode.Machine Black", False },
2779 { "menuMode.Machine White", False },
2780 { "menuMode.Analysis Mode", False },
2781 { "menuMode.Analyze File", False },
2782 { "menuMode.Two Machines", False },
2783 { "menuMode.Machine Match", False },
2785 { "menuEngine.Hint", False },
2786 { "menuEngine.Book", False },
2787 { "menuEngine.Move Now", False },
2788 #ifndef OPTIONSDIALOG
2789 { "menuOptions.Periodic Updates", False },
2790 { "menuOptions.Hide Thinking", False },
2791 { "menuOptions.Ponder Next Move", False },
2793 { "menuEngine.Engine #1 Settings", False },
2795 { "menuEngine.Engine #2 Settings", False },
2796 { "menuEdit.Annotate", False },
2800 Enables ncpEnables[] = {
2801 { "menuFile.Mail Move", False },
2802 { "menuFile.Reload CMail Message", False },
2803 { "menuMode.Machine White", False },
2804 { "menuMode.Machine Black", False },
2805 { "menuMode.Analysis Mode", False },
2806 { "menuMode.Analyze File", False },
2807 { "menuMode.Two Machines", False },
2808 { "menuMode.Machine Match", False },
2809 { "menuMode.ICS Client", False },
2810 { "menuView.ICStex", False },
2811 { "menuView.ICS Input Box", False },
2812 { "Action", False },
2813 { "menuEdit.Revert", False },
2814 { "menuEdit.Annotate", False },
2815 { "menuEngine.Engine #1 Settings", False },
2816 { "menuEngine.Engine #2 Settings", False },
2817 { "menuEngine.Move Now", False },
2818 { "menuEngine.Retract Move", False },
2819 { "menuOptions.ICS", False },
2820 #ifndef OPTIONSDIALOG
2821 { "menuOptions.Auto Flag", False },
2822 { "menuOptions.Auto Flip View", False },
2823 // { "menuOptions.ICS Alarm", False },
2824 { "menuOptions.Move Sound", False },
2825 { "menuOptions.Hide Thinking", False },
2826 { "menuOptions.Periodic Updates", False },
2827 { "menuOptions.Ponder Next Move", False },
2829 { "menuEngine.Hint", False },
2830 { "menuEngine.Book", False },
2834 Enables gnuEnables[] = {
2835 { "menuMode.ICS Client", False },
2836 { "menuView.ICStex", False },
2837 { "menuView.ICS Input Box", False },
2838 { "menuAction.Accept", False },
2839 { "menuAction.Decline", False },
2840 { "menuAction.Rematch", False },
2841 { "menuAction.Adjourn", False },
2842 { "menuAction.Stop Examining", False },
2843 { "menuAction.Stop Observing", False },
2844 { "menuAction.Upload to Examine", False },
2845 { "menuEdit.Revert", False },
2846 { "menuEdit.Annotate", False },
2847 { "menuOptions.ICS", False },
2849 /* The next two options rely on SetCmailMode being called *after* */
2850 /* SetGNUMode so that when GNU is being used to give hints these */
2851 /* menu options are still available */
2853 { "menuFile.Mail Move", False },
2854 { "menuFile.Reload CMail Message", False },
2858 Enables cmailEnables[] = {
2860 { "menuAction.Call Flag", False },
2861 { "menuAction.Draw", True },
2862 { "menuAction.Adjourn", False },
2863 { "menuAction.Abort", False },
2864 { "menuAction.Stop Observing", False },
2865 { "menuAction.Stop Examining", False },
2866 { "menuFile.Mail Move", True },
2867 { "menuFile.Reload CMail Message", True },
2871 Enables trainingOnEnables[] = {
2872 { "menuMode.Edit Comment", False },
2873 { "menuMode.Pause", False },
2874 { "menuEdit.Forward", False },
2875 { "menuEdit.Backward", False },
2876 { "menuEdit.Forward to End", False },
2877 { "menuEdit.Back to Start", False },
2878 { "menuEngine.Move Now", False },
2879 { "menuEdit.Truncate Game", False },
2883 Enables trainingOffEnables[] = {
2884 { "menuMode.Edit Comment", True },
2885 { "menuMode.Pause", True },
2886 { "menuEdit.Forward", True },
2887 { "menuEdit.Backward", True },
2888 { "menuEdit.Forward to End", True },
2889 { "menuEdit.Back to Start", True },
2890 { "menuEngine.Move Now", True },
2891 { "menuEdit.Truncate Game", True },
2895 Enables machineThinkingEnables[] = {
2896 { "menuFile.Load Game", False },
2897 // { "menuFile.Load Next Game", False },
2898 // { "menuFile.Load Previous Game", False },
2899 // { "menuFile.Reload Same Game", False },
2900 { "menuEdit.Paste Game", False },
2901 { "menuFile.Load Position", False },
2902 // { "menuFile.Load Next Position", False },
2903 // { "menuFile.Load Previous Position", False },
2904 // { "menuFile.Reload Same Position", False },
2905 { "menuEdit.Paste Position", False },
2906 { "menuMode.Machine White", False },
2907 { "menuMode.Machine Black", False },
2908 { "menuMode.Two Machines", False },
2909 { "menuMode.Machine Match", False },
2910 { "menuEngine.Retract Move", False },
2914 Enables userThinkingEnables[] = {
2915 { "menuFile.Load Game", True },
2916 // { "menuFile.Load Next Game", True },
2917 // { "menuFile.Load Previous Game", True },
2918 // { "menuFile.Reload Same Game", True },
2919 { "menuEdit.Paste Game", True },
2920 { "menuFile.Load Position", True },
2921 // { "menuFile.Load Next Position", True },
2922 // { "menuFile.Load Previous Position", True },
2923 // { "menuFile.Reload Same Position", True },
2924 { "menuEdit.Paste Position", True },
2925 { "menuMode.Machine White", True },
2926 { "menuMode.Machine Black", True },
2927 { "menuMode.Two Machines", True },
2928 { "menuMode.Machine Match", True },
2929 { "menuEngine.Retract Move", True },
2935 SetMenuEnables(icsEnables);
2938 if (appData.zippyPlay && !appData.noChessProgram) /* [DM] icsEngineAnalyze */
2939 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2946 SetMenuEnables(ncpEnables);
2952 SetMenuEnables(gnuEnables);
2958 SetMenuEnables(cmailEnables);
2964 SetMenuEnables(trainingOnEnables);
2965 if (appData.showButtonBar) {
2966 XtSetSensitive(buttonBarWidget, False);
2972 SetTrainingModeOff()
2974 SetMenuEnables(trainingOffEnables);
2975 if (appData.showButtonBar) {
2976 XtSetSensitive(buttonBarWidget, True);
2981 SetUserThinkingEnables()
2983 if (appData.noChessProgram) return;
2984 SetMenuEnables(userThinkingEnables);
2988 SetMachineThinkingEnables()
2990 if (appData.noChessProgram) return;
2991 SetMenuEnables(machineThinkingEnables);
2993 case MachinePlaysBlack:
2994 case MachinePlaysWhite:
2995 case TwoMachinesPlay:
2996 XtSetSensitive(XtNameToWidget(menuBarWidget,
2997 ModeToWidgetName(gameMode)), True);
3004 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3005 #define HISTORY_SIZE 64
3006 static char *history[HISTORY_SIZE];
3007 int histIn = 0, histP = 0;
3010 SaveInHistory(char *cmd)
3012 if (history[histIn] != NULL) {
3013 free(history[histIn]);
3014 history[histIn] = NULL;
3016 if (*cmd == NULLCHAR) return;
3017 history[histIn] = StrSave(cmd);
3018 histIn = (histIn + 1) % HISTORY_SIZE;
3019 if (history[histIn] != NULL) {
3020 free(history[histIn]);
3021 history[histIn] = NULL;
3027 PrevInHistory(char *cmd)
3030 if (histP == histIn) {
3031 if (history[histIn] != NULL) free(history[histIn]);
3032 history[histIn] = StrSave(cmd);
3034 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3035 if (newhp == histIn || history[newhp] == NULL) return NULL;
3037 return history[histP];
3043 if (histP == histIn) return NULL;
3044 histP = (histP + 1) % HISTORY_SIZE;
3045 return history[histP];
3047 // end of borrowed code
3049 #define Abs(n) ((n)<0 ? -(n) : (n))
3052 * Find a font that matches "pattern" that is as close as
3053 * possible to the targetPxlSize. Prefer fonts that are k
3054 * pixels smaller to fonts that are k pixels larger. The
3055 * pattern must be in the X Consortium standard format,
3056 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3057 * The return value should be freed with XtFree when no
3061 FindFont(pattern, targetPxlSize)
3065 char **fonts, *p, *best, *scalable, *scalableTail;
3066 int i, j, nfonts, minerr, err, pxlSize;
3069 char **missing_list;
3071 char *def_string, *base_fnt_lst, strInt[3];
3073 XFontStruct **fnt_list;
3074 base_fnt_lst = calloc(1, strlen(pattern) + 3);
3075 snprintf(strInt, sizeof(strInt)/sizeof(strInt[0]), "%d", targetPxlSize);
3076 p = strstr(pattern, "--");
3077 strncpy(base_fnt_lst, pattern, p - pattern + 2);
3078 strcat(base_fnt_lst, strInt);
3079 strcat(base_fnt_lst, strchr(p + 2, '-'));
3081 if ((fntSet = XCreateFontSet(xDisplay,
3085 &def_string)) == NULL) {
3087 fprintf(stderr, _("Unable to create font set.\n"));
3091 nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
3093 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3095 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3096 programName, pattern);
3104 for (i=0; i<nfonts; i++) {
3107 if (*p != '-') continue;
3109 if (*p == NULLCHAR) break;
3110 if (*p++ == '-') j++;
3112 if (j < 7) continue;
3115 scalable = fonts[i];
3118 err = pxlSize - targetPxlSize;
3119 if (Abs(err) < Abs(minerr) ||
3120 (minerr > 0 && err < 0 && -err == minerr)) {
3126 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3127 /* If the error is too big and there is a scalable font,
3128 use the scalable font. */
3129 int headlen = scalableTail - scalable;
3130 p = (char *) XtMalloc(strlen(scalable) + 10);
3131 while (isdigit(*scalableTail)) scalableTail++;
3132 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3134 p = (char *) XtMalloc(strlen(best) + 2);
3135 safeStrCpy(p, best, strlen(best)+1 );
3137 if (appData.debugMode) {
3138 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3139 pattern, targetPxlSize, p);
3142 if (missing_count > 0)
3143 XFreeStringList(missing_list);
3144 XFreeFontSet(xDisplay, fntSet);
3146 XFreeFontNames(fonts);
3152 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3153 // must be called before all non-first callse to CreateGCs()
3154 XtReleaseGC(shellWidget, highlineGC);
3155 XtReleaseGC(shellWidget, lightSquareGC);
3156 XtReleaseGC(shellWidget, darkSquareGC);
3157 XtReleaseGC(shellWidget, lineGC);
3158 if (appData.monoMode) {
3159 if (DefaultDepth(xDisplay, xScreen) == 1) {
3160 XtReleaseGC(shellWidget, wbPieceGC);
3162 XtReleaseGC(shellWidget, bwPieceGC);
3165 XtReleaseGC(shellWidget, prelineGC);
3166 XtReleaseGC(shellWidget, jailSquareGC);
3167 XtReleaseGC(shellWidget, wdPieceGC);
3168 XtReleaseGC(shellWidget, wlPieceGC);
3169 XtReleaseGC(shellWidget, wjPieceGC);
3170 XtReleaseGC(shellWidget, bdPieceGC);
3171 XtReleaseGC(shellWidget, blPieceGC);
3172 XtReleaseGC(shellWidget, bjPieceGC);
3176 void CreateGCs(int redo)
3178 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3179 | GCBackground | GCFunction | GCPlaneMask;
3180 XGCValues gc_values;
3183 gc_values.plane_mask = AllPlanes;
3184 gc_values.line_width = lineGap;
3185 gc_values.line_style = LineSolid;
3186 gc_values.function = GXcopy;
3189 DeleteGCs(); // called a second time; clean up old GCs first
3190 } else { // [HGM] grid and font GCs created on first call only
3191 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3192 gc_values.background = XWhitePixel(xDisplay, xScreen);
3193 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3194 XSetFont(xDisplay, coordGC, coordFontID);
3196 // [HGM] make font for holdings counts (white on black)
3197 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3198 gc_values.background = XBlackPixel(xDisplay, xScreen);
3199 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3200 XSetFont(xDisplay, countGC, countFontID);
3202 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3203 gc_values.background = XBlackPixel(xDisplay, xScreen);
3204 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3206 if (appData.monoMode) {
3207 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3208 gc_values.background = XWhitePixel(xDisplay, xScreen);
3209 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3211 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3212 gc_values.background = XBlackPixel(xDisplay, xScreen);
3213 lightSquareGC = wbPieceGC
3214 = XtGetGC(shellWidget, value_mask, &gc_values);
3216 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3217 gc_values.background = XWhitePixel(xDisplay, xScreen);
3218 darkSquareGC = bwPieceGC
3219 = XtGetGC(shellWidget, value_mask, &gc_values);
3221 if (DefaultDepth(xDisplay, xScreen) == 1) {
3222 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3223 gc_values.function = GXcopyInverted;
3224 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3225 gc_values.function = GXcopy;
3226 if (XBlackPixel(xDisplay, xScreen) == 1) {
3227 bwPieceGC = darkSquareGC;
3228 wbPieceGC = copyInvertedGC;
3230 bwPieceGC = copyInvertedGC;
3231 wbPieceGC = lightSquareGC;
3235 gc_values.foreground = highlightSquareColor;
3236 gc_values.background = highlightSquareColor;
3237 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3239 gc_values.foreground = premoveHighlightColor;
3240 gc_values.background = premoveHighlightColor;
3241 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3243 gc_values.foreground = lightSquareColor;
3244 gc_values.background = darkSquareColor;
3245 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3247 gc_values.foreground = darkSquareColor;
3248 gc_values.background = lightSquareColor;
3249 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3251 gc_values.foreground = jailSquareColor;
3252 gc_values.background = jailSquareColor;
3253 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3255 gc_values.foreground = whitePieceColor;
3256 gc_values.background = darkSquareColor;
3257 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3259 gc_values.foreground = whitePieceColor;
3260 gc_values.background = lightSquareColor;
3261 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3263 gc_values.foreground = whitePieceColor;
3264 gc_values.background = jailSquareColor;
3265 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3267 gc_values.foreground = blackPieceColor;
3268 gc_values.background = darkSquareColor;
3269 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3271 gc_values.foreground = blackPieceColor;
3272 gc_values.background = lightSquareColor;
3273 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3275 gc_values.foreground = blackPieceColor;
3276 gc_values.background = jailSquareColor;
3277 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3281 void loadXIM(xim, xmask, filename, dest, mask)
3294 fp = fopen(filename, "rb");
3296 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3303 for (y=0; y<h; ++y) {
3304 for (x=0; x<h; ++x) {
3309 XPutPixel(xim, x, y, blackPieceColor);
3311 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3314 XPutPixel(xim, x, y, darkSquareColor);
3316 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3319 XPutPixel(xim, x, y, whitePieceColor);
3321 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3324 XPutPixel(xim, x, y, lightSquareColor);
3326 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3334 /* create Pixmap of piece */
3335 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3337 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3340 /* create Pixmap of clipmask
3341 Note: We assume the white/black pieces have the same
3342 outline, so we make only 6 masks. This is okay
3343 since the XPM clipmask routines do the same. */
3345 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3347 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3350 /* now create the 1-bit version */
3351 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3354 values.foreground = 1;
3355 values.background = 0;
3357 /* Don't use XtGetGC, not read only */
3358 maskGC = XCreateGC(xDisplay, *mask,
3359 GCForeground | GCBackground, &values);
3360 XCopyPlane(xDisplay, temp, *mask, maskGC,
3361 0, 0, squareSize, squareSize, 0, 0, 1);
3362 XFreePixmap(xDisplay, temp);
3367 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3369 void CreateXIMPieces()
3374 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3379 /* The XSynchronize calls were copied from CreatePieces.
3380 Not sure if needed, but can't hurt */
3381 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3384 /* temp needed by loadXIM() */
3385 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3386 0, 0, ss, ss, AllPlanes, XYPixmap);
3388 if (strlen(appData.pixmapDirectory) == 0) {
3392 if (appData.monoMode) {
3393 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3397 fprintf(stderr, _("\nLoading XIMs...\n"));
3399 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3400 fprintf(stderr, "%d", piece+1);
3401 for (kind=0; kind<4; kind++) {
3402 fprintf(stderr, ".");
3403 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3404 ExpandPathName(appData.pixmapDirectory),
3405 piece <= (int) WhiteKing ? "" : "w",
3406 pieceBitmapNames[piece],
3408 ximPieceBitmap[kind][piece] =
3409 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3410 0, 0, ss, ss, AllPlanes, XYPixmap);
3411 if (appData.debugMode)
3412 fprintf(stderr, _("(File:%s:) "), buf);
3413 loadXIM(ximPieceBitmap[kind][piece],
3415 &(xpmPieceBitmap2[kind][piece]),
3416 &(ximMaskPm2[piece]));
3417 if(piece <= (int)WhiteKing)
3418 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3420 fprintf(stderr," ");
3422 /* Load light and dark squares */
3423 /* If the LSQ and DSQ pieces don't exist, we will
3424 draw them with solid squares. */
3425 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3426 if (access(buf, 0) != 0) {
3430 fprintf(stderr, _("light square "));
3432 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3433 0, 0, ss, ss, AllPlanes, XYPixmap);
3434 if (appData.debugMode)
3435 fprintf(stderr, _("(File:%s:) "), buf);
3437 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3438 fprintf(stderr, _("dark square "));
3439 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3440 ExpandPathName(appData.pixmapDirectory), ss);
3441 if (appData.debugMode)
3442 fprintf(stderr, _("(File:%s:) "), buf);
3444 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3445 0, 0, ss, ss, AllPlanes, XYPixmap);
3446 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3447 xpmJailSquare = xpmLightSquare;
3449 fprintf(stderr, _("Done.\n"));
3451 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3454 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3457 void CreateXPMBoard(char *s, int kind)
3461 if(s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3462 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3463 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3467 void FreeXPMPieces()
3468 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3469 // thisroutine has to be called t free the old piece pixmaps
3471 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3472 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3474 XFreePixmap(xDisplay, xpmLightSquare);
3475 XFreePixmap(xDisplay, xpmDarkSquare);
3479 void CreateXPMPieces()
3483 u_int ss = squareSize;
3485 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3486 XpmColorSymbol symbols[4];
3487 static int redo = False;
3489 if(redo) FreeXPMPieces(); else redo = 1;
3491 /* The XSynchronize calls were copied from CreatePieces.
3492 Not sure if needed, but can't hurt */
3493 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3495 /* Setup translations so piece colors match square colors */
3496 symbols[0].name = "light_piece";
3497 symbols[0].value = appData.whitePieceColor;
3498 symbols[1].name = "dark_piece";
3499 symbols[1].value = appData.blackPieceColor;
3500 symbols[2].name = "light_square";
3501 symbols[2].value = appData.lightSquareColor;
3502 symbols[3].name = "dark_square";
3503 symbols[3].value = appData.darkSquareColor;
3505 attr.valuemask = XpmColorSymbols;
3506 attr.colorsymbols = symbols;
3507 attr.numsymbols = 4;
3509 if (appData.monoMode) {
3510 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3514 if (strlen(appData.pixmapDirectory) == 0) {
3515 XpmPieces* pieces = builtInXpms;
3518 while (pieces->size != squareSize && pieces->size) pieces++;
3519 if (!pieces->size) {
3520 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3523 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3524 for (kind=0; kind<4; kind++) {
3526 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3527 pieces->xpm[piece][kind],
3528 &(xpmPieceBitmap2[kind][piece]),
3529 NULL, &attr)) != 0) {
3530 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3534 if(piece <= (int) WhiteKing)
3535 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3539 xpmJailSquare = xpmLightSquare;
3543 fprintf(stderr, _("\nLoading XPMs...\n"));
3546 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3547 fprintf(stderr, "%d ", piece+1);
3548 for (kind=0; kind<4; kind++) {
3549 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3550 ExpandPathName(appData.pixmapDirectory),
3551 piece > (int) WhiteKing ? "w" : "",
3552 pieceBitmapNames[piece],
3554 if (appData.debugMode) {
3555 fprintf(stderr, _("(File:%s:) "), buf);
3557 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3558 &(xpmPieceBitmap2[kind][piece]),
3559 NULL, &attr)) != 0) {
3560 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3561 // [HGM] missing: read of unorthodox piece failed; substitute King.
3562 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3563 ExpandPathName(appData.pixmapDirectory),
3565 if (appData.debugMode) {
3566 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3568 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3569 &(xpmPieceBitmap2[kind][piece]),
3573 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3578 if(piece <= (int) WhiteKing)
3579 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3582 /* Load light and dark squares */
3583 /* If the LSQ and DSQ pieces don't exist, we will
3584 draw them with solid squares. */
3585 fprintf(stderr, _("light square "));
3586 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3587 if (access(buf, 0) != 0) {
3591 if (appData.debugMode)
3592 fprintf(stderr, _("(File:%s:) "), buf);
3594 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3595 &xpmLightSquare, NULL, &attr)) != 0) {
3596 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3599 fprintf(stderr, _("dark square "));
3600 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3601 ExpandPathName(appData.pixmapDirectory), ss);
3602 if (appData.debugMode) {
3603 fprintf(stderr, _("(File:%s:) "), buf);
3605 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3606 &xpmDarkSquare, NULL, &attr)) != 0) {
3607 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3611 xpmJailSquare = xpmLightSquare;
3612 fprintf(stderr, _("Done.\n"));
3614 oldVariant = -1; // kludge to force re-makig of animation masks
3615 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3618 #endif /* HAVE_LIBXPM */
3621 /* No built-in bitmaps */
3626 u_int ss = squareSize;
3628 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3631 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3632 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3633 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3634 pieceBitmapNames[piece],
3635 ss, kind == SOLID ? 's' : 'o');
3636 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3637 if(piece <= (int)WhiteKing)
3638 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3642 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3646 /* With built-in bitmaps */
3649 BuiltInBits* bib = builtInBits;
3652 u_int ss = squareSize;
3654 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3657 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3659 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3660 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3661 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3662 pieceBitmapNames[piece],
3663 ss, kind == SOLID ? 's' : 'o');
3664 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3665 bib->bits[kind][piece], ss, ss);
3666 if(piece <= (int)WhiteKing)
3667 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3671 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3676 void ReadBitmap(pm, name, bits, wreq, hreq)
3679 unsigned char bits[];
3685 char msg[MSG_SIZ], fullname[MSG_SIZ];
3687 if (*appData.bitmapDirectory != NULLCHAR) {
3688 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3689 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3690 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3691 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3692 &w, &h, pm, &x_hot, &y_hot);
3693 fprintf(stderr, "load %s\n", name);
3694 if (errcode != BitmapSuccess) {
3696 case BitmapOpenFailed:
3697 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3699 case BitmapFileInvalid:
3700 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3702 case BitmapNoMemory:
3703 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3707 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3711 fprintf(stderr, _("%s: %s...using built-in\n"),
3713 } else if (w != wreq || h != hreq) {
3715 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3716 programName, fullname, w, h, wreq, hreq);
3722 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3731 if (lineGap == 0) return;
3733 /* [HR] Split this into 2 loops for non-square boards. */
3735 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3736 gridSegments[i].x1 = 0;
3737 gridSegments[i].x2 =
3738 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3739 gridSegments[i].y1 = gridSegments[i].y2
3740 = lineGap / 2 + (i * (squareSize + lineGap));
3743 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3744 gridSegments[j + i].y1 = 0;
3745 gridSegments[j + i].y2 =
3746 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3747 gridSegments[j + i].x1 = gridSegments[j + i].x2
3748 = lineGap / 2 + (j * (squareSize + lineGap));
3752 static void MenuBarSelect(w, addr, index)
3757 XtActionProc proc = (XtActionProc) addr;
3759 (proc)(NULL, NULL, NULL, NULL);
3762 void CreateMenuBarPopup(parent, name, mb)
3772 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3775 XtSetArg(args[j], XtNleftMargin, 20); j++;
3776 XtSetArg(args[j], XtNrightMargin, 20); j++;
3778 while (mi->string != NULL) {
3779 if (strcmp(mi->string, "----") == 0) {
3780 entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
3783 XtSetArg(args[j], XtNlabel, XtNewString(mi->string));
3784 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3786 XtAddCallback(entry, XtNcallback,
3787 (XtCallbackProc) MenuBarSelect,
3788 (caddr_t) mi->proc);
3794 Widget CreateMenuBar(mb)
3798 Widget anchor, menuBar;
3800 char menuName[MSG_SIZ];
3803 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3804 XtSetArg(args[j], XtNvSpace, 0); j++;
3805 XtSetArg(args[j], XtNborderWidth, 0); j++;
3806 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3807 formWidget, args, j);
3809 while (mb->name != NULL) {
3810 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3811 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3813 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3816 shortName[0] = mb->name[0];
3817 shortName[1] = NULLCHAR;
3818 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3821 XtSetArg(args[j], XtNlabel, XtNewString(mb->name)); j++;
3824 XtSetArg(args[j], XtNborderWidth, 0); j++;
3825 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3827 CreateMenuBarPopup(menuBar, menuName, mb);
3833 Widget CreateButtonBar(mi)
3837 Widget button, buttonBar;
3841 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3843 XtSetArg(args[j], XtNhSpace, 0); j++;
3845 XtSetArg(args[j], XtNborderWidth, 0); j++;
3846 XtSetArg(args[j], XtNvSpace, 0); j++;
3847 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3848 formWidget, args, j);
3850 while (mi->string != NULL) {
3853 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3854 XtSetArg(args[j], XtNborderWidth, 0); j++;
3856 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3857 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3858 buttonBar, args, j);
3859 XtAddCallback(button, XtNcallback,
3860 (XtCallbackProc) MenuBarSelect,
3861 (caddr_t) mi->proc);
3868 CreatePieceMenu(name, color)
3875 ChessSquare selection;
3877 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3878 boardWidget, args, 0);
3880 for (i = 0; i < PIECE_MENU_SIZE; i++) {
3881 String item = pieceMenuStrings[color][i];
3883 if (strcmp(item, "----") == 0) {
3884 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3887 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3888 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3890 selection = pieceMenuTranslation[color][i];
3891 XtAddCallback(entry, XtNcallback,
3892 (XtCallbackProc) PieceMenuSelect,
3893 (caddr_t) selection);
3894 if (selection == WhitePawn || selection == BlackPawn) {
3895 XtSetArg(args[0], XtNpopupOnEntry, entry);
3896 XtSetValues(menu, args, 1);
3909 ChessSquare selection;
3911 whitePieceMenu = CreatePieceMenu("menuW", 0);
3912 blackPieceMenu = CreatePieceMenu("menuB", 1);
3914 XtRegisterGrabAction(PieceMenuPopup, True,
3915 (unsigned)(ButtonPressMask|ButtonReleaseMask),
3916 GrabModeAsync, GrabModeAsync);
3918 XtSetArg(args[0], XtNlabel, _("Drop"));
3919 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3920 boardWidget, args, 1);
3921 for (i = 0; i < DROP_MENU_SIZE; i++) {
3922 String item = dropMenuStrings[i];
3924 if (strcmp(item, "----") == 0) {
3925 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3928 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3929 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3931 selection = dropMenuTranslation[i];
3932 XtAddCallback(entry, XtNcallback,
3933 (XtCallbackProc) DropMenuSelect,
3934 (caddr_t) selection);
3939 void SetupDropMenu()
3947 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3948 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3949 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3950 dmEnables[i].piece);
3951 XtSetSensitive(entry, p != NULL || !appData.testLegality
3952 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3953 && !appData.icsActive));
3955 while (p && *p++ == dmEnables[i].piece) count++;
3956 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3958 XtSetArg(args[j], XtNlabel, label); j++;
3959 XtSetValues(entry, args, j);
3963 void PieceMenuPopup(w, event, params, num_params)
3967 Cardinal *num_params;
3969 String whichMenu; int menuNr;
3970 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3971 if (event->type == ButtonRelease)
3972 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3973 else if (event->type == ButtonPress)
3974 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3976 case 0: whichMenu = params[0]; break;
3977 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3979 case -1: if (errorUp) ErrorPopDown();
3982 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3985 static void PieceMenuSelect(w, piece, junk)
3990 if (pmFromX < 0 || pmFromY < 0) return;
3991 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3994 static void DropMenuSelect(w, piece, junk)
3999 if (pmFromX < 0 || pmFromY < 0) return;
4000 DropMenuEvent(piece, pmFromX, pmFromY);
4003 void WhiteClock(w, event, prms, nprms)
4012 void BlackClock(w, event, prms, nprms)
4023 * If the user selects on a border boundary, return -1; if off the board,
4024 * return -2. Otherwise map the event coordinate to the square.
4026 int EventToSquare(x, limit)
4034 if ((x % (squareSize + lineGap)) >= squareSize)
4036 x /= (squareSize + lineGap);
4042 static void do_flash_delay(msec)
4048 static void drawHighlight(file, rank, gc)
4054 if (lineGap == 0) return;
4057 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4058 (squareSize + lineGap);
4059 y = lineGap/2 + rank * (squareSize + lineGap);
4061 x = lineGap/2 + file * (squareSize + lineGap);
4062 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4063 (squareSize + lineGap);
4066 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4067 squareSize+lineGap, squareSize+lineGap);
4070 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4071 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4074 SetHighlights(fromX, fromY, toX, toY)
4075 int fromX, fromY, toX, toY;
4077 if (hi1X != fromX || hi1Y != fromY) {
4078 if (hi1X >= 0 && hi1Y >= 0) {
4079 drawHighlight(hi1X, hi1Y, lineGC);
4081 } // [HGM] first erase both, then draw new!
4082 if (hi2X != toX || hi2Y != toY) {
4083 if (hi2X >= 0 && hi2Y >= 0) {
4084 drawHighlight(hi2X, hi2Y, lineGC);
4087 if (hi1X != fromX || hi1Y != fromY) {
4088 if (fromX >= 0 && fromY >= 0) {
4089 drawHighlight(fromX, fromY, highlineGC);
4092 if (hi2X != toX || hi2Y != toY) {
4093 if (toX >= 0 && toY >= 0) {
4094 drawHighlight(toX, toY, highlineGC);
4106 SetHighlights(-1, -1, -1, -1);
4111 SetPremoveHighlights(fromX, fromY, toX, toY)
4112 int fromX, fromY, toX, toY;
4114 if (pm1X != fromX || pm1Y != fromY) {
4115 if (pm1X >= 0 && pm1Y >= 0) {
4116 drawHighlight(pm1X, pm1Y, lineGC);
4118 if (fromX >= 0 && fromY >= 0) {
4119 drawHighlight(fromX, fromY, prelineGC);
4122 if (pm2X != toX || pm2Y != toY) {
4123 if (pm2X >= 0 && pm2Y >= 0) {
4124 drawHighlight(pm2X, pm2Y, lineGC);
4126 if (toX >= 0 && toY >= 0) {
4127 drawHighlight(toX, toY, prelineGC);
4137 ClearPremoveHighlights()
4139 SetPremoveHighlights(-1, -1, -1, -1);
4142 static int CutOutSquare(x, y, x0, y0, kind)
4143 int x, y, *x0, *y0, kind;
4145 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4146 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4148 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4149 if(textureW[kind] < W*squareSize)
4150 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4152 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4153 if(textureH[kind] < H*squareSize)
4154 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4156 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4160 static void BlankSquare(x, y, color, piece, dest, fac)
4161 int x, y, color, fac;
4164 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4166 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4167 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4168 squareSize, squareSize, x*fac, y*fac);
4170 if (useImages && useImageSqs) {
4174 pm = xpmLightSquare;
4179 case 2: /* neutral */
4184 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4185 squareSize, squareSize, x*fac, y*fac);
4195 case 2: /* neutral */
4200 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4205 I split out the routines to draw a piece so that I could
4206 make a generic flash routine.
4208 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4210 int square_color, x, y;
4213 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4214 switch (square_color) {
4216 case 2: /* neutral */
4218 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4219 ? *pieceToOutline(piece)
4220 : *pieceToSolid(piece),
4221 dest, bwPieceGC, 0, 0,
4222 squareSize, squareSize, x, y);
4225 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4226 ? *pieceToSolid(piece)
4227 : *pieceToOutline(piece),
4228 dest, wbPieceGC, 0, 0,
4229 squareSize, squareSize, x, y);
4234 static void monoDrawPiece(piece, square_color, x, y, dest)
4236 int square_color, x, y;
4239 switch (square_color) {
4241 case 2: /* neutral */
4243 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4244 ? *pieceToOutline(piece)
4245 : *pieceToSolid(piece),
4246 dest, bwPieceGC, 0, 0,
4247 squareSize, squareSize, x, y, 1);
4250 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4251 ? *pieceToSolid(piece)
4252 : *pieceToOutline(piece),
4253 dest, wbPieceGC, 0, 0,
4254 squareSize, squareSize, x, y, 1);
4259 static void colorDrawPiece(piece, square_color, x, y, dest)
4261 int square_color, x, y;
4264 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4265 switch (square_color) {
4267 XCopyPlane(xDisplay, *pieceToSolid(piece),
4268 dest, (int) piece < (int) BlackPawn
4269 ? wlPieceGC : blPieceGC, 0, 0,
4270 squareSize, squareSize, x, y, 1);
4273 XCopyPlane(xDisplay, *pieceToSolid(piece),
4274 dest, (int) piece < (int) BlackPawn
4275 ? wdPieceGC : bdPieceGC, 0, 0,
4276 squareSize, squareSize, x, y, 1);
4278 case 2: /* neutral */
4280 XCopyPlane(xDisplay, *pieceToSolid(piece),
4281 dest, (int) piece < (int) BlackPawn
4282 ? wjPieceGC : bjPieceGC, 0, 0,
4283 squareSize, squareSize, x, y, 1);
4288 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4290 int square_color, x, y;
4293 int kind, p = piece;
4295 switch (square_color) {
4297 case 2: /* neutral */
4299 if ((int)piece < (int) BlackPawn) {
4307 if ((int)piece < (int) BlackPawn) {
4315 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4316 if(useTexture & square_color+1) {
4317 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4318 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4319 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4320 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4321 XSetClipMask(xDisplay, wlPieceGC, None);
4322 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4324 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4325 dest, wlPieceGC, 0, 0,
4326 squareSize, squareSize, x, y);
4329 typedef void (*DrawFunc)();
4331 DrawFunc ChooseDrawFunc()
4333 if (appData.monoMode) {
4334 if (DefaultDepth(xDisplay, xScreen) == 1) {
4335 return monoDrawPiece_1bit;
4337 return monoDrawPiece;
4341 return colorDrawPieceImage;
4343 return colorDrawPiece;
4347 /* [HR] determine square color depending on chess variant. */
4348 static int SquareColor(row, column)
4353 if (gameInfo.variant == VariantXiangqi) {
4354 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4356 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4358 } else if (row <= 4) {
4364 square_color = ((column + row) % 2) == 1;
4367 /* [hgm] holdings: next line makes all holdings squares light */
4368 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4370 return square_color;
4373 void DrawSquare(row, column, piece, do_flash)
4374 int row, column, do_flash;
4377 int square_color, x, y, direction, font_ascent, font_descent;
4380 XCharStruct overall;
4384 /* Calculate delay in milliseconds (2-delays per complete flash) */
4385 flash_delay = 500 / appData.flashRate;
4388 x = lineGap + ((BOARD_WIDTH-1)-column) *
4389 (squareSize + lineGap);
4390 y = lineGap + row * (squareSize + lineGap);
4392 x = lineGap + column * (squareSize + lineGap);
4393 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4394 (squareSize + lineGap);
4397 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4399 square_color = SquareColor(row, column);
4401 if ( // [HGM] holdings: blank out area between board and holdings
4402 column == BOARD_LEFT-1 || column == BOARD_RGHT
4403 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4404 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4405 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4407 // [HGM] print piece counts next to holdings
4408 string[1] = NULLCHAR;
4409 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4410 string[0] = '0' + piece;
4411 XTextExtents(countFontStruct, string, 1, &direction,
4412 &font_ascent, &font_descent, &overall);
4413 if (appData.monoMode) {
4414 XDrawImageString(xDisplay, xBoardWindow, countGC,
4415 x + squareSize - overall.width - 2,
4416 y + font_ascent + 1, string, 1);
4418 XDrawString(xDisplay, xBoardWindow, countGC,
4419 x + squareSize - overall.width - 2,
4420 y + font_ascent + 1, string, 1);
4423 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4424 string[0] = '0' + piece;
4425 XTextExtents(countFontStruct, string, 1, &direction,
4426 &font_ascent, &font_descent, &overall);
4427 if (appData.monoMode) {
4428 XDrawImageString(xDisplay, xBoardWindow, countGC,
4429 x + 2, y + font_ascent + 1, string, 1);
4431 XDrawString(xDisplay, xBoardWindow, countGC,
4432 x + 2, y + font_ascent + 1, string, 1);
4436 if (piece == EmptySquare || appData.blindfold) {
4437 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4439 drawfunc = ChooseDrawFunc();
4441 if (do_flash && appData.flashCount > 0) {
4442 for (i=0; i<appData.flashCount; ++i) {
4443 drawfunc(piece, square_color, x, y, xBoardWindow);
4444 XSync(xDisplay, False);
4445 do_flash_delay(flash_delay);
4447 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4448 XSync(xDisplay, False);
4449 do_flash_delay(flash_delay);
4452 drawfunc(piece, square_color, x, y, xBoardWindow);
4456 string[1] = NULLCHAR;
4457 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4458 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4459 string[0] = 'a' + column - BOARD_LEFT;
4460 XTextExtents(coordFontStruct, string, 1, &direction,
4461 &font_ascent, &font_descent, &overall);
4462 if (appData.monoMode) {
4463 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4464 x + squareSize - overall.width - 2,
4465 y + squareSize - font_descent - 1, string, 1);
4467 XDrawString(xDisplay, xBoardWindow, coordGC,
4468 x + squareSize - overall.width - 2,
4469 y + squareSize - font_descent - 1, string, 1);
4472 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4473 string[0] = ONE + row;
4474 XTextExtents(coordFontStruct, string, 1, &direction,
4475 &font_ascent, &font_descent, &overall);
4476 if (appData.monoMode) {
4477 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4478 x + 2, y + font_ascent + 1, string, 1);
4480 XDrawString(xDisplay, xBoardWindow, coordGC,
4481 x + 2, y + font_ascent + 1, string, 1);
4484 if(!partnerUp && marker[row][column]) {
4485 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4486 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4491 /* Why is this needed on some versions of X? */
4492 void EventProc(widget, unused, event)
4497 if (!XtIsRealized(widget))
4500 switch (event->type) {
4502 if (event->xexpose.count > 0) return; /* no clipping is done */
4503 XDrawPosition(widget, True, NULL);
4504 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4505 flipView = !flipView; partnerUp = !partnerUp;
4506 XDrawPosition(widget, True, NULL);
4507 flipView = !flipView; partnerUp = !partnerUp;
4511 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4518 void DrawPosition(fullRedraw, board)
4519 /*Boolean*/int fullRedraw;
4522 XDrawPosition(boardWidget, fullRedraw, board);
4525 /* Returns 1 if there are "too many" differences between b1 and b2
4526 (i.e. more than 1 move was made) */
4527 static int too_many_diffs(b1, b2)
4533 for (i=0; i<BOARD_HEIGHT; ++i) {
4534 for (j=0; j<BOARD_WIDTH; ++j) {
4535 if (b1[i][j] != b2[i][j]) {
4536 if (++c > 4) /* Castling causes 4 diffs */
4544 /* Matrix describing castling maneuvers */
4545 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4546 static int castling_matrix[4][5] = {
4547 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4548 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4549 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4550 { 7, 7, 4, 5, 6 } /* 0-0, black */
4553 /* Checks whether castling occurred. If it did, *rrow and *rcol
4554 are set to the destination (row,col) of the rook that moved.
4556 Returns 1 if castling occurred, 0 if not.
4558 Note: Only handles a max of 1 castling move, so be sure
4559 to call too_many_diffs() first.
4561 static int check_castle_draw(newb, oldb, rrow, rcol)
4568 /* For each type of castling... */
4569 for (i=0; i<4; ++i) {
4570 r = castling_matrix[i];
4572 /* Check the 4 squares involved in the castling move */
4574 for (j=1; j<=4; ++j) {
4575 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4582 /* All 4 changed, so it must be a castling move */
4591 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4592 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4594 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4597 void DrawSeekBackground( int left, int top, int right, int bottom )
4599 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4602 void DrawSeekText(char *buf, int x, int y)
4604 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4607 void DrawSeekDot(int x, int y, int colorNr)
4609 int square = colorNr & 0x80;
4612 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4614 XFillRectangle(xDisplay, xBoardWindow, color,
4615 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4617 XFillArc(xDisplay, xBoardWindow, color,
4618 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4621 static int damage[2][BOARD_RANKS][BOARD_FILES];
4624 * event handler for redrawing the board
4626 void XDrawPosition(w, repaint, board)
4628 /*Boolean*/int repaint;
4632 static int lastFlipView = 0;
4633 static int lastBoardValid[2] = {0, 0};
4634 static Board lastBoard[2];
4637 int nr = twoBoards*partnerUp;
4639 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4641 if (board == NULL) {
4642 if (!lastBoardValid[nr]) return;
4643 board = lastBoard[nr];
4645 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4646 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4647 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4652 * It would be simpler to clear the window with XClearWindow()
4653 * but this causes a very distracting flicker.
4656 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4658 if ( lineGap && IsDrawArrowEnabled())
4659 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4660 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4662 /* If too much changes (begin observing new game, etc.), don't
4664 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4666 /* Special check for castling so we don't flash both the king
4667 and the rook (just flash the king). */
4669 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4670 /* Draw rook with NO flashing. King will be drawn flashing later */
4671 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4672 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4676 /* First pass -- Draw (newly) empty squares and repair damage.
4677 This prevents you from having a piece show up twice while it
4678 is flashing on its new square */
4679 for (i = 0; i < BOARD_HEIGHT; i++)
4680 for (j = 0; j < BOARD_WIDTH; j++)
4681 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4682 || damage[nr][i][j]) {
4683 DrawSquare(i, j, board[i][j], 0);
4684 damage[nr][i][j] = False;
4687 /* Second pass -- Draw piece(s) in new position and flash them */
4688 for (i = 0; i < BOARD_HEIGHT; i++)
4689 for (j = 0; j < BOARD_WIDTH; j++)
4690 if (board[i][j] != lastBoard[nr][i][j]) {
4691 DrawSquare(i, j, board[i][j], do_flash);
4695 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4696 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4697 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4699 for (i = 0; i < BOARD_HEIGHT; i++)
4700 for (j = 0; j < BOARD_WIDTH; j++) {
4701 DrawSquare(i, j, board[i][j], 0);
4702 damage[nr][i][j] = False;
4706 CopyBoard(lastBoard[nr], board);
4707 lastBoardValid[nr] = 1;
4708 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4709 lastFlipView = flipView;
4711 /* Draw highlights */
4712 if (pm1X >= 0 && pm1Y >= 0) {
4713 drawHighlight(pm1X, pm1Y, prelineGC);
4715 if (pm2X >= 0 && pm2Y >= 0) {
4716 drawHighlight(pm2X, pm2Y, prelineGC);
4718 if (hi1X >= 0 && hi1Y >= 0) {
4719 drawHighlight(hi1X, hi1Y, highlineGC);
4721 if (hi2X >= 0 && hi2Y >= 0) {
4722 drawHighlight(hi2X, hi2Y, highlineGC);
4724 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4726 /* If piece being dragged around board, must redraw that too */
4729 XSync(xDisplay, False);
4734 * event handler for redrawing the board
4736 void DrawPositionProc(w, event, prms, nprms)
4742 XDrawPosition(w, True, NULL);
4747 * event handler for parsing user moves
4749 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4750 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4751 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4752 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4753 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4754 // and at the end FinishMove() to perform the move after optional promotion popups.
4755 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4756 void HandleUserMove(w, event, prms, nprms)
4762 if (w != boardWidget || errorExitStatus != -1) return;
4763 if(nprms) shiftKey = !strcmp(prms[0], "1");
4766 if (event->type == ButtonPress) {
4767 XtPopdown(promotionShell);
4768 XtDestroyWidget(promotionShell);
4769 promotionUp = False;
4777 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4778 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4779 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4782 void AnimateUserMove (Widget w, XEvent * event,
4783 String * params, Cardinal * nParams)
4785 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4786 DragPieceMove(event->xmotion.x, event->xmotion.y);
4789 void HandlePV (Widget w, XEvent * event,
4790 String * params, Cardinal * nParams)
4791 { // [HGM] pv: walk PV
4792 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4795 static int savedIndex; /* gross that this is global */
4797 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4800 XawTextPosition index, dummy;
4803 XawTextGetSelectionPos(w, &index, &dummy);
4804 XtSetArg(arg, XtNstring, &val);
4805 XtGetValues(w, &arg, 1);
4806 ReplaceComment(savedIndex, val);
4807 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4808 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4811 void EditCommentPopUp(index, title, text)
4816 if (text == NULL) text = "";
4817 NewCommentPopup(title, text, index);
4820 void ICSInputBoxPopUp()
4825 extern Option boxOptions[];
4827 void ICSInputSendText()
4834 edit = boxOptions[0].handle;
4836 XtSetArg(args[j], XtNstring, &val); j++;
4837 XtGetValues(edit, args, j);
4839 SendMultiLineToICS(val);
4840 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4841 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4844 void ICSInputBoxPopDown()
4849 void CommentPopUp(title, text)
4852 savedIndex = currentMove; // [HGM] vari
4853 NewCommentPopup(title, text, currentMove);
4856 void CommentPopDown()
4861 void FileNamePopUp(label, def, filter, proc, openMode)
4868 fileProc = proc; /* I can't see a way not */
4869 fileOpenMode = openMode; /* to use globals here */
4870 { // [HGM] use file-selector dialog stolen from Ghostview
4872 int index; // this is not supported yet
4874 if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
4875 (def[0] ? def : NULL), filter, openMode, NULL, &name))
4876 (void) (*fileProc)(f, index=0, name);
4880 void FileNamePopDown()
4882 if (!filenameUp) return;
4883 XtPopdown(fileNameShell);
4884 XtDestroyWidget(fileNameShell);
4889 void FileNameCallback(w, client_data, call_data)
4891 XtPointer client_data, call_data;
4896 XtSetArg(args[0], XtNlabel, &name);
4897 XtGetValues(w, args, 1);
4899 if (strcmp(name, _("cancel")) == 0) {
4904 FileNameAction(w, NULL, NULL, NULL);
4907 void FileNameAction(w, event, prms, nprms)
4919 name = XawDialogGetValueString(w = XtParent(w));
4921 if ((name != NULL) && (*name != NULLCHAR)) {
4922 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
4923 XtPopdown(w = XtParent(XtParent(w)));
4927 p = strrchr(buf, ' ');
4934 fullname = ExpandPathName(buf);
4936 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
4939 f = fopen(fullname, fileOpenMode);
4941 DisplayError(_("Failed to open file"), errno);
4943 (void) (*fileProc)(f, index, buf);
4950 XtPopdown(w = XtParent(XtParent(w)));
4956 void PromotionPopUp()
4959 Widget dialog, layout;
4961 Dimension bw_width, pw_width;
4965 XtSetArg(args[j], XtNwidth, &bw_width); j++;
4966 XtGetValues(boardWidget, args, j);
4969 XtSetArg(args[j], XtNresizable, True); j++;
4970 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
4972 XtCreatePopupShell("Promotion", transientShellWidgetClass,
4973 shellWidget, args, j);
4975 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
4976 layoutArgs, XtNumber(layoutArgs));
4979 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
4980 XtSetArg(args[j], XtNborderWidth, 0); j++;
4981 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
4984 if(gameInfo.variant != VariantShogi) {
4985 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
4986 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback,
4987 (XtPointer) dialog);
4988 XawDialogAddButton(dialog, _("General"), PromotionCallback,
4989 (XtPointer) dialog);
4990 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback,
4991 (XtPointer) dialog);
4992 XawDialogAddButton(dialog, _("Captain"), PromotionCallback,
4993 (XtPointer) dialog);
4995 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
4996 (XtPointer) dialog);
4997 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
4998 (XtPointer) dialog);
4999 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
5000 (XtPointer) dialog);
5001 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
5002 (XtPointer) dialog);
5004 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5005 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
5006 gameInfo.variant == VariantGiveaway) {
5007 XawDialogAddButton(dialog, _("King"), PromotionCallback,
5008 (XtPointer) dialog);
5010 if(gameInfo.variant == VariantCapablanca ||
5011 gameInfo.variant == VariantGothic ||
5012 gameInfo.variant == VariantCapaRandom) {
5013 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5014 (XtPointer) dialog);
5015 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5016 (XtPointer) dialog);
5018 } else // [HGM] shogi
5020 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5021 (XtPointer) dialog);
5022 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5023 (XtPointer) dialog);
5025 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5026 (XtPointer) dialog);
5028 XtRealizeWidget(promotionShell);
5029 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5032 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5033 XtGetValues(promotionShell, args, j);
5035 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5036 lineGap + squareSize/3 +
5037 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5038 0 : 6*(squareSize + lineGap)), &x, &y);
5041 XtSetArg(args[j], XtNx, x); j++;
5042 XtSetArg(args[j], XtNy, y); j++;
5043 XtSetValues(promotionShell, args, j);
5045 XtPopup(promotionShell, XtGrabNone);
5050 void PromotionPopDown()
5052 if (!promotionUp) return;
5053 XtPopdown(promotionShell);
5054 XtDestroyWidget(promotionShell);
5055 promotionUp = False;
5058 void PromotionCallback(w, client_data, call_data)
5060 XtPointer client_data, call_data;
5066 XtSetArg(args[0], XtNlabel, &name);
5067 XtGetValues(w, args, 1);
5071 if (fromX == -1) return;
5073 if (strcmp(name, _("cancel")) == 0) {
5077 } else if (strcmp(name, _("Knight")) == 0) {
5079 } else if (strcmp(name, _("Promote")) == 0) {
5081 } else if (strcmp(name, _("Defer")) == 0) {
5084 promoChar = ToLower(name[0]);
5087 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5089 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5090 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5095 void ErrorCallback(w, client_data, call_data)
5097 XtPointer client_data, call_data;
5100 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5102 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5108 if (!errorUp) return;
5110 XtPopdown(errorShell);
5111 XtDestroyWidget(errorShell);
5112 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5115 void ErrorPopUp(title, label, modal)
5116 char *title, *label;
5120 Widget dialog, layout;
5124 Dimension bw_width, pw_width;
5125 Dimension pw_height;
5129 XtSetArg(args[i], XtNresizable, True); i++;
5130 XtSetArg(args[i], XtNtitle, title); i++;
5132 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5133 shellWidget, args, i);
5135 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5136 layoutArgs, XtNumber(layoutArgs));
5139 XtSetArg(args[i], XtNlabel, label); i++;
5140 XtSetArg(args[i], XtNborderWidth, 0); i++;
5141 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5144 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5146 XtRealizeWidget(errorShell);
5147 CatchDeleteWindow(errorShell, "ErrorPopDown");
5150 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5151 XtGetValues(boardWidget, args, i);
5153 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5154 XtSetArg(args[i], XtNheight, &pw_height); i++;
5155 XtGetValues(errorShell, args, i);
5158 /* This code seems to tickle an X bug if it is executed too soon
5159 after xboard starts up. The coordinates get transformed as if
5160 the main window was positioned at (0, 0).
5162 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5163 0 - pw_height + squareSize / 3, &x, &y);
5165 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5166 RootWindowOfScreen(XtScreen(boardWidget)),
5167 (bw_width - pw_width) / 2,
5168 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5172 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5175 XtSetArg(args[i], XtNx, x); i++;
5176 XtSetArg(args[i], XtNy, y); i++;
5177 XtSetValues(errorShell, args, i);
5180 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5183 /* Disable all user input other than deleting the window */
5184 static int frozen = 0;
5188 /* Grab by a widget that doesn't accept input */
5189 XtAddGrab(messageWidget, TRUE, FALSE);
5193 /* Undo a FreezeUI */
5196 if (!frozen) return;
5197 XtRemoveGrab(messageWidget);
5201 char *ModeToWidgetName(mode)
5205 case BeginningOfGame:
5206 if (appData.icsActive)
5207 return "menuMode.ICS Client";
5208 else if (appData.noChessProgram ||
5209 *appData.cmailGameName != NULLCHAR)
5210 return "menuMode.Edit Game";
5212 return "menuMode.Machine Black";
5213 case MachinePlaysBlack:
5214 return "menuMode.Machine Black";
5215 case MachinePlaysWhite:
5216 return "menuMode.Machine White";
5218 return "menuMode.Analysis Mode";
5220 return "menuMode.Analyze File";
5221 case TwoMachinesPlay:
5222 return "menuMode.Two Machines";
5224 return "menuMode.Edit Game";
5225 case PlayFromGameFile:
5226 return "menuFile.Load Game";
5228 return "menuMode.Edit Position";
5230 return "menuMode.Training";
5231 case IcsPlayingWhite:
5232 case IcsPlayingBlack:
5236 return "menuMode.ICS Client";
5243 void ModeHighlight()
5246 static int oldPausing = FALSE;
5247 static GameMode oldmode = (GameMode) -1;
5250 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5252 if (pausing != oldPausing) {
5253 oldPausing = pausing;
5255 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5257 XtSetArg(args[0], XtNleftBitmap, None);
5259 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5262 if (appData.showButtonBar) {
5263 /* Always toggle, don't set. Previous code messes up when
5264 invoked while the button is pressed, as releasing it
5265 toggles the state again. */
5268 XtSetArg(args[0], XtNbackground, &oldbg);
5269 XtSetArg(args[1], XtNforeground, &oldfg);
5270 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5272 XtSetArg(args[0], XtNbackground, oldfg);
5273 XtSetArg(args[1], XtNforeground, oldbg);
5275 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5279 wname = ModeToWidgetName(oldmode);
5280 if (wname != NULL) {
5281 XtSetArg(args[0], XtNleftBitmap, None);
5282 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5284 wname = ModeToWidgetName(gameMode);
5285 if (wname != NULL) {
5286 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5287 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5291 /* Maybe all the enables should be handled here, not just this one */
5292 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5293 gameMode == Training || gameMode == PlayFromGameFile);
5298 * Button/menu procedures
5300 void ResetProc(w, event, prms, nprms)
5309 int LoadGamePopUp(f, gameNumber, title)
5314 cmailMsgLoaded = FALSE;
5315 if (gameNumber == 0) {
5316 int error = GameListBuild(f);
5318 DisplayError(_("Cannot build game list"), error);
5319 } else if (!ListEmpty(&gameList) &&
5320 ((ListGame *) gameList.tailPred)->number > 1) {
5321 GameListPopUp(f, title);
5327 return LoadGame(f, gameNumber, title, FALSE);
5330 void LoadGameProc(w, event, prms, nprms)
5336 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5339 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5342 void LoadNextGameProc(w, event, prms, nprms)
5351 void LoadPrevGameProc(w, event, prms, nprms)
5360 void ReloadGameProc(w, event, prms, nprms)
5369 void LoadNextPositionProc(w, event, prms, nprms)
5378 void LoadPrevPositionProc(w, event, prms, nprms)
5387 void ReloadPositionProc(w, event, prms, nprms)
5396 void LoadPositionProc(w, event, prms, nprms)
5402 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5405 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5408 void SaveGameProc(w, event, prms, nprms)
5414 FileNamePopUp(_("Save game file name?"),
5415 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5416 appData.oldSaveStyle ? ".game" : ".pgn",
5420 void SavePositionProc(w, event, prms, nprms)
5426 FileNamePopUp(_("Save position file name?"),
5427 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5428 appData.oldSaveStyle ? ".pos" : ".fen",
5432 void ReloadCmailMsgProc(w, event, prms, nprms)
5438 ReloadCmailMsgEvent(FALSE);
5441 void MailMoveProc(w, event, prms, nprms)
5450 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5451 char *selected_fen_position=NULL;
5454 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5455 Atom *type_return, XtPointer *value_return,
5456 unsigned long *length_return, int *format_return)
5458 char *selection_tmp;
5460 if (!selected_fen_position) return False; /* should never happen */
5461 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5462 /* note: since no XtSelectionDoneProc was registered, Xt will
5463 * automatically call XtFree on the value returned. So have to
5464 * make a copy of it allocated with XtMalloc */
5465 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5466 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5468 *value_return=selection_tmp;
5469 *length_return=strlen(selection_tmp);
5470 *type_return=*target;
5471 *format_return = 8; /* bits per byte */
5473 } else if (*target == XA_TARGETS(xDisplay)) {
5474 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5475 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5476 targets_tmp[1] = XA_STRING;
5477 *value_return = targets_tmp;
5478 *type_return = XA_ATOM;
5480 *format_return = 8 * sizeof(Atom);
5481 if (*format_return > 32) {
5482 *length_return *= *format_return / 32;
5483 *format_return = 32;
5491 /* note: when called from menu all parameters are NULL, so no clue what the
5492 * Widget which was clicked on was, or what the click event was
5494 void CopyPositionProc(w, event, prms, nprms)
5501 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5502 * have a notion of a position that is selected but not copied.
5503 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5505 if(gameMode == EditPosition) EditPositionDone(TRUE);
5506 if (selected_fen_position) free(selected_fen_position);
5507 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5508 if (!selected_fen_position) return;
5509 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5511 SendPositionSelection,
5512 NULL/* lose_ownership_proc */ ,
5513 NULL/* transfer_done_proc */);
5514 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5516 SendPositionSelection,
5517 NULL/* lose_ownership_proc */ ,
5518 NULL/* transfer_done_proc */);
5521 /* function called when the data to Paste is ready */
5523 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5524 Atom *type, XtPointer value, unsigned long *len, int *format)
5527 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5528 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5529 EditPositionPasteFEN(fenstr);
5533 /* called when Paste Position button is pressed,
5534 * all parameters will be NULL */
5535 void PastePositionProc(w, event, prms, nprms)
5541 XtGetSelectionValue(menuBarWidget,
5542 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5543 /* (XtSelectionCallbackProc) */ PastePositionCB,
5544 NULL, /* client_data passed to PastePositionCB */
5546 /* better to use the time field from the event that triggered the
5547 * call to this function, but that isn't trivial to get
5555 SendGameSelection(Widget w, Atom *selection, Atom *target,
5556 Atom *type_return, XtPointer *value_return,
5557 unsigned long *length_return, int *format_return)
5559 char *selection_tmp;
5561 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5562 FILE* f = fopen(gameCopyFilename, "r");
5565 if (f == NULL) return False;
5569 selection_tmp = XtMalloc(len + 1);
5570 count = fread(selection_tmp, 1, len, f);
5573 XtFree(selection_tmp);
5576 selection_tmp[len] = NULLCHAR;
5577 *value_return = selection_tmp;
5578 *length_return = len;
5579 *type_return = *target;
5580 *format_return = 8; /* bits per byte */
5582 } else if (*target == XA_TARGETS(xDisplay)) {
5583 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5584 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5585 targets_tmp[1] = XA_STRING;
5586 *value_return = targets_tmp;
5587 *type_return = XA_ATOM;
5589 *format_return = 8 * sizeof(Atom);
5590 if (*format_return > 32) {
5591 *length_return *= *format_return / 32;
5592 *format_return = 32;
5600 void CopySomething()
5605 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5606 * have a notion of a game that is selected but not copied.
5607 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5609 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5612 NULL/* lose_ownership_proc */ ,
5613 NULL/* transfer_done_proc */);
5614 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5617 NULL/* lose_ownership_proc */ ,
5618 NULL/* transfer_done_proc */);
5621 /* note: when called from menu all parameters are NULL, so no clue what the
5622 * Widget which was clicked on was, or what the click event was
5624 void CopyGameProc(w, event, prms, nprms)
5632 ret = SaveGameToFile(gameCopyFilename, FALSE);
5638 void CopyGameListProc(w, event, prms, nprms)
5644 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5648 /* function called when the data to Paste is ready */
5650 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5651 Atom *type, XtPointer value, unsigned long *len, int *format)
5654 if (value == NULL || *len == 0) {
5655 return; /* nothing had been selected to copy */
5657 f = fopen(gamePasteFilename, "w");
5659 DisplayError(_("Can't open temp file"), errno);
5662 fwrite(value, 1, *len, f);
5665 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5668 /* called when Paste Game button is pressed,
5669 * all parameters will be NULL */
5670 void PasteGameProc(w, event, prms, nprms)
5676 XtGetSelectionValue(menuBarWidget,
5677 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5678 /* (XtSelectionCallbackProc) */ PasteGameCB,
5679 NULL, /* client_data passed to PasteGameCB */
5681 /* better to use the time field from the event that triggered the
5682 * call to this function, but that isn't trivial to get
5692 SaveGameProc(NULL, NULL, NULL, NULL);
5696 void QuitProc(w, event, prms, nprms)
5705 void PauseProc(w, event, prms, nprms)
5715 void MachineBlackProc(w, event, prms, nprms)
5721 MachineBlackEvent();
5724 void MachineWhiteProc(w, event, prms, nprms)
5730 MachineWhiteEvent();
5733 void AnalyzeModeProc(w, event, prms, nprms)
5741 if (!first.analysisSupport) {
5742 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5743 DisplayError(buf, 0);
5746 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5747 if (appData.icsActive) {
5748 if (gameMode != IcsObserving) {
5749 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5750 DisplayError(buf, 0);
5752 if (appData.icsEngineAnalyze) {
5753 if (appData.debugMode)
5754 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5760 /* if enable, use want disable icsEngineAnalyze */
5761 if (appData.icsEngineAnalyze) {
5766 appData.icsEngineAnalyze = TRUE;
5767 if (appData.debugMode)
5768 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5770 #ifndef OPTIONSDIALOG
5771 if (!appData.showThinking)
5772 ShowThinkingProc(w,event,prms,nprms);
5778 void AnalyzeFileProc(w, event, prms, nprms)
5784 if (!first.analysisSupport) {
5786 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5787 DisplayError(buf, 0);
5791 #ifndef OPTIONSDIALOG
5792 if (!appData.showThinking)
5793 ShowThinkingProc(w,event,prms,nprms);
5796 FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5797 AnalysisPeriodicEvent(1);
5800 void TwoMachinesProc(w, event, prms, nprms)
5809 void MatchProc(w, event, prms, nprms)
5815 if(gameMode != BeginningOfGame) { DisplayError(_("You can only start a match from the initial position."), 0); return; }
5816 appData.matchGames = appData.defaultMatchGames;
5820 void IcsClientProc(w, event, prms, nprms)
5829 void EditGameProc(w, event, prms, nprms)
5838 void EditPositionProc(w, event, prms, nprms)
5844 EditPositionEvent();
5847 void TrainingProc(w, event, prms, nprms)
5856 void EditCommentProc(w, event, prms, nprms)
5864 if (PopDown(1)) { // popdown succesful
5866 XtSetArg(args[j], XtNleftBitmap, None); j++;
5867 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
5868 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
5869 } else // was not up
5873 void IcsInputBoxProc(w, event, prms, nprms)
5879 if (!PopDown(4)) ICSInputBoxPopUp();
5882 void AcceptProc(w, event, prms, nprms)
5891 void DeclineProc(w, event, prms, nprms)
5900 void RematchProc(w, event, prms, nprms)
5909 void CallFlagProc(w, event, prms, nprms)
5918 void DrawProc(w, event, prms, nprms)
5927 void AbortProc(w, event, prms, nprms)
5936 void AdjournProc(w, event, prms, nprms)
5945 void ResignProc(w, event, prms, nprms)
5954 void AdjuWhiteProc(w, event, prms, nprms)
5960 UserAdjudicationEvent(+1);
5963 void AdjuBlackProc(w, event, prms, nprms)
5969 UserAdjudicationEvent(-1);
5972 void AdjuDrawProc(w, event, prms, nprms)
5978 UserAdjudicationEvent(0);
5981 void EnterKeyProc(w, event, prms, nprms)
5987 if (shellUp[4] == True)
5991 void UpKeyProc(w, event, prms, nprms)
5996 { // [HGM] input: let up-arrow recall previous line from history
6003 if (!shellUp[4]) return;
6004 edit = boxOptions[0].handle;
6006 XtSetArg(args[j], XtNstring, &val); j++;
6007 XtGetValues(edit, args, j);
6008 val = PrevInHistory(val);
6009 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6010 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6012 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6013 XawTextReplace(edit, 0, 0, &t);
6014 XawTextSetInsertionPoint(edit, 9999);
6018 void DownKeyProc(w, event, prms, nprms)
6023 { // [HGM] input: let down-arrow recall next line from history
6028 if (!shellUp[4]) return;
6029 edit = boxOptions[0].handle;
6030 val = NextInHistory();
6031 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6032 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6034 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6035 XawTextReplace(edit, 0, 0, &t);
6036 XawTextSetInsertionPoint(edit, 9999);
6040 void StopObservingProc(w, event, prms, nprms)
6046 StopObservingEvent();
6049 void StopExaminingProc(w, event, prms, nprms)
6055 StopExaminingEvent();
6058 void UploadProc(w, event, prms, nprms)
6068 void ForwardProc(w, event, prms, nprms)
6078 void BackwardProc(w, event, prms, nprms)
6087 void ToStartProc(w, event, prms, nprms)
6096 void ToEndProc(w, event, prms, nprms)
6105 void RevertProc(w, event, prms, nprms)
6114 void AnnotateProc(w, event, prms, nprms)
6123 void TruncateGameProc(w, event, prms, nprms)
6129 TruncateGameEvent();
6131 void RetractMoveProc(w, event, prms, nprms)
6140 void MoveNowProc(w, event, prms, nprms)
6149 void FlipViewProc(w, event, prms, nprms)
6155 flipView = !flipView;
6156 DrawPosition(True, NULL);
6159 void PonderNextMoveProc(w, event, prms, nprms)
6167 PonderNextMoveEvent(!appData.ponderNextMove);
6168 #ifndef OPTIONSDIALOG
6169 if (appData.ponderNextMove) {
6170 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6172 XtSetArg(args[0], XtNleftBitmap, None);
6174 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6179 #ifndef OPTIONSDIALOG
6180 void AlwaysQueenProc(w, event, prms, nprms)
6188 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6190 if (appData.alwaysPromoteToQueen) {
6191 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6193 XtSetArg(args[0], XtNleftBitmap, None);
6195 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6199 void AnimateDraggingProc(w, event, prms, nprms)
6207 appData.animateDragging = !appData.animateDragging;
6209 if (appData.animateDragging) {
6210 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6213 XtSetArg(args[0], XtNleftBitmap, None);
6215 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6219 void AnimateMovingProc(w, event, prms, nprms)
6227 appData.animate = !appData.animate;
6229 if (appData.animate) {
6230 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6233 XtSetArg(args[0], XtNleftBitmap, None);
6235 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6239 void AutoflagProc(w, event, prms, nprms)
6247 appData.autoCallFlag = !appData.autoCallFlag;
6249 if (appData.autoCallFlag) {
6250 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6252 XtSetArg(args[0], XtNleftBitmap, None);
6254 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6258 void AutoflipProc(w, event, prms, nprms)
6266 appData.autoFlipView = !appData.autoFlipView;
6268 if (appData.autoFlipView) {
6269 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6271 XtSetArg(args[0], XtNleftBitmap, None);
6273 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6277 void BlindfoldProc(w, event, prms, nprms)
6285 appData.blindfold = !appData.blindfold;
6287 if (appData.blindfold) {
6288 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6290 XtSetArg(args[0], XtNleftBitmap, None);
6292 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6295 DrawPosition(True, NULL);
6298 void TestLegalityProc(w, event, prms, nprms)
6306 appData.testLegality = !appData.testLegality;
6308 if (appData.testLegality) {
6309 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6311 XtSetArg(args[0], XtNleftBitmap, None);
6313 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6318 void FlashMovesProc(w, event, prms, nprms)
6326 if (appData.flashCount == 0) {
6327 appData.flashCount = 3;
6329 appData.flashCount = -appData.flashCount;
6332 if (appData.flashCount > 0) {
6333 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6335 XtSetArg(args[0], XtNleftBitmap, None);
6337 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6342 void HighlightDraggingProc(w, event, prms, nprms)
6350 appData.highlightDragging = !appData.highlightDragging;
6352 if (appData.highlightDragging) {
6353 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6355 XtSetArg(args[0], XtNleftBitmap, None);
6357 XtSetValues(XtNameToWidget(menuBarWidget,
6358 "menuOptions.Highlight Dragging"), args, 1);
6362 void HighlightLastMoveProc(w, event, prms, nprms)
6370 appData.highlightLastMove = !appData.highlightLastMove;
6372 if (appData.highlightLastMove) {
6373 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6375 XtSetArg(args[0], XtNleftBitmap, None);
6377 XtSetValues(XtNameToWidget(menuBarWidget,
6378 "menuOptions.Highlight Last Move"), args, 1);
6381 void HighlightArrowProc(w, event, prms, nprms)
6389 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6391 if (appData.highlightMoveWithArrow) {
6392 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6394 XtSetArg(args[0], XtNleftBitmap, None);
6396 XtSetValues(XtNameToWidget(menuBarWidget,
6397 "menuOptions.Arrow"), args, 1);
6401 void IcsAlarmProc(w, event, prms, nprms)
6409 appData.icsAlarm = !appData.icsAlarm;
6411 if (appData.icsAlarm) {
6412 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6414 XtSetArg(args[0], XtNleftBitmap, None);
6416 XtSetValues(XtNameToWidget(menuBarWidget,
6417 "menuOptions.ICS Alarm"), args, 1);
6421 void MoveSoundProc(w, event, prms, nprms)
6429 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6431 if (appData.ringBellAfterMoves) {
6432 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6434 XtSetArg(args[0], XtNleftBitmap, None);
6436 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6440 void OneClickProc(w, event, prms, nprms)
6448 appData.oneClick = !appData.oneClick;
6450 if (appData.oneClick) {
6451 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6453 XtSetArg(args[0], XtNleftBitmap, None);
6455 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6459 void PeriodicUpdatesProc(w, event, prms, nprms)
6467 PeriodicUpdatesEvent(!appData.periodicUpdates);
6469 if (appData.periodicUpdates) {
6470 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6472 XtSetArg(args[0], XtNleftBitmap, None);
6474 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6478 void PopupExitMessageProc(w, event, prms, nprms)
6486 appData.popupExitMessage = !appData.popupExitMessage;
6488 if (appData.popupExitMessage) {
6489 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6491 XtSetArg(args[0], XtNleftBitmap, None);
6493 XtSetValues(XtNameToWidget(menuBarWidget,
6494 "menuOptions.Popup Exit Message"), args, 1);
6497 void PopupMoveErrorsProc(w, event, prms, nprms)
6505 appData.popupMoveErrors = !appData.popupMoveErrors;
6507 if (appData.popupMoveErrors) {
6508 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6510 XtSetArg(args[0], XtNleftBitmap, None);
6512 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6517 void PremoveProc(w, event, prms, nprms)
6525 appData.premove = !appData.premove;
6527 if (appData.premove) {
6528 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6530 XtSetArg(args[0], XtNleftBitmap, None);
6532 XtSetValues(XtNameToWidget(menuBarWidget,
6533 "menuOptions.Premove"), args, 1);
6537 void ShowCoordsProc(w, event, prms, nprms)
6545 appData.showCoords = !appData.showCoords;
6547 if (appData.showCoords) {
6548 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6550 XtSetArg(args[0], XtNleftBitmap, None);
6552 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6555 DrawPosition(True, NULL);
6558 void ShowThinkingProc(w, event, prms, nprms)
6564 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6565 ShowThinkingEvent();
6568 void HideThinkingProc(w, event, prms, nprms)
6576 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6577 ShowThinkingEvent();
6579 if (appData.hideThinkingFromHuman) {
6580 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6582 XtSetArg(args[0], XtNleftBitmap, None);
6584 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6589 void SaveOnExitProc(w, event, prms, nprms)
6597 saveSettingsOnExit = !saveSettingsOnExit;
6599 if (saveSettingsOnExit) {
6600 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6602 XtSetArg(args[0], XtNleftBitmap, None);
6604 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6608 void SaveSettingsProc(w, event, prms, nprms)
6614 SaveSettings(settingsFileName);
6617 void InfoProc(w, event, prms, nprms)
6624 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6629 void ManProc(w, event, prms, nprms)
6637 if (nprms && *nprms > 0)
6641 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6645 void HintProc(w, event, prms, nprms)
6654 void BookProc(w, event, prms, nprms)
6663 void AboutProc(w, event, prms, nprms)
6671 char *zippy = " (with Zippy code)";
6675 snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
6676 programVersion, zippy,
6677 "Copyright 1991 Digital Equipment Corporation",
6678 "Enhancements Copyright 1992-2009 Free Software Foundation",
6679 "Enhancements Copyright 2005 Alessandro Scotti",
6680 PACKAGE, " is free software and carries NO WARRANTY;",
6681 "see the file COPYING for more information.");
6682 ErrorPopUp(_("About XBoard"), buf, FALSE);
6685 void DebugProc(w, event, prms, nprms)
6691 appData.debugMode = !appData.debugMode;
6694 void AboutGameProc(w, event, prms, nprms)
6703 void NothingProc(w, event, prms, nprms)
6712 void Iconify(w, event, prms, nprms)
6721 XtSetArg(args[0], XtNiconic, True);
6722 XtSetValues(shellWidget, args, 1);
6725 void DisplayMessage(message, extMessage)
6726 char *message, *extMessage;
6728 /* display a message in the message widget */
6737 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6742 message = extMessage;
6746 /* need to test if messageWidget already exists, since this function
6747 can also be called during the startup, if for example a Xresource
6748 is not set up correctly */
6751 XtSetArg(arg, XtNlabel, message);
6752 XtSetValues(messageWidget, &arg, 1);
6758 void DisplayTitle(text)
6763 char title[MSG_SIZ];
6766 if (text == NULL) text = "";
6768 if (appData.titleInWindow) {
6770 XtSetArg(args[i], XtNlabel, text); i++;
6771 XtSetValues(titleWidget, args, i);
6774 if (*text != NULLCHAR) {
6775 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6776 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6777 } else if (appData.icsActive) {
6778 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6779 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6780 } else if (appData.cmailGameName[0] != NULLCHAR) {
6781 snprintf(icon, sizeof(icon), "%s", "CMail");
6782 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6784 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6785 } else if (gameInfo.variant == VariantGothic) {
6786 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6787 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6790 } else if (gameInfo.variant == VariantFalcon) {
6791 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6792 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6794 } else if (appData.noChessProgram) {
6795 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6796 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6798 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6799 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6802 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6803 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6804 XtSetValues(shellWidget, args, i);
6809 DisplayError(message, error)
6816 if (appData.debugMode || appData.matchMode) {
6817 fprintf(stderr, "%s: %s\n", programName, message);
6820 if (appData.debugMode || appData.matchMode) {
6821 fprintf(stderr, "%s: %s: %s\n",
6822 programName, message, strerror(error));
6824 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6827 ErrorPopUp(_("Error"), message, FALSE);
6831 void DisplayMoveError(message)
6836 DrawPosition(FALSE, NULL);
6837 if (appData.debugMode || appData.matchMode) {
6838 fprintf(stderr, "%s: %s\n", programName, message);
6840 if (appData.popupMoveErrors) {
6841 ErrorPopUp(_("Error"), message, FALSE);
6843 DisplayMessage(message, "");
6848 void DisplayFatalError(message, error, status)
6854 errorExitStatus = status;
6856 fprintf(stderr, "%s: %s\n", programName, message);
6858 fprintf(stderr, "%s: %s: %s\n",
6859 programName, message, strerror(error));
6860 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6863 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6864 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6870 void DisplayInformation(message)
6874 ErrorPopUp(_("Information"), message, TRUE);
6877 void DisplayNote(message)
6881 ErrorPopUp(_("Note"), message, FALSE);
6885 NullXErrorCheck(dpy, error_event)
6887 XErrorEvent *error_event;
6892 void DisplayIcsInteractionTitle(message)
6895 if (oldICSInteractionTitle == NULL) {
6896 /* Magic to find the old window title, adapted from vim */
6897 char *wina = getenv("WINDOWID");
6899 Window win = (Window) atoi(wina);
6900 Window root, parent, *children;
6901 unsigned int nchildren;
6902 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6904 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6905 if (!XQueryTree(xDisplay, win, &root, &parent,
6906 &children, &nchildren)) break;
6907 if (children) XFree((void *)children);
6908 if (parent == root || parent == 0) break;
6911 XSetErrorHandler(oldHandler);
6913 if (oldICSInteractionTitle == NULL) {
6914 oldICSInteractionTitle = "xterm";
6917 printf("\033]0;%s\007", message);
6921 char pendingReplyPrefix[MSG_SIZ];
6922 ProcRef pendingReplyPR;
6924 void AskQuestionProc(w, event, prms, nprms)
6931 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
6935 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6938 void AskQuestionPopDown()
6940 if (!askQuestionUp) return;
6941 XtPopdown(askQuestionShell);
6942 XtDestroyWidget(askQuestionShell);
6943 askQuestionUp = False;
6946 void AskQuestionReplyAction(w, event, prms, nprms)
6956 reply = XawDialogGetValueString(w = XtParent(w));
6957 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
6958 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
6959 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
6960 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
6961 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6962 AskQuestionPopDown();
6964 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
6967 void AskQuestionCallback(w, client_data, call_data)
6969 XtPointer client_data, call_data;
6974 XtSetArg(args[0], XtNlabel, &name);
6975 XtGetValues(w, args, 1);
6977 if (strcmp(name, _("cancel")) == 0) {
6978 AskQuestionPopDown();
6980 AskQuestionReplyAction(w, NULL, NULL, NULL);
6984 void AskQuestion(title, question, replyPrefix, pr)
6985 char *title, *question, *replyPrefix;
6989 Widget popup, layout, dialog, edit;
6995 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
6996 pendingReplyPR = pr;
6999 XtSetArg(args[i], XtNresizable, True); i++;
7000 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7001 askQuestionShell = popup =
7002 XtCreatePopupShell(title, transientShellWidgetClass,
7003 shellWidget, args, i);
7006 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7007 layoutArgs, XtNumber(layoutArgs));
7010 XtSetArg(args[i], XtNlabel, question); i++;
7011 XtSetArg(args[i], XtNvalue, ""); i++;
7012 XtSetArg(args[i], XtNborderWidth, 0); i++;
7013 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7016 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7017 (XtPointer) dialog);
7018 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7019 (XtPointer) dialog);
7021 XtRealizeWidget(popup);
7022 CatchDeleteWindow(popup, "AskQuestionPopDown");
7024 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7025 &x, &y, &win_x, &win_y, &mask);
7027 XtSetArg(args[0], XtNx, x - 10);
7028 XtSetArg(args[1], XtNy, y - 30);
7029 XtSetValues(popup, args, 2);
7031 XtPopup(popup, XtGrabExclusive);
7032 askQuestionUp = True;
7034 edit = XtNameToWidget(dialog, "*value");
7035 XtSetKeyboardFocus(popup, edit);
7043 if (*name == NULLCHAR) {
7045 } else if (strcmp(name, "$") == 0) {
7046 putc(BELLCHAR, stderr);
7049 char *prefix = "", *sep = "";
7050 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7051 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7059 PlaySound(appData.soundMove);
7065 PlaySound(appData.soundIcsWin);
7071 PlaySound(appData.soundIcsLoss);
7077 PlaySound(appData.soundIcsDraw);
7081 PlayIcsUnfinishedSound()
7083 PlaySound(appData.soundIcsUnfinished);
7089 PlaySound(appData.soundIcsAlarm);
7095 system("stty echo");
7101 system("stty -echo");
7105 Colorize(cc, continuation)
7110 int count, outCount, error;
7112 if (textColors[(int)cc].bg > 0) {
7113 if (textColors[(int)cc].fg > 0) {
7114 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7115 textColors[(int)cc].fg, textColors[(int)cc].bg);
7117 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7118 textColors[(int)cc].bg);
7121 if (textColors[(int)cc].fg > 0) {
7122 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7123 textColors[(int)cc].fg);
7125 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7128 count = strlen(buf);
7129 outCount = OutputToProcess(NoProc, buf, count, &error);
7130 if (outCount < count) {
7131 DisplayFatalError(_("Error writing to display"), error, 1);
7134 if (continuation) return;
7137 PlaySound(appData.soundShout);
7140 PlaySound(appData.soundSShout);
7143 PlaySound(appData.soundChannel1);
7146 PlaySound(appData.soundChannel);
7149 PlaySound(appData.soundKibitz);
7152 PlaySound(appData.soundTell);
7154 case ColorChallenge:
7155 PlaySound(appData.soundChallenge);
7158 PlaySound(appData.soundRequest);
7161 PlaySound(appData.soundSeek);
7172 return getpwuid(getuid())->pw_name;
7176 ExpandPathName(path)
7179 static char static_buf[4*MSG_SIZ];
7180 char *d, *s, buf[4*MSG_SIZ];
7186 while (*s && isspace(*s))
7195 if (*(s+1) == '/') {
7196 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7200 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7201 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7202 pwd = getpwnam(buf);
7205 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7209 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7210 strcat(d, strchr(s+1, '/'));
7214 safeStrCpy(d, s, 4*MSG_SIZ );
7221 static char host_name[MSG_SIZ];
7223 #if HAVE_GETHOSTNAME
7224 gethostname(host_name, MSG_SIZ);
7226 #else /* not HAVE_GETHOSTNAME */
7227 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7228 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7230 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7232 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7233 #endif /* not HAVE_GETHOSTNAME */
7236 XtIntervalId delayedEventTimerXID = 0;
7237 DelayedEventCallback delayedEventCallback = 0;
7242 delayedEventTimerXID = 0;
7243 delayedEventCallback();
7247 ScheduleDelayedEvent(cb, millisec)
7248 DelayedEventCallback cb; long millisec;
7250 if(delayedEventTimerXID && delayedEventCallback == cb)
7251 // [HGM] alive: replace, rather than add or flush identical event
7252 XtRemoveTimeOut(delayedEventTimerXID);
7253 delayedEventCallback = cb;
7254 delayedEventTimerXID =
7255 XtAppAddTimeOut(appContext, millisec,
7256 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7259 DelayedEventCallback
7262 if (delayedEventTimerXID) {
7263 return delayedEventCallback;
7270 CancelDelayedEvent()
7272 if (delayedEventTimerXID) {
7273 XtRemoveTimeOut(delayedEventTimerXID);
7274 delayedEventTimerXID = 0;
7278 XtIntervalId loadGameTimerXID = 0;
7280 int LoadGameTimerRunning()
7282 return loadGameTimerXID != 0;
7285 int StopLoadGameTimer()
7287 if (loadGameTimerXID != 0) {
7288 XtRemoveTimeOut(loadGameTimerXID);
7289 loadGameTimerXID = 0;
7297 LoadGameTimerCallback(arg, id)
7301 loadGameTimerXID = 0;
7306 StartLoadGameTimer(millisec)
7310 XtAppAddTimeOut(appContext, millisec,
7311 (XtTimerCallbackProc) LoadGameTimerCallback,
7315 XtIntervalId analysisClockXID = 0;
7318 AnalysisClockCallback(arg, id)
7322 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7323 || appData.icsEngineAnalyze) { // [DM]
7324 AnalysisPeriodicEvent(0);
7325 StartAnalysisClock();
7330 StartAnalysisClock()
7333 XtAppAddTimeOut(appContext, 2000,
7334 (XtTimerCallbackProc) AnalysisClockCallback,
7338 XtIntervalId clockTimerXID = 0;
7340 int ClockTimerRunning()
7342 return clockTimerXID != 0;
7345 int StopClockTimer()
7347 if (clockTimerXID != 0) {
7348 XtRemoveTimeOut(clockTimerXID);
7357 ClockTimerCallback(arg, id)
7366 StartClockTimer(millisec)
7370 XtAppAddTimeOut(appContext, millisec,
7371 (XtTimerCallbackProc) ClockTimerCallback,
7376 DisplayTimerLabel(w, color, timer, highlight)
7385 /* check for low time warning */
7386 Pixel foregroundOrWarningColor = timerForegroundPixel;
7389 appData.lowTimeWarning &&
7390 (timer / 1000) < appData.icsAlarmTime)
7391 foregroundOrWarningColor = lowTimeWarningColor;
7393 if (appData.clockMode) {
7394 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7395 XtSetArg(args[0], XtNlabel, buf);
7397 snprintf(buf, MSG_SIZ, "%s ", color);
7398 XtSetArg(args[0], XtNlabel, buf);
7403 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7404 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7406 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7407 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7410 XtSetValues(w, args, 3);
7414 DisplayWhiteClock(timeRemaining, highlight)
7420 if(appData.noGUI) return;
7421 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7422 if (highlight && iconPixmap == bIconPixmap) {
7423 iconPixmap = wIconPixmap;
7424 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7425 XtSetValues(shellWidget, args, 1);
7430 DisplayBlackClock(timeRemaining, highlight)
7436 if(appData.noGUI) return;
7437 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7438 if (highlight && iconPixmap == wIconPixmap) {
7439 iconPixmap = bIconPixmap;
7440 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7441 XtSetValues(shellWidget, args, 1);
7459 int StartChildProcess(cmdLine, dir, pr)
7466 int to_prog[2], from_prog[2];
7470 if (appData.debugMode) {
7471 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7474 /* We do NOT feed the cmdLine to the shell; we just
7475 parse it into blank-separated arguments in the
7476 most simple-minded way possible.
7479 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7482 while(*p == ' ') p++;
7484 if(*p == '"' || *p == '\'')
7485 p = strchr(++argv[i-1], *p);
7486 else p = strchr(p, ' ');
7487 if (p == NULL) break;
7492 SetUpChildIO(to_prog, from_prog);
7494 if ((pid = fork()) == 0) {
7496 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7497 close(to_prog[1]); // first close the unused pipe ends
7498 close(from_prog[0]);
7499 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7500 dup2(from_prog[1], 1);
7501 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7502 close(from_prog[1]); // and closing again loses one of the pipes!
7503 if(fileno(stderr) >= 2) // better safe than sorry...
7504 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7506 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7511 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7513 execvp(argv[0], argv);
7515 /* If we get here, exec failed */
7520 /* Parent process */
7522 close(from_prog[1]);
7524 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7527 cp->fdFrom = from_prog[0];
7528 cp->fdTo = to_prog[1];
7533 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7534 static RETSIGTYPE AlarmCallBack(int n)
7540 DestroyChildProcess(pr, signalType)
7544 ChildProc *cp = (ChildProc *) pr;
7546 if (cp->kind != CPReal) return;
7548 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7549 signal(SIGALRM, AlarmCallBack);
7551 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7552 kill(cp->pid, SIGKILL); // kill it forcefully
7553 wait((int *) 0); // and wait again
7557 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7559 /* Process is exiting either because of the kill or because of
7560 a quit command sent by the backend; either way, wait for it to die.
7569 InterruptChildProcess(pr)
7572 ChildProc *cp = (ChildProc *) pr;
7574 if (cp->kind != CPReal) return;
7575 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7578 int OpenTelnet(host, port, pr)
7583 char cmdLine[MSG_SIZ];
7585 if (port[0] == NULLCHAR) {
7586 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7588 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7590 return StartChildProcess(cmdLine, "", pr);
7593 int OpenTCP(host, port, pr)
7599 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7600 #else /* !OMIT_SOCKETS */
7602 struct sockaddr_in sa;
7604 unsigned short uport;
7607 if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
7611 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7612 sa.sin_family = AF_INET;
7613 sa.sin_addr.s_addr = INADDR_ANY;
7614 uport = (unsigned short) 0;
7615 sa.sin_port = htons(uport);
7616 if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
7620 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7621 if (!(hp = gethostbyname(host))) {
7623 if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
7624 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
7625 hp->h_addrtype = AF_INET;
7627 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
7628 hp->h_addr_list[0] = (char *) malloc(4);
7629 hp->h_addr_list[0][0] = b0;
7630 hp->h_addr_list[0][1] = b1;
7631 hp->h_addr_list[0][2] = b2;
7632 hp->h_addr_list[0][3] = b3;
7637 sa.sin_family = hp->h_addrtype;
7638 uport = (unsigned short) atoi(port);
7639 sa.sin_port = htons(uport);
7640 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
7642 if (connect(s, (struct sockaddr *) &sa,
7643 sizeof(struct sockaddr_in)) < 0) {
7647 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7654 #endif /* !OMIT_SOCKETS */
7659 int OpenCommPort(name, pr)
7666 fd = open(name, 2, 0);
7667 if (fd < 0) return errno;
7669 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7679 int OpenLoopback(pr)
7685 SetUpChildIO(to, from);
7687 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7690 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7697 int OpenRcmd(host, user, cmd, pr)
7698 char *host, *user, *cmd;
7701 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7705 #define INPUT_SOURCE_BUF_SIZE 8192
7714 char buf[INPUT_SOURCE_BUF_SIZE];
7719 DoInputCallback(closure, source, xid)
7724 InputSource *is = (InputSource *) closure;
7729 if (is->lineByLine) {
7730 count = read(is->fd, is->unused,
7731 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7733 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7736 is->unused += count;
7738 while (p < is->unused) {
7739 q = memchr(p, '\n', is->unused - p);
7740 if (q == NULL) break;
7742 (is->func)(is, is->closure, p, q - p, 0);
7746 while (p < is->unused) {
7751 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7756 (is->func)(is, is->closure, is->buf, count, error);
7760 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7767 ChildProc *cp = (ChildProc *) pr;
7769 is = (InputSource *) calloc(1, sizeof(InputSource));
7770 is->lineByLine = lineByLine;
7774 is->fd = fileno(stdin);
7776 is->kind = cp->kind;
7777 is->fd = cp->fdFrom;
7780 is->unused = is->buf;
7783 is->xid = XtAppAddInput(appContext, is->fd,
7784 (XtPointer) (XtInputReadMask),
7785 (XtInputCallbackProc) DoInputCallback,
7787 is->closure = closure;
7788 return (InputSourceRef) is;
7792 RemoveInputSource(isr)
7795 InputSource *is = (InputSource *) isr;
7797 if (is->xid == 0) return;
7798 XtRemoveInput(is->xid);
7802 int OutputToProcess(pr, message, count, outError)
7808 static int line = 0;
7809 ChildProc *cp = (ChildProc *) pr;
7814 if (appData.noJoin || !appData.useInternalWrap)
7815 outCount = fwrite(message, 1, count, stdout);
7818 int width = get_term_width();
7819 int len = wrap(NULL, message, count, width, &line);
7820 char *msg = malloc(len);
7824 outCount = fwrite(message, 1, count, stdout);
7827 dbgchk = wrap(msg, message, count, width, &line);
7828 if (dbgchk != len && appData.debugMode)
7829 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7830 outCount = fwrite(msg, 1, dbgchk, stdout);
7836 outCount = write(cp->fdTo, message, count);
7846 /* Output message to process, with "ms" milliseconds of delay
7847 between each character. This is needed when sending the logon
7848 script to ICC, which for some reason doesn't like the
7849 instantaneous send. */
7850 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
7857 ChildProc *cp = (ChildProc *) pr;
7862 r = write(cp->fdTo, message++, 1);
7875 /**** Animation code by Hugh Fisher, DCS, ANU.
7877 Known problem: if a window overlapping the board is
7878 moved away while a piece is being animated underneath,
7879 the newly exposed area won't be updated properly.
7880 I can live with this.
7882 Known problem: if you look carefully at the animation
7883 of pieces in mono mode, they are being drawn as solid
7884 shapes without interior detail while moving. Fixing
7885 this would be a major complication for minimal return.
7888 /* Masks for XPM pieces. Black and white pieces can have
7889 different shapes, but in the interest of retaining my
7890 sanity pieces must have the same outline on both light
7891 and dark squares, and all pieces must use the same
7892 background square colors/images. */
7894 static int xpmDone = 0;
7897 CreateAnimMasks (pieceDepth)
7904 unsigned long plane;
7907 /* Need a bitmap just to get a GC with right depth */
7908 buf = XCreatePixmap(xDisplay, xBoardWindow,
7910 values.foreground = 1;
7911 values.background = 0;
7912 /* Don't use XtGetGC, not read only */
7913 maskGC = XCreateGC(xDisplay, buf,
7914 GCForeground | GCBackground, &values);
7915 XFreePixmap(xDisplay, buf);
7917 buf = XCreatePixmap(xDisplay, xBoardWindow,
7918 squareSize, squareSize, pieceDepth);
7919 values.foreground = XBlackPixel(xDisplay, xScreen);
7920 values.background = XWhitePixel(xDisplay, xScreen);
7921 bufGC = XCreateGC(xDisplay, buf,
7922 GCForeground | GCBackground, &values);
7924 for (piece = WhitePawn; piece <= BlackKing; piece++) {
7925 /* Begin with empty mask */
7926 if(!xpmDone) // [HGM] pieces: keep using existing
7927 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7928 squareSize, squareSize, 1);
7929 XSetFunction(xDisplay, maskGC, GXclear);
7930 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7931 0, 0, squareSize, squareSize);
7933 /* Take a copy of the piece */
7938 XSetFunction(xDisplay, bufGC, GXcopy);
7939 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
7941 0, 0, squareSize, squareSize, 0, 0);
7943 /* XOR the background (light) over the piece */
7944 XSetFunction(xDisplay, bufGC, GXxor);
7946 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7947 0, 0, squareSize, squareSize, 0, 0);
7949 XSetForeground(xDisplay, bufGC, lightSquareColor);
7950 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7953 /* We now have an inverted piece image with the background
7954 erased. Construct mask by just selecting all the non-zero
7955 pixels - no need to reconstruct the original image. */
7956 XSetFunction(xDisplay, maskGC, GXor);
7958 /* Might be quicker to download an XImage and create bitmap
7959 data from it rather than this N copies per piece, but it
7960 only takes a fraction of a second and there is a much
7961 longer delay for loading the pieces. */
7962 for (n = 0; n < pieceDepth; n ++) {
7963 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7964 0, 0, squareSize, squareSize,
7970 XFreePixmap(xDisplay, buf);
7971 XFreeGC(xDisplay, bufGC);
7972 XFreeGC(xDisplay, maskGC);
7976 InitAnimState (anim, info)
7978 XWindowAttributes * info;
7983 /* Each buffer is square size, same depth as window */
7984 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
7985 squareSize, squareSize, info->depth);
7986 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
7987 squareSize, squareSize, info->depth);
7989 /* Create a plain GC for blitting */
7990 mask = GCForeground | GCBackground | GCFunction |
7991 GCPlaneMask | GCGraphicsExposures;
7992 values.foreground = XBlackPixel(xDisplay, xScreen);
7993 values.background = XWhitePixel(xDisplay, xScreen);
7994 values.function = GXcopy;
7995 values.plane_mask = AllPlanes;
7996 values.graphics_exposures = False;
7997 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
7999 /* Piece will be copied from an existing context at
8000 the start of each new animation/drag. */
8001 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8003 /* Outline will be a read-only copy of an existing */
8004 anim->outlineGC = None;
8010 XWindowAttributes info;
8012 if (xpmDone && gameInfo.variant == oldVariant) return;
8013 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
8014 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8016 InitAnimState(&game, &info);
8017 InitAnimState(&player, &info);
8019 /* For XPM pieces, we need bitmaps to use as masks. */
8021 CreateAnimMasks(info.depth), xpmDone = 1;
8026 static Boolean frameWaiting;
8028 static RETSIGTYPE FrameAlarm (sig)
8031 frameWaiting = False;
8032 /* In case System-V style signals. Needed?? */
8033 signal(SIGALRM, FrameAlarm);
8040 struct itimerval delay;
8042 XSync(xDisplay, False);
8045 frameWaiting = True;
8046 signal(SIGALRM, FrameAlarm);
8047 delay.it_interval.tv_sec =
8048 delay.it_value.tv_sec = time / 1000;
8049 delay.it_interval.tv_usec =
8050 delay.it_value.tv_usec = (time % 1000) * 1000;
8051 setitimer(ITIMER_REAL, &delay, NULL);
8052 while (frameWaiting) pause();
8053 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8054 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8055 setitimer(ITIMER_REAL, &delay, NULL);
8065 XSync(xDisplay, False);
8067 usleep(time * 1000);
8072 /* Convert board position to corner of screen rect and color */
8075 ScreenSquare(column, row, pt, color)
8076 int column; int row; XPoint * pt; int * color;
8079 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8080 pt->y = lineGap + row * (squareSize + lineGap);
8082 pt->x = lineGap + column * (squareSize + lineGap);
8083 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8085 *color = SquareColor(row, column);
8088 /* Convert window coords to square */
8091 BoardSquare(x, y, column, row)
8092 int x; int y; int * column; int * row;
8094 *column = EventToSquare(x, BOARD_WIDTH);
8095 if (flipView && *column >= 0)
8096 *column = BOARD_WIDTH - 1 - *column;
8097 *row = EventToSquare(y, BOARD_HEIGHT);
8098 if (!flipView && *row >= 0)
8099 *row = BOARD_HEIGHT - 1 - *row;
8104 #undef Max /* just in case */
8106 #define Max(a, b) ((a) > (b) ? (a) : (b))
8107 #define Min(a, b) ((a) < (b) ? (a) : (b))
8110 SetRect(rect, x, y, width, height)
8111 XRectangle * rect; int x; int y; int width; int height;
8115 rect->width = width;
8116 rect->height = height;
8119 /* Test if two frames overlap. If they do, return
8120 intersection rect within old and location of
8121 that rect within new. */
8124 Intersect(old, new, size, area, pt)
8125 XPoint * old; XPoint * new;
8126 int size; XRectangle * area; XPoint * pt;
8128 if (old->x > new->x + size || new->x > old->x + size ||
8129 old->y > new->y + size || new->y > old->y + size) {
8132 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8133 size - abs(old->x - new->x), size - abs(old->y - new->y));
8134 pt->x = Max(old->x - new->x, 0);
8135 pt->y = Max(old->y - new->y, 0);
8140 /* For two overlapping frames, return the rect(s)
8141 in the old that do not intersect with the new. */
8144 CalcUpdateRects(old, new, size, update, nUpdates)
8145 XPoint * old; XPoint * new; int size;
8146 XRectangle update[]; int * nUpdates;
8150 /* If old = new (shouldn't happen) then nothing to draw */
8151 if (old->x == new->x && old->y == new->y) {
8155 /* Work out what bits overlap. Since we know the rects
8156 are the same size we don't need a full intersect calc. */
8158 /* Top or bottom edge? */
8159 if (new->y > old->y) {
8160 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8162 } else if (old->y > new->y) {
8163 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8164 size, old->y - new->y);
8167 /* Left or right edge - don't overlap any update calculated above. */
8168 if (new->x > old->x) {
8169 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8170 new->x - old->x, size - abs(new->y - old->y));
8172 } else if (old->x > new->x) {
8173 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8174 old->x - new->x, size - abs(new->y - old->y));
8181 /* Generate a series of frame coords from start->mid->finish.
8182 The movement rate doubles until the half way point is
8183 reached, then halves back down to the final destination,
8184 which gives a nice slow in/out effect. The algorithmn
8185 may seem to generate too many intermediates for short
8186 moves, but remember that the purpose is to attract the
8187 viewers attention to the piece about to be moved and
8188 then to where it ends up. Too few frames would be less
8192 Tween(start, mid, finish, factor, frames, nFrames)
8193 XPoint * start; XPoint * mid;
8194 XPoint * finish; int factor;
8195 XPoint frames[]; int * nFrames;
8197 int fraction, n, count;
8201 /* Slow in, stepping 1/16th, then 1/8th, ... */
8203 for (n = 0; n < factor; n++)
8205 for (n = 0; n < factor; n++) {
8206 frames[count].x = start->x + (mid->x - start->x) / fraction;
8207 frames[count].y = start->y + (mid->y - start->y) / fraction;
8209 fraction = fraction / 2;
8213 frames[count] = *mid;
8216 /* Slow out, stepping 1/2, then 1/4, ... */
8218 for (n = 0; n < factor; n++) {
8219 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8220 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8222 fraction = fraction * 2;
8227 /* Draw a piece on the screen without disturbing what's there */
8230 SelectGCMask(piece, clip, outline, mask)
8231 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8235 /* Bitmap for piece being moved. */
8236 if (appData.monoMode) {
8237 *mask = *pieceToSolid(piece);
8238 } else if (useImages) {
8240 *mask = xpmMask[piece];
8242 *mask = ximMaskPm[piece];
8245 *mask = *pieceToSolid(piece);
8248 /* GC for piece being moved. Square color doesn't matter, but
8249 since it gets modified we make a copy of the original. */
8251 if (appData.monoMode)
8256 if (appData.monoMode)
8261 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8263 /* Outline only used in mono mode and is not modified */
8265 *outline = bwPieceGC;
8267 *outline = wbPieceGC;
8271 OverlayPiece(piece, clip, outline, dest)
8272 ChessSquare piece; GC clip; GC outline; Drawable dest;
8277 /* Draw solid rectangle which will be clipped to shape of piece */
8278 XFillRectangle(xDisplay, dest, clip,
8279 0, 0, squareSize, squareSize);
8280 if (appData.monoMode)
8281 /* Also draw outline in contrasting color for black
8282 on black / white on white cases */
8283 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8284 0, 0, squareSize, squareSize, 0, 0, 1);
8286 /* Copy the piece */
8291 if(appData.upsideDown && flipView) kind ^= 2;
8292 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8294 0, 0, squareSize, squareSize,
8299 /* Animate the movement of a single piece */
8302 BeginAnimation(anim, piece, startColor, start)
8310 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8311 /* The old buffer is initialised with the start square (empty) */
8312 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8313 anim->prevFrame = *start;
8315 /* The piece will be drawn using its own bitmap as a matte */
8316 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8317 XSetClipMask(xDisplay, anim->pieceGC, mask);
8321 AnimationFrame(anim, frame, piece)
8326 XRectangle updates[4];
8331 /* Save what we are about to draw into the new buffer */
8332 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8333 frame->x, frame->y, squareSize, squareSize,
8336 /* Erase bits of the previous frame */
8337 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8338 /* Where the new frame overlapped the previous,
8339 the contents in newBuf are wrong. */
8340 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8341 overlap.x, overlap.y,
8342 overlap.width, overlap.height,
8344 /* Repaint the areas in the old that don't overlap new */
8345 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8346 for (i = 0; i < count; i++)
8347 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8348 updates[i].x - anim->prevFrame.x,
8349 updates[i].y - anim->prevFrame.y,
8350 updates[i].width, updates[i].height,
8351 updates[i].x, updates[i].y);
8353 /* Easy when no overlap */
8354 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8355 0, 0, squareSize, squareSize,
8356 anim->prevFrame.x, anim->prevFrame.y);
8359 /* Save this frame for next time round */
8360 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8361 0, 0, squareSize, squareSize,
8363 anim->prevFrame = *frame;
8365 /* Draw piece over original screen contents, not current,
8366 and copy entire rect. Wipes out overlapping piece images. */
8367 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8368 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8369 0, 0, squareSize, squareSize,
8370 frame->x, frame->y);
8374 EndAnimation (anim, finish)
8378 XRectangle updates[4];
8383 /* The main code will redraw the final square, so we
8384 only need to erase the bits that don't overlap. */
8385 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8386 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8387 for (i = 0; i < count; i++)
8388 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8389 updates[i].x - anim->prevFrame.x,
8390 updates[i].y - anim->prevFrame.y,
8391 updates[i].width, updates[i].height,
8392 updates[i].x, updates[i].y);
8394 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8395 0, 0, squareSize, squareSize,
8396 anim->prevFrame.x, anim->prevFrame.y);
8401 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8403 ChessSquare piece; int startColor;
8404 XPoint * start; XPoint * finish;
8405 XPoint frames[]; int nFrames;
8409 BeginAnimation(anim, piece, startColor, start);
8410 for (n = 0; n < nFrames; n++) {
8411 AnimationFrame(anim, &(frames[n]), piece);
8412 FrameDelay(appData.animSpeed);
8414 EndAnimation(anim, finish);
8418 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8421 ChessSquare piece = board[fromY][toY];
8422 board[fromY][toY] = EmptySquare;
8423 DrawPosition(FALSE, board);
8425 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8426 y = lineGap + toY * (squareSize + lineGap);
8428 x = lineGap + toX * (squareSize + lineGap);
8429 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8431 for(i=1; i<4*kFactor; i++) {
8432 int r = squareSize * 9 * i/(20*kFactor - 5);
8433 XFillArc(xDisplay, xBoardWindow, highlineGC,
8434 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8435 FrameDelay(appData.animSpeed);
8437 board[fromY][toY] = piece;
8440 /* Main control logic for deciding what to animate and how */
8443 AnimateMove(board, fromX, fromY, toX, toY)
8452 XPoint start, finish, mid;
8453 XPoint frames[kFactor * 2 + 1];
8454 int nFrames, startColor, endColor;
8456 /* Are we animating? */
8457 if (!appData.animate || appData.blindfold)
8460 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8461 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8462 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8464 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8465 piece = board[fromY][fromX];
8466 if (piece >= EmptySquare) return;
8471 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8474 if (appData.debugMode) {
8475 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8476 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8477 piece, fromX, fromY, toX, toY); }
8479 ScreenSquare(fromX, fromY, &start, &startColor);
8480 ScreenSquare(toX, toY, &finish, &endColor);
8483 /* Knight: make straight movement then diagonal */
8484 if (abs(toY - fromY) < abs(toX - fromX)) {
8485 mid.x = start.x + (finish.x - start.x) / 2;
8489 mid.y = start.y + (finish.y - start.y) / 2;
8492 mid.x = start.x + (finish.x - start.x) / 2;
8493 mid.y = start.y + (finish.y - start.y) / 2;
8496 /* Don't use as many frames for very short moves */
8497 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8498 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8500 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8501 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8502 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8504 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8505 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8508 /* Be sure end square is redrawn */
8509 damage[0][toY][toX] = True;
8513 DragPieceBegin(x, y)
8516 int boardX, boardY, color;
8519 /* Are we animating? */
8520 if (!appData.animateDragging || appData.blindfold)
8523 /* Figure out which square we start in and the
8524 mouse position relative to top left corner. */
8525 BoardSquare(x, y, &boardX, &boardY);
8526 player.startBoardX = boardX;
8527 player.startBoardY = boardY;
8528 ScreenSquare(boardX, boardY, &corner, &color);
8529 player.startSquare = corner;
8530 player.startColor = color;
8531 /* As soon as we start dragging, the piece will jump slightly to
8532 be centered over the mouse pointer. */
8533 player.mouseDelta.x = squareSize/2;
8534 player.mouseDelta.y = squareSize/2;
8535 /* Initialise animation */
8536 player.dragPiece = PieceForSquare(boardX, boardY);
8538 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8539 player.dragActive = True;
8540 BeginAnimation(&player, player.dragPiece, color, &corner);
8541 /* Mark this square as needing to be redrawn. Note that
8542 we don't remove the piece though, since logically (ie
8543 as seen by opponent) the move hasn't been made yet. */
8544 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8545 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8546 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8547 corner.x, corner.y, squareSize, squareSize,
8548 0, 0); // [HGM] zh: unstack in stead of grab
8549 if(gatingPiece != EmptySquare) {
8550 /* Kludge alert: When gating we want the introduced
8551 piece to appear on the from square. To generate an
8552 image of it, we draw it on the board, copy the image,
8553 and draw the original piece again. */
8554 ChessSquare piece = boards[currentMove][boardY][boardX];
8555 DrawSquare(boardY, boardX, gatingPiece, 0);
8556 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8557 corner.x, corner.y, squareSize, squareSize, 0, 0);
8558 DrawSquare(boardY, boardX, piece, 0);
8560 damage[0][boardY][boardX] = True;
8562 player.dragActive = False;
8567 ChangeDragPiece(ChessSquare piece)
8570 player.dragPiece = piece;
8571 /* The piece will be drawn using its own bitmap as a matte */
8572 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8573 XSetClipMask(xDisplay, player.pieceGC, mask);
8582 /* Are we animating? */
8583 if (!appData.animateDragging || appData.blindfold)
8587 if (! player.dragActive)
8589 /* Move piece, maintaining same relative position
8590 of mouse within square */
8591 corner.x = x - player.mouseDelta.x;
8592 corner.y = y - player.mouseDelta.y;
8593 AnimationFrame(&player, &corner, player.dragPiece);
8595 if (appData.highlightDragging) {
8597 BoardSquare(x, y, &boardX, &boardY);
8598 SetHighlights(fromX, fromY, boardX, boardY);
8607 int boardX, boardY, color;
8610 /* Are we animating? */
8611 if (!appData.animateDragging || appData.blindfold)
8615 if (! player.dragActive)
8617 /* Last frame in sequence is square piece is
8618 placed on, which may not match mouse exactly. */
8619 BoardSquare(x, y, &boardX, &boardY);
8620 ScreenSquare(boardX, boardY, &corner, &color);
8621 EndAnimation(&player, &corner);
8623 /* Be sure end square is redrawn */
8624 damage[0][boardY][boardX] = True;
8626 /* This prevents weird things happening with fast successive
8627 clicks which on my Sun at least can cause motion events
8628 without corresponding press/release. */
8629 player.dragActive = False;
8632 /* Handle expose event while piece being dragged */
8637 if (!player.dragActive || appData.blindfold)
8640 /* What we're doing: logically, the move hasn't been made yet,
8641 so the piece is still in it's original square. But visually
8642 it's being dragged around the board. So we erase the square
8643 that the piece is on and draw it at the last known drag point. */
8644 BlankSquare(player.startSquare.x, player.startSquare.y,
8645 player.startColor, EmptySquare, xBoardWindow, 1);
8646 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8647 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8650 #include <sys/ioctl.h>
8651 int get_term_width()
8653 int fd, default_width;
8656 default_width = 79; // this is FICS default anyway...
8658 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8660 if (!ioctl(fd, TIOCGSIZE, &win))
8661 default_width = win.ts_cols;
8662 #elif defined(TIOCGWINSZ)
8664 if (!ioctl(fd, TIOCGWINSZ, &win))
8665 default_width = win.ws_col;
8667 return default_width;
8673 static int old_width = 0;
8674 int new_width = get_term_width();
8676 if (old_width != new_width)
8677 ics_printf("set width %d\n", new_width);
8678 old_width = new_width;
8681 void NotifyFrontendLogin()
8686 /* [AS] Arrow highlighting support */
8688 static double A_WIDTH = 5; /* Width of arrow body */
8690 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8691 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8693 static double Sqr( double x )
8698 static int Round( double x )
8700 return (int) (x + 0.5);
8703 void SquareToPos(int rank, int file, int *x, int *y)
8706 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8707 *y = lineGap + rank * (squareSize + lineGap);
8709 *x = lineGap + file * (squareSize + lineGap);
8710 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8714 /* Draw an arrow between two points using current settings */
8715 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8718 double dx, dy, j, k, x, y;
8721 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8723 arrow[0].x = s_x + A_WIDTH + 0.5;
8726 arrow[1].x = s_x + A_WIDTH + 0.5;
8727 arrow[1].y = d_y - h;
8729 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8730 arrow[2].y = d_y - h;
8735 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8736 arrow[5].y = d_y - h;
8738 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8739 arrow[4].y = d_y - h;
8741 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8744 else if( d_y == s_y ) {
8745 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8748 arrow[0].y = s_y + A_WIDTH + 0.5;
8750 arrow[1].x = d_x - w;
8751 arrow[1].y = s_y + A_WIDTH + 0.5;
8753 arrow[2].x = d_x - w;
8754 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8759 arrow[5].x = d_x - w;
8760 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8762 arrow[4].x = d_x - w;
8763 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8766 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8769 /* [AS] Needed a lot of paper for this! :-) */
8770 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8771 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8773 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8775 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8780 arrow[0].x = Round(x - j);
8781 arrow[0].y = Round(y + j*dx);
8783 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8784 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8787 x = (double) d_x - k;
8788 y = (double) d_y - k*dy;
8791 x = (double) d_x + k;
8792 y = (double) d_y + k*dy;
8795 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8797 arrow[6].x = Round(x - j);
8798 arrow[6].y = Round(y + j*dx);
8800 arrow[2].x = Round(arrow[6].x + 2*j);
8801 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8803 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8804 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8809 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8810 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8813 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8814 // Polygon( hdc, arrow, 7 );
8817 /* [AS] Draw an arrow between two squares */
8818 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
8820 int s_x, s_y, d_x, d_y, hor, vert, i;
8822 if( s_col == d_col && s_row == d_row ) {
8826 /* Get source and destination points */
8827 SquareToPos( s_row, s_col, &s_x, &s_y);
8828 SquareToPos( d_row, d_col, &d_x, &d_y);
8831 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8833 else if( d_y < s_y ) {
8834 d_y += squareSize / 2 + squareSize / 4;
8837 d_y += squareSize / 2;
8841 d_x += squareSize / 2 - squareSize / 4;
8843 else if( d_x < s_x ) {
8844 d_x += squareSize / 2 + squareSize / 4;
8847 d_x += squareSize / 2;
8850 s_x += squareSize / 2;
8851 s_y += squareSize / 2;
8854 A_WIDTH = squareSize / 14.; //[HGM] make float
8856 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8858 hor = 64*s_col + 32; vert = 64*s_row + 32;
8859 for(i=0; i<= 64; i++) {
8860 damage[0][vert+6>>6][hor+6>>6] = True;
8861 damage[0][vert-6>>6][hor+6>>6] = True;
8862 damage[0][vert+6>>6][hor-6>>6] = True;
8863 damage[0][vert-6>>6][hor-6>>6] = True;
8864 hor += d_col - s_col; vert += d_row - s_row;
8868 Boolean IsDrawArrowEnabled()
8870 return appData.highlightMoveWithArrow && squareSize >= 32;
8873 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
8875 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8876 DrawArrowBetweenSquares(fromX, fromY, toX, toY);