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 LoadEngineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
458 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
459 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
460 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
461 void IcsOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
462 void SoundOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
463 void BoardOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
464 void LoadOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
465 void SaveOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
466 void GameListOptionsPopDown P(());
467 void ShufflePopDown P(());
468 void TimeControlPopDown P(());
469 void GenericPopDown P(());
470 void update_ics_width P(());
471 int get_term_width P(());
472 int CopyMemoProc P(());
473 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
474 Boolean IsDrawArrowEnabled P(());
477 * XBoard depends on Xt R4 or higher
479 int xtVersion = XtSpecificationRelease;
484 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
485 jailSquareColor, highlightSquareColor, premoveHighlightColor;
486 Pixel lowTimeWarningColor;
487 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
488 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
489 wjPieceGC, bjPieceGC, prelineGC, countGC;
490 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
491 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
492 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
493 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
494 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
495 ICSInputShell, fileNameShell, askQuestionShell;
496 Widget historyShell, evalGraphShell, gameListShell;
497 int hOffset; // [HGM] dual
498 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
499 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
500 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
501 Font clockFontID, coordFontID, countFontID;
502 XFontStruct *clockFontStruct, *coordFontStruct, *countFontStruct;
503 XtAppContext appContext;
505 char *oldICSInteractionTitle;
509 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
511 Position commentX = -1, commentY = -1;
512 Dimension commentW, commentH;
513 typedef unsigned int BoardSize;
515 Boolean chessProgram;
517 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
518 int squareSize, smallLayout = 0, tinyLayout = 0,
519 marginW, marginH, // [HGM] for run-time resizing
520 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
521 ICSInputBoxUp = False, askQuestionUp = False,
522 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
523 errorUp = False, errorExitStatus = -1, lineGap, defaultLineGap;
524 Pixel timerForegroundPixel, timerBackgroundPixel;
525 Pixel buttonForegroundPixel, buttonBackgroundPixel;
526 char *chessDir, *programName, *programVersion,
527 *gameCopyFilename, *gamePasteFilename;
528 Boolean alwaysOnTop = False;
529 Boolean saveSettingsOnExit;
530 char *settingsFileName;
531 char *icsTextMenuString;
533 char *firstChessProgramNames;
534 char *secondChessProgramNames;
536 WindowPlacement wpMain;
537 WindowPlacement wpConsole;
538 WindowPlacement wpComment;
539 WindowPlacement wpMoveHistory;
540 WindowPlacement wpEvalGraph;
541 WindowPlacement wpEngineOutput;
542 WindowPlacement wpGameList;
543 WindowPlacement wpTags;
545 extern Widget shells[];
546 extern Boolean shellUp[];
550 Pixmap pieceBitmap[2][(int)BlackPawn];
551 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
552 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
553 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
554 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
555 Pixmap xpmBoardBitmap[2];
556 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
557 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
558 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
559 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
560 XImage *ximLightSquare, *ximDarkSquare;
563 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
564 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
566 #define White(piece) ((int)(piece) < (int)BlackPawn)
568 /* Variables for doing smooth animation. This whole thing
569 would be much easier if the board was double-buffered,
570 but that would require a fairly major rewrite. */
575 GC blitGC, pieceGC, outlineGC;
576 XPoint startSquare, prevFrame, mouseDelta;
580 int startBoardX, startBoardY;
583 /* There can be two pieces being animated at once: a player
584 can begin dragging a piece before the remote opponent has moved. */
586 static AnimState game, player;
588 /* Bitmaps for use as masks when drawing XPM pieces.
589 Need one for each black and white piece. */
590 static Pixmap xpmMask[BlackKing + 1];
592 /* This magic number is the number of intermediate frames used
593 in each half of the animation. For short moves it's reduced
594 by 1. The total number of frames will be factor * 2 + 1. */
597 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
599 MenuItem fileMenu[] = {
600 {N_("New Game Ctrl+N"), "New Game", ResetProc},
601 {N_("New Shuffle Game ..."), "New Shuffle Game", ShuffleMenuProc},
602 {N_("New Variant ... Alt+Shift+V"), "New Variant", NewVariantProc}, // [HGM] variant: not functional yet
603 {"----", NULL, NothingProc},
604 {N_("Load Game Ctrl+O"), "Load Game", LoadGameProc},
605 {N_("Load Position Ctrl+Shift+O"), "Load Position", LoadPositionProc},
606 // {N_("Load Next Game"), "Load Next Game", LoadNextGameProc},
607 // {N_("Load Previous Game"), "Load Previous Game", LoadPrevGameProc},
608 // {N_("Reload Same Game"), "Reload Same Game", ReloadGameProc},
609 {N_("Next Position Shift+PgDn"), "Load Next Position", LoadNextPositionProc},
610 {N_("Prev Position Shift+PgUp"), "Load Previous Position", LoadPrevPositionProc},
611 {"----", NULL, NothingProc},
612 // {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
613 {N_("Save Game Ctrl+S"), "Save Game", SaveGameProc},
614 {N_("Save Position Ctrl+Shift+S"), "Save Position", SavePositionProc},
615 {"----", NULL, NothingProc},
616 {N_("Mail Move"), "Mail Move", MailMoveProc},
617 {N_("Reload CMail Message"), "Reload CMail Message", ReloadCmailMsgProc},
618 {"----", NULL, NothingProc},
619 {N_("Quit Ctr+Q"), "Exit", QuitProc},
623 MenuItem editMenu[] = {
624 {N_("Copy Game Ctrl+C"), "Copy Game", CopyGameProc},
625 {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
626 {N_("Copy Game List"), "Copy Game List", CopyGameListProc},
627 {"----", NULL, NothingProc},
628 {N_("Paste Game Ctrl+V"), "Paste Game", PasteGameProc},
629 {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
630 {"----", NULL, NothingProc},
631 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
632 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
633 {N_("Edit Tags"), "Edit Tags", EditTagsProc},
634 {N_("Edit Comment"), "Edit Comment", EditCommentProc},
635 {"----", NULL, NothingProc},
636 {N_("Revert Home"), "Revert", RevertProc},
637 {N_("Annotate"), "Annotate", AnnotateProc},
638 {N_("Truncate Game End"), "Truncate Game", TruncateGameProc},
639 {"----", NULL, NothingProc},
640 {N_("Backward Alt+Left"), "Backward", BackwardProc},
641 {N_("Forward Alt+Right"), "Forward", ForwardProc},
642 {N_("Back to Start Alt+Home"), "Back to Start", ToStartProc},
643 {N_("Forward to End Alt+End"), "Forward to End", ToEndProc},
647 MenuItem viewMenu[] = {
648 {N_("Flip View F2"), "Flip View", FlipViewProc},
649 {"----", NULL, NothingProc},
650 {N_("Engine Output Alt+Shift+O"), "Show Engine Output", EngineOutputProc},
651 {N_("Move History Alt+Shift+H"), "Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
652 {N_("Evaluation Graph Alt+Shift+E"), "Show Evaluation Graph", EvalGraphProc},
653 {N_("Game List Alt+Shift+G"), "Show Game List", ShowGameListProc},
654 {N_("ICS text menu"), "ICStex", IcsTextProc},
655 {"----", NULL, NothingProc},
656 {N_("Tags"), "Show Tags", EditTagsProc},
657 {N_("Comments"), "Show Comments", EditCommentProc},
658 {N_("ICS Input Box"), "ICS Input Box", IcsInputBoxProc},
659 {"----", NULL, NothingProc},
660 {N_("Board..."), "Board Options", BoardOptionsProc},
661 {N_("Game List Tags..."), "Game List", GameListOptionsPopUp},
665 MenuItem modeMenu[] = {
666 {N_("Machine White Ctrl+W"), "Machine White", MachineWhiteProc},
667 {N_("Machine Black Ctrl+B"), "Machine Black", MachineBlackProc},
668 {N_("Two Machines Ctrl+T"), "Two Machines", TwoMachinesProc},
669 {N_("Analysis Mode Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
670 {N_("Analyze File Ctrl+F"), "Analyze File", AnalyzeFileProc },
671 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
672 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
673 {N_("Training"), "Training", TrainingProc},
674 {N_("ICS Client"), "ICS Client", IcsClientProc},
675 {"----", NULL, NothingProc},
676 {N_("Machine Match"), "Machine Match", MatchProc},
677 {N_("Pause Pause"), "Pause", PauseProc},
681 MenuItem actionMenu[] = {
682 {N_("Accept F3"), "Accept", AcceptProc},
683 {N_("Decline F4"), "Decline", DeclineProc},
684 {N_("Rematch F12"), "Rematch", RematchProc},
685 {"----", NULL, NothingProc},
686 {N_("Call Flag F5"), "Call Flag", CallFlagProc},
687 {N_("Draw F6"), "Draw", DrawProc},
688 {N_("Adjourn F7"), "Adjourn", AdjournProc},
689 {N_("Abort F8"),"Abort", AbortProc},
690 {N_("Resign F9"), "Resign", ResignProc},
691 {"----", NULL, NothingProc},
692 {N_("Stop Observing F10"), "Stop Observing", StopObservingProc},
693 {N_("Stop Examining F11"), "Stop Examining", StopExaminingProc},
694 {N_("Upload to Examine"), "Upload to Examine", UploadProc},
695 {"----", NULL, NothingProc},
696 {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
697 {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
698 {N_("Adjudicate Draw"), "Adjudicate Draw", AdjuDrawProc},
702 MenuItem engineMenu[] = {
703 {N_("Load New Engine ..."), "Load Engine", LoadEngineProc},
704 {"----", NULL, NothingProc},
705 {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
706 {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
707 {"----", NULL, NothingProc},
708 {N_("Hint"), "Hint", HintProc},
709 {N_("Book"), "Book", BookProc},
710 {"----", NULL, NothingProc},
711 {N_("Move Now Ctrl+M"), "Move Now", MoveNowProc},
712 {N_("Retract Move Ctrl+X"), "Retract Move", RetractMoveProc},
716 MenuItem optionsMenu[] = {
717 #define OPTIONSDIALOG
719 {N_("General ..."), "General", OptionsProc},
721 {N_("Time Control ... Alt+Shift+T"), "Time Control", TimeControlProc},
722 {N_("Common Engine ... Alt+Shift+U"), "Common Engine", UciMenuProc},
723 {N_("Adjudications ... Alt+Shift+J"), "Adjudications", EngineMenuProc},
724 {N_("ICS ..."), "ICS", IcsOptionsProc},
725 {N_("Match ..."), "Match", MatchOptionsProc},
726 {N_("Load Game ..."), "Load Game", LoadOptionsProc},
727 {N_("Save Game ..."), "Save Game", SaveOptionsProc},
728 // {N_(" ..."), "", OptionsProc},
729 {N_("Game List ..."), "Game List", GameListOptionsPopUp},
730 {N_("Sounds ..."), "Sounds", SoundOptionsProc},
731 {"----", NULL, NothingProc},
732 #ifndef OPTIONSDIALOG
733 {N_("Always Queen Ctrl+Shift+Q"), "Always Queen", AlwaysQueenProc},
734 {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
735 {N_("Animate Moving Ctrl+Shift+A"), "Animate Moving", AnimateMovingProc},
736 {N_("Auto Flag Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
737 {N_("Auto Flip View"), "Auto Flip View", AutoflipProc},
738 {N_("Blindfold"), "Blindfold", BlindfoldProc},
739 {N_("Flash Moves"), "Flash Moves", FlashMovesProc},
741 {N_("Highlight Dragging"), "Highlight Dragging", HighlightDraggingProc},
743 {N_("Highlight Last Move"), "Highlight Last Move", HighlightLastMoveProc},
744 {N_("Highlight With Arrow"), "Arrow", HighlightArrowProc},
745 {N_("Move Sound"), "Move Sound", MoveSoundProc},
746 // {N_("ICS Alarm"), "ICS Alarm", IcsAlarmProc},
747 {N_("One-Click Moving"), "OneClick", OneClickProc},
748 {N_("Periodic Updates"), "Periodic Updates", PeriodicUpdatesProc},
749 {N_("Ponder Next Move Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
750 {N_("Popup Exit Message"), "Popup Exit Message", PopupExitMessageProc},
751 {N_("Popup Move Errors"), "Popup Move Errors", PopupMoveErrorsProc},
752 // {N_("Premove"), "Premove", PremoveProc},
753 {N_("Show Coords"), "Show Coords", ShowCoordsProc},
754 {N_("Hide Thinking Ctrl+Shift+H"), "Hide Thinking", HideThinkingProc},
755 {N_("Test Legality Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
756 {"----", NULL, NothingProc},
758 {N_("Save Settings Now"), "Save Settings Now", SaveSettingsProc},
759 {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
763 MenuItem helpMenu[] = {
764 {N_("Info XBoard"), "Info XBoard", InfoProc},
765 {N_("Man XBoard F1"), "Man XBoard", ManProc},
766 {"----", NULL, NothingProc},
767 {N_("About XBoard"), "About XBoard", AboutProc},
772 {N_("File"), "File", fileMenu},
773 {N_("Edit"), "Edit", editMenu},
774 {N_("View"), "View", viewMenu},
775 {N_("Mode"), "Mode", modeMenu},
776 {N_("Action"), "Action", actionMenu},
777 {N_("Engine"), "Engine", engineMenu},
778 {N_("Options"), "Options", optionsMenu},
779 {N_("Help"), "Help", helpMenu},
783 #define PAUSE_BUTTON "P"
784 MenuItem buttonBar[] = {
785 {"<<", "<<", ToStartProc},
786 {"<", "<", BackwardProc},
787 {PAUSE_BUTTON, PAUSE_BUTTON, PauseProc},
788 {">", ">", ForwardProc},
789 {">>", ">>", ToEndProc},
793 #define PIECE_MENU_SIZE 18
794 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
795 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
796 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
797 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
798 N_("Empty square"), N_("Clear board") },
799 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
800 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
801 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
802 N_("Empty square"), N_("Clear board") }
804 /* must be in same order as PieceMenuStrings! */
805 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
806 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
807 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
808 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
809 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
810 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
811 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
812 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
813 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
816 #define DROP_MENU_SIZE 6
817 String dropMenuStrings[DROP_MENU_SIZE] = {
818 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
820 /* must be in same order as PieceMenuStrings! */
821 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
822 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
823 WhiteRook, WhiteQueen
831 DropMenuEnables dmEnables[] = {
849 { XtNborderWidth, 0 },
850 { XtNdefaultDistance, 0 },
854 { XtNborderWidth, 0 },
855 { XtNresizable, (XtArgVal) True },
859 { XtNborderWidth, 0 },
865 { XtNjustify, (XtArgVal) XtJustifyRight },
866 { XtNlabel, (XtArgVal) "..." },
867 { XtNresizable, (XtArgVal) True },
868 { XtNresize, (XtArgVal) False }
871 Arg messageArgs[] = {
872 { XtNjustify, (XtArgVal) XtJustifyLeft },
873 { XtNlabel, (XtArgVal) "..." },
874 { XtNresizable, (XtArgVal) True },
875 { XtNresize, (XtArgVal) False }
879 { XtNborderWidth, 0 },
880 { XtNjustify, (XtArgVal) XtJustifyLeft }
883 XtResource clientResources[] = {
884 { "flashCount", "flashCount", XtRInt, sizeof(int),
885 XtOffset(AppDataPtr, flashCount), XtRImmediate,
886 (XtPointer) FLASH_COUNT },
889 XrmOptionDescRec shellOptions[] = {
890 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
891 { "-flash", "flashCount", XrmoptionNoArg, "3" },
892 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
895 XtActionsRec boardActions[] = {
896 { "DrawPosition", DrawPositionProc },
897 { "HandleUserMove", HandleUserMove },
898 { "AnimateUserMove", AnimateUserMove },
899 { "HandlePV", HandlePV },
900 { "SelectPV", SelectPV },
901 { "StopPV", StopPV },
902 { "FileNameAction", FileNameAction },
903 { "AskQuestionProc", AskQuestionProc },
904 { "AskQuestionReplyAction", AskQuestionReplyAction },
905 { "PieceMenuPopup", PieceMenuPopup },
906 { "WhiteClock", WhiteClock },
907 { "BlackClock", BlackClock },
908 { "Iconify", Iconify },
909 { "ResetProc", ResetProc },
910 { "NewVariantProc", NewVariantProc },
911 { "LoadGameProc", LoadGameProc },
912 { "LoadNextGameProc", LoadNextGameProc },
913 { "LoadPrevGameProc", LoadPrevGameProc },
914 { "LoadSelectedProc", LoadSelectedProc },
915 { "SetFilterProc", SetFilterProc },
916 { "ReloadGameProc", ReloadGameProc },
917 { "LoadPositionProc", LoadPositionProc },
918 { "LoadNextPositionProc", LoadNextPositionProc },
919 { "LoadPrevPositionProc", LoadPrevPositionProc },
920 { "ReloadPositionProc", ReloadPositionProc },
921 { "CopyPositionProc", CopyPositionProc },
922 { "PastePositionProc", PastePositionProc },
923 { "CopyGameProc", CopyGameProc },
924 { "CopyGameListProc", CopyGameListProc },
925 { "PasteGameProc", PasteGameProc },
926 { "SaveGameProc", SaveGameProc },
927 { "SavePositionProc", SavePositionProc },
928 { "MailMoveProc", MailMoveProc },
929 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
930 { "QuitProc", QuitProc },
931 { "MachineWhiteProc", MachineWhiteProc },
932 { "MachineBlackProc", MachineBlackProc },
933 { "AnalysisModeProc", AnalyzeModeProc },
934 { "AnalyzeFileProc", AnalyzeFileProc },
935 { "TwoMachinesProc", TwoMachinesProc },
936 { "IcsClientProc", IcsClientProc },
937 { "EditGameProc", EditGameProc },
938 { "EditPositionProc", EditPositionProc },
939 { "TrainingProc", EditPositionProc },
940 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
941 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
942 { "ShowGameListProc", ShowGameListProc },
943 { "ShowMoveListProc", HistoryShowProc},
944 { "EditTagsProc", EditCommentProc },
945 { "EditCommentProc", EditCommentProc },
946 { "IcsInputBoxProc", IcsInputBoxProc },
947 { "PauseProc", PauseProc },
948 { "AcceptProc", AcceptProc },
949 { "DeclineProc", DeclineProc },
950 { "RematchProc", RematchProc },
951 { "CallFlagProc", CallFlagProc },
952 { "DrawProc", DrawProc },
953 { "AdjournProc", AdjournProc },
954 { "AbortProc", AbortProc },
955 { "ResignProc", ResignProc },
956 { "AdjuWhiteProc", AdjuWhiteProc },
957 { "AdjuBlackProc", AdjuBlackProc },
958 { "AdjuDrawProc", AdjuDrawProc },
959 { "TypeInProc", TypeInProc },
960 { "EnterKeyProc", EnterKeyProc },
961 { "UpKeyProc", UpKeyProc },
962 { "DownKeyProc", DownKeyProc },
963 { "StopObservingProc", StopObservingProc },
964 { "StopExaminingProc", StopExaminingProc },
965 { "UploadProc", UploadProc },
966 { "BackwardProc", BackwardProc },
967 { "ForwardProc", ForwardProc },
968 { "ToStartProc", ToStartProc },
969 { "ToEndProc", ToEndProc },
970 { "RevertProc", RevertProc },
971 { "AnnotateProc", AnnotateProc },
972 { "TruncateGameProc", TruncateGameProc },
973 { "MoveNowProc", MoveNowProc },
974 { "RetractMoveProc", RetractMoveProc },
975 { "EngineMenuProc", (XtActionProc) EngineMenuProc },
976 { "UciMenuProc", (XtActionProc) UciMenuProc },
977 { "TimeControlProc", (XtActionProc) TimeControlProc },
978 { "FlipViewProc", FlipViewProc },
979 { "PonderNextMoveProc", PonderNextMoveProc },
980 #ifndef OPTIONSDIALOG
981 { "AlwaysQueenProc", AlwaysQueenProc },
982 { "AnimateDraggingProc", AnimateDraggingProc },
983 { "AnimateMovingProc", AnimateMovingProc },
984 { "AutoflagProc", AutoflagProc },
985 { "AutoflipProc", AutoflipProc },
986 { "BlindfoldProc", BlindfoldProc },
987 { "FlashMovesProc", FlashMovesProc },
989 { "HighlightDraggingProc", HighlightDraggingProc },
991 { "HighlightLastMoveProc", HighlightLastMoveProc },
992 // { "IcsAlarmProc", IcsAlarmProc },
993 { "MoveSoundProc", MoveSoundProc },
994 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
995 { "PopupExitMessageProc", PopupExitMessageProc },
996 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
997 // { "PremoveProc", PremoveProc },
998 { "ShowCoordsProc", ShowCoordsProc },
999 { "ShowThinkingProc", ShowThinkingProc },
1000 { "HideThinkingProc", HideThinkingProc },
1001 { "TestLegalityProc", TestLegalityProc },
1003 { "SaveSettingsProc", SaveSettingsProc },
1004 { "SaveOnExitProc", SaveOnExitProc },
1005 { "InfoProc", InfoProc },
1006 { "ManProc", ManProc },
1007 { "HintProc", HintProc },
1008 { "BookProc", BookProc },
1009 { "AboutGameProc", AboutGameProc },
1010 { "AboutProc", AboutProc },
1011 { "DebugProc", DebugProc },
1012 { "NothingProc", NothingProc },
1013 { "CommentClick", (XtActionProc) CommentClick },
1014 { "CommentPopDown", (XtActionProc) CommentPopDown },
1015 { "TagsPopDown", (XtActionProc) TagsPopDown },
1016 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1017 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1018 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1019 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1020 { "GameListPopDown", (XtActionProc) GameListPopDown },
1021 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1022 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1023 { "HistoryPopDown", (XtActionProc) HistoryPopDown },
1024 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1025 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1026 { "ShufflePopDown", (XtActionProc) ShufflePopDown },
1027 { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
1028 { "GenericPopDown", (XtActionProc) GenericPopDown },
1029 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1032 char globalTranslations[] =
1033 ":<Key>F9: ResignProc() \n \
1034 :Ctrl<Key>n: ResetProc() \n \
1035 :Meta<Key>V: NewVariantProc() \n \
1036 :Ctrl<Key>o: LoadGameProc() \n \
1037 :Meta<Key>Next: LoadNextGameProc() \n \
1038 :Meta<Key>Prior: LoadPrevGameProc() \n \
1039 :Ctrl<Key>s: SaveGameProc() \n \
1040 :Ctrl<Key>c: CopyGameProc() \n \
1041 :Ctrl<Key>v: PasteGameProc() \n \
1042 :Ctrl<Key>O: LoadPositionProc() \n \
1043 :Shift<Key>Next: LoadNextPositionProc() \n \
1044 :Shift<Key>Prior: LoadPrevPositionProc() \n \
1045 :Ctrl<Key>S: SavePositionProc() \n \
1046 :Ctrl<Key>C: CopyPositionProc() \n \
1047 :Ctrl<Key>V: PastePositionProc() \n \
1048 :Ctrl<Key>q: QuitProc() \n \
1049 :Ctrl<Key>w: MachineWhiteProc() \n \
1050 :Ctrl<Key>b: MachineBlackProc() \n \
1051 :Ctrl<Key>t: TwoMachinesProc() \n \
1052 :Ctrl<Key>a: AnalysisModeProc() \n \
1053 :Ctrl<Key>f: AnalyzeFileProc() \n \
1054 :Ctrl<Key>e: EditGameProc() \n \
1055 :Ctrl<Key>E: EditPositionProc() \n \
1056 :Meta<Key>O: EngineOutputProc() \n \
1057 :Meta<Key>E: EvalGraphProc() \n \
1058 :Meta<Key>G: ShowGameListProc() \n \
1059 :Meta<Key>H: ShowMoveListProc() \n \
1060 :<Key>Pause: PauseProc() \n \
1061 :<Key>F3: AcceptProc() \n \
1062 :<Key>F4: DeclineProc() \n \
1063 :<Key>F12: RematchProc() \n \
1064 :<Key>F5: CallFlagProc() \n \
1065 :<Key>F6: DrawProc() \n \
1066 :<Key>F7: AdjournProc() \n \
1067 :<Key>F8: AbortProc() \n \
1068 :<Key>F10: StopObservingProc() \n \
1069 :<Key>F11: StopExaminingProc() \n \
1070 :Meta Ctrl<Key>F12: DebugProc() \n \
1071 :Meta<Key>End: ToEndProc() \n \
1072 :Meta<Key>Right: ForwardProc() \n \
1073 :Meta<Key>Home: ToStartProc() \n \
1074 :Meta<Key>Left: BackwardProc() \n \
1075 :<Key>Home: RevertProc() \n \
1076 :<Key>End: TruncateGameProc() \n \
1077 :Ctrl<Key>m: MoveNowProc() \n \
1078 :Ctrl<Key>x: RetractMoveProc() \n \
1079 :Meta<Key>J: EngineMenuProc() \n \
1080 :Meta<Key>U: UciMenuProc() \n \
1081 :Meta<Key>T: TimeControlProc() \n \
1082 :Ctrl<Key>P: PonderNextMoveProc() \n "
1083 #ifndef OPTIONSDIALOG
1085 :Ctrl<Key>Q: AlwaysQueenProc() \n \
1086 :Ctrl<Key>F: AutoflagProc() \n \
1087 :Ctrl<Key>A: AnimateMovingProc() \n \
1088 :Ctrl<Key>L: TestLegalityProc() \n \
1089 :Ctrl<Key>H: HideThinkingProc() \n "
1092 :<Key>-: Iconify() \n \
1093 :<Key>F1: ManProc() \n \
1094 :<Key>F2: FlipViewProc() \n \
1095 <KeyDown>.: BackwardProc() \n \
1096 <KeyUp>.: ForwardProc() \n \
1097 Shift<Key>1: AskQuestionProc(\"Direct command\",\
1098 \"Send to chess program:\",,1) \n \
1099 Shift<Key>2: AskQuestionProc(\"Direct command\",\
1100 \"Send to second chess program:\",,2) \n";
1102 char boardTranslations[] =
1103 "<Btn1Down>: HandleUserMove(0) \n \
1104 Shift<Btn1Up>: HandleUserMove(1) \n \
1105 <Btn1Up>: HandleUserMove(0) \n \
1106 <Btn1Motion>: AnimateUserMove() \n \
1107 <Btn3Motion>: HandlePV() \n \
1108 <Btn3Up>: PieceMenuPopup(menuB) \n \
1109 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1110 PieceMenuPopup(menuB) \n \
1111 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1112 PieceMenuPopup(menuW) \n \
1113 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1114 PieceMenuPopup(menuW) \n \
1115 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1116 PieceMenuPopup(menuB) \n";
1118 char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
1119 char blackTranslations[] = "<BtnDown>: BlackClock()\n";
1121 char ICSInputTranslations[] =
1122 "<Key>Up: UpKeyProc() \n "
1123 "<Key>Down: DownKeyProc() \n "
1124 "<Key>Return: EnterKeyProc() \n";
1126 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1127 // as the widget is destroyed before the up-click can call extend-end
1128 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1130 String xboardResources[] = {
1131 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1132 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1133 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1138 /* Max possible square size */
1139 #define MAXSQSIZE 256
1141 static int xpm_avail[MAXSQSIZE];
1143 #ifdef HAVE_DIR_STRUCT
1145 /* Extract piece size from filename */
1147 xpm_getsize(name, len, ext)
1158 if ((p=strchr(name, '.')) == NULL ||
1159 StrCaseCmp(p+1, ext) != 0)
1165 while (*p && isdigit(*p))
1172 /* Setup xpm_avail */
1174 xpm_getavail(dirname, ext)
1182 for (i=0; i<MAXSQSIZE; ++i)
1185 if (appData.debugMode)
1186 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1188 dir = opendir(dirname);
1191 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1192 programName, dirname);
1196 while ((ent=readdir(dir)) != NULL) {
1197 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1198 if (i > 0 && i < MAXSQSIZE)
1208 xpm_print_avail(fp, ext)
1214 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1215 for (i=1; i<MAXSQSIZE; ++i) {
1221 /* Return XPM piecesize closest to size */
1223 xpm_closest_to(dirname, size, ext)
1229 int sm_diff = MAXSQSIZE;
1233 xpm_getavail(dirname, ext);
1235 if (appData.debugMode)
1236 xpm_print_avail(stderr, ext);
1238 for (i=1; i<MAXSQSIZE; ++i) {
1241 diff = (diff<0) ? -diff : diff;
1242 if (diff < sm_diff) {
1250 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1256 #else /* !HAVE_DIR_STRUCT */
1257 /* If we are on a system without a DIR struct, we can't
1258 read the directory, so we can't collect a list of
1259 filenames, etc., so we can't do any size-fitting. */
1261 xpm_closest_to(dirname, size, ext)
1266 fprintf(stderr, _("\
1267 Warning: No DIR structure found on this system --\n\
1268 Unable to autosize for XPM/XIM pieces.\n\
1269 Please report this error to %s.\n\
1270 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
1273 #endif /* HAVE_DIR_STRUCT */
1275 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1276 "magenta", "cyan", "white" };
1280 TextColors textColors[(int)NColorClasses];
1282 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1284 parse_color(str, which)
1288 char *p, buf[100], *d;
1291 if (strlen(str) > 99) /* watch bounds on buf */
1296 for (i=0; i<which; ++i) {
1303 /* Could be looking at something like:
1305 .. in which case we want to stop on a comma also */
1306 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1310 return -1; /* Use default for empty field */
1313 if (which == 2 || isdigit(*p))
1316 while (*p && isalpha(*p))
1321 for (i=0; i<8; ++i) {
1322 if (!StrCaseCmp(buf, cnames[i]))
1323 return which? (i+40) : (i+30);
1325 if (!StrCaseCmp(buf, "default")) return -1;
1327 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1332 parse_cpair(cc, str)
1336 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1337 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1342 /* bg and attr are optional */
1343 textColors[(int)cc].bg = parse_color(str, 1);
1344 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1345 textColors[(int)cc].attr = 0;
1351 /* Arrange to catch delete-window events */
1352 Atom wm_delete_window;
1354 CatchDeleteWindow(Widget w, String procname)
1357 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1358 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1359 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1366 XtSetArg(args[0], XtNiconic, False);
1367 XtSetValues(shellWidget, args, 1);
1369 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1372 //---------------------------------------------------------------------------------------------------------
1373 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1376 #define CW_USEDEFAULT (1<<31)
1377 #define ICS_TEXT_MENU_SIZE 90
1378 #define DEBUG_FILE "xboard.debug"
1379 #define SetCurrentDirectory chdir
1380 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1384 // these two must some day move to frontend.h, when they are implemented
1385 Boolean GameListIsUp();
1387 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1390 // front-end part of option handling
1392 // [HGM] This platform-dependent table provides the location for storing the color info
1393 extern char *crWhite, * crBlack;
1397 &appData.whitePieceColor,
1398 &appData.blackPieceColor,
1399 &appData.lightSquareColor,
1400 &appData.darkSquareColor,
1401 &appData.highlightSquareColor,
1402 &appData.premoveHighlightColor,
1403 &appData.lowTimeWarningColor,
1414 // [HGM] font: keep a font for each square size, even non-stndard ones
1415 #define NUM_SIZES 18
1416 #define MAX_SIZE 130
1417 Boolean fontSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1418 char *fontTable[NUM_FONTS][MAX_SIZE];
1421 ParseFont(char *name, int number)
1422 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1424 if(sscanf(name, "size%d:", &size)) {
1425 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1426 // defer processing it until we know if it matches our board size
1427 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1428 fontTable[number][size] = strdup(strchr(name, ':')+1);
1429 fontValid[number][size] = True;
1434 case 0: // CLOCK_FONT
1435 appData.clockFont = strdup(name);
1437 case 1: // MESSAGE_FONT
1438 appData.font = strdup(name);
1440 case 2: // COORD_FONT
1441 appData.coordFont = strdup(name);
1446 fontSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1451 { // only 2 fonts currently
1452 appData.clockFont = CLOCK_FONT_NAME;
1453 appData.coordFont = COORD_FONT_NAME;
1454 appData.font = DEFAULT_FONT_NAME;
1459 { // no-op, until we identify the code for this already in XBoard and move it here
1463 ParseColor(int n, char *name)
1464 { // in XBoard, just copy the color-name string
1465 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1469 ParseTextAttribs(ColorClass cc, char *s)
1471 (&appData.colorShout)[cc] = strdup(s);
1475 ParseBoardSize(void *addr, char *name)
1477 appData.boardSize = strdup(name);
1482 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1486 SetCommPortDefaults()
1487 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1490 // [HGM] args: these three cases taken out to stay in front-end
1492 SaveFontArg(FILE *f, ArgDescriptor *ad)
1495 int i, n = (int)(intptr_t)ad->argLoc;
1497 case 0: // CLOCK_FONT
1498 name = appData.clockFont;
1500 case 1: // MESSAGE_FONT
1501 name = appData.font;
1503 case 2: // COORD_FONT
1504 name = appData.coordFont;
1509 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1510 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1511 fontTable[n][squareSize] = strdup(name);
1512 fontValid[n][squareSize] = True;
1515 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1516 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1521 { // nothing to do, as the sounds are at all times represented by their text-string names already
1525 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1526 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1527 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1531 SaveColor(FILE *f, ArgDescriptor *ad)
1532 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1533 if(colorVariable[(int)(intptr_t)ad->argLoc])
1534 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1538 SaveBoardSize(FILE *f, char *name, void *addr)
1539 { // wrapper to shield back-end from BoardSize & sizeInfo
1540 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1544 ParseCommPortSettings(char *s)
1545 { // no such option in XBoard (yet)
1548 extern Widget engineOutputShell;
1551 GetActualPlacement(Widget wg, WindowPlacement *wp)
1561 XtSetArg(args[i], XtNx, &x); i++;
1562 XtSetArg(args[i], XtNy, &y); i++;
1563 XtSetArg(args[i], XtNwidth, &w); i++;
1564 XtSetArg(args[i], XtNheight, &h); i++;
1565 XtGetValues(wg, args, i);
1574 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1575 // In XBoard this will have to wait until awareness of window parameters is implemented
1576 GetActualPlacement(shellWidget, &wpMain);
1577 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1578 if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1579 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1580 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1581 if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1582 if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1586 PrintCommPortSettings(FILE *f, char *name)
1587 { // This option does not exist in XBoard
1591 MySearchPath(char *installDir, char *name, char *fullname)
1592 { // just append installDir and name. Perhaps ExpandPath should be used here?
1593 name = ExpandPathName(name);
1594 if(name && name[0] == '/')
1595 safeStrCpy(fullname, name, MSG_SIZ );
1597 sprintf(fullname, "%s%c%s", installDir, '/', name);
1603 MyGetFullPathName(char *name, char *fullname)
1604 { // should use ExpandPath?
1605 name = ExpandPathName(name);
1606 safeStrCpy(fullname, name, MSG_SIZ );
1611 EnsureOnScreen(int *x, int *y, int minX, int minY)
1618 { // [HGM] args: allows testing if main window is realized from back-end
1619 return xBoardWindow != 0;
1623 PopUpStartupDialog()
1624 { // start menu not implemented in XBoard
1628 ConvertToLine(int argc, char **argv)
1630 static char line[128*1024], buf[1024];
1634 for(i=1; i<argc; i++)
1636 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1637 && argv[i][0] != '{' )
1638 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1640 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1641 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1644 line[strlen(line)-1] = NULLCHAR;
1648 //--------------------------------------------------------------------------------------------
1650 extern Boolean twoBoards, partnerUp;
1653 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1655 #define BoardSize int
1656 void InitDrawingSizes(BoardSize boardSize, int flags)
1657 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1658 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1660 XtGeometryResult gres;
1663 if(!formWidget) return;
1666 * Enable shell resizing.
1668 shellArgs[0].value = (XtArgVal) &w;
1669 shellArgs[1].value = (XtArgVal) &h;
1670 XtGetValues(shellWidget, shellArgs, 2);
1672 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1673 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1674 XtSetValues(shellWidget, &shellArgs[2], 4);
1676 XtSetArg(args[0], XtNdefaultDistance, &sep);
1677 XtGetValues(formWidget, args, 1);
1679 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1680 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1681 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1683 hOffset = boardWidth + 10;
1684 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1685 secondSegments[i] = gridSegments[i];
1686 secondSegments[i].x1 += hOffset;
1687 secondSegments[i].x2 += hOffset;
1690 XtSetArg(args[0], XtNwidth, boardWidth);
1691 XtSetArg(args[1], XtNheight, boardHeight);
1692 XtSetValues(boardWidget, args, 2);
1694 timerWidth = (boardWidth - sep) / 2;
1695 XtSetArg(args[0], XtNwidth, timerWidth);
1696 XtSetValues(whiteTimerWidget, args, 1);
1697 XtSetValues(blackTimerWidget, args, 1);
1699 XawFormDoLayout(formWidget, False);
1701 if (appData.titleInWindow) {
1703 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1704 XtSetArg(args[i], XtNheight, &h); i++;
1705 XtGetValues(titleWidget, args, i);
1707 w = boardWidth - 2*bor;
1709 XtSetArg(args[0], XtNwidth, &w);
1710 XtGetValues(menuBarWidget, args, 1);
1711 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1714 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1715 if (gres != XtGeometryYes && appData.debugMode) {
1717 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1718 programName, gres, w, h, wr, hr);
1722 XawFormDoLayout(formWidget, True);
1725 * Inhibit shell resizing.
1727 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1728 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1729 shellArgs[4].value = shellArgs[2].value = w;
1730 shellArgs[5].value = shellArgs[3].value = h;
1731 XtSetValues(shellWidget, &shellArgs[0], 6);
1733 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1736 for(i=0; i<4; i++) {
1738 for(p=0; p<=(int)WhiteKing; p++)
1739 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1740 if(gameInfo.variant == VariantShogi) {
1741 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1742 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1743 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1744 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1745 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1748 if(gameInfo.variant == VariantGothic) {
1749 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1752 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1753 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1754 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1757 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1758 for(p=0; p<=(int)WhiteKing; p++)
1759 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1760 if(gameInfo.variant == VariantShogi) {
1761 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1762 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1763 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1764 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1765 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1768 if(gameInfo.variant == VariantGothic) {
1769 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1772 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1773 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1774 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1779 for(i=0; i<2; i++) {
1781 for(p=0; p<=(int)WhiteKing; p++)
1782 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1783 if(gameInfo.variant == VariantShogi) {
1784 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1785 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1786 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1787 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1788 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1791 if(gameInfo.variant == VariantGothic) {
1792 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1795 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1796 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1797 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1807 void ParseIcsTextColors()
1808 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1809 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1810 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1811 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1812 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1813 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1814 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1815 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1816 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1817 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1818 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1820 if (appData.colorize) {
1822 _("%s: can't parse color names; disabling colorization\n"),
1825 appData.colorize = FALSE;
1830 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1831 XrmValue vFrom, vTo;
1832 int forceMono = False;
1834 if (!appData.monoMode) {
1835 vFrom.addr = (caddr_t) appData.lightSquareColor;
1836 vFrom.size = strlen(appData.lightSquareColor);
1837 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1838 if (vTo.addr == NULL) {
1839 appData.monoMode = True;
1842 lightSquareColor = *(Pixel *) vTo.addr;
1845 if (!appData.monoMode) {
1846 vFrom.addr = (caddr_t) appData.darkSquareColor;
1847 vFrom.size = strlen(appData.darkSquareColor);
1848 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1849 if (vTo.addr == NULL) {
1850 appData.monoMode = True;
1853 darkSquareColor = *(Pixel *) vTo.addr;
1856 if (!appData.monoMode) {
1857 vFrom.addr = (caddr_t) appData.whitePieceColor;
1858 vFrom.size = strlen(appData.whitePieceColor);
1859 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1860 if (vTo.addr == NULL) {
1861 appData.monoMode = True;
1864 whitePieceColor = *(Pixel *) vTo.addr;
1867 if (!appData.monoMode) {
1868 vFrom.addr = (caddr_t) appData.blackPieceColor;
1869 vFrom.size = strlen(appData.blackPieceColor);
1870 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1871 if (vTo.addr == NULL) {
1872 appData.monoMode = True;
1875 blackPieceColor = *(Pixel *) vTo.addr;
1879 if (!appData.monoMode) {
1880 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1881 vFrom.size = strlen(appData.highlightSquareColor);
1882 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1883 if (vTo.addr == NULL) {
1884 appData.monoMode = True;
1887 highlightSquareColor = *(Pixel *) vTo.addr;
1891 if (!appData.monoMode) {
1892 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1893 vFrom.size = strlen(appData.premoveHighlightColor);
1894 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1895 if (vTo.addr == NULL) {
1896 appData.monoMode = True;
1899 premoveHighlightColor = *(Pixel *) vTo.addr;
1907 { // [HGM] taken out of main
1909 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1910 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1911 appData.bitmapDirectory = DEF_BITMAP_DIR;
1913 if (appData.bitmapDirectory[0] != NULLCHAR) {
1917 CreateXPMBoard(appData.liteBackTextureFile, 1);
1918 CreateXPMBoard(appData.darkBackTextureFile, 0);
1922 /* Create regular pieces */
1923 if (!useImages) CreatePieces();
1932 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1933 XSetWindowAttributes window_attributes;
1935 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1936 XrmValue vFrom, vTo;
1937 XtGeometryResult gres;
1940 int forceMono = False;
1942 srandom(time(0)); // [HGM] book: make random truly random
1944 setbuf(stdout, NULL);
1945 setbuf(stderr, NULL);
1948 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1949 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1953 programName = strrchr(argv[0], '/');
1954 if (programName == NULL)
1955 programName = argv[0];
1960 XtSetLanguageProc(NULL, NULL, NULL);
1961 bindtextdomain(PACKAGE, LOCALEDIR);
1962 textdomain(PACKAGE);
1966 XtAppInitialize(&appContext, "XBoard", shellOptions,
1967 XtNumber(shellOptions),
1968 &argc, argv, xboardResources, NULL, 0);
1969 appData.boardSize = "";
1970 InitAppData(ConvertToLine(argc, argv));
1972 if (p == NULL) p = "/tmp";
1973 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1974 gameCopyFilename = (char*) malloc(i);
1975 gamePasteFilename = (char*) malloc(i);
1976 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1977 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1979 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1980 clientResources, XtNumber(clientResources),
1983 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1984 static char buf[MSG_SIZ];
1985 EscapeExpand(buf, appData.firstInitString);
1986 appData.firstInitString = strdup(buf);
1987 EscapeExpand(buf, appData.secondInitString);
1988 appData.secondInitString = strdup(buf);
1989 EscapeExpand(buf, appData.firstComputerString);
1990 appData.firstComputerString = strdup(buf);
1991 EscapeExpand(buf, appData.secondComputerString);
1992 appData.secondComputerString = strdup(buf);
1995 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1998 if (chdir(chessDir) != 0) {
1999 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2005 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2006 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2007 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
2008 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2011 setbuf(debugFP, NULL);
2014 /* [HGM,HR] make sure board size is acceptable */
2015 if(appData.NrFiles > BOARD_FILES ||
2016 appData.NrRanks > BOARD_RANKS )
2017 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
2020 /* This feature does not work; animation needs a rewrite */
2021 appData.highlightDragging = FALSE;
2025 xDisplay = XtDisplay(shellWidget);
2026 xScreen = DefaultScreen(xDisplay);
2027 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2029 gameInfo.variant = StringToVariant(appData.variant);
2030 InitPosition(FALSE);
2033 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2035 if (isdigit(appData.boardSize[0])) {
2036 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2037 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2038 &fontPxlSize, &smallLayout, &tinyLayout);
2040 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2041 programName, appData.boardSize);
2045 /* Find some defaults; use the nearest known size */
2046 SizeDefaults *szd, *nearest;
2047 int distance = 99999;
2048 nearest = szd = sizeDefaults;
2049 while (szd->name != NULL) {
2050 if (abs(szd->squareSize - squareSize) < distance) {
2052 distance = abs(szd->squareSize - squareSize);
2053 if (distance == 0) break;
2057 if (i < 2) lineGap = nearest->lineGap;
2058 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2059 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2060 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2061 if (i < 6) smallLayout = nearest->smallLayout;
2062 if (i < 7) tinyLayout = nearest->tinyLayout;
2065 SizeDefaults *szd = sizeDefaults;
2066 if (*appData.boardSize == NULLCHAR) {
2067 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2068 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2071 if (szd->name == NULL) szd--;
2072 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2074 while (szd->name != NULL &&
2075 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2076 if (szd->name == NULL) {
2077 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2078 programName, appData.boardSize);
2082 squareSize = szd->squareSize;
2083 lineGap = szd->lineGap;
2084 clockFontPxlSize = szd->clockFontPxlSize;
2085 coordFontPxlSize = szd->coordFontPxlSize;
2086 fontPxlSize = szd->fontPxlSize;
2087 smallLayout = szd->smallLayout;
2088 tinyLayout = szd->tinyLayout;
2089 // [HGM] font: use defaults from settings file if available and not overruled
2091 if(!fontSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2092 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2093 if(!fontSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2094 appData.font = fontTable[MESSAGE_FONT][squareSize];
2095 if(!fontSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2096 appData.coordFont = fontTable[COORD_FONT][squareSize];
2098 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2099 if (strlen(appData.pixmapDirectory) > 0) {
2100 p = ExpandPathName(appData.pixmapDirectory);
2102 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2103 appData.pixmapDirectory);
2106 if (appData.debugMode) {
2107 fprintf(stderr, _("\
2108 XBoard square size (hint): %d\n\
2109 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2111 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2112 if (appData.debugMode) {
2113 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2116 defaultLineGap = lineGap;
2117 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2119 /* [HR] height treated separately (hacked) */
2120 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2121 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2122 if (appData.showJail == 1) {
2123 /* Jail on top and bottom */
2124 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2125 XtSetArg(boardArgs[2], XtNheight,
2126 boardHeight + 2*(lineGap + squareSize));
2127 } else if (appData.showJail == 2) {
2129 XtSetArg(boardArgs[1], XtNwidth,
2130 boardWidth + 2*(lineGap + squareSize));
2131 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2134 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2135 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2139 * Determine what fonts to use.
2141 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2142 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2143 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2144 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2145 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2146 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2147 appData.font = FindFont(appData.font, fontPxlSize);
2148 countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
2149 countFontStruct = XQueryFont(xDisplay, countFontID);
2150 // appData.font = FindFont(appData.font, fontPxlSize);
2152 xdb = XtDatabase(xDisplay);
2153 XrmPutStringResource(&xdb, "*font", appData.font);
2156 * Detect if there are not enough colors available and adapt.
2158 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2159 appData.monoMode = True;
2162 forceMono = MakeColors();
2165 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2167 appData.monoMode = True;
2170 if (appData.lowTimeWarning && !appData.monoMode) {
2171 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2172 vFrom.size = strlen(appData.lowTimeWarningColor);
2173 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2174 if (vTo.addr == NULL)
2175 appData.monoMode = True;
2177 lowTimeWarningColor = *(Pixel *) vTo.addr;
2180 if (appData.monoMode && appData.debugMode) {
2181 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2182 (unsigned long) XWhitePixel(xDisplay, xScreen),
2183 (unsigned long) XBlackPixel(xDisplay, xScreen));
2186 ParseIcsTextColors();
2187 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2188 textColors[ColorNone].attr = 0;
2190 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2196 layoutName = "tinyLayout";
2197 } else if (smallLayout) {
2198 layoutName = "smallLayout";
2200 layoutName = "normalLayout";
2202 /* Outer layoutWidget is there only to provide a name for use in
2203 resources that depend on the layout style */
2205 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2206 layoutArgs, XtNumber(layoutArgs));
2208 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2209 formArgs, XtNumber(formArgs));
2210 XtSetArg(args[0], XtNdefaultDistance, &sep);
2211 XtGetValues(formWidget, args, 1);
2214 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2215 XtSetArg(args[0], XtNtop, XtChainTop);
2216 XtSetArg(args[1], XtNbottom, XtChainTop);
2217 XtSetArg(args[2], XtNright, XtChainLeft);
2218 XtSetValues(menuBarWidget, args, 3);
2220 widgetList[j++] = whiteTimerWidget =
2221 XtCreateWidget("whiteTime", labelWidgetClass,
2222 formWidget, timerArgs, XtNumber(timerArgs));
2223 XtSetArg(args[0], XtNfont, clockFontStruct);
2224 XtSetArg(args[1], XtNtop, XtChainTop);
2225 XtSetArg(args[2], XtNbottom, XtChainTop);
2226 XtSetValues(whiteTimerWidget, args, 3);
2228 widgetList[j++] = blackTimerWidget =
2229 XtCreateWidget("blackTime", labelWidgetClass,
2230 formWidget, timerArgs, XtNumber(timerArgs));
2231 XtSetArg(args[0], XtNfont, clockFontStruct);
2232 XtSetArg(args[1], XtNtop, XtChainTop);
2233 XtSetArg(args[2], XtNbottom, XtChainTop);
2234 XtSetValues(blackTimerWidget, args, 3);
2236 if (appData.titleInWindow) {
2237 widgetList[j++] = titleWidget =
2238 XtCreateWidget("title", labelWidgetClass, formWidget,
2239 titleArgs, XtNumber(titleArgs));
2240 XtSetArg(args[0], XtNtop, XtChainTop);
2241 XtSetArg(args[1], XtNbottom, XtChainTop);
2242 XtSetValues(titleWidget, args, 2);
2245 if (appData.showButtonBar) {
2246 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2247 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2248 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2249 XtSetArg(args[2], XtNtop, XtChainTop);
2250 XtSetArg(args[3], XtNbottom, XtChainTop);
2251 XtSetValues(buttonBarWidget, args, 4);
2254 widgetList[j++] = messageWidget =
2255 XtCreateWidget("message", labelWidgetClass, formWidget,
2256 messageArgs, XtNumber(messageArgs));
2257 XtSetArg(args[0], XtNtop, XtChainTop);
2258 XtSetArg(args[1], XtNbottom, XtChainTop);
2259 XtSetValues(messageWidget, args, 2);
2261 widgetList[j++] = boardWidget =
2262 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2263 XtNumber(boardArgs));
2265 XtManageChildren(widgetList, j);
2267 timerWidth = (boardWidth - sep) / 2;
2268 XtSetArg(args[0], XtNwidth, timerWidth);
2269 XtSetValues(whiteTimerWidget, args, 1);
2270 XtSetValues(blackTimerWidget, args, 1);
2272 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2273 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2274 XtGetValues(whiteTimerWidget, args, 2);
2276 if (appData.showButtonBar) {
2277 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2278 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2279 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2283 * formWidget uses these constraints but they are stored
2287 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2288 XtSetValues(menuBarWidget, args, i);
2289 if (appData.titleInWindow) {
2292 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2293 XtSetValues(whiteTimerWidget, args, i);
2295 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2296 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2297 XtSetValues(blackTimerWidget, args, i);
2299 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2300 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2301 XtSetValues(titleWidget, args, i);
2303 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2304 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2305 XtSetValues(messageWidget, args, i);
2306 if (appData.showButtonBar) {
2308 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2309 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2310 XtSetValues(buttonBarWidget, args, i);
2314 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2315 XtSetValues(whiteTimerWidget, args, i);
2317 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2318 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2319 XtSetValues(blackTimerWidget, args, i);
2321 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2322 XtSetValues(titleWidget, args, i);
2324 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2325 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2326 XtSetValues(messageWidget, args, i);
2327 if (appData.showButtonBar) {
2329 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2330 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2331 XtSetValues(buttonBarWidget, args, i);
2336 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2337 XtSetValues(whiteTimerWidget, args, i);
2339 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2340 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2341 XtSetValues(blackTimerWidget, args, i);
2343 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2344 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2345 XtSetValues(messageWidget, args, i);
2346 if (appData.showButtonBar) {
2348 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2349 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2350 XtSetValues(buttonBarWidget, args, i);
2354 XtSetArg(args[0], XtNfromVert, messageWidget);
2355 XtSetArg(args[1], XtNtop, XtChainTop);
2356 XtSetArg(args[2], XtNbottom, XtChainBottom);
2357 XtSetArg(args[3], XtNleft, XtChainLeft);
2358 XtSetArg(args[4], XtNright, XtChainRight);
2359 XtSetValues(boardWidget, args, 5);
2361 XtRealizeWidget(shellWidget);
2364 XtSetArg(args[0], XtNx, wpMain.x);
2365 XtSetArg(args[1], XtNy, wpMain.y);
2366 XtSetValues(shellWidget, args, 2);
2370 * Correct the width of the message and title widgets.
2371 * It is not known why some systems need the extra fudge term.
2372 * The value "2" is probably larger than needed.
2374 XawFormDoLayout(formWidget, False);
2376 #define WIDTH_FUDGE 2
2378 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2379 XtSetArg(args[i], XtNheight, &h); i++;
2380 XtGetValues(messageWidget, args, i);
2381 if (appData.showButtonBar) {
2383 XtSetArg(args[i], XtNwidth, &w); i++;
2384 XtGetValues(buttonBarWidget, args, i);
2385 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2387 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2390 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2391 if (gres != XtGeometryYes && appData.debugMode) {
2392 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2393 programName, gres, w, h, wr, hr);
2396 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2397 /* The size used for the child widget in layout lags one resize behind
2398 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2400 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2401 if (gres != XtGeometryYes && appData.debugMode) {
2402 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2403 programName, gres, w, h, wr, hr);
2406 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2407 XtSetArg(args[1], XtNright, XtChainRight);
2408 XtSetValues(messageWidget, args, 2);
2410 if (appData.titleInWindow) {
2412 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2413 XtSetArg(args[i], XtNheight, &h); i++;
2414 XtGetValues(titleWidget, args, i);
2416 w = boardWidth - 2*bor;
2418 XtSetArg(args[0], XtNwidth, &w);
2419 XtGetValues(menuBarWidget, args, 1);
2420 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2423 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2424 if (gres != XtGeometryYes && appData.debugMode) {
2426 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2427 programName, gres, w, h, wr, hr);
2430 XawFormDoLayout(formWidget, True);
2432 xBoardWindow = XtWindow(boardWidget);
2434 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2435 // not need to go into InitDrawingSizes().
2439 * Create X checkmark bitmap and initialize option menu checks.
2441 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2442 checkmark_bits, checkmark_width, checkmark_height);
2443 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2444 #ifndef OPTIONSDIALOG
2445 if (appData.alwaysPromoteToQueen) {
2446 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2449 if (appData.animateDragging) {
2450 XtSetValues(XtNameToWidget(menuBarWidget,
2451 "menuOptions.Animate Dragging"),
2454 if (appData.animate) {
2455 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2458 if (appData.autoCallFlag) {
2459 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2462 if (appData.autoFlipView) {
2463 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2466 if (appData.blindfold) {
2467 XtSetValues(XtNameToWidget(menuBarWidget,
2468 "menuOptions.Blindfold"), args, 1);
2470 if (appData.flashCount > 0) {
2471 XtSetValues(XtNameToWidget(menuBarWidget,
2472 "menuOptions.Flash Moves"),
2476 if (appData.highlightDragging) {
2477 XtSetValues(XtNameToWidget(menuBarWidget,
2478 "menuOptions.Highlight Dragging"),
2482 if (appData.highlightLastMove) {
2483 XtSetValues(XtNameToWidget(menuBarWidget,
2484 "menuOptions.Highlight Last Move"),
2487 if (appData.highlightMoveWithArrow) {
2488 XtSetValues(XtNameToWidget(menuBarWidget,
2489 "menuOptions.Arrow"),
2492 // if (appData.icsAlarm) {
2493 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2496 if (appData.ringBellAfterMoves) {
2497 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2500 if (appData.oneClick) {
2501 XtSetValues(XtNameToWidget(menuBarWidget,
2502 "menuOptions.OneClick"), args, 1);
2504 if (appData.periodicUpdates) {
2505 XtSetValues(XtNameToWidget(menuBarWidget,
2506 "menuOptions.Periodic Updates"), args, 1);
2508 if (appData.ponderNextMove) {
2509 XtSetValues(XtNameToWidget(menuBarWidget,
2510 "menuOptions.Ponder Next Move"), args, 1);
2512 if (appData.popupExitMessage) {
2513 XtSetValues(XtNameToWidget(menuBarWidget,
2514 "menuOptions.Popup Exit Message"), args, 1);
2516 if (appData.popupMoveErrors) {
2517 XtSetValues(XtNameToWidget(menuBarWidget,
2518 "menuOptions.Popup Move Errors"), args, 1);
2520 // if (appData.premove) {
2521 // XtSetValues(XtNameToWidget(menuBarWidget,
2522 // "menuOptions.Premove"), args, 1);
2524 if (appData.showCoords) {
2525 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2528 if (appData.hideThinkingFromHuman) {
2529 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2532 if (appData.testLegality) {
2533 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2537 if (saveSettingsOnExit) {
2538 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2545 ReadBitmap(&wIconPixmap, "icon_white.bm",
2546 icon_white_bits, icon_white_width, icon_white_height);
2547 ReadBitmap(&bIconPixmap, "icon_black.bm",
2548 icon_black_bits, icon_black_width, icon_black_height);
2549 iconPixmap = wIconPixmap;
2551 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2552 XtSetValues(shellWidget, args, i);
2555 * Create a cursor for the board widget.
2557 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2558 XChangeWindowAttributes(xDisplay, xBoardWindow,
2559 CWCursor, &window_attributes);
2562 * Inhibit shell resizing.
2564 shellArgs[0].value = (XtArgVal) &w;
2565 shellArgs[1].value = (XtArgVal) &h;
2566 XtGetValues(shellWidget, shellArgs, 2);
2567 shellArgs[4].value = shellArgs[2].value = w;
2568 shellArgs[5].value = shellArgs[3].value = h;
2569 XtSetValues(shellWidget, &shellArgs[2], 4);
2570 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2571 marginH = h - boardHeight;
2573 CatchDeleteWindow(shellWidget, "QuitProc");
2581 if (appData.animate || appData.animateDragging)
2584 XtAugmentTranslations(formWidget,
2585 XtParseTranslationTable(globalTranslations));
2586 XtAugmentTranslations(boardWidget,
2587 XtParseTranslationTable(boardTranslations));
2588 XtAugmentTranslations(whiteTimerWidget,
2589 XtParseTranslationTable(whiteTranslations));
2590 XtAugmentTranslations(blackTimerWidget,
2591 XtParseTranslationTable(blackTranslations));
2593 /* Why is the following needed on some versions of X instead
2594 * of a translation? */
2595 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2596 (XtEventHandler) EventProc, NULL);
2598 XtAddEventHandler(formWidget, KeyPressMask, False,
2599 (XtEventHandler) MoveTypeInProc, NULL);
2601 /* [AS] Restore layout */
2602 if( wpMoveHistory.visible ) {
2606 if( wpEvalGraph.visible )
2611 if( wpEngineOutput.visible ) {
2612 EngineOutputPopUp();
2617 if (errorExitStatus == -1) {
2618 if (appData.icsActive) {
2619 /* We now wait until we see "login:" from the ICS before
2620 sending the logon script (problems with timestamp otherwise) */
2621 /*ICSInitScript();*/
2622 if (appData.icsInputBox) ICSInputBoxPopUp();
2626 signal(SIGWINCH, TermSizeSigHandler);
2628 signal(SIGINT, IntSigHandler);
2629 signal(SIGTERM, IntSigHandler);
2630 if (*appData.cmailGameName != NULLCHAR) {
2631 signal(SIGUSR1, CmailSigHandler);
2634 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2636 // XtSetKeyboardFocus(shellWidget, formWidget);
2637 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2639 XtAppMainLoop(appContext);
2640 if (appData.debugMode) fclose(debugFP); // [DM] debug
2647 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2648 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2650 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2651 unlink(gameCopyFilename);
2652 unlink(gamePasteFilename);
2655 RETSIGTYPE TermSizeSigHandler(int sig)
2668 CmailSigHandler(sig)
2674 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2676 /* Activate call-back function CmailSigHandlerCallBack() */
2677 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2679 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2683 CmailSigHandlerCallBack(isr, closure, message, count, error)
2691 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2693 /**** end signal code ****/
2699 /* try to open the icsLogon script, either in the location given
2700 * or in the users HOME directory
2707 f = fopen(appData.icsLogon, "r");
2710 homedir = getenv("HOME");
2711 if (homedir != NULL)
2713 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2714 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2715 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2716 f = fopen(buf, "r");
2721 ProcessICSInitScript(f);
2723 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2746 if (!menuBarWidget) return;
2747 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2749 DisplayError("menuEdit.Revert", 0);
2751 XtSetSensitive(w, !grey);
2753 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2755 DisplayError("menuEdit.Annotate", 0);
2757 XtSetSensitive(w, !grey);
2762 SetMenuEnables(enab)
2766 if (!menuBarWidget) return;
2767 while (enab->name != NULL) {
2768 w = XtNameToWidget(menuBarWidget, enab->name);
2770 DisplayError(enab->name, 0);
2772 XtSetSensitive(w, enab->value);
2778 Enables icsEnables[] = {
2779 { "menuFile.Mail Move", False },
2780 { "menuFile.Reload CMail Message", False },
2781 { "menuMode.Machine Black", False },
2782 { "menuMode.Machine White", False },
2783 { "menuMode.Analysis Mode", False },
2784 { "menuMode.Analyze File", False },
2785 { "menuMode.Two Machines", False },
2786 { "menuMode.Machine Match", False },
2788 { "menuEngine.Hint", False },
2789 { "menuEngine.Book", False },
2790 { "menuEngine.Move Now", False },
2791 #ifndef OPTIONSDIALOG
2792 { "menuOptions.Periodic Updates", False },
2793 { "menuOptions.Hide Thinking", False },
2794 { "menuOptions.Ponder Next Move", False },
2797 { "menuEngine.Engine #1 Settings", False },
2798 { "menuEngine.Engine #2 Settings", False },
2799 { "menuEngine.Load Engine", False },
2800 { "menuEdit.Annotate", False },
2801 { "menuOptions.Match", False },
2805 Enables ncpEnables[] = {
2806 { "menuFile.Mail Move", False },
2807 { "menuFile.Reload CMail Message", False },
2808 { "menuMode.Machine White", False },
2809 { "menuMode.Machine Black", False },
2810 { "menuMode.Analysis Mode", False },
2811 { "menuMode.Analyze File", False },
2812 { "menuMode.Two Machines", False },
2813 { "menuMode.Machine Match", False },
2814 { "menuMode.ICS Client", False },
2815 { "menuView.ICStex", False },
2816 { "menuView.ICS Input Box", False },
2817 { "Action", False },
2818 { "menuEdit.Revert", False },
2819 { "menuEdit.Annotate", False },
2820 { "menuEngine.Engine #1 Settings", False },
2821 { "menuEngine.Engine #2 Settings", False },
2822 { "menuEngine.Move Now", False },
2823 { "menuEngine.Retract Move", False },
2824 { "menuOptions.ICS", False },
2825 #ifndef OPTIONSDIALOG
2826 { "menuOptions.Auto Flag", False },
2827 { "menuOptions.Auto Flip View", False },
2828 // { "menuOptions.ICS Alarm", False },
2829 { "menuOptions.Move Sound", False },
2830 { "menuOptions.Hide Thinking", False },
2831 { "menuOptions.Periodic Updates", False },
2832 { "menuOptions.Ponder Next Move", False },
2834 { "menuEngine.Hint", False },
2835 { "menuEngine.Book", False },
2839 Enables gnuEnables[] = {
2840 { "menuMode.ICS Client", False },
2841 { "menuView.ICStex", False },
2842 { "menuView.ICS Input Box", False },
2843 { "menuAction.Accept", False },
2844 { "menuAction.Decline", False },
2845 { "menuAction.Rematch", False },
2846 { "menuAction.Adjourn", False },
2847 { "menuAction.Stop Examining", False },
2848 { "menuAction.Stop Observing", False },
2849 { "menuAction.Upload to Examine", False },
2850 { "menuEdit.Revert", False },
2851 { "menuEdit.Annotate", False },
2852 { "menuOptions.ICS", False },
2854 /* The next two options rely on SetCmailMode being called *after* */
2855 /* SetGNUMode so that when GNU is being used to give hints these */
2856 /* menu options are still available */
2858 { "menuFile.Mail Move", False },
2859 { "menuFile.Reload CMail Message", False },
2860 // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2861 { "menuMode.Machine White", True },
2862 { "menuMode.Machine Black", True },
2863 { "menuMode.Analysis Mode", True },
2864 { "menuMode.Analyze File", True },
2865 { "menuMode.Two Machines", True },
2866 { "menuMode.Machine Match", True },
2867 { "menuEngine.Engine #1 Settings", True },
2868 { "menuEngine.Engine #2 Settings", True },
2869 { "menuEngine.Hint", True },
2870 { "menuEngine.Book", True },
2871 { "menuEngine.Move Now", True },
2872 { "menuEngine.Retract Move", True },
2877 Enables cmailEnables[] = {
2879 { "menuAction.Call Flag", False },
2880 { "menuAction.Draw", True },
2881 { "menuAction.Adjourn", False },
2882 { "menuAction.Abort", False },
2883 { "menuAction.Stop Observing", False },
2884 { "menuAction.Stop Examining", False },
2885 { "menuFile.Mail Move", True },
2886 { "menuFile.Reload CMail Message", True },
2890 Enables trainingOnEnables[] = {
2891 { "menuMode.Edit Comment", False },
2892 { "menuMode.Pause", False },
2893 { "menuEdit.Forward", False },
2894 { "menuEdit.Backward", False },
2895 { "menuEdit.Forward to End", False },
2896 { "menuEdit.Back to Start", False },
2897 { "menuEngine.Move Now", False },
2898 { "menuEdit.Truncate Game", False },
2902 Enables trainingOffEnables[] = {
2903 { "menuMode.Edit Comment", True },
2904 { "menuMode.Pause", True },
2905 { "menuEdit.Forward", True },
2906 { "menuEdit.Backward", True },
2907 { "menuEdit.Forward to End", True },
2908 { "menuEdit.Back to Start", True },
2909 { "menuEngine.Move Now", True },
2910 { "menuEdit.Truncate Game", True },
2914 Enables machineThinkingEnables[] = {
2915 { "menuFile.Load Game", False },
2916 // { "menuFile.Load Next Game", False },
2917 // { "menuFile.Load Previous Game", False },
2918 // { "menuFile.Reload Same Game", False },
2919 { "menuEdit.Paste Game", False },
2920 { "menuFile.Load Position", False },
2921 // { "menuFile.Load Next Position", False },
2922 // { "menuFile.Load Previous Position", False },
2923 // { "menuFile.Reload Same Position", False },
2924 { "menuEdit.Paste Position", False },
2925 { "menuMode.Machine White", False },
2926 { "menuMode.Machine Black", False },
2927 { "menuMode.Two Machines", False },
2928 { "menuMode.Machine Match", False },
2929 { "menuEngine.Retract Move", False },
2933 Enables userThinkingEnables[] = {
2934 { "menuFile.Load Game", True },
2935 // { "menuFile.Load Next Game", True },
2936 // { "menuFile.Load Previous Game", True },
2937 // { "menuFile.Reload Same Game", True },
2938 { "menuEdit.Paste Game", True },
2939 { "menuFile.Load Position", True },
2940 // { "menuFile.Load Next Position", True },
2941 // { "menuFile.Load Previous Position", True },
2942 // { "menuFile.Reload Same Position", True },
2943 { "menuEdit.Paste Position", True },
2944 { "menuMode.Machine White", True },
2945 { "menuMode.Machine Black", True },
2946 { "menuMode.Two Machines", True },
2947 { "menuMode.Machine Match", True },
2948 { "menuEngine.Retract Move", True },
2954 SetMenuEnables(icsEnables);
2957 if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
2958 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2959 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
2967 SetMenuEnables(ncpEnables);
2973 SetMenuEnables(gnuEnables);
2979 SetMenuEnables(cmailEnables);
2985 SetMenuEnables(trainingOnEnables);
2986 if (appData.showButtonBar) {
2987 XtSetSensitive(buttonBarWidget, False);
2993 SetTrainingModeOff()
2995 SetMenuEnables(trainingOffEnables);
2996 if (appData.showButtonBar) {
2997 XtSetSensitive(buttonBarWidget, True);
3002 SetUserThinkingEnables()
3004 if (appData.noChessProgram) return;
3005 SetMenuEnables(userThinkingEnables);
3009 SetMachineThinkingEnables()
3011 if (appData.noChessProgram) return;
3012 SetMenuEnables(machineThinkingEnables);
3014 case MachinePlaysBlack:
3015 case MachinePlaysWhite:
3016 case TwoMachinesPlay:
3017 XtSetSensitive(XtNameToWidget(menuBarWidget,
3018 ModeToWidgetName(gameMode)), True);
3025 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3026 #define HISTORY_SIZE 64
3027 static char *history[HISTORY_SIZE];
3028 int histIn = 0, histP = 0;
3031 SaveInHistory(char *cmd)
3033 if (history[histIn] != NULL) {
3034 free(history[histIn]);
3035 history[histIn] = NULL;
3037 if (*cmd == NULLCHAR) return;
3038 history[histIn] = StrSave(cmd);
3039 histIn = (histIn + 1) % HISTORY_SIZE;
3040 if (history[histIn] != NULL) {
3041 free(history[histIn]);
3042 history[histIn] = NULL;
3048 PrevInHistory(char *cmd)
3051 if (histP == histIn) {
3052 if (history[histIn] != NULL) free(history[histIn]);
3053 history[histIn] = StrSave(cmd);
3055 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3056 if (newhp == histIn || history[newhp] == NULL) return NULL;
3058 return history[histP];
3064 if (histP == histIn) return NULL;
3065 histP = (histP + 1) % HISTORY_SIZE;
3066 return history[histP];
3068 // end of borrowed code
3070 #define Abs(n) ((n)<0 ? -(n) : (n))
3073 * Find a font that matches "pattern" that is as close as
3074 * possible to the targetPxlSize. Prefer fonts that are k
3075 * pixels smaller to fonts that are k pixels larger. The
3076 * pattern must be in the X Consortium standard format,
3077 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3078 * The return value should be freed with XtFree when no
3082 FindFont(pattern, targetPxlSize)
3086 char **fonts, *p, *best, *scalable, *scalableTail;
3087 int i, j, nfonts, minerr, err, pxlSize;
3090 char **missing_list;
3092 char *def_string, *base_fnt_lst, strInt[3];
3094 XFontStruct **fnt_list;
3095 base_fnt_lst = calloc(1, strlen(pattern) + 3);
3096 snprintf(strInt, sizeof(strInt)/sizeof(strInt[0]), "%d", targetPxlSize);
3097 p = strstr(pattern, "--");
3098 strncpy(base_fnt_lst, pattern, p - pattern + 2);
3099 strcat(base_fnt_lst, strInt);
3100 strcat(base_fnt_lst, strchr(p + 2, '-'));
3102 if ((fntSet = XCreateFontSet(xDisplay,
3106 &def_string)) == NULL) {
3108 fprintf(stderr, _("Unable to create font set.\n"));
3112 nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
3114 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3116 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3117 programName, pattern);
3125 for (i=0; i<nfonts; i++) {
3128 if (*p != '-') continue;
3130 if (*p == NULLCHAR) break;
3131 if (*p++ == '-') j++;
3133 if (j < 7) continue;
3136 scalable = fonts[i];
3139 err = pxlSize - targetPxlSize;
3140 if (Abs(err) < Abs(minerr) ||
3141 (minerr > 0 && err < 0 && -err == minerr)) {
3147 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3148 /* If the error is too big and there is a scalable font,
3149 use the scalable font. */
3150 int headlen = scalableTail - scalable;
3151 p = (char *) XtMalloc(strlen(scalable) + 10);
3152 while (isdigit(*scalableTail)) scalableTail++;
3153 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3155 p = (char *) XtMalloc(strlen(best) + 2);
3156 safeStrCpy(p, best, strlen(best)+1 );
3158 if (appData.debugMode) {
3159 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3160 pattern, targetPxlSize, p);
3163 if (missing_count > 0)
3164 XFreeStringList(missing_list);
3165 XFreeFontSet(xDisplay, fntSet);
3167 XFreeFontNames(fonts);
3173 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3174 // must be called before all non-first callse to CreateGCs()
3175 XtReleaseGC(shellWidget, highlineGC);
3176 XtReleaseGC(shellWidget, lightSquareGC);
3177 XtReleaseGC(shellWidget, darkSquareGC);
3178 XtReleaseGC(shellWidget, lineGC);
3179 if (appData.monoMode) {
3180 if (DefaultDepth(xDisplay, xScreen) == 1) {
3181 XtReleaseGC(shellWidget, wbPieceGC);
3183 XtReleaseGC(shellWidget, bwPieceGC);
3186 XtReleaseGC(shellWidget, prelineGC);
3187 XtReleaseGC(shellWidget, jailSquareGC);
3188 XtReleaseGC(shellWidget, wdPieceGC);
3189 XtReleaseGC(shellWidget, wlPieceGC);
3190 XtReleaseGC(shellWidget, wjPieceGC);
3191 XtReleaseGC(shellWidget, bdPieceGC);
3192 XtReleaseGC(shellWidget, blPieceGC);
3193 XtReleaseGC(shellWidget, bjPieceGC);
3197 void CreateGCs(int redo)
3199 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3200 | GCBackground | GCFunction | GCPlaneMask;
3201 XGCValues gc_values;
3204 gc_values.plane_mask = AllPlanes;
3205 gc_values.line_width = lineGap;
3206 gc_values.line_style = LineSolid;
3207 gc_values.function = GXcopy;
3210 DeleteGCs(); // called a second time; clean up old GCs first
3211 } else { // [HGM] grid and font GCs created on first call only
3212 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3213 gc_values.background = XWhitePixel(xDisplay, xScreen);
3214 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3215 XSetFont(xDisplay, coordGC, coordFontID);
3217 // [HGM] make font for holdings counts (white on black)
3218 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3219 gc_values.background = XBlackPixel(xDisplay, xScreen);
3220 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3221 XSetFont(xDisplay, countGC, countFontID);
3223 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3224 gc_values.background = XBlackPixel(xDisplay, xScreen);
3225 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3227 if (appData.monoMode) {
3228 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3229 gc_values.background = XWhitePixel(xDisplay, xScreen);
3230 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3232 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3233 gc_values.background = XBlackPixel(xDisplay, xScreen);
3234 lightSquareGC = wbPieceGC
3235 = XtGetGC(shellWidget, value_mask, &gc_values);
3237 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3238 gc_values.background = XWhitePixel(xDisplay, xScreen);
3239 darkSquareGC = bwPieceGC
3240 = XtGetGC(shellWidget, value_mask, &gc_values);
3242 if (DefaultDepth(xDisplay, xScreen) == 1) {
3243 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3244 gc_values.function = GXcopyInverted;
3245 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3246 gc_values.function = GXcopy;
3247 if (XBlackPixel(xDisplay, xScreen) == 1) {
3248 bwPieceGC = darkSquareGC;
3249 wbPieceGC = copyInvertedGC;
3251 bwPieceGC = copyInvertedGC;
3252 wbPieceGC = lightSquareGC;
3256 gc_values.foreground = highlightSquareColor;
3257 gc_values.background = highlightSquareColor;
3258 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3260 gc_values.foreground = premoveHighlightColor;
3261 gc_values.background = premoveHighlightColor;
3262 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3264 gc_values.foreground = lightSquareColor;
3265 gc_values.background = darkSquareColor;
3266 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3268 gc_values.foreground = darkSquareColor;
3269 gc_values.background = lightSquareColor;
3270 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3272 gc_values.foreground = jailSquareColor;
3273 gc_values.background = jailSquareColor;
3274 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3276 gc_values.foreground = whitePieceColor;
3277 gc_values.background = darkSquareColor;
3278 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3280 gc_values.foreground = whitePieceColor;
3281 gc_values.background = lightSquareColor;
3282 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3284 gc_values.foreground = whitePieceColor;
3285 gc_values.background = jailSquareColor;
3286 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3288 gc_values.foreground = blackPieceColor;
3289 gc_values.background = darkSquareColor;
3290 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3292 gc_values.foreground = blackPieceColor;
3293 gc_values.background = lightSquareColor;
3294 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3296 gc_values.foreground = blackPieceColor;
3297 gc_values.background = jailSquareColor;
3298 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3302 void loadXIM(xim, xmask, filename, dest, mask)
3315 fp = fopen(filename, "rb");
3317 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3324 for (y=0; y<h; ++y) {
3325 for (x=0; x<h; ++x) {
3330 XPutPixel(xim, x, y, blackPieceColor);
3332 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3335 XPutPixel(xim, x, y, darkSquareColor);
3337 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3340 XPutPixel(xim, x, y, whitePieceColor);
3342 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3345 XPutPixel(xim, x, y, lightSquareColor);
3347 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3355 /* create Pixmap of piece */
3356 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3358 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3361 /* create Pixmap of clipmask
3362 Note: We assume the white/black pieces have the same
3363 outline, so we make only 6 masks. This is okay
3364 since the XPM clipmask routines do the same. */
3366 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3368 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3371 /* now create the 1-bit version */
3372 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3375 values.foreground = 1;
3376 values.background = 0;
3378 /* Don't use XtGetGC, not read only */
3379 maskGC = XCreateGC(xDisplay, *mask,
3380 GCForeground | GCBackground, &values);
3381 XCopyPlane(xDisplay, temp, *mask, maskGC,
3382 0, 0, squareSize, squareSize, 0, 0, 1);
3383 XFreePixmap(xDisplay, temp);
3388 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3390 void CreateXIMPieces()
3395 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3400 /* The XSynchronize calls were copied from CreatePieces.
3401 Not sure if needed, but can't hurt */
3402 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3405 /* temp needed by loadXIM() */
3406 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3407 0, 0, ss, ss, AllPlanes, XYPixmap);
3409 if (strlen(appData.pixmapDirectory) == 0) {
3413 if (appData.monoMode) {
3414 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3418 fprintf(stderr, _("\nLoading XIMs...\n"));
3420 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3421 fprintf(stderr, "%d", piece+1);
3422 for (kind=0; kind<4; kind++) {
3423 fprintf(stderr, ".");
3424 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3425 ExpandPathName(appData.pixmapDirectory),
3426 piece <= (int) WhiteKing ? "" : "w",
3427 pieceBitmapNames[piece],
3429 ximPieceBitmap[kind][piece] =
3430 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3431 0, 0, ss, ss, AllPlanes, XYPixmap);
3432 if (appData.debugMode)
3433 fprintf(stderr, _("(File:%s:) "), buf);
3434 loadXIM(ximPieceBitmap[kind][piece],
3436 &(xpmPieceBitmap2[kind][piece]),
3437 &(ximMaskPm2[piece]));
3438 if(piece <= (int)WhiteKing)
3439 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3441 fprintf(stderr," ");
3443 /* Load light and dark squares */
3444 /* If the LSQ and DSQ pieces don't exist, we will
3445 draw them with solid squares. */
3446 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3447 if (access(buf, 0) != 0) {
3451 fprintf(stderr, _("light square "));
3453 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3454 0, 0, ss, ss, AllPlanes, XYPixmap);
3455 if (appData.debugMode)
3456 fprintf(stderr, _("(File:%s:) "), buf);
3458 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3459 fprintf(stderr, _("dark square "));
3460 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3461 ExpandPathName(appData.pixmapDirectory), ss);
3462 if (appData.debugMode)
3463 fprintf(stderr, _("(File:%s:) "), buf);
3465 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3466 0, 0, ss, ss, AllPlanes, XYPixmap);
3467 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3468 xpmJailSquare = xpmLightSquare;
3470 fprintf(stderr, _("Done.\n"));
3472 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3475 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3478 void CreateXPMBoard(char *s, int kind)
3482 if(s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3483 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3484 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3488 void FreeXPMPieces()
3489 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3490 // thisroutine has to be called t free the old piece pixmaps
3492 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3493 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3495 XFreePixmap(xDisplay, xpmLightSquare);
3496 XFreePixmap(xDisplay, xpmDarkSquare);
3500 void CreateXPMPieces()
3504 u_int ss = squareSize;
3506 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3507 XpmColorSymbol symbols[4];
3508 static int redo = False;
3510 if(redo) FreeXPMPieces(); else redo = 1;
3512 /* The XSynchronize calls were copied from CreatePieces.
3513 Not sure if needed, but can't hurt */
3514 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3516 /* Setup translations so piece colors match square colors */
3517 symbols[0].name = "light_piece";
3518 symbols[0].value = appData.whitePieceColor;
3519 symbols[1].name = "dark_piece";
3520 symbols[1].value = appData.blackPieceColor;
3521 symbols[2].name = "light_square";
3522 symbols[2].value = appData.lightSquareColor;
3523 symbols[3].name = "dark_square";
3524 symbols[3].value = appData.darkSquareColor;
3526 attr.valuemask = XpmColorSymbols;
3527 attr.colorsymbols = symbols;
3528 attr.numsymbols = 4;
3530 if (appData.monoMode) {
3531 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3535 if (strlen(appData.pixmapDirectory) == 0) {
3536 XpmPieces* pieces = builtInXpms;
3539 while (pieces->size != squareSize && pieces->size) pieces++;
3540 if (!pieces->size) {
3541 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3544 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3545 for (kind=0; kind<4; kind++) {
3547 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3548 pieces->xpm[piece][kind],
3549 &(xpmPieceBitmap2[kind][piece]),
3550 NULL, &attr)) != 0) {
3551 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3555 if(piece <= (int) WhiteKing)
3556 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3560 xpmJailSquare = xpmLightSquare;
3564 fprintf(stderr, _("\nLoading XPMs...\n"));
3567 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3568 fprintf(stderr, "%d ", piece+1);
3569 for (kind=0; kind<4; kind++) {
3570 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3571 ExpandPathName(appData.pixmapDirectory),
3572 piece > (int) WhiteKing ? "w" : "",
3573 pieceBitmapNames[piece],
3575 if (appData.debugMode) {
3576 fprintf(stderr, _("(File:%s:) "), buf);
3578 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3579 &(xpmPieceBitmap2[kind][piece]),
3580 NULL, &attr)) != 0) {
3581 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3582 // [HGM] missing: read of unorthodox piece failed; substitute King.
3583 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3584 ExpandPathName(appData.pixmapDirectory),
3586 if (appData.debugMode) {
3587 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3589 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3590 &(xpmPieceBitmap2[kind][piece]),
3594 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3599 if(piece <= (int) WhiteKing)
3600 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3603 /* Load light and dark squares */
3604 /* If the LSQ and DSQ pieces don't exist, we will
3605 draw them with solid squares. */
3606 fprintf(stderr, _("light square "));
3607 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3608 if (access(buf, 0) != 0) {
3612 if (appData.debugMode)
3613 fprintf(stderr, _("(File:%s:) "), buf);
3615 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3616 &xpmLightSquare, NULL, &attr)) != 0) {
3617 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3620 fprintf(stderr, _("dark square "));
3621 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3622 ExpandPathName(appData.pixmapDirectory), ss);
3623 if (appData.debugMode) {
3624 fprintf(stderr, _("(File:%s:) "), buf);
3626 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3627 &xpmDarkSquare, NULL, &attr)) != 0) {
3628 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3632 xpmJailSquare = xpmLightSquare;
3633 fprintf(stderr, _("Done.\n"));
3635 oldVariant = -1; // kludge to force re-makig of animation masks
3636 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3639 #endif /* HAVE_LIBXPM */
3642 /* No built-in bitmaps */
3647 u_int ss = squareSize;
3649 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3652 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3653 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3654 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3655 pieceBitmapNames[piece],
3656 ss, kind == SOLID ? 's' : 'o');
3657 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3658 if(piece <= (int)WhiteKing)
3659 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3663 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3667 /* With built-in bitmaps */
3670 BuiltInBits* bib = builtInBits;
3673 u_int ss = squareSize;
3675 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3678 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3680 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3681 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3682 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3683 pieceBitmapNames[piece],
3684 ss, kind == SOLID ? 's' : 'o');
3685 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3686 bib->bits[kind][piece], ss, ss);
3687 if(piece <= (int)WhiteKing)
3688 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3692 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3697 void ReadBitmap(pm, name, bits, wreq, hreq)
3700 unsigned char bits[];
3706 char msg[MSG_SIZ], fullname[MSG_SIZ];
3708 if (*appData.bitmapDirectory != NULLCHAR) {
3709 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3710 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3711 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3712 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3713 &w, &h, pm, &x_hot, &y_hot);
3714 fprintf(stderr, "load %s\n", name);
3715 if (errcode != BitmapSuccess) {
3717 case BitmapOpenFailed:
3718 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3720 case BitmapFileInvalid:
3721 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3723 case BitmapNoMemory:
3724 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3728 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3732 fprintf(stderr, _("%s: %s...using built-in\n"),
3734 } else if (w != wreq || h != hreq) {
3736 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3737 programName, fullname, w, h, wreq, hreq);
3743 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3752 if (lineGap == 0) return;
3754 /* [HR] Split this into 2 loops for non-square boards. */
3756 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3757 gridSegments[i].x1 = 0;
3758 gridSegments[i].x2 =
3759 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3760 gridSegments[i].y1 = gridSegments[i].y2
3761 = lineGap / 2 + (i * (squareSize + lineGap));
3764 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3765 gridSegments[j + i].y1 = 0;
3766 gridSegments[j + i].y2 =
3767 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3768 gridSegments[j + i].x1 = gridSegments[j + i].x2
3769 = lineGap / 2 + (j * (squareSize + lineGap));
3773 static void MenuBarSelect(w, addr, index)
3778 XtActionProc proc = (XtActionProc) addr;
3780 (proc)(NULL, NULL, NULL, NULL);
3783 void CreateMenuBarPopup(parent, name, mb)
3793 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3796 XtSetArg(args[j], XtNleftMargin, 20); j++;
3797 XtSetArg(args[j], XtNrightMargin, 20); j++;
3799 while (mi->string != NULL) {
3800 if (strcmp(mi->string, "----") == 0) {
3801 entry = XtCreateManagedWidget(_(mi->string), smeLineObjectClass,
3804 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3805 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3807 XtAddCallback(entry, XtNcallback,
3808 (XtCallbackProc) MenuBarSelect,
3809 (caddr_t) mi->proc);
3815 Widget CreateMenuBar(mb)
3819 Widget anchor, menuBar;
3821 char menuName[MSG_SIZ];
3824 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3825 XtSetArg(args[j], XtNvSpace, 0); j++;
3826 XtSetArg(args[j], XtNborderWidth, 0); j++;
3827 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3828 formWidget, args, j);
3830 while (mb->name != NULL) {
3831 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3832 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3834 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3837 shortName[0] = mb->name[0];
3838 shortName[1] = NULLCHAR;
3839 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3842 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3845 XtSetArg(args[j], XtNborderWidth, 0); j++;
3846 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3848 CreateMenuBarPopup(menuBar, menuName, mb);
3854 Widget CreateButtonBar(mi)
3858 Widget button, buttonBar;
3862 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3864 XtSetArg(args[j], XtNhSpace, 0); j++;
3866 XtSetArg(args[j], XtNborderWidth, 0); j++;
3867 XtSetArg(args[j], XtNvSpace, 0); j++;
3868 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3869 formWidget, args, j);
3871 while (mi->string != NULL) {
3874 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3875 XtSetArg(args[j], XtNborderWidth, 0); j++;
3877 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3878 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3879 buttonBar, args, j);
3880 XtAddCallback(button, XtNcallback,
3881 (XtCallbackProc) MenuBarSelect,
3882 (caddr_t) mi->proc);
3889 CreatePieceMenu(name, color)
3896 ChessSquare selection;
3898 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3899 boardWidget, args, 0);
3901 for (i = 0; i < PIECE_MENU_SIZE; i++) {
3902 String item = pieceMenuStrings[color][i];
3904 if (strcmp(item, "----") == 0) {
3905 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3908 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3909 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3911 selection = pieceMenuTranslation[color][i];
3912 XtAddCallback(entry, XtNcallback,
3913 (XtCallbackProc) PieceMenuSelect,
3914 (caddr_t) selection);
3915 if (selection == WhitePawn || selection == BlackPawn) {
3916 XtSetArg(args[0], XtNpopupOnEntry, entry);
3917 XtSetValues(menu, args, 1);
3930 ChessSquare selection;
3932 whitePieceMenu = CreatePieceMenu("menuW", 0);
3933 blackPieceMenu = CreatePieceMenu("menuB", 1);
3935 XtRegisterGrabAction(PieceMenuPopup, True,
3936 (unsigned)(ButtonPressMask|ButtonReleaseMask),
3937 GrabModeAsync, GrabModeAsync);
3939 XtSetArg(args[0], XtNlabel, _("Drop"));
3940 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3941 boardWidget, args, 1);
3942 for (i = 0; i < DROP_MENU_SIZE; i++) {
3943 String item = dropMenuStrings[i];
3945 if (strcmp(item, "----") == 0) {
3946 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3949 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3950 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3952 selection = dropMenuTranslation[i];
3953 XtAddCallback(entry, XtNcallback,
3954 (XtCallbackProc) DropMenuSelect,
3955 (caddr_t) selection);
3960 void SetupDropMenu()
3968 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3969 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3970 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3971 dmEnables[i].piece);
3972 XtSetSensitive(entry, p != NULL || !appData.testLegality
3973 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3974 && !appData.icsActive));
3976 while (p && *p++ == dmEnables[i].piece) count++;
3977 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3979 XtSetArg(args[j], XtNlabel, label); j++;
3980 XtSetValues(entry, args, j);
3984 void PieceMenuPopup(w, event, params, num_params)
3988 Cardinal *num_params;
3990 String whichMenu; int menuNr = -2;
3991 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3992 if (event->type == ButtonRelease)
3993 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3994 else if (event->type == ButtonPress)
3995 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3997 case 0: whichMenu = params[0]; break;
3998 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
4000 case -1: if (errorUp) ErrorPopDown();
4003 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
4006 static void PieceMenuSelect(w, piece, junk)
4011 if (pmFromX < 0 || pmFromY < 0) return;
4012 EditPositionMenuEvent(piece, pmFromX, pmFromY);
4015 static void DropMenuSelect(w, piece, junk)
4020 if (pmFromX < 0 || pmFromY < 0) return;
4021 DropMenuEvent(piece, pmFromX, pmFromY);
4024 void WhiteClock(w, event, prms, nprms)
4033 void BlackClock(w, event, prms, nprms)
4044 * If the user selects on a border boundary, return -1; if off the board,
4045 * return -2. Otherwise map the event coordinate to the square.
4047 int EventToSquare(x, limit)
4055 if ((x % (squareSize + lineGap)) >= squareSize)
4057 x /= (squareSize + lineGap);
4063 static void do_flash_delay(msec)
4069 static void drawHighlight(file, rank, gc)
4075 if (lineGap == 0) return;
4078 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4079 (squareSize + lineGap);
4080 y = lineGap/2 + rank * (squareSize + lineGap);
4082 x = lineGap/2 + file * (squareSize + lineGap);
4083 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4084 (squareSize + lineGap);
4087 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4088 squareSize+lineGap, squareSize+lineGap);
4091 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4092 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4095 SetHighlights(fromX, fromY, toX, toY)
4096 int fromX, fromY, toX, toY;
4098 if (hi1X != fromX || hi1Y != fromY) {
4099 if (hi1X >= 0 && hi1Y >= 0) {
4100 drawHighlight(hi1X, hi1Y, lineGC);
4102 } // [HGM] first erase both, then draw new!
4103 if (hi2X != toX || hi2Y != toY) {
4104 if (hi2X >= 0 && hi2Y >= 0) {
4105 drawHighlight(hi2X, hi2Y, lineGC);
4108 if (hi1X != fromX || hi1Y != fromY) {
4109 if (fromX >= 0 && fromY >= 0) {
4110 drawHighlight(fromX, fromY, highlineGC);
4113 if (hi2X != toX || hi2Y != toY) {
4114 if (toX >= 0 && toY >= 0) {
4115 drawHighlight(toX, toY, highlineGC);
4127 SetHighlights(-1, -1, -1, -1);
4132 SetPremoveHighlights(fromX, fromY, toX, toY)
4133 int fromX, fromY, toX, toY;
4135 if (pm1X != fromX || pm1Y != fromY) {
4136 if (pm1X >= 0 && pm1Y >= 0) {
4137 drawHighlight(pm1X, pm1Y, lineGC);
4139 if (fromX >= 0 && fromY >= 0) {
4140 drawHighlight(fromX, fromY, prelineGC);
4143 if (pm2X != toX || pm2Y != toY) {
4144 if (pm2X >= 0 && pm2Y >= 0) {
4145 drawHighlight(pm2X, pm2Y, lineGC);
4147 if (toX >= 0 && toY >= 0) {
4148 drawHighlight(toX, toY, prelineGC);
4158 ClearPremoveHighlights()
4160 SetPremoveHighlights(-1, -1, -1, -1);
4163 static int CutOutSquare(x, y, x0, y0, kind)
4164 int x, y, *x0, *y0, kind;
4166 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4167 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4169 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4170 if(textureW[kind] < W*squareSize)
4171 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4173 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4174 if(textureH[kind] < H*squareSize)
4175 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4177 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4181 static void BlankSquare(x, y, color, piece, dest, fac)
4182 int x, y, color, fac;
4185 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4187 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4188 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4189 squareSize, squareSize, x*fac, y*fac);
4191 if (useImages && useImageSqs) {
4195 pm = xpmLightSquare;
4200 case 2: /* neutral */
4205 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4206 squareSize, squareSize, x*fac, y*fac);
4216 case 2: /* neutral */
4221 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4226 I split out the routines to draw a piece so that I could
4227 make a generic flash routine.
4229 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4231 int square_color, x, y;
4234 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4235 switch (square_color) {
4237 case 2: /* neutral */
4239 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4240 ? *pieceToOutline(piece)
4241 : *pieceToSolid(piece),
4242 dest, bwPieceGC, 0, 0,
4243 squareSize, squareSize, x, y);
4246 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4247 ? *pieceToSolid(piece)
4248 : *pieceToOutline(piece),
4249 dest, wbPieceGC, 0, 0,
4250 squareSize, squareSize, x, y);
4255 static void monoDrawPiece(piece, square_color, x, y, dest)
4257 int square_color, x, y;
4260 switch (square_color) {
4262 case 2: /* neutral */
4264 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4265 ? *pieceToOutline(piece)
4266 : *pieceToSolid(piece),
4267 dest, bwPieceGC, 0, 0,
4268 squareSize, squareSize, x, y, 1);
4271 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4272 ? *pieceToSolid(piece)
4273 : *pieceToOutline(piece),
4274 dest, wbPieceGC, 0, 0,
4275 squareSize, squareSize, x, y, 1);
4280 static void colorDrawPiece(piece, square_color, x, y, dest)
4282 int square_color, x, y;
4285 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4286 switch (square_color) {
4288 XCopyPlane(xDisplay, *pieceToSolid(piece),
4289 dest, (int) piece < (int) BlackPawn
4290 ? wlPieceGC : blPieceGC, 0, 0,
4291 squareSize, squareSize, x, y, 1);
4294 XCopyPlane(xDisplay, *pieceToSolid(piece),
4295 dest, (int) piece < (int) BlackPawn
4296 ? wdPieceGC : bdPieceGC, 0, 0,
4297 squareSize, squareSize, x, y, 1);
4299 case 2: /* neutral */
4301 XCopyPlane(xDisplay, *pieceToSolid(piece),
4302 dest, (int) piece < (int) BlackPawn
4303 ? wjPieceGC : bjPieceGC, 0, 0,
4304 squareSize, squareSize, x, y, 1);
4309 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4311 int square_color, x, y;
4314 int kind, p = piece;
4316 switch (square_color) {
4318 case 2: /* neutral */
4320 if ((int)piece < (int) BlackPawn) {
4328 if ((int)piece < (int) BlackPawn) {
4336 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4337 if(useTexture & square_color+1) {
4338 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4339 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4340 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4341 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4342 XSetClipMask(xDisplay, wlPieceGC, None);
4343 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4345 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4346 dest, wlPieceGC, 0, 0,
4347 squareSize, squareSize, x, y);
4350 typedef void (*DrawFunc)();
4352 DrawFunc ChooseDrawFunc()
4354 if (appData.monoMode) {
4355 if (DefaultDepth(xDisplay, xScreen) == 1) {
4356 return monoDrawPiece_1bit;
4358 return monoDrawPiece;
4362 return colorDrawPieceImage;
4364 return colorDrawPiece;
4368 /* [HR] determine square color depending on chess variant. */
4369 static int SquareColor(row, column)
4374 if (gameInfo.variant == VariantXiangqi) {
4375 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4377 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4379 } else if (row <= 4) {
4385 square_color = ((column + row) % 2) == 1;
4388 /* [hgm] holdings: next line makes all holdings squares light */
4389 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4391 return square_color;
4394 void DrawSquare(row, column, piece, do_flash)
4395 int row, column, do_flash;
4398 int square_color, x, y, direction, font_ascent, font_descent;
4401 XCharStruct overall;
4405 /* Calculate delay in milliseconds (2-delays per complete flash) */
4406 flash_delay = 500 / appData.flashRate;
4409 x = lineGap + ((BOARD_WIDTH-1)-column) *
4410 (squareSize + lineGap);
4411 y = lineGap + row * (squareSize + lineGap);
4413 x = lineGap + column * (squareSize + lineGap);
4414 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4415 (squareSize + lineGap);
4418 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4420 square_color = SquareColor(row, column);
4422 if ( // [HGM] holdings: blank out area between board and holdings
4423 column == BOARD_LEFT-1 || column == BOARD_RGHT
4424 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4425 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4426 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4428 // [HGM] print piece counts next to holdings
4429 string[1] = NULLCHAR;
4430 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4431 string[0] = '0' + piece;
4432 XTextExtents(countFontStruct, string, 1, &direction,
4433 &font_ascent, &font_descent, &overall);
4434 if (appData.monoMode) {
4435 XDrawImageString(xDisplay, xBoardWindow, countGC,
4436 x + squareSize - overall.width - 2,
4437 y + font_ascent + 1, string, 1);
4439 XDrawString(xDisplay, xBoardWindow, countGC,
4440 x + squareSize - overall.width - 2,
4441 y + font_ascent + 1, string, 1);
4444 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4445 string[0] = '0' + piece;
4446 XTextExtents(countFontStruct, string, 1, &direction,
4447 &font_ascent, &font_descent, &overall);
4448 if (appData.monoMode) {
4449 XDrawImageString(xDisplay, xBoardWindow, countGC,
4450 x + 2, y + font_ascent + 1, string, 1);
4452 XDrawString(xDisplay, xBoardWindow, countGC,
4453 x + 2, y + font_ascent + 1, string, 1);
4457 if (piece == EmptySquare || appData.blindfold) {
4458 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4460 drawfunc = ChooseDrawFunc();
4462 if (do_flash && appData.flashCount > 0) {
4463 for (i=0; i<appData.flashCount; ++i) {
4464 drawfunc(piece, square_color, x, y, xBoardWindow);
4465 XSync(xDisplay, False);
4466 do_flash_delay(flash_delay);
4468 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4469 XSync(xDisplay, False);
4470 do_flash_delay(flash_delay);
4473 drawfunc(piece, square_color, x, y, xBoardWindow);
4477 string[1] = NULLCHAR;
4478 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4479 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4480 string[0] = 'a' + column - BOARD_LEFT;
4481 XTextExtents(coordFontStruct, string, 1, &direction,
4482 &font_ascent, &font_descent, &overall);
4483 if (appData.monoMode) {
4484 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4485 x + squareSize - overall.width - 2,
4486 y + squareSize - font_descent - 1, string, 1);
4488 XDrawString(xDisplay, xBoardWindow, coordGC,
4489 x + squareSize - overall.width - 2,
4490 y + squareSize - font_descent - 1, string, 1);
4493 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4494 string[0] = ONE + row;
4495 XTextExtents(coordFontStruct, string, 1, &direction,
4496 &font_ascent, &font_descent, &overall);
4497 if (appData.monoMode) {
4498 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4499 x + 2, y + font_ascent + 1, string, 1);
4501 XDrawString(xDisplay, xBoardWindow, coordGC,
4502 x + 2, y + font_ascent + 1, string, 1);
4505 if(!partnerUp && marker[row][column]) {
4506 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4507 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4512 /* Why is this needed on some versions of X? */
4513 void EventProc(widget, unused, event)
4518 if (!XtIsRealized(widget))
4521 switch (event->type) {
4523 if (event->xexpose.count > 0) return; /* no clipping is done */
4524 XDrawPosition(widget, True, NULL);
4525 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4526 flipView = !flipView; partnerUp = !partnerUp;
4527 XDrawPosition(widget, True, NULL);
4528 flipView = !flipView; partnerUp = !partnerUp;
4532 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4539 void DrawPosition(fullRedraw, board)
4540 /*Boolean*/int fullRedraw;
4543 XDrawPosition(boardWidget, fullRedraw, board);
4546 /* Returns 1 if there are "too many" differences between b1 and b2
4547 (i.e. more than 1 move was made) */
4548 static int too_many_diffs(b1, b2)
4554 for (i=0; i<BOARD_HEIGHT; ++i) {
4555 for (j=0; j<BOARD_WIDTH; ++j) {
4556 if (b1[i][j] != b2[i][j]) {
4557 if (++c > 4) /* Castling causes 4 diffs */
4565 /* Matrix describing castling maneuvers */
4566 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4567 static int castling_matrix[4][5] = {
4568 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4569 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4570 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4571 { 7, 7, 4, 5, 6 } /* 0-0, black */
4574 /* Checks whether castling occurred. If it did, *rrow and *rcol
4575 are set to the destination (row,col) of the rook that moved.
4577 Returns 1 if castling occurred, 0 if not.
4579 Note: Only handles a max of 1 castling move, so be sure
4580 to call too_many_diffs() first.
4582 static int check_castle_draw(newb, oldb, rrow, rcol)
4589 /* For each type of castling... */
4590 for (i=0; i<4; ++i) {
4591 r = castling_matrix[i];
4593 /* Check the 4 squares involved in the castling move */
4595 for (j=1; j<=4; ++j) {
4596 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4603 /* All 4 changed, so it must be a castling move */
4612 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4613 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4615 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4618 void DrawSeekBackground( int left, int top, int right, int bottom )
4620 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4623 void DrawSeekText(char *buf, int x, int y)
4625 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4628 void DrawSeekDot(int x, int y, int colorNr)
4630 int square = colorNr & 0x80;
4633 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4635 XFillRectangle(xDisplay, xBoardWindow, color,
4636 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4638 XFillArc(xDisplay, xBoardWindow, color,
4639 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4642 static int damage[2][BOARD_RANKS][BOARD_FILES];
4645 * event handler for redrawing the board
4647 void XDrawPosition(w, repaint, board)
4649 /*Boolean*/int repaint;
4653 static int lastFlipView = 0;
4654 static int lastBoardValid[2] = {0, 0};
4655 static Board lastBoard[2];
4658 int nr = twoBoards*partnerUp;
4660 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4662 if (board == NULL) {
4663 if (!lastBoardValid[nr]) return;
4664 board = lastBoard[nr];
4666 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4667 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4668 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4673 * It would be simpler to clear the window with XClearWindow()
4674 * but this causes a very distracting flicker.
4677 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4679 if ( lineGap && IsDrawArrowEnabled())
4680 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4681 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4683 /* If too much changes (begin observing new game, etc.), don't
4685 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4687 /* Special check for castling so we don't flash both the king
4688 and the rook (just flash the king). */
4690 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4691 /* Draw rook with NO flashing. King will be drawn flashing later */
4692 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4693 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4697 /* First pass -- Draw (newly) empty squares and repair damage.
4698 This prevents you from having a piece show up twice while it
4699 is flashing on its new square */
4700 for (i = 0; i < BOARD_HEIGHT; i++)
4701 for (j = 0; j < BOARD_WIDTH; j++)
4702 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4703 || damage[nr][i][j]) {
4704 DrawSquare(i, j, board[i][j], 0);
4705 damage[nr][i][j] = False;
4708 /* Second pass -- Draw piece(s) in new position and flash them */
4709 for (i = 0; i < BOARD_HEIGHT; i++)
4710 for (j = 0; j < BOARD_WIDTH; j++)
4711 if (board[i][j] != lastBoard[nr][i][j]) {
4712 DrawSquare(i, j, board[i][j], do_flash);
4716 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4717 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4718 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4720 for (i = 0; i < BOARD_HEIGHT; i++)
4721 for (j = 0; j < BOARD_WIDTH; j++) {
4722 DrawSquare(i, j, board[i][j], 0);
4723 damage[nr][i][j] = False;
4727 CopyBoard(lastBoard[nr], board);
4728 lastBoardValid[nr] = 1;
4729 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4730 lastFlipView = flipView;
4732 /* Draw highlights */
4733 if (pm1X >= 0 && pm1Y >= 0) {
4734 drawHighlight(pm1X, pm1Y, prelineGC);
4736 if (pm2X >= 0 && pm2Y >= 0) {
4737 drawHighlight(pm2X, pm2Y, prelineGC);
4739 if (hi1X >= 0 && hi1Y >= 0) {
4740 drawHighlight(hi1X, hi1Y, highlineGC);
4742 if (hi2X >= 0 && hi2Y >= 0) {
4743 drawHighlight(hi2X, hi2Y, highlineGC);
4745 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4747 /* If piece being dragged around board, must redraw that too */
4750 XSync(xDisplay, False);
4755 * event handler for redrawing the board
4757 void DrawPositionProc(w, event, prms, nprms)
4763 XDrawPosition(w, True, NULL);
4768 * event handler for parsing user moves
4770 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4771 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4772 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4773 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4774 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4775 // and at the end FinishMove() to perform the move after optional promotion popups.
4776 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4777 void HandleUserMove(w, event, prms, nprms)
4783 if (w != boardWidget || errorExitStatus != -1) return;
4784 if(nprms) shiftKey = !strcmp(prms[0], "1");
4787 if (event->type == ButtonPress) {
4788 XtPopdown(promotionShell);
4789 XtDestroyWidget(promotionShell);
4790 promotionUp = False;
4798 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4799 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4800 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4803 void AnimateUserMove (Widget w, XEvent * event,
4804 String * params, Cardinal * nParams)
4806 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4807 DragPieceMove(event->xmotion.x, event->xmotion.y);
4810 void HandlePV (Widget w, XEvent * event,
4811 String * params, Cardinal * nParams)
4812 { // [HGM] pv: walk PV
4813 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4816 static int savedIndex; /* gross that this is global */
4818 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4821 XawTextPosition index, dummy;
4824 XawTextGetSelectionPos(w, &index, &dummy);
4825 XtSetArg(arg, XtNstring, &val);
4826 XtGetValues(w, &arg, 1);
4827 ReplaceComment(savedIndex, val);
4828 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4829 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4832 void EditCommentPopUp(index, title, text)
4837 if (text == NULL) text = "";
4838 NewCommentPopup(title, text, index);
4841 void ICSInputBoxPopUp()
4846 extern Option boxOptions[];
4848 void ICSInputSendText()
4855 edit = boxOptions[0].handle;
4857 XtSetArg(args[j], XtNstring, &val); j++;
4858 XtGetValues(edit, args, j);
4860 SendMultiLineToICS(val);
4861 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4862 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4865 void ICSInputBoxPopDown()
4870 void CommentPopUp(title, text)
4873 savedIndex = currentMove; // [HGM] vari
4874 NewCommentPopup(title, text, currentMove);
4877 void CommentPopDown()
4882 void FileNamePopUp(label, def, filter, proc, openMode)
4889 fileProc = proc; /* I can't see a way not */
4890 fileOpenMode = openMode; /* to use globals here */
4891 { // [HGM] use file-selector dialog stolen from Ghostview
4893 int index; // this is not supported yet
4895 if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
4896 (def[0] ? def : NULL), filter, openMode, NULL, &name))
4897 (void) (*fileProc)(f, index=0, name);
4901 void FileNamePopDown()
4903 if (!filenameUp) return;
4904 XtPopdown(fileNameShell);
4905 XtDestroyWidget(fileNameShell);
4910 void FileNameCallback(w, client_data, call_data)
4912 XtPointer client_data, call_data;
4917 XtSetArg(args[0], XtNlabel, &name);
4918 XtGetValues(w, args, 1);
4920 if (strcmp(name, _("cancel")) == 0) {
4925 FileNameAction(w, NULL, NULL, NULL);
4928 void FileNameAction(w, event, prms, nprms)
4940 name = XawDialogGetValueString(w = XtParent(w));
4942 if ((name != NULL) && (*name != NULLCHAR)) {
4943 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
4944 XtPopdown(w = XtParent(XtParent(w)));
4948 p = strrchr(buf, ' ');
4955 fullname = ExpandPathName(buf);
4957 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
4960 f = fopen(fullname, fileOpenMode);
4962 DisplayError(_("Failed to open file"), errno);
4964 (void) (*fileProc)(f, index, buf);
4971 XtPopdown(w = XtParent(XtParent(w)));
4977 void PromotionPopUp()
4980 Widget dialog, layout;
4982 Dimension bw_width, pw_width;
4986 XtSetArg(args[j], XtNwidth, &bw_width); j++;
4987 XtGetValues(boardWidget, args, j);
4990 XtSetArg(args[j], XtNresizable, True); j++;
4991 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
4993 XtCreatePopupShell("Promotion", transientShellWidgetClass,
4994 shellWidget, args, j);
4996 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
4997 layoutArgs, XtNumber(layoutArgs));
5000 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5001 XtSetArg(args[j], XtNborderWidth, 0); j++;
5002 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5005 if(gameInfo.variant != VariantShogi) {
5006 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5007 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback,
5008 (XtPointer) dialog);
5009 XawDialogAddButton(dialog, _("General"), PromotionCallback,
5010 (XtPointer) dialog);
5011 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback,
5012 (XtPointer) dialog);
5013 XawDialogAddButton(dialog, _("Captain"), PromotionCallback,
5014 (XtPointer) dialog);
5016 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
5017 (XtPointer) dialog);
5018 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
5019 (XtPointer) dialog);
5020 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
5021 (XtPointer) dialog);
5022 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
5023 (XtPointer) dialog);
5025 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5026 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
5027 gameInfo.variant == VariantGiveaway) {
5028 XawDialogAddButton(dialog, _("King"), PromotionCallback,
5029 (XtPointer) dialog);
5031 if(gameInfo.variant == VariantCapablanca ||
5032 gameInfo.variant == VariantGothic ||
5033 gameInfo.variant == VariantCapaRandom) {
5034 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5035 (XtPointer) dialog);
5036 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5037 (XtPointer) dialog);
5039 } else // [HGM] shogi
5041 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5042 (XtPointer) dialog);
5043 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5044 (XtPointer) dialog);
5046 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5047 (XtPointer) dialog);
5049 XtRealizeWidget(promotionShell);
5050 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5053 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5054 XtGetValues(promotionShell, args, j);
5056 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5057 lineGap + squareSize/3 +
5058 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5059 0 : 6*(squareSize + lineGap)), &x, &y);
5062 XtSetArg(args[j], XtNx, x); j++;
5063 XtSetArg(args[j], XtNy, y); j++;
5064 XtSetValues(promotionShell, args, j);
5066 XtPopup(promotionShell, XtGrabNone);
5071 void PromotionPopDown()
5073 if (!promotionUp) return;
5074 XtPopdown(promotionShell);
5075 XtDestroyWidget(promotionShell);
5076 promotionUp = False;
5079 void PromotionCallback(w, client_data, call_data)
5081 XtPointer client_data, call_data;
5087 XtSetArg(args[0], XtNlabel, &name);
5088 XtGetValues(w, args, 1);
5092 if (fromX == -1) return;
5094 if (strcmp(name, _("cancel")) == 0) {
5098 } else if (strcmp(name, _("Knight")) == 0) {
5100 } else if (strcmp(name, _("Promote")) == 0) {
5102 } else if (strcmp(name, _("Defer")) == 0) {
5105 promoChar = ToLower(name[0]);
5108 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5110 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5111 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5116 void ErrorCallback(w, client_data, call_data)
5118 XtPointer client_data, call_data;
5121 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5123 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5129 if (!errorUp) return;
5131 XtPopdown(errorShell);
5132 XtDestroyWidget(errorShell);
5133 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5136 void ErrorPopUp(title, label, modal)
5137 char *title, *label;
5141 Widget dialog, layout;
5145 Dimension bw_width, pw_width;
5146 Dimension pw_height;
5150 XtSetArg(args[i], XtNresizable, True); i++;
5151 XtSetArg(args[i], XtNtitle, title); i++;
5153 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5154 shellWidget, args, i);
5156 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5157 layoutArgs, XtNumber(layoutArgs));
5160 XtSetArg(args[i], XtNlabel, label); i++;
5161 XtSetArg(args[i], XtNborderWidth, 0); i++;
5162 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5165 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5167 XtRealizeWidget(errorShell);
5168 CatchDeleteWindow(errorShell, "ErrorPopDown");
5171 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5172 XtGetValues(boardWidget, args, i);
5174 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5175 XtSetArg(args[i], XtNheight, &pw_height); i++;
5176 XtGetValues(errorShell, args, i);
5179 /* This code seems to tickle an X bug if it is executed too soon
5180 after xboard starts up. The coordinates get transformed as if
5181 the main window was positioned at (0, 0).
5183 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5184 0 - pw_height + squareSize / 3, &x, &y);
5186 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5187 RootWindowOfScreen(XtScreen(boardWidget)),
5188 (bw_width - pw_width) / 2,
5189 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5193 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5196 XtSetArg(args[i], XtNx, x); i++;
5197 XtSetArg(args[i], XtNy, y); i++;
5198 XtSetValues(errorShell, args, i);
5201 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5204 /* Disable all user input other than deleting the window */
5205 static int frozen = 0;
5209 /* Grab by a widget that doesn't accept input */
5210 XtAddGrab(messageWidget, TRUE, FALSE);
5214 /* Undo a FreezeUI */
5217 if (!frozen) return;
5218 XtRemoveGrab(messageWidget);
5222 char *ModeToWidgetName(mode)
5226 case BeginningOfGame:
5227 if (appData.icsActive)
5228 return "menuMode.ICS Client";
5229 else if (appData.noChessProgram ||
5230 *appData.cmailGameName != NULLCHAR)
5231 return "menuMode.Edit Game";
5233 return "menuMode.Machine Black";
5234 case MachinePlaysBlack:
5235 return "menuMode.Machine Black";
5236 case MachinePlaysWhite:
5237 return "menuMode.Machine White";
5239 return "menuMode.Analysis Mode";
5241 return "menuMode.Analyze File";
5242 case TwoMachinesPlay:
5243 return "menuMode.Two Machines";
5245 return "menuMode.Edit Game";
5246 case PlayFromGameFile:
5247 return "menuFile.Load Game";
5249 return "menuMode.Edit Position";
5251 return "menuMode.Training";
5252 case IcsPlayingWhite:
5253 case IcsPlayingBlack:
5257 return "menuMode.ICS Client";
5264 void ModeHighlight()
5267 static int oldPausing = FALSE;
5268 static GameMode oldmode = (GameMode) -1;
5271 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5273 if (pausing != oldPausing) {
5274 oldPausing = pausing;
5276 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5278 XtSetArg(args[0], XtNleftBitmap, None);
5280 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5283 if (appData.showButtonBar) {
5284 /* Always toggle, don't set. Previous code messes up when
5285 invoked while the button is pressed, as releasing it
5286 toggles the state again. */
5289 XtSetArg(args[0], XtNbackground, &oldbg);
5290 XtSetArg(args[1], XtNforeground, &oldfg);
5291 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5293 XtSetArg(args[0], XtNbackground, oldfg);
5294 XtSetArg(args[1], XtNforeground, oldbg);
5296 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5300 wname = ModeToWidgetName(oldmode);
5301 if (wname != NULL) {
5302 XtSetArg(args[0], XtNleftBitmap, None);
5303 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5305 wname = ModeToWidgetName(gameMode);
5306 if (wname != NULL) {
5307 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5308 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5312 /* Maybe all the enables should be handled here, not just this one */
5313 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5314 gameMode == Training || gameMode == PlayFromGameFile);
5319 * Button/menu procedures
5321 void ResetProc(w, event, prms, nprms)
5330 int LoadGamePopUp(f, gameNumber, title)
5335 cmailMsgLoaded = FALSE;
5336 if (gameNumber == 0) {
5337 int error = GameListBuild(f);
5339 DisplayError(_("Cannot build game list"), error);
5340 } else if (!ListEmpty(&gameList) &&
5341 ((ListGame *) gameList.tailPred)->number > 1) {
5342 GameListPopUp(f, title);
5348 return LoadGame(f, gameNumber, title, FALSE);
5351 void LoadGameProc(w, event, prms, nprms)
5357 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5360 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5363 void LoadNextGameProc(w, event, prms, nprms)
5372 void LoadPrevGameProc(w, event, prms, nprms)
5381 void ReloadGameProc(w, event, prms, nprms)
5390 void LoadNextPositionProc(w, event, prms, nprms)
5399 void LoadPrevPositionProc(w, event, prms, nprms)
5408 void ReloadPositionProc(w, event, prms, nprms)
5417 void LoadPositionProc(w, event, prms, nprms)
5423 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5426 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5429 void SaveGameProc(w, event, prms, nprms)
5435 FileNamePopUp(_("Save game file name?"),
5436 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5437 appData.oldSaveStyle ? ".game" : ".pgn",
5441 void SavePositionProc(w, event, prms, nprms)
5447 FileNamePopUp(_("Save position file name?"),
5448 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5449 appData.oldSaveStyle ? ".pos" : ".fen",
5453 void ReloadCmailMsgProc(w, event, prms, nprms)
5459 ReloadCmailMsgEvent(FALSE);
5462 void MailMoveProc(w, event, prms, nprms)
5471 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5472 char *selected_fen_position=NULL;
5475 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5476 Atom *type_return, XtPointer *value_return,
5477 unsigned long *length_return, int *format_return)
5479 char *selection_tmp;
5481 if (!selected_fen_position) return False; /* should never happen */
5482 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5483 /* note: since no XtSelectionDoneProc was registered, Xt will
5484 * automatically call XtFree on the value returned. So have to
5485 * make a copy of it allocated with XtMalloc */
5486 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5487 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5489 *value_return=selection_tmp;
5490 *length_return=strlen(selection_tmp);
5491 *type_return=*target;
5492 *format_return = 8; /* bits per byte */
5494 } else if (*target == XA_TARGETS(xDisplay)) {
5495 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5496 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5497 targets_tmp[1] = XA_STRING;
5498 *value_return = targets_tmp;
5499 *type_return = XA_ATOM;
5501 *format_return = 8 * sizeof(Atom);
5502 if (*format_return > 32) {
5503 *length_return *= *format_return / 32;
5504 *format_return = 32;
5512 /* note: when called from menu all parameters are NULL, so no clue what the
5513 * Widget which was clicked on was, or what the click event was
5515 void CopyPositionProc(w, event, prms, nprms)
5522 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5523 * have a notion of a position that is selected but not copied.
5524 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5526 if(gameMode == EditPosition) EditPositionDone(TRUE);
5527 if (selected_fen_position) free(selected_fen_position);
5528 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5529 if (!selected_fen_position) return;
5530 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5532 SendPositionSelection,
5533 NULL/* lose_ownership_proc */ ,
5534 NULL/* transfer_done_proc */);
5535 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5537 SendPositionSelection,
5538 NULL/* lose_ownership_proc */ ,
5539 NULL/* transfer_done_proc */);
5542 /* function called when the data to Paste is ready */
5544 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5545 Atom *type, XtPointer value, unsigned long *len, int *format)
5548 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5549 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5550 EditPositionPasteFEN(fenstr);
5554 /* called when Paste Position button is pressed,
5555 * all parameters will be NULL */
5556 void PastePositionProc(w, event, prms, nprms)
5562 XtGetSelectionValue(menuBarWidget,
5563 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5564 /* (XtSelectionCallbackProc) */ PastePositionCB,
5565 NULL, /* client_data passed to PastePositionCB */
5567 /* better to use the time field from the event that triggered the
5568 * call to this function, but that isn't trivial to get
5576 SendGameSelection(Widget w, Atom *selection, Atom *target,
5577 Atom *type_return, XtPointer *value_return,
5578 unsigned long *length_return, int *format_return)
5580 char *selection_tmp;
5582 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5583 FILE* f = fopen(gameCopyFilename, "r");
5586 if (f == NULL) return False;
5590 selection_tmp = XtMalloc(len + 1);
5591 count = fread(selection_tmp, 1, len, f);
5594 XtFree(selection_tmp);
5597 selection_tmp[len] = NULLCHAR;
5598 *value_return = selection_tmp;
5599 *length_return = len;
5600 *type_return = *target;
5601 *format_return = 8; /* bits per byte */
5603 } else if (*target == XA_TARGETS(xDisplay)) {
5604 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5605 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5606 targets_tmp[1] = XA_STRING;
5607 *value_return = targets_tmp;
5608 *type_return = XA_ATOM;
5610 *format_return = 8 * sizeof(Atom);
5611 if (*format_return > 32) {
5612 *length_return *= *format_return / 32;
5613 *format_return = 32;
5621 void CopySomething()
5626 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5627 * have a notion of a game that is selected but not copied.
5628 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5630 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5633 NULL/* lose_ownership_proc */ ,
5634 NULL/* transfer_done_proc */);
5635 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5638 NULL/* lose_ownership_proc */ ,
5639 NULL/* transfer_done_proc */);
5642 /* note: when called from menu all parameters are NULL, so no clue what the
5643 * Widget which was clicked on was, or what the click event was
5645 void CopyGameProc(w, event, prms, nprms)
5653 ret = SaveGameToFile(gameCopyFilename, FALSE);
5659 void CopyGameListProc(w, event, prms, nprms)
5665 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5669 /* function called when the data to Paste is ready */
5671 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5672 Atom *type, XtPointer value, unsigned long *len, int *format)
5675 if (value == NULL || *len == 0) {
5676 return; /* nothing had been selected to copy */
5678 f = fopen(gamePasteFilename, "w");
5680 DisplayError(_("Can't open temp file"), errno);
5683 fwrite(value, 1, *len, f);
5686 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5689 /* called when Paste Game button is pressed,
5690 * all parameters will be NULL */
5691 void PasteGameProc(w, event, prms, nprms)
5697 XtGetSelectionValue(menuBarWidget,
5698 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5699 /* (XtSelectionCallbackProc) */ PasteGameCB,
5700 NULL, /* client_data passed to PasteGameCB */
5702 /* better to use the time field from the event that triggered the
5703 * call to this function, but that isn't trivial to get
5713 SaveGameProc(NULL, NULL, NULL, NULL);
5717 void QuitProc(w, event, prms, nprms)
5726 void PauseProc(w, event, prms, nprms)
5736 void MachineBlackProc(w, event, prms, nprms)
5742 MachineBlackEvent();
5745 void MachineWhiteProc(w, event, prms, nprms)
5751 MachineWhiteEvent();
5754 void AnalyzeModeProc(w, event, prms, nprms)
5762 if (!first.analysisSupport) {
5763 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5764 DisplayError(buf, 0);
5767 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5768 if (appData.icsActive) {
5769 if (gameMode != IcsObserving) {
5770 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5771 DisplayError(buf, 0);
5773 if (appData.icsEngineAnalyze) {
5774 if (appData.debugMode)
5775 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5781 /* if enable, use want disable icsEngineAnalyze */
5782 if (appData.icsEngineAnalyze) {
5787 appData.icsEngineAnalyze = TRUE;
5788 if (appData.debugMode)
5789 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5791 #ifndef OPTIONSDIALOG
5792 if (!appData.showThinking)
5793 ShowThinkingProc(w,event,prms,nprms);
5799 void AnalyzeFileProc(w, event, prms, nprms)
5805 if (!first.analysisSupport) {
5807 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5808 DisplayError(buf, 0);
5812 #ifndef OPTIONSDIALOG
5813 if (!appData.showThinking)
5814 ShowThinkingProc(w,event,prms,nprms);
5817 FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5818 AnalysisPeriodicEvent(1);
5821 void TwoMachinesProc(w, event, prms, nprms)
5830 void MatchProc(w, event, prms, nprms)
5836 if(gameMode != BeginningOfGame) { DisplayError(_("You can only start a match from the initial position."), 0); return; }
5837 appData.matchGames = appData.defaultMatchGames;
5841 void IcsClientProc(w, event, prms, nprms)
5850 void EditGameProc(w, event, prms, nprms)
5859 void EditPositionProc(w, event, prms, nprms)
5865 EditPositionEvent();
5868 void TrainingProc(w, event, prms, nprms)
5877 void EditCommentProc(w, event, prms, nprms)
5885 if (PopDown(1)) { // popdown succesful
5887 XtSetArg(args[j], XtNleftBitmap, None); j++;
5888 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
5889 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
5890 } else // was not up
5894 void IcsInputBoxProc(w, event, prms, nprms)
5900 if (!PopDown(4)) ICSInputBoxPopUp();
5903 void AcceptProc(w, event, prms, nprms)
5912 void DeclineProc(w, event, prms, nprms)
5921 void RematchProc(w, event, prms, nprms)
5930 void CallFlagProc(w, event, prms, nprms)
5939 void DrawProc(w, event, prms, nprms)
5948 void AbortProc(w, event, prms, nprms)
5957 void AdjournProc(w, event, prms, nprms)
5966 void ResignProc(w, event, prms, nprms)
5975 void AdjuWhiteProc(w, event, prms, nprms)
5981 UserAdjudicationEvent(+1);
5984 void AdjuBlackProc(w, event, prms, nprms)
5990 UserAdjudicationEvent(-1);
5993 void AdjuDrawProc(w, event, prms, nprms)
5999 UserAdjudicationEvent(0);
6002 void EnterKeyProc(w, event, prms, nprms)
6008 if (shellUp[4] == True)
6012 void UpKeyProc(w, event, prms, nprms)
6017 { // [HGM] input: let up-arrow recall previous line from history
6024 if (!shellUp[4]) return;
6025 edit = boxOptions[0].handle;
6027 XtSetArg(args[j], XtNstring, &val); j++;
6028 XtGetValues(edit, args, j);
6029 val = PrevInHistory(val);
6030 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6031 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6033 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6034 XawTextReplace(edit, 0, 0, &t);
6035 XawTextSetInsertionPoint(edit, 9999);
6039 void DownKeyProc(w, event, prms, nprms)
6044 { // [HGM] input: let down-arrow recall next line from history
6049 if (!shellUp[4]) return;
6050 edit = boxOptions[0].handle;
6051 val = NextInHistory();
6052 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6053 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6055 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6056 XawTextReplace(edit, 0, 0, &t);
6057 XawTextSetInsertionPoint(edit, 9999);
6061 void StopObservingProc(w, event, prms, nprms)
6067 StopObservingEvent();
6070 void StopExaminingProc(w, event, prms, nprms)
6076 StopExaminingEvent();
6079 void UploadProc(w, event, prms, nprms)
6089 void ForwardProc(w, event, prms, nprms)
6099 void BackwardProc(w, event, prms, nprms)
6108 void ToStartProc(w, event, prms, nprms)
6117 void ToEndProc(w, event, prms, nprms)
6126 void RevertProc(w, event, prms, nprms)
6135 void AnnotateProc(w, event, prms, nprms)
6144 void TruncateGameProc(w, event, prms, nprms)
6150 TruncateGameEvent();
6152 void RetractMoveProc(w, event, prms, nprms)
6161 void MoveNowProc(w, event, prms, nprms)
6170 void FlipViewProc(w, event, prms, nprms)
6176 flipView = !flipView;
6177 DrawPosition(True, NULL);
6180 void PonderNextMoveProc(w, event, prms, nprms)
6188 PonderNextMoveEvent(!appData.ponderNextMove);
6189 #ifndef OPTIONSDIALOG
6190 if (appData.ponderNextMove) {
6191 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6193 XtSetArg(args[0], XtNleftBitmap, None);
6195 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6200 #ifndef OPTIONSDIALOG
6201 void AlwaysQueenProc(w, event, prms, nprms)
6209 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6211 if (appData.alwaysPromoteToQueen) {
6212 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6214 XtSetArg(args[0], XtNleftBitmap, None);
6216 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6220 void AnimateDraggingProc(w, event, prms, nprms)
6228 appData.animateDragging = !appData.animateDragging;
6230 if (appData.animateDragging) {
6231 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6234 XtSetArg(args[0], XtNleftBitmap, None);
6236 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6240 void AnimateMovingProc(w, event, prms, nprms)
6248 appData.animate = !appData.animate;
6250 if (appData.animate) {
6251 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6254 XtSetArg(args[0], XtNleftBitmap, None);
6256 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6260 void AutoflagProc(w, event, prms, nprms)
6268 appData.autoCallFlag = !appData.autoCallFlag;
6270 if (appData.autoCallFlag) {
6271 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6273 XtSetArg(args[0], XtNleftBitmap, None);
6275 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6279 void AutoflipProc(w, event, prms, nprms)
6287 appData.autoFlipView = !appData.autoFlipView;
6289 if (appData.autoFlipView) {
6290 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6292 XtSetArg(args[0], XtNleftBitmap, None);
6294 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6298 void BlindfoldProc(w, event, prms, nprms)
6306 appData.blindfold = !appData.blindfold;
6308 if (appData.blindfold) {
6309 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6311 XtSetArg(args[0], XtNleftBitmap, None);
6313 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6316 DrawPosition(True, NULL);
6319 void TestLegalityProc(w, event, prms, nprms)
6327 appData.testLegality = !appData.testLegality;
6329 if (appData.testLegality) {
6330 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6332 XtSetArg(args[0], XtNleftBitmap, None);
6334 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6339 void FlashMovesProc(w, event, prms, nprms)
6347 if (appData.flashCount == 0) {
6348 appData.flashCount = 3;
6350 appData.flashCount = -appData.flashCount;
6353 if (appData.flashCount > 0) {
6354 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6356 XtSetArg(args[0], XtNleftBitmap, None);
6358 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6363 void HighlightDraggingProc(w, event, prms, nprms)
6371 appData.highlightDragging = !appData.highlightDragging;
6373 if (appData.highlightDragging) {
6374 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6376 XtSetArg(args[0], XtNleftBitmap, None);
6378 XtSetValues(XtNameToWidget(menuBarWidget,
6379 "menuOptions.Highlight Dragging"), args, 1);
6383 void HighlightLastMoveProc(w, event, prms, nprms)
6391 appData.highlightLastMove = !appData.highlightLastMove;
6393 if (appData.highlightLastMove) {
6394 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6396 XtSetArg(args[0], XtNleftBitmap, None);
6398 XtSetValues(XtNameToWidget(menuBarWidget,
6399 "menuOptions.Highlight Last Move"), args, 1);
6402 void HighlightArrowProc(w, event, prms, nprms)
6410 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6412 if (appData.highlightMoveWithArrow) {
6413 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6415 XtSetArg(args[0], XtNleftBitmap, None);
6417 XtSetValues(XtNameToWidget(menuBarWidget,
6418 "menuOptions.Arrow"), args, 1);
6422 void IcsAlarmProc(w, event, prms, nprms)
6430 appData.icsAlarm = !appData.icsAlarm;
6432 if (appData.icsAlarm) {
6433 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6435 XtSetArg(args[0], XtNleftBitmap, None);
6437 XtSetValues(XtNameToWidget(menuBarWidget,
6438 "menuOptions.ICS Alarm"), args, 1);
6442 void MoveSoundProc(w, event, prms, nprms)
6450 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6452 if (appData.ringBellAfterMoves) {
6453 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6455 XtSetArg(args[0], XtNleftBitmap, None);
6457 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6461 void OneClickProc(w, event, prms, nprms)
6469 appData.oneClick = !appData.oneClick;
6471 if (appData.oneClick) {
6472 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6474 XtSetArg(args[0], XtNleftBitmap, None);
6476 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6480 void PeriodicUpdatesProc(w, event, prms, nprms)
6488 PeriodicUpdatesEvent(!appData.periodicUpdates);
6490 if (appData.periodicUpdates) {
6491 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6493 XtSetArg(args[0], XtNleftBitmap, None);
6495 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6499 void PopupExitMessageProc(w, event, prms, nprms)
6507 appData.popupExitMessage = !appData.popupExitMessage;
6509 if (appData.popupExitMessage) {
6510 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6512 XtSetArg(args[0], XtNleftBitmap, None);
6514 XtSetValues(XtNameToWidget(menuBarWidget,
6515 "menuOptions.Popup Exit Message"), args, 1);
6518 void PopupMoveErrorsProc(w, event, prms, nprms)
6526 appData.popupMoveErrors = !appData.popupMoveErrors;
6528 if (appData.popupMoveErrors) {
6529 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6531 XtSetArg(args[0], XtNleftBitmap, None);
6533 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6538 void PremoveProc(w, event, prms, nprms)
6546 appData.premove = !appData.premove;
6548 if (appData.premove) {
6549 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6551 XtSetArg(args[0], XtNleftBitmap, None);
6553 XtSetValues(XtNameToWidget(menuBarWidget,
6554 "menuOptions.Premove"), args, 1);
6558 void ShowCoordsProc(w, event, prms, nprms)
6566 appData.showCoords = !appData.showCoords;
6568 if (appData.showCoords) {
6569 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6571 XtSetArg(args[0], XtNleftBitmap, None);
6573 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6576 DrawPosition(True, NULL);
6579 void ShowThinkingProc(w, event, prms, nprms)
6585 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6586 ShowThinkingEvent();
6589 void HideThinkingProc(w, event, prms, nprms)
6597 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6598 ShowThinkingEvent();
6600 if (appData.hideThinkingFromHuman) {
6601 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6603 XtSetArg(args[0], XtNleftBitmap, None);
6605 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6610 void SaveOnExitProc(w, event, prms, nprms)
6618 saveSettingsOnExit = !saveSettingsOnExit;
6620 if (saveSettingsOnExit) {
6621 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6623 XtSetArg(args[0], XtNleftBitmap, None);
6625 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6629 void SaveSettingsProc(w, event, prms, nprms)
6635 SaveSettings(settingsFileName);
6638 void InfoProc(w, event, prms, nprms)
6645 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6650 void ManProc(w, event, prms, nprms)
6658 if (nprms && *nprms > 0)
6662 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6666 void HintProc(w, event, prms, nprms)
6675 void BookProc(w, event, prms, nprms)
6684 void AboutProc(w, event, prms, nprms)
6692 char *zippy = " (with Zippy code)";
6696 snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
6697 programVersion, zippy,
6698 "Copyright 1991 Digital Equipment Corporation",
6699 "Enhancements Copyright 1992-2009 Free Software Foundation",
6700 "Enhancements Copyright 2005 Alessandro Scotti",
6701 PACKAGE, " is free software and carries NO WARRANTY;",
6702 "see the file COPYING for more information.");
6703 ErrorPopUp(_("About XBoard"), buf, FALSE);
6706 void DebugProc(w, event, prms, nprms)
6712 appData.debugMode = !appData.debugMode;
6715 void AboutGameProc(w, event, prms, nprms)
6724 void NothingProc(w, event, prms, nprms)
6733 void Iconify(w, event, prms, nprms)
6742 XtSetArg(args[0], XtNiconic, True);
6743 XtSetValues(shellWidget, args, 1);
6746 void DisplayMessage(message, extMessage)
6747 char *message, *extMessage;
6749 /* display a message in the message widget */
6758 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6763 message = extMessage;
6767 /* need to test if messageWidget already exists, since this function
6768 can also be called during the startup, if for example a Xresource
6769 is not set up correctly */
6772 XtSetArg(arg, XtNlabel, message);
6773 XtSetValues(messageWidget, &arg, 1);
6779 void DisplayTitle(text)
6784 char title[MSG_SIZ];
6787 if (text == NULL) text = "";
6789 if (appData.titleInWindow) {
6791 XtSetArg(args[i], XtNlabel, text); i++;
6792 XtSetValues(titleWidget, args, i);
6795 if (*text != NULLCHAR) {
6796 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6797 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6798 } else if (appData.icsActive) {
6799 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6800 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6801 } else if (appData.cmailGameName[0] != NULLCHAR) {
6802 snprintf(icon, sizeof(icon), "%s", "CMail");
6803 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6805 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6806 } else if (gameInfo.variant == VariantGothic) {
6807 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6808 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6811 } else if (gameInfo.variant == VariantFalcon) {
6812 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6813 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6815 } else if (appData.noChessProgram) {
6816 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6817 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6819 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6820 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6823 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6824 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6825 XtSetValues(shellWidget, args, i);
6830 DisplayError(message, error)
6837 if (appData.debugMode || appData.matchMode) {
6838 fprintf(stderr, "%s: %s\n", programName, message);
6841 if (appData.debugMode || appData.matchMode) {
6842 fprintf(stderr, "%s: %s: %s\n",
6843 programName, message, strerror(error));
6845 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6848 ErrorPopUp(_("Error"), message, FALSE);
6852 void DisplayMoveError(message)
6857 DrawPosition(FALSE, NULL);
6858 if (appData.debugMode || appData.matchMode) {
6859 fprintf(stderr, "%s: %s\n", programName, message);
6861 if (appData.popupMoveErrors) {
6862 ErrorPopUp(_("Error"), message, FALSE);
6864 DisplayMessage(message, "");
6869 void DisplayFatalError(message, error, status)
6875 errorExitStatus = status;
6877 fprintf(stderr, "%s: %s\n", programName, message);
6879 fprintf(stderr, "%s: %s: %s\n",
6880 programName, message, strerror(error));
6881 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6884 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6885 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6891 void DisplayInformation(message)
6895 ErrorPopUp(_("Information"), message, TRUE);
6898 void DisplayNote(message)
6902 ErrorPopUp(_("Note"), message, FALSE);
6906 NullXErrorCheck(dpy, error_event)
6908 XErrorEvent *error_event;
6913 void DisplayIcsInteractionTitle(message)
6916 if (oldICSInteractionTitle == NULL) {
6917 /* Magic to find the old window title, adapted from vim */
6918 char *wina = getenv("WINDOWID");
6920 Window win = (Window) atoi(wina);
6921 Window root, parent, *children;
6922 unsigned int nchildren;
6923 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6925 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6926 if (!XQueryTree(xDisplay, win, &root, &parent,
6927 &children, &nchildren)) break;
6928 if (children) XFree((void *)children);
6929 if (parent == root || parent == 0) break;
6932 XSetErrorHandler(oldHandler);
6934 if (oldICSInteractionTitle == NULL) {
6935 oldICSInteractionTitle = "xterm";
6938 printf("\033]0;%s\007", message);
6942 char pendingReplyPrefix[MSG_SIZ];
6943 ProcRef pendingReplyPR;
6945 void AskQuestionProc(w, event, prms, nprms)
6952 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
6956 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6959 void AskQuestionPopDown()
6961 if (!askQuestionUp) return;
6962 XtPopdown(askQuestionShell);
6963 XtDestroyWidget(askQuestionShell);
6964 askQuestionUp = False;
6967 void AskQuestionReplyAction(w, event, prms, nprms)
6977 reply = XawDialogGetValueString(w = XtParent(w));
6978 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
6979 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
6980 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
6981 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
6982 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6983 AskQuestionPopDown();
6985 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
6988 void AskQuestionCallback(w, client_data, call_data)
6990 XtPointer client_data, call_data;
6995 XtSetArg(args[0], XtNlabel, &name);
6996 XtGetValues(w, args, 1);
6998 if (strcmp(name, _("cancel")) == 0) {
6999 AskQuestionPopDown();
7001 AskQuestionReplyAction(w, NULL, NULL, NULL);
7005 void AskQuestion(title, question, replyPrefix, pr)
7006 char *title, *question, *replyPrefix;
7010 Widget popup, layout, dialog, edit;
7016 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7017 pendingReplyPR = pr;
7020 XtSetArg(args[i], XtNresizable, True); i++;
7021 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7022 askQuestionShell = popup =
7023 XtCreatePopupShell(title, transientShellWidgetClass,
7024 shellWidget, args, i);
7027 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7028 layoutArgs, XtNumber(layoutArgs));
7031 XtSetArg(args[i], XtNlabel, question); i++;
7032 XtSetArg(args[i], XtNvalue, ""); i++;
7033 XtSetArg(args[i], XtNborderWidth, 0); i++;
7034 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7037 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7038 (XtPointer) dialog);
7039 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7040 (XtPointer) dialog);
7042 XtRealizeWidget(popup);
7043 CatchDeleteWindow(popup, "AskQuestionPopDown");
7045 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7046 &x, &y, &win_x, &win_y, &mask);
7048 XtSetArg(args[0], XtNx, x - 10);
7049 XtSetArg(args[1], XtNy, y - 30);
7050 XtSetValues(popup, args, 2);
7052 XtPopup(popup, XtGrabExclusive);
7053 askQuestionUp = True;
7055 edit = XtNameToWidget(dialog, "*value");
7056 XtSetKeyboardFocus(popup, edit);
7064 if (*name == NULLCHAR) {
7066 } else if (strcmp(name, "$") == 0) {
7067 putc(BELLCHAR, stderr);
7070 char *prefix = "", *sep = "";
7071 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7072 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7080 PlaySound(appData.soundMove);
7086 PlaySound(appData.soundIcsWin);
7092 PlaySound(appData.soundIcsLoss);
7098 PlaySound(appData.soundIcsDraw);
7102 PlayIcsUnfinishedSound()
7104 PlaySound(appData.soundIcsUnfinished);
7110 PlaySound(appData.soundIcsAlarm);
7116 system("stty echo");
7122 system("stty -echo");
7126 Colorize(cc, continuation)
7131 int count, outCount, error;
7133 if (textColors[(int)cc].bg > 0) {
7134 if (textColors[(int)cc].fg > 0) {
7135 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7136 textColors[(int)cc].fg, textColors[(int)cc].bg);
7138 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7139 textColors[(int)cc].bg);
7142 if (textColors[(int)cc].fg > 0) {
7143 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7144 textColors[(int)cc].fg);
7146 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7149 count = strlen(buf);
7150 outCount = OutputToProcess(NoProc, buf, count, &error);
7151 if (outCount < count) {
7152 DisplayFatalError(_("Error writing to display"), error, 1);
7155 if (continuation) return;
7158 PlaySound(appData.soundShout);
7161 PlaySound(appData.soundSShout);
7164 PlaySound(appData.soundChannel1);
7167 PlaySound(appData.soundChannel);
7170 PlaySound(appData.soundKibitz);
7173 PlaySound(appData.soundTell);
7175 case ColorChallenge:
7176 PlaySound(appData.soundChallenge);
7179 PlaySound(appData.soundRequest);
7182 PlaySound(appData.soundSeek);
7193 return getpwuid(getuid())->pw_name;
7197 ExpandPathName(path)
7200 static char static_buf[4*MSG_SIZ];
7201 char *d, *s, buf[4*MSG_SIZ];
7207 while (*s && isspace(*s))
7216 if (*(s+1) == '/') {
7217 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7221 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7222 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7223 pwd = getpwnam(buf);
7226 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7230 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7231 strcat(d, strchr(s+1, '/'));
7235 safeStrCpy(d, s, 4*MSG_SIZ );
7242 static char host_name[MSG_SIZ];
7244 #if HAVE_GETHOSTNAME
7245 gethostname(host_name, MSG_SIZ);
7247 #else /* not HAVE_GETHOSTNAME */
7248 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7249 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7251 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7253 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7254 #endif /* not HAVE_GETHOSTNAME */
7257 XtIntervalId delayedEventTimerXID = 0;
7258 DelayedEventCallback delayedEventCallback = 0;
7263 delayedEventTimerXID = 0;
7264 delayedEventCallback();
7268 ScheduleDelayedEvent(cb, millisec)
7269 DelayedEventCallback cb; long millisec;
7271 if(delayedEventTimerXID && delayedEventCallback == cb)
7272 // [HGM] alive: replace, rather than add or flush identical event
7273 XtRemoveTimeOut(delayedEventTimerXID);
7274 delayedEventCallback = cb;
7275 delayedEventTimerXID =
7276 XtAppAddTimeOut(appContext, millisec,
7277 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7280 DelayedEventCallback
7283 if (delayedEventTimerXID) {
7284 return delayedEventCallback;
7291 CancelDelayedEvent()
7293 if (delayedEventTimerXID) {
7294 XtRemoveTimeOut(delayedEventTimerXID);
7295 delayedEventTimerXID = 0;
7299 XtIntervalId loadGameTimerXID = 0;
7301 int LoadGameTimerRunning()
7303 return loadGameTimerXID != 0;
7306 int StopLoadGameTimer()
7308 if (loadGameTimerXID != 0) {
7309 XtRemoveTimeOut(loadGameTimerXID);
7310 loadGameTimerXID = 0;
7318 LoadGameTimerCallback(arg, id)
7322 loadGameTimerXID = 0;
7327 StartLoadGameTimer(millisec)
7331 XtAppAddTimeOut(appContext, millisec,
7332 (XtTimerCallbackProc) LoadGameTimerCallback,
7336 XtIntervalId analysisClockXID = 0;
7339 AnalysisClockCallback(arg, id)
7343 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7344 || appData.icsEngineAnalyze) { // [DM]
7345 AnalysisPeriodicEvent(0);
7346 StartAnalysisClock();
7351 StartAnalysisClock()
7354 XtAppAddTimeOut(appContext, 2000,
7355 (XtTimerCallbackProc) AnalysisClockCallback,
7359 XtIntervalId clockTimerXID = 0;
7361 int ClockTimerRunning()
7363 return clockTimerXID != 0;
7366 int StopClockTimer()
7368 if (clockTimerXID != 0) {
7369 XtRemoveTimeOut(clockTimerXID);
7378 ClockTimerCallback(arg, id)
7387 StartClockTimer(millisec)
7391 XtAppAddTimeOut(appContext, millisec,
7392 (XtTimerCallbackProc) ClockTimerCallback,
7397 DisplayTimerLabel(w, color, timer, highlight)
7406 /* check for low time warning */
7407 Pixel foregroundOrWarningColor = timerForegroundPixel;
7410 appData.lowTimeWarning &&
7411 (timer / 1000) < appData.icsAlarmTime)
7412 foregroundOrWarningColor = lowTimeWarningColor;
7414 if (appData.clockMode) {
7415 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7416 XtSetArg(args[0], XtNlabel, buf);
7418 snprintf(buf, MSG_SIZ, "%s ", color);
7419 XtSetArg(args[0], XtNlabel, buf);
7424 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7425 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7427 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7428 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7431 XtSetValues(w, args, 3);
7435 DisplayWhiteClock(timeRemaining, highlight)
7441 if(appData.noGUI) return;
7442 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7443 if (highlight && iconPixmap == bIconPixmap) {
7444 iconPixmap = wIconPixmap;
7445 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7446 XtSetValues(shellWidget, args, 1);
7451 DisplayBlackClock(timeRemaining, highlight)
7457 if(appData.noGUI) return;
7458 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7459 if (highlight && iconPixmap == wIconPixmap) {
7460 iconPixmap = bIconPixmap;
7461 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7462 XtSetValues(shellWidget, args, 1);
7480 int StartChildProcess(cmdLine, dir, pr)
7487 int to_prog[2], from_prog[2];
7491 if (appData.debugMode) {
7492 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7495 /* We do NOT feed the cmdLine to the shell; we just
7496 parse it into blank-separated arguments in the
7497 most simple-minded way possible.
7500 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7503 while(*p == ' ') p++;
7505 if(*p == '"' || *p == '\'')
7506 p = strchr(++argv[i-1], *p);
7507 else p = strchr(p, ' ');
7508 if (p == NULL) break;
7513 SetUpChildIO(to_prog, from_prog);
7515 if ((pid = fork()) == 0) {
7517 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7518 close(to_prog[1]); // first close the unused pipe ends
7519 close(from_prog[0]);
7520 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7521 dup2(from_prog[1], 1);
7522 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7523 close(from_prog[1]); // and closing again loses one of the pipes!
7524 if(fileno(stderr) >= 2) // better safe than sorry...
7525 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7527 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7532 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7534 execvp(argv[0], argv);
7536 /* If we get here, exec failed */
7541 /* Parent process */
7543 close(from_prog[1]);
7545 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7548 cp->fdFrom = from_prog[0];
7549 cp->fdTo = to_prog[1];
7554 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7555 static RETSIGTYPE AlarmCallBack(int n)
7561 DestroyChildProcess(pr, signalType)
7565 ChildProc *cp = (ChildProc *) pr;
7567 if (cp->kind != CPReal) return;
7569 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7570 signal(SIGALRM, AlarmCallBack);
7572 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7573 kill(cp->pid, SIGKILL); // kill it forcefully
7574 wait((int *) 0); // and wait again
7578 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7580 /* Process is exiting either because of the kill or because of
7581 a quit command sent by the backend; either way, wait for it to die.
7590 InterruptChildProcess(pr)
7593 ChildProc *cp = (ChildProc *) pr;
7595 if (cp->kind != CPReal) return;
7596 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7599 int OpenTelnet(host, port, pr)
7604 char cmdLine[MSG_SIZ];
7606 if (port[0] == NULLCHAR) {
7607 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7609 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7611 return StartChildProcess(cmdLine, "", pr);
7614 int OpenTCP(host, port, pr)
7620 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7621 #else /* !OMIT_SOCKETS */
7622 struct addrinfo hints;
7623 struct addrinfo *ais, *ai;
7628 memset(&hints, 0, sizeof(hints));
7629 hints.ai_family = AF_UNSPEC;
7630 hints.ai_socktype = SOCK_STREAM;
7632 error = getaddrinfo(host, port, &hints, &ais);
7634 /* a getaddrinfo error is not an errno, so can't return it */
7635 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7636 host, port, gai_strerror(error));
7640 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7641 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7645 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7658 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7664 #endif /* !OMIT_SOCKETS */
7669 int OpenCommPort(name, pr)
7676 fd = open(name, 2, 0);
7677 if (fd < 0) return errno;
7679 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7689 int OpenLoopback(pr)
7695 SetUpChildIO(to, from);
7697 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7700 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7707 int OpenRcmd(host, user, cmd, pr)
7708 char *host, *user, *cmd;
7711 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7715 #define INPUT_SOURCE_BUF_SIZE 8192
7724 char buf[INPUT_SOURCE_BUF_SIZE];
7729 DoInputCallback(closure, source, xid)
7734 InputSource *is = (InputSource *) closure;
7739 if (is->lineByLine) {
7740 count = read(is->fd, is->unused,
7741 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7743 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7746 is->unused += count;
7748 while (p < is->unused) {
7749 q = memchr(p, '\n', is->unused - p);
7750 if (q == NULL) break;
7752 (is->func)(is, is->closure, p, q - p, 0);
7756 while (p < is->unused) {
7761 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7766 (is->func)(is, is->closure, is->buf, count, error);
7770 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7777 ChildProc *cp = (ChildProc *) pr;
7779 is = (InputSource *) calloc(1, sizeof(InputSource));
7780 is->lineByLine = lineByLine;
7784 is->fd = fileno(stdin);
7786 is->kind = cp->kind;
7787 is->fd = cp->fdFrom;
7790 is->unused = is->buf;
7793 is->xid = XtAppAddInput(appContext, is->fd,
7794 (XtPointer) (XtInputReadMask),
7795 (XtInputCallbackProc) DoInputCallback,
7797 is->closure = closure;
7798 return (InputSourceRef) is;
7802 RemoveInputSource(isr)
7805 InputSource *is = (InputSource *) isr;
7807 if (is->xid == 0) return;
7808 XtRemoveInput(is->xid);
7812 int OutputToProcess(pr, message, count, outError)
7818 static int line = 0;
7819 ChildProc *cp = (ChildProc *) pr;
7824 if (appData.noJoin || !appData.useInternalWrap)
7825 outCount = fwrite(message, 1, count, stdout);
7828 int width = get_term_width();
7829 int len = wrap(NULL, message, count, width, &line);
7830 char *msg = malloc(len);
7834 outCount = fwrite(message, 1, count, stdout);
7837 dbgchk = wrap(msg, message, count, width, &line);
7838 if (dbgchk != len && appData.debugMode)
7839 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7840 outCount = fwrite(msg, 1, dbgchk, stdout);
7846 outCount = write(cp->fdTo, message, count);
7856 /* Output message to process, with "ms" milliseconds of delay
7857 between each character. This is needed when sending the logon
7858 script to ICC, which for some reason doesn't like the
7859 instantaneous send. */
7860 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
7867 ChildProc *cp = (ChildProc *) pr;
7872 r = write(cp->fdTo, message++, 1);
7885 /**** Animation code by Hugh Fisher, DCS, ANU.
7887 Known problem: if a window overlapping the board is
7888 moved away while a piece is being animated underneath,
7889 the newly exposed area won't be updated properly.
7890 I can live with this.
7892 Known problem: if you look carefully at the animation
7893 of pieces in mono mode, they are being drawn as solid
7894 shapes without interior detail while moving. Fixing
7895 this would be a major complication for minimal return.
7898 /* Masks for XPM pieces. Black and white pieces can have
7899 different shapes, but in the interest of retaining my
7900 sanity pieces must have the same outline on both light
7901 and dark squares, and all pieces must use the same
7902 background square colors/images. */
7904 static int xpmDone = 0;
7907 CreateAnimMasks (pieceDepth)
7914 unsigned long plane;
7917 /* Need a bitmap just to get a GC with right depth */
7918 buf = XCreatePixmap(xDisplay, xBoardWindow,
7920 values.foreground = 1;
7921 values.background = 0;
7922 /* Don't use XtGetGC, not read only */
7923 maskGC = XCreateGC(xDisplay, buf,
7924 GCForeground | GCBackground, &values);
7925 XFreePixmap(xDisplay, buf);
7927 buf = XCreatePixmap(xDisplay, xBoardWindow,
7928 squareSize, squareSize, pieceDepth);
7929 values.foreground = XBlackPixel(xDisplay, xScreen);
7930 values.background = XWhitePixel(xDisplay, xScreen);
7931 bufGC = XCreateGC(xDisplay, buf,
7932 GCForeground | GCBackground, &values);
7934 for (piece = WhitePawn; piece <= BlackKing; piece++) {
7935 /* Begin with empty mask */
7936 if(!xpmDone) // [HGM] pieces: keep using existing
7937 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7938 squareSize, squareSize, 1);
7939 XSetFunction(xDisplay, maskGC, GXclear);
7940 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7941 0, 0, squareSize, squareSize);
7943 /* Take a copy of the piece */
7948 XSetFunction(xDisplay, bufGC, GXcopy);
7949 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
7951 0, 0, squareSize, squareSize, 0, 0);
7953 /* XOR the background (light) over the piece */
7954 XSetFunction(xDisplay, bufGC, GXxor);
7956 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7957 0, 0, squareSize, squareSize, 0, 0);
7959 XSetForeground(xDisplay, bufGC, lightSquareColor);
7960 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7963 /* We now have an inverted piece image with the background
7964 erased. Construct mask by just selecting all the non-zero
7965 pixels - no need to reconstruct the original image. */
7966 XSetFunction(xDisplay, maskGC, GXor);
7968 /* Might be quicker to download an XImage and create bitmap
7969 data from it rather than this N copies per piece, but it
7970 only takes a fraction of a second and there is a much
7971 longer delay for loading the pieces. */
7972 for (n = 0; n < pieceDepth; n ++) {
7973 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7974 0, 0, squareSize, squareSize,
7980 XFreePixmap(xDisplay, buf);
7981 XFreeGC(xDisplay, bufGC);
7982 XFreeGC(xDisplay, maskGC);
7986 InitAnimState (anim, info)
7988 XWindowAttributes * info;
7993 /* Each buffer is square size, same depth as window */
7994 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
7995 squareSize, squareSize, info->depth);
7996 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
7997 squareSize, squareSize, info->depth);
7999 /* Create a plain GC for blitting */
8000 mask = GCForeground | GCBackground | GCFunction |
8001 GCPlaneMask | GCGraphicsExposures;
8002 values.foreground = XBlackPixel(xDisplay, xScreen);
8003 values.background = XWhitePixel(xDisplay, xScreen);
8004 values.function = GXcopy;
8005 values.plane_mask = AllPlanes;
8006 values.graphics_exposures = False;
8007 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8009 /* Piece will be copied from an existing context at
8010 the start of each new animation/drag. */
8011 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8013 /* Outline will be a read-only copy of an existing */
8014 anim->outlineGC = None;
8020 XWindowAttributes info;
8022 if (xpmDone && gameInfo.variant == oldVariant) return;
8023 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
8024 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8026 InitAnimState(&game, &info);
8027 InitAnimState(&player, &info);
8029 /* For XPM pieces, we need bitmaps to use as masks. */
8031 CreateAnimMasks(info.depth), xpmDone = 1;
8036 static Boolean frameWaiting;
8038 static RETSIGTYPE FrameAlarm (sig)
8041 frameWaiting = False;
8042 /* In case System-V style signals. Needed?? */
8043 signal(SIGALRM, FrameAlarm);
8050 struct itimerval delay;
8052 XSync(xDisplay, False);
8055 frameWaiting = True;
8056 signal(SIGALRM, FrameAlarm);
8057 delay.it_interval.tv_sec =
8058 delay.it_value.tv_sec = time / 1000;
8059 delay.it_interval.tv_usec =
8060 delay.it_value.tv_usec = (time % 1000) * 1000;
8061 setitimer(ITIMER_REAL, &delay, NULL);
8062 while (frameWaiting) pause();
8063 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8064 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8065 setitimer(ITIMER_REAL, &delay, NULL);
8075 XSync(xDisplay, False);
8077 usleep(time * 1000);
8082 /* Convert board position to corner of screen rect and color */
8085 ScreenSquare(column, row, pt, color)
8086 int column; int row; XPoint * pt; int * color;
8089 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8090 pt->y = lineGap + row * (squareSize + lineGap);
8092 pt->x = lineGap + column * (squareSize + lineGap);
8093 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8095 *color = SquareColor(row, column);
8098 /* Convert window coords to square */
8101 BoardSquare(x, y, column, row)
8102 int x; int y; int * column; int * row;
8104 *column = EventToSquare(x, BOARD_WIDTH);
8105 if (flipView && *column >= 0)
8106 *column = BOARD_WIDTH - 1 - *column;
8107 *row = EventToSquare(y, BOARD_HEIGHT);
8108 if (!flipView && *row >= 0)
8109 *row = BOARD_HEIGHT - 1 - *row;
8114 #undef Max /* just in case */
8116 #define Max(a, b) ((a) > (b) ? (a) : (b))
8117 #define Min(a, b) ((a) < (b) ? (a) : (b))
8120 SetRect(rect, x, y, width, height)
8121 XRectangle * rect; int x; int y; int width; int height;
8125 rect->width = width;
8126 rect->height = height;
8129 /* Test if two frames overlap. If they do, return
8130 intersection rect within old and location of
8131 that rect within new. */
8134 Intersect(old, new, size, area, pt)
8135 XPoint * old; XPoint * new;
8136 int size; XRectangle * area; XPoint * pt;
8138 if (old->x > new->x + size || new->x > old->x + size ||
8139 old->y > new->y + size || new->y > old->y + size) {
8142 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8143 size - abs(old->x - new->x), size - abs(old->y - new->y));
8144 pt->x = Max(old->x - new->x, 0);
8145 pt->y = Max(old->y - new->y, 0);
8150 /* For two overlapping frames, return the rect(s)
8151 in the old that do not intersect with the new. */
8154 CalcUpdateRects(old, new, size, update, nUpdates)
8155 XPoint * old; XPoint * new; int size;
8156 XRectangle update[]; int * nUpdates;
8160 /* If old = new (shouldn't happen) then nothing to draw */
8161 if (old->x == new->x && old->y == new->y) {
8165 /* Work out what bits overlap. Since we know the rects
8166 are the same size we don't need a full intersect calc. */
8168 /* Top or bottom edge? */
8169 if (new->y > old->y) {
8170 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8172 } else if (old->y > new->y) {
8173 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8174 size, old->y - new->y);
8177 /* Left or right edge - don't overlap any update calculated above. */
8178 if (new->x > old->x) {
8179 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8180 new->x - old->x, size - abs(new->y - old->y));
8182 } else if (old->x > new->x) {
8183 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8184 old->x - new->x, size - abs(new->y - old->y));
8191 /* Generate a series of frame coords from start->mid->finish.
8192 The movement rate doubles until the half way point is
8193 reached, then halves back down to the final destination,
8194 which gives a nice slow in/out effect. The algorithmn
8195 may seem to generate too many intermediates for short
8196 moves, but remember that the purpose is to attract the
8197 viewers attention to the piece about to be moved and
8198 then to where it ends up. Too few frames would be less
8202 Tween(start, mid, finish, factor, frames, nFrames)
8203 XPoint * start; XPoint * mid;
8204 XPoint * finish; int factor;
8205 XPoint frames[]; int * nFrames;
8207 int fraction, n, count;
8211 /* Slow in, stepping 1/16th, then 1/8th, ... */
8213 for (n = 0; n < factor; n++)
8215 for (n = 0; n < factor; n++) {
8216 frames[count].x = start->x + (mid->x - start->x) / fraction;
8217 frames[count].y = start->y + (mid->y - start->y) / fraction;
8219 fraction = fraction / 2;
8223 frames[count] = *mid;
8226 /* Slow out, stepping 1/2, then 1/4, ... */
8228 for (n = 0; n < factor; n++) {
8229 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8230 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8232 fraction = fraction * 2;
8237 /* Draw a piece on the screen without disturbing what's there */
8240 SelectGCMask(piece, clip, outline, mask)
8241 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8245 /* Bitmap for piece being moved. */
8246 if (appData.monoMode) {
8247 *mask = *pieceToSolid(piece);
8248 } else if (useImages) {
8250 *mask = xpmMask[piece];
8252 *mask = ximMaskPm[piece];
8255 *mask = *pieceToSolid(piece);
8258 /* GC for piece being moved. Square color doesn't matter, but
8259 since it gets modified we make a copy of the original. */
8261 if (appData.monoMode)
8266 if (appData.monoMode)
8271 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8273 /* Outline only used in mono mode and is not modified */
8275 *outline = bwPieceGC;
8277 *outline = wbPieceGC;
8281 OverlayPiece(piece, clip, outline, dest)
8282 ChessSquare piece; GC clip; GC outline; Drawable dest;
8287 /* Draw solid rectangle which will be clipped to shape of piece */
8288 XFillRectangle(xDisplay, dest, clip,
8289 0, 0, squareSize, squareSize);
8290 if (appData.monoMode)
8291 /* Also draw outline in contrasting color for black
8292 on black / white on white cases */
8293 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8294 0, 0, squareSize, squareSize, 0, 0, 1);
8296 /* Copy the piece */
8301 if(appData.upsideDown && flipView) kind ^= 2;
8302 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8304 0, 0, squareSize, squareSize,
8309 /* Animate the movement of a single piece */
8312 BeginAnimation(anim, piece, startColor, start)
8320 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8321 /* The old buffer is initialised with the start square (empty) */
8322 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8323 anim->prevFrame = *start;
8325 /* The piece will be drawn using its own bitmap as a matte */
8326 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8327 XSetClipMask(xDisplay, anim->pieceGC, mask);
8331 AnimationFrame(anim, frame, piece)
8336 XRectangle updates[4];
8341 /* Save what we are about to draw into the new buffer */
8342 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8343 frame->x, frame->y, squareSize, squareSize,
8346 /* Erase bits of the previous frame */
8347 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8348 /* Where the new frame overlapped the previous,
8349 the contents in newBuf are wrong. */
8350 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8351 overlap.x, overlap.y,
8352 overlap.width, overlap.height,
8354 /* Repaint the areas in the old that don't overlap new */
8355 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8356 for (i = 0; i < count; i++)
8357 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8358 updates[i].x - anim->prevFrame.x,
8359 updates[i].y - anim->prevFrame.y,
8360 updates[i].width, updates[i].height,
8361 updates[i].x, updates[i].y);
8363 /* Easy when no overlap */
8364 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8365 0, 0, squareSize, squareSize,
8366 anim->prevFrame.x, anim->prevFrame.y);
8369 /* Save this frame for next time round */
8370 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8371 0, 0, squareSize, squareSize,
8373 anim->prevFrame = *frame;
8375 /* Draw piece over original screen contents, not current,
8376 and copy entire rect. Wipes out overlapping piece images. */
8377 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8378 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8379 0, 0, squareSize, squareSize,
8380 frame->x, frame->y);
8384 EndAnimation (anim, finish)
8388 XRectangle updates[4];
8393 /* The main code will redraw the final square, so we
8394 only need to erase the bits that don't overlap. */
8395 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8396 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8397 for (i = 0; i < count; i++)
8398 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8399 updates[i].x - anim->prevFrame.x,
8400 updates[i].y - anim->prevFrame.y,
8401 updates[i].width, updates[i].height,
8402 updates[i].x, updates[i].y);
8404 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8405 0, 0, squareSize, squareSize,
8406 anim->prevFrame.x, anim->prevFrame.y);
8411 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8413 ChessSquare piece; int startColor;
8414 XPoint * start; XPoint * finish;
8415 XPoint frames[]; int nFrames;
8419 BeginAnimation(anim, piece, startColor, start);
8420 for (n = 0; n < nFrames; n++) {
8421 AnimationFrame(anim, &(frames[n]), piece);
8422 FrameDelay(appData.animSpeed);
8424 EndAnimation(anim, finish);
8428 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8431 ChessSquare piece = board[fromY][toY];
8432 board[fromY][toY] = EmptySquare;
8433 DrawPosition(FALSE, board);
8435 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8436 y = lineGap + toY * (squareSize + lineGap);
8438 x = lineGap + toX * (squareSize + lineGap);
8439 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8441 for(i=1; i<4*kFactor; i++) {
8442 int r = squareSize * 9 * i/(20*kFactor - 5);
8443 XFillArc(xDisplay, xBoardWindow, highlineGC,
8444 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8445 FrameDelay(appData.animSpeed);
8447 board[fromY][toY] = piece;
8450 /* Main control logic for deciding what to animate and how */
8453 AnimateMove(board, fromX, fromY, toX, toY)
8462 XPoint start, finish, mid;
8463 XPoint frames[kFactor * 2 + 1];
8464 int nFrames, startColor, endColor;
8466 /* Are we animating? */
8467 if (!appData.animate || appData.blindfold)
8470 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8471 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8472 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8474 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8475 piece = board[fromY][fromX];
8476 if (piece >= EmptySquare) return;
8481 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8484 if (appData.debugMode) {
8485 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8486 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8487 piece, fromX, fromY, toX, toY); }
8489 ScreenSquare(fromX, fromY, &start, &startColor);
8490 ScreenSquare(toX, toY, &finish, &endColor);
8493 /* Knight: make straight movement then diagonal */
8494 if (abs(toY - fromY) < abs(toX - fromX)) {
8495 mid.x = start.x + (finish.x - start.x) / 2;
8499 mid.y = start.y + (finish.y - start.y) / 2;
8502 mid.x = start.x + (finish.x - start.x) / 2;
8503 mid.y = start.y + (finish.y - start.y) / 2;
8506 /* Don't use as many frames for very short moves */
8507 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8508 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8510 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8511 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8512 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8514 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8515 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8518 /* Be sure end square is redrawn */
8519 damage[0][toY][toX] = True;
8523 DragPieceBegin(x, y)
8526 int boardX, boardY, color;
8529 /* Are we animating? */
8530 if (!appData.animateDragging || appData.blindfold)
8533 /* Figure out which square we start in and the
8534 mouse position relative to top left corner. */
8535 BoardSquare(x, y, &boardX, &boardY);
8536 player.startBoardX = boardX;
8537 player.startBoardY = boardY;
8538 ScreenSquare(boardX, boardY, &corner, &color);
8539 player.startSquare = corner;
8540 player.startColor = color;
8541 /* As soon as we start dragging, the piece will jump slightly to
8542 be centered over the mouse pointer. */
8543 player.mouseDelta.x = squareSize/2;
8544 player.mouseDelta.y = squareSize/2;
8545 /* Initialise animation */
8546 player.dragPiece = PieceForSquare(boardX, boardY);
8548 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8549 player.dragActive = True;
8550 BeginAnimation(&player, player.dragPiece, color, &corner);
8551 /* Mark this square as needing to be redrawn. Note that
8552 we don't remove the piece though, since logically (ie
8553 as seen by opponent) the move hasn't been made yet. */
8554 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8555 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8556 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8557 corner.x, corner.y, squareSize, squareSize,
8558 0, 0); // [HGM] zh: unstack in stead of grab
8559 if(gatingPiece != EmptySquare) {
8560 /* Kludge alert: When gating we want the introduced
8561 piece to appear on the from square. To generate an
8562 image of it, we draw it on the board, copy the image,
8563 and draw the original piece again. */
8564 ChessSquare piece = boards[currentMove][boardY][boardX];
8565 DrawSquare(boardY, boardX, gatingPiece, 0);
8566 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8567 corner.x, corner.y, squareSize, squareSize, 0, 0);
8568 DrawSquare(boardY, boardX, piece, 0);
8570 damage[0][boardY][boardX] = True;
8572 player.dragActive = False;
8577 ChangeDragPiece(ChessSquare piece)
8580 player.dragPiece = piece;
8581 /* The piece will be drawn using its own bitmap as a matte */
8582 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8583 XSetClipMask(xDisplay, player.pieceGC, mask);
8592 /* Are we animating? */
8593 if (!appData.animateDragging || appData.blindfold)
8597 if (! player.dragActive)
8599 /* Move piece, maintaining same relative position
8600 of mouse within square */
8601 corner.x = x - player.mouseDelta.x;
8602 corner.y = y - player.mouseDelta.y;
8603 AnimationFrame(&player, &corner, player.dragPiece);
8605 if (appData.highlightDragging) {
8607 BoardSquare(x, y, &boardX, &boardY);
8608 SetHighlights(fromX, fromY, boardX, boardY);
8617 int boardX, boardY, color;
8620 /* Are we animating? */
8621 if (!appData.animateDragging || appData.blindfold)
8625 if (! player.dragActive)
8627 /* Last frame in sequence is square piece is
8628 placed on, which may not match mouse exactly. */
8629 BoardSquare(x, y, &boardX, &boardY);
8630 ScreenSquare(boardX, boardY, &corner, &color);
8631 EndAnimation(&player, &corner);
8633 /* Be sure end square is redrawn */
8634 damage[0][boardY][boardX] = True;
8636 /* This prevents weird things happening with fast successive
8637 clicks which on my Sun at least can cause motion events
8638 without corresponding press/release. */
8639 player.dragActive = False;
8642 /* Handle expose event while piece being dragged */
8647 if (!player.dragActive || appData.blindfold)
8650 /* What we're doing: logically, the move hasn't been made yet,
8651 so the piece is still in it's original square. But visually
8652 it's being dragged around the board. So we erase the square
8653 that the piece is on and draw it at the last known drag point. */
8654 BlankSquare(player.startSquare.x, player.startSquare.y,
8655 player.startColor, EmptySquare, xBoardWindow, 1);
8656 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8657 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8660 #include <sys/ioctl.h>
8661 int get_term_width()
8663 int fd, default_width;
8666 default_width = 79; // this is FICS default anyway...
8668 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8670 if (!ioctl(fd, TIOCGSIZE, &win))
8671 default_width = win.ts_cols;
8672 #elif defined(TIOCGWINSZ)
8674 if (!ioctl(fd, TIOCGWINSZ, &win))
8675 default_width = win.ws_col;
8677 return default_width;
8683 static int old_width = 0;
8684 int new_width = get_term_width();
8686 if (old_width != new_width)
8687 ics_printf("set width %d\n", new_width);
8688 old_width = new_width;
8691 void NotifyFrontendLogin()
8696 /* [AS] Arrow highlighting support */
8698 static double A_WIDTH = 5; /* Width of arrow body */
8700 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8701 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8703 static double Sqr( double x )
8708 static int Round( double x )
8710 return (int) (x + 0.5);
8713 void SquareToPos(int rank, int file, int *x, int *y)
8716 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8717 *y = lineGap + rank * (squareSize + lineGap);
8719 *x = lineGap + file * (squareSize + lineGap);
8720 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8724 /* Draw an arrow between two points using current settings */
8725 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8728 double dx, dy, j, k, x, y;
8731 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8733 arrow[0].x = s_x + A_WIDTH + 0.5;
8736 arrow[1].x = s_x + A_WIDTH + 0.5;
8737 arrow[1].y = d_y - h;
8739 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8740 arrow[2].y = d_y - h;
8745 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8746 arrow[5].y = d_y - h;
8748 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8749 arrow[4].y = d_y - h;
8751 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8754 else if( d_y == s_y ) {
8755 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8758 arrow[0].y = s_y + A_WIDTH + 0.5;
8760 arrow[1].x = d_x - w;
8761 arrow[1].y = s_y + A_WIDTH + 0.5;
8763 arrow[2].x = d_x - w;
8764 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8769 arrow[5].x = d_x - w;
8770 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8772 arrow[4].x = d_x - w;
8773 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8776 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8779 /* [AS] Needed a lot of paper for this! :-) */
8780 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8781 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8783 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8785 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8790 arrow[0].x = Round(x - j);
8791 arrow[0].y = Round(y + j*dx);
8793 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8794 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8797 x = (double) d_x - k;
8798 y = (double) d_y - k*dy;
8801 x = (double) d_x + k;
8802 y = (double) d_y + k*dy;
8805 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8807 arrow[6].x = Round(x - j);
8808 arrow[6].y = Round(y + j*dx);
8810 arrow[2].x = Round(arrow[6].x + 2*j);
8811 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8813 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8814 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8819 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8820 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8823 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8824 // Polygon( hdc, arrow, 7 );
8827 /* [AS] Draw an arrow between two squares */
8828 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
8830 int s_x, s_y, d_x, d_y, hor, vert, i;
8832 if( s_col == d_col && s_row == d_row ) {
8836 /* Get source and destination points */
8837 SquareToPos( s_row, s_col, &s_x, &s_y);
8838 SquareToPos( d_row, d_col, &d_x, &d_y);
8841 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8843 else if( d_y < s_y ) {
8844 d_y += squareSize / 2 + squareSize / 4;
8847 d_y += squareSize / 2;
8851 d_x += squareSize / 2 - squareSize / 4;
8853 else if( d_x < s_x ) {
8854 d_x += squareSize / 2 + squareSize / 4;
8857 d_x += squareSize / 2;
8860 s_x += squareSize / 2;
8861 s_y += squareSize / 2;
8864 A_WIDTH = squareSize / 14.; //[HGM] make float
8866 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8868 hor = 64*s_col + 32; vert = 64*s_row + 32;
8869 for(i=0; i<= 64; i++) {
8870 damage[0][vert+6>>6][hor+6>>6] = True;
8871 damage[0][vert-6>>6][hor+6>>6] = True;
8872 damage[0][vert+6>>6][hor-6>>6] = True;
8873 damage[0][vert-6>>6][hor-6>>6] = True;
8874 hor += d_col - s_col; vert += d_row - s_row;
8878 Boolean IsDrawArrowEnabled()
8880 return appData.highlightMoveWithArrow && squareSize >= 32;
8883 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
8885 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8886 DrawArrowBetweenSquares(fromX, fromY, toX, toY);