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);
5311 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5312 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5314 /* Maybe all the enables should be handled here, not just this one */
5315 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5316 gameMode == Training || gameMode == PlayFromGameFile);
5321 * Button/menu procedures
5323 void ResetProc(w, event, prms, nprms)
5332 int LoadGamePopUp(f, gameNumber, title)
5337 cmailMsgLoaded = FALSE;
5338 if (gameNumber == 0) {
5339 int error = GameListBuild(f);
5341 DisplayError(_("Cannot build game list"), error);
5342 } else if (!ListEmpty(&gameList) &&
5343 ((ListGame *) gameList.tailPred)->number > 1) {
5344 GameListPopUp(f, title);
5350 return LoadGame(f, gameNumber, title, FALSE);
5353 void LoadGameProc(w, event, prms, nprms)
5359 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5362 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5365 void LoadNextGameProc(w, event, prms, nprms)
5374 void LoadPrevGameProc(w, event, prms, nprms)
5383 void ReloadGameProc(w, event, prms, nprms)
5392 void LoadNextPositionProc(w, event, prms, nprms)
5401 void LoadPrevPositionProc(w, event, prms, nprms)
5410 void ReloadPositionProc(w, event, prms, nprms)
5419 void LoadPositionProc(w, event, prms, nprms)
5425 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5428 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5431 void SaveGameProc(w, event, prms, nprms)
5437 FileNamePopUp(_("Save game file name?"),
5438 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5439 appData.oldSaveStyle ? ".game" : ".pgn",
5443 void SavePositionProc(w, event, prms, nprms)
5449 FileNamePopUp(_("Save position file name?"),
5450 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5451 appData.oldSaveStyle ? ".pos" : ".fen",
5455 void ReloadCmailMsgProc(w, event, prms, nprms)
5461 ReloadCmailMsgEvent(FALSE);
5464 void MailMoveProc(w, event, prms, nprms)
5473 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5474 char *selected_fen_position=NULL;
5477 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5478 Atom *type_return, XtPointer *value_return,
5479 unsigned long *length_return, int *format_return)
5481 char *selection_tmp;
5483 if (!selected_fen_position) return False; /* should never happen */
5484 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5485 /* note: since no XtSelectionDoneProc was registered, Xt will
5486 * automatically call XtFree on the value returned. So have to
5487 * make a copy of it allocated with XtMalloc */
5488 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5489 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5491 *value_return=selection_tmp;
5492 *length_return=strlen(selection_tmp);
5493 *type_return=*target;
5494 *format_return = 8; /* bits per byte */
5496 } else if (*target == XA_TARGETS(xDisplay)) {
5497 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5498 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5499 targets_tmp[1] = XA_STRING;
5500 *value_return = targets_tmp;
5501 *type_return = XA_ATOM;
5503 *format_return = 8 * sizeof(Atom);
5504 if (*format_return > 32) {
5505 *length_return *= *format_return / 32;
5506 *format_return = 32;
5514 /* note: when called from menu all parameters are NULL, so no clue what the
5515 * Widget which was clicked on was, or what the click event was
5517 void CopyPositionProc(w, event, prms, nprms)
5524 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5525 * have a notion of a position that is selected but not copied.
5526 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5528 if(gameMode == EditPosition) EditPositionDone(TRUE);
5529 if (selected_fen_position) free(selected_fen_position);
5530 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5531 if (!selected_fen_position) return;
5532 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5534 SendPositionSelection,
5535 NULL/* lose_ownership_proc */ ,
5536 NULL/* transfer_done_proc */);
5537 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5539 SendPositionSelection,
5540 NULL/* lose_ownership_proc */ ,
5541 NULL/* transfer_done_proc */);
5544 /* function called when the data to Paste is ready */
5546 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5547 Atom *type, XtPointer value, unsigned long *len, int *format)
5550 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5551 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5552 EditPositionPasteFEN(fenstr);
5556 /* called when Paste Position button is pressed,
5557 * all parameters will be NULL */
5558 void PastePositionProc(w, event, prms, nprms)
5564 XtGetSelectionValue(menuBarWidget,
5565 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5566 /* (XtSelectionCallbackProc) */ PastePositionCB,
5567 NULL, /* client_data passed to PastePositionCB */
5569 /* better to use the time field from the event that triggered the
5570 * call to this function, but that isn't trivial to get
5578 SendGameSelection(Widget w, Atom *selection, Atom *target,
5579 Atom *type_return, XtPointer *value_return,
5580 unsigned long *length_return, int *format_return)
5582 char *selection_tmp;
5584 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5585 FILE* f = fopen(gameCopyFilename, "r");
5588 if (f == NULL) return False;
5592 selection_tmp = XtMalloc(len + 1);
5593 count = fread(selection_tmp, 1, len, f);
5596 XtFree(selection_tmp);
5599 selection_tmp[len] = NULLCHAR;
5600 *value_return = selection_tmp;
5601 *length_return = len;
5602 *type_return = *target;
5603 *format_return = 8; /* bits per byte */
5605 } else if (*target == XA_TARGETS(xDisplay)) {
5606 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5607 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5608 targets_tmp[1] = XA_STRING;
5609 *value_return = targets_tmp;
5610 *type_return = XA_ATOM;
5612 *format_return = 8 * sizeof(Atom);
5613 if (*format_return > 32) {
5614 *length_return *= *format_return / 32;
5615 *format_return = 32;
5623 void CopySomething()
5628 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5629 * have a notion of a game that is selected but not copied.
5630 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5632 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5635 NULL/* lose_ownership_proc */ ,
5636 NULL/* transfer_done_proc */);
5637 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5640 NULL/* lose_ownership_proc */ ,
5641 NULL/* transfer_done_proc */);
5644 /* note: when called from menu all parameters are NULL, so no clue what the
5645 * Widget which was clicked on was, or what the click event was
5647 void CopyGameProc(w, event, prms, nprms)
5655 ret = SaveGameToFile(gameCopyFilename, FALSE);
5661 void CopyGameListProc(w, event, prms, nprms)
5667 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5671 /* function called when the data to Paste is ready */
5673 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5674 Atom *type, XtPointer value, unsigned long *len, int *format)
5677 if (value == NULL || *len == 0) {
5678 return; /* nothing had been selected to copy */
5680 f = fopen(gamePasteFilename, "w");
5682 DisplayError(_("Can't open temp file"), errno);
5685 fwrite(value, 1, *len, f);
5688 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5691 /* called when Paste Game button is pressed,
5692 * all parameters will be NULL */
5693 void PasteGameProc(w, event, prms, nprms)
5699 XtGetSelectionValue(menuBarWidget,
5700 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5701 /* (XtSelectionCallbackProc) */ PasteGameCB,
5702 NULL, /* client_data passed to PasteGameCB */
5704 /* better to use the time field from the event that triggered the
5705 * call to this function, but that isn't trivial to get
5715 SaveGameProc(NULL, NULL, NULL, NULL);
5719 void QuitProc(w, event, prms, nprms)
5728 void PauseProc(w, event, prms, nprms)
5738 void MachineBlackProc(w, event, prms, nprms)
5744 MachineBlackEvent();
5747 void MachineWhiteProc(w, event, prms, nprms)
5753 MachineWhiteEvent();
5756 void AnalyzeModeProc(w, event, prms, nprms)
5764 if (!first.analysisSupport) {
5765 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5766 DisplayError(buf, 0);
5769 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5770 if (appData.icsActive) {
5771 if (gameMode != IcsObserving) {
5772 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5773 DisplayError(buf, 0);
5775 if (appData.icsEngineAnalyze) {
5776 if (appData.debugMode)
5777 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5783 /* if enable, use want disable icsEngineAnalyze */
5784 if (appData.icsEngineAnalyze) {
5789 appData.icsEngineAnalyze = TRUE;
5790 if (appData.debugMode)
5791 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5793 #ifndef OPTIONSDIALOG
5794 if (!appData.showThinking)
5795 ShowThinkingProc(w,event,prms,nprms);
5801 void AnalyzeFileProc(w, event, prms, nprms)
5807 if (!first.analysisSupport) {
5809 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5810 DisplayError(buf, 0);
5814 #ifndef OPTIONSDIALOG
5815 if (!appData.showThinking)
5816 ShowThinkingProc(w,event,prms,nprms);
5819 FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5820 AnalysisPeriodicEvent(1);
5823 void TwoMachinesProc(w, event, prms, nprms)
5832 void MatchProc(w, event, prms, nprms)
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 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6769 /* need to test if messageWidget already exists, since this function
6770 can also be called during the startup, if for example a Xresource
6771 is not set up correctly */
6774 XtSetArg(arg, XtNlabel, message);
6775 XtSetValues(messageWidget, &arg, 1);
6781 void DisplayTitle(text)
6786 char title[MSG_SIZ];
6789 if (text == NULL) text = "";
6791 if (appData.titleInWindow) {
6793 XtSetArg(args[i], XtNlabel, text); i++;
6794 XtSetValues(titleWidget, args, i);
6797 if (*text != NULLCHAR) {
6798 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6799 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6800 } else if (appData.icsActive) {
6801 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6802 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6803 } else if (appData.cmailGameName[0] != NULLCHAR) {
6804 snprintf(icon, sizeof(icon), "%s", "CMail");
6805 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6807 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6808 } else if (gameInfo.variant == VariantGothic) {
6809 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6810 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6813 } else if (gameInfo.variant == VariantFalcon) {
6814 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6815 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6817 } else if (appData.noChessProgram) {
6818 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6819 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6821 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6822 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6825 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6826 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6827 XtSetValues(shellWidget, args, i);
6832 DisplayError(message, error)
6839 if (appData.debugMode || appData.matchMode) {
6840 fprintf(stderr, "%s: %s\n", programName, message);
6843 if (appData.debugMode || appData.matchMode) {
6844 fprintf(stderr, "%s: %s: %s\n",
6845 programName, message, strerror(error));
6847 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6850 ErrorPopUp(_("Error"), message, FALSE);
6854 void DisplayMoveError(message)
6859 DrawPosition(FALSE, NULL);
6860 if (appData.debugMode || appData.matchMode) {
6861 fprintf(stderr, "%s: %s\n", programName, message);
6863 if (appData.popupMoveErrors) {
6864 ErrorPopUp(_("Error"), message, FALSE);
6866 DisplayMessage(message, "");
6871 void DisplayFatalError(message, error, status)
6877 errorExitStatus = status;
6879 fprintf(stderr, "%s: %s\n", programName, message);
6881 fprintf(stderr, "%s: %s: %s\n",
6882 programName, message, strerror(error));
6883 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6886 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6887 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6893 void DisplayInformation(message)
6897 ErrorPopUp(_("Information"), message, TRUE);
6900 void DisplayNote(message)
6904 ErrorPopUp(_("Note"), message, FALSE);
6908 NullXErrorCheck(dpy, error_event)
6910 XErrorEvent *error_event;
6915 void DisplayIcsInteractionTitle(message)
6918 if (oldICSInteractionTitle == NULL) {
6919 /* Magic to find the old window title, adapted from vim */
6920 char *wina = getenv("WINDOWID");
6922 Window win = (Window) atoi(wina);
6923 Window root, parent, *children;
6924 unsigned int nchildren;
6925 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6927 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6928 if (!XQueryTree(xDisplay, win, &root, &parent,
6929 &children, &nchildren)) break;
6930 if (children) XFree((void *)children);
6931 if (parent == root || parent == 0) break;
6934 XSetErrorHandler(oldHandler);
6936 if (oldICSInteractionTitle == NULL) {
6937 oldICSInteractionTitle = "xterm";
6940 printf("\033]0;%s\007", message);
6944 char pendingReplyPrefix[MSG_SIZ];
6945 ProcRef pendingReplyPR;
6947 void AskQuestionProc(w, event, prms, nprms)
6954 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
6958 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6961 void AskQuestionPopDown()
6963 if (!askQuestionUp) return;
6964 XtPopdown(askQuestionShell);
6965 XtDestroyWidget(askQuestionShell);
6966 askQuestionUp = False;
6969 void AskQuestionReplyAction(w, event, prms, nprms)
6979 reply = XawDialogGetValueString(w = XtParent(w));
6980 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
6981 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
6982 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
6983 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
6984 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6985 AskQuestionPopDown();
6987 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
6990 void AskQuestionCallback(w, client_data, call_data)
6992 XtPointer client_data, call_data;
6997 XtSetArg(args[0], XtNlabel, &name);
6998 XtGetValues(w, args, 1);
7000 if (strcmp(name, _("cancel")) == 0) {
7001 AskQuestionPopDown();
7003 AskQuestionReplyAction(w, NULL, NULL, NULL);
7007 void AskQuestion(title, question, replyPrefix, pr)
7008 char *title, *question, *replyPrefix;
7012 Widget popup, layout, dialog, edit;
7018 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7019 pendingReplyPR = pr;
7022 XtSetArg(args[i], XtNresizable, True); i++;
7023 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7024 askQuestionShell = popup =
7025 XtCreatePopupShell(title, transientShellWidgetClass,
7026 shellWidget, args, i);
7029 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7030 layoutArgs, XtNumber(layoutArgs));
7033 XtSetArg(args[i], XtNlabel, question); i++;
7034 XtSetArg(args[i], XtNvalue, ""); i++;
7035 XtSetArg(args[i], XtNborderWidth, 0); i++;
7036 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7039 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7040 (XtPointer) dialog);
7041 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7042 (XtPointer) dialog);
7044 XtRealizeWidget(popup);
7045 CatchDeleteWindow(popup, "AskQuestionPopDown");
7047 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7048 &x, &y, &win_x, &win_y, &mask);
7050 XtSetArg(args[0], XtNx, x - 10);
7051 XtSetArg(args[1], XtNy, y - 30);
7052 XtSetValues(popup, args, 2);
7054 XtPopup(popup, XtGrabExclusive);
7055 askQuestionUp = True;
7057 edit = XtNameToWidget(dialog, "*value");
7058 XtSetKeyboardFocus(popup, edit);
7066 if (*name == NULLCHAR) {
7068 } else if (strcmp(name, "$") == 0) {
7069 putc(BELLCHAR, stderr);
7072 char *prefix = "", *sep = "";
7073 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7074 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7082 PlaySound(appData.soundMove);
7088 PlaySound(appData.soundIcsWin);
7094 PlaySound(appData.soundIcsLoss);
7100 PlaySound(appData.soundIcsDraw);
7104 PlayIcsUnfinishedSound()
7106 PlaySound(appData.soundIcsUnfinished);
7112 PlaySound(appData.soundIcsAlarm);
7118 system("stty echo");
7124 system("stty -echo");
7128 Colorize(cc, continuation)
7133 int count, outCount, error;
7135 if (textColors[(int)cc].bg > 0) {
7136 if (textColors[(int)cc].fg > 0) {
7137 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7138 textColors[(int)cc].fg, textColors[(int)cc].bg);
7140 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7141 textColors[(int)cc].bg);
7144 if (textColors[(int)cc].fg > 0) {
7145 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7146 textColors[(int)cc].fg);
7148 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7151 count = strlen(buf);
7152 outCount = OutputToProcess(NoProc, buf, count, &error);
7153 if (outCount < count) {
7154 DisplayFatalError(_("Error writing to display"), error, 1);
7157 if (continuation) return;
7160 PlaySound(appData.soundShout);
7163 PlaySound(appData.soundSShout);
7166 PlaySound(appData.soundChannel1);
7169 PlaySound(appData.soundChannel);
7172 PlaySound(appData.soundKibitz);
7175 PlaySound(appData.soundTell);
7177 case ColorChallenge:
7178 PlaySound(appData.soundChallenge);
7181 PlaySound(appData.soundRequest);
7184 PlaySound(appData.soundSeek);
7195 return getpwuid(getuid())->pw_name;
7199 ExpandPathName(path)
7202 static char static_buf[4*MSG_SIZ];
7203 char *d, *s, buf[4*MSG_SIZ];
7209 while (*s && isspace(*s))
7218 if (*(s+1) == '/') {
7219 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7223 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7224 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7225 pwd = getpwnam(buf);
7228 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7232 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7233 strcat(d, strchr(s+1, '/'));
7237 safeStrCpy(d, s, 4*MSG_SIZ );
7244 static char host_name[MSG_SIZ];
7246 #if HAVE_GETHOSTNAME
7247 gethostname(host_name, MSG_SIZ);
7249 #else /* not HAVE_GETHOSTNAME */
7250 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7251 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7253 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7255 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7256 #endif /* not HAVE_GETHOSTNAME */
7259 XtIntervalId delayedEventTimerXID = 0;
7260 DelayedEventCallback delayedEventCallback = 0;
7265 delayedEventTimerXID = 0;
7266 delayedEventCallback();
7270 ScheduleDelayedEvent(cb, millisec)
7271 DelayedEventCallback cb; long millisec;
7273 if(delayedEventTimerXID && delayedEventCallback == cb)
7274 // [HGM] alive: replace, rather than add or flush identical event
7275 XtRemoveTimeOut(delayedEventTimerXID);
7276 delayedEventCallback = cb;
7277 delayedEventTimerXID =
7278 XtAppAddTimeOut(appContext, millisec,
7279 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7282 DelayedEventCallback
7285 if (delayedEventTimerXID) {
7286 return delayedEventCallback;
7293 CancelDelayedEvent()
7295 if (delayedEventTimerXID) {
7296 XtRemoveTimeOut(delayedEventTimerXID);
7297 delayedEventTimerXID = 0;
7301 XtIntervalId loadGameTimerXID = 0;
7303 int LoadGameTimerRunning()
7305 return loadGameTimerXID != 0;
7308 int StopLoadGameTimer()
7310 if (loadGameTimerXID != 0) {
7311 XtRemoveTimeOut(loadGameTimerXID);
7312 loadGameTimerXID = 0;
7320 LoadGameTimerCallback(arg, id)
7324 loadGameTimerXID = 0;
7329 StartLoadGameTimer(millisec)
7333 XtAppAddTimeOut(appContext, millisec,
7334 (XtTimerCallbackProc) LoadGameTimerCallback,
7338 XtIntervalId analysisClockXID = 0;
7341 AnalysisClockCallback(arg, id)
7345 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7346 || appData.icsEngineAnalyze) { // [DM]
7347 AnalysisPeriodicEvent(0);
7348 StartAnalysisClock();
7353 StartAnalysisClock()
7356 XtAppAddTimeOut(appContext, 2000,
7357 (XtTimerCallbackProc) AnalysisClockCallback,
7361 XtIntervalId clockTimerXID = 0;
7363 int ClockTimerRunning()
7365 return clockTimerXID != 0;
7368 int StopClockTimer()
7370 if (clockTimerXID != 0) {
7371 XtRemoveTimeOut(clockTimerXID);
7380 ClockTimerCallback(arg, id)
7389 StartClockTimer(millisec)
7393 XtAppAddTimeOut(appContext, millisec,
7394 (XtTimerCallbackProc) ClockTimerCallback,
7399 DisplayTimerLabel(w, color, timer, highlight)
7408 /* check for low time warning */
7409 Pixel foregroundOrWarningColor = timerForegroundPixel;
7412 appData.lowTimeWarning &&
7413 (timer / 1000) < appData.icsAlarmTime)
7414 foregroundOrWarningColor = lowTimeWarningColor;
7416 if (appData.clockMode) {
7417 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7418 XtSetArg(args[0], XtNlabel, buf);
7420 snprintf(buf, MSG_SIZ, "%s ", color);
7421 XtSetArg(args[0], XtNlabel, buf);
7426 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7427 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7429 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7430 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7433 XtSetValues(w, args, 3);
7437 DisplayWhiteClock(timeRemaining, highlight)
7443 if(appData.noGUI) return;
7444 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7445 if (highlight && iconPixmap == bIconPixmap) {
7446 iconPixmap = wIconPixmap;
7447 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7448 XtSetValues(shellWidget, args, 1);
7453 DisplayBlackClock(timeRemaining, highlight)
7459 if(appData.noGUI) return;
7460 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7461 if (highlight && iconPixmap == wIconPixmap) {
7462 iconPixmap = bIconPixmap;
7463 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7464 XtSetValues(shellWidget, args, 1);
7482 int StartChildProcess(cmdLine, dir, pr)
7489 int to_prog[2], from_prog[2];
7493 if (appData.debugMode) {
7494 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7497 /* We do NOT feed the cmdLine to the shell; we just
7498 parse it into blank-separated arguments in the
7499 most simple-minded way possible.
7502 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7505 while(*p == ' ') p++;
7507 if(*p == '"' || *p == '\'')
7508 p = strchr(++argv[i-1], *p);
7509 else p = strchr(p, ' ');
7510 if (p == NULL) break;
7515 SetUpChildIO(to_prog, from_prog);
7517 if ((pid = fork()) == 0) {
7519 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7520 close(to_prog[1]); // first close the unused pipe ends
7521 close(from_prog[0]);
7522 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7523 dup2(from_prog[1], 1);
7524 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7525 close(from_prog[1]); // and closing again loses one of the pipes!
7526 if(fileno(stderr) >= 2) // better safe than sorry...
7527 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7529 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7534 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7536 execvp(argv[0], argv);
7538 /* If we get here, exec failed */
7543 /* Parent process */
7545 close(from_prog[1]);
7547 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7550 cp->fdFrom = from_prog[0];
7551 cp->fdTo = to_prog[1];
7556 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7557 static RETSIGTYPE AlarmCallBack(int n)
7563 DestroyChildProcess(pr, signalType)
7567 ChildProc *cp = (ChildProc *) pr;
7569 if (cp->kind != CPReal) return;
7571 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7572 signal(SIGALRM, AlarmCallBack);
7574 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7575 kill(cp->pid, SIGKILL); // kill it forcefully
7576 wait((int *) 0); // and wait again
7580 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7582 /* Process is exiting either because of the kill or because of
7583 a quit command sent by the backend; either way, wait for it to die.
7592 InterruptChildProcess(pr)
7595 ChildProc *cp = (ChildProc *) pr;
7597 if (cp->kind != CPReal) return;
7598 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7601 int OpenTelnet(host, port, pr)
7606 char cmdLine[MSG_SIZ];
7608 if (port[0] == NULLCHAR) {
7609 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7611 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7613 return StartChildProcess(cmdLine, "", pr);
7616 int OpenTCP(host, port, pr)
7622 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7623 #else /* !OMIT_SOCKETS */
7624 struct addrinfo hints;
7625 struct addrinfo *ais, *ai;
7630 memset(&hints, 0, sizeof(hints));
7631 hints.ai_family = AF_UNSPEC;
7632 hints.ai_socktype = SOCK_STREAM;
7634 error = getaddrinfo(host, port, &hints, &ais);
7636 /* a getaddrinfo error is not an errno, so can't return it */
7637 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7638 host, port, gai_strerror(error));
7642 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7643 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7647 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7660 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7666 #endif /* !OMIT_SOCKETS */
7671 int OpenCommPort(name, pr)
7678 fd = open(name, 2, 0);
7679 if (fd < 0) return errno;
7681 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7691 int OpenLoopback(pr)
7697 SetUpChildIO(to, from);
7699 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7702 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7709 int OpenRcmd(host, user, cmd, pr)
7710 char *host, *user, *cmd;
7713 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7717 #define INPUT_SOURCE_BUF_SIZE 8192
7726 char buf[INPUT_SOURCE_BUF_SIZE];
7731 DoInputCallback(closure, source, xid)
7736 InputSource *is = (InputSource *) closure;
7741 if (is->lineByLine) {
7742 count = read(is->fd, is->unused,
7743 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7745 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7748 is->unused += count;
7750 while (p < is->unused) {
7751 q = memchr(p, '\n', is->unused - p);
7752 if (q == NULL) break;
7754 (is->func)(is, is->closure, p, q - p, 0);
7758 while (p < is->unused) {
7763 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7768 (is->func)(is, is->closure, is->buf, count, error);
7772 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7779 ChildProc *cp = (ChildProc *) pr;
7781 is = (InputSource *) calloc(1, sizeof(InputSource));
7782 is->lineByLine = lineByLine;
7786 is->fd = fileno(stdin);
7788 is->kind = cp->kind;
7789 is->fd = cp->fdFrom;
7792 is->unused = is->buf;
7795 is->xid = XtAppAddInput(appContext, is->fd,
7796 (XtPointer) (XtInputReadMask),
7797 (XtInputCallbackProc) DoInputCallback,
7799 is->closure = closure;
7800 return (InputSourceRef) is;
7804 RemoveInputSource(isr)
7807 InputSource *is = (InputSource *) isr;
7809 if (is->xid == 0) return;
7810 XtRemoveInput(is->xid);
7814 int OutputToProcess(pr, message, count, outError)
7820 static int line = 0;
7821 ChildProc *cp = (ChildProc *) pr;
7826 if (appData.noJoin || !appData.useInternalWrap)
7827 outCount = fwrite(message, 1, count, stdout);
7830 int width = get_term_width();
7831 int len = wrap(NULL, message, count, width, &line);
7832 char *msg = malloc(len);
7836 outCount = fwrite(message, 1, count, stdout);
7839 dbgchk = wrap(msg, message, count, width, &line);
7840 if (dbgchk != len && appData.debugMode)
7841 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7842 outCount = fwrite(msg, 1, dbgchk, stdout);
7848 outCount = write(cp->fdTo, message, count);
7858 /* Output message to process, with "ms" milliseconds of delay
7859 between each character. This is needed when sending the logon
7860 script to ICC, which for some reason doesn't like the
7861 instantaneous send. */
7862 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
7869 ChildProc *cp = (ChildProc *) pr;
7874 r = write(cp->fdTo, message++, 1);
7887 /**** Animation code by Hugh Fisher, DCS, ANU.
7889 Known problem: if a window overlapping the board is
7890 moved away while a piece is being animated underneath,
7891 the newly exposed area won't be updated properly.
7892 I can live with this.
7894 Known problem: if you look carefully at the animation
7895 of pieces in mono mode, they are being drawn as solid
7896 shapes without interior detail while moving. Fixing
7897 this would be a major complication for minimal return.
7900 /* Masks for XPM pieces. Black and white pieces can have
7901 different shapes, but in the interest of retaining my
7902 sanity pieces must have the same outline on both light
7903 and dark squares, and all pieces must use the same
7904 background square colors/images. */
7906 static int xpmDone = 0;
7909 CreateAnimMasks (pieceDepth)
7916 unsigned long plane;
7919 /* Need a bitmap just to get a GC with right depth */
7920 buf = XCreatePixmap(xDisplay, xBoardWindow,
7922 values.foreground = 1;
7923 values.background = 0;
7924 /* Don't use XtGetGC, not read only */
7925 maskGC = XCreateGC(xDisplay, buf,
7926 GCForeground | GCBackground, &values);
7927 XFreePixmap(xDisplay, buf);
7929 buf = XCreatePixmap(xDisplay, xBoardWindow,
7930 squareSize, squareSize, pieceDepth);
7931 values.foreground = XBlackPixel(xDisplay, xScreen);
7932 values.background = XWhitePixel(xDisplay, xScreen);
7933 bufGC = XCreateGC(xDisplay, buf,
7934 GCForeground | GCBackground, &values);
7936 for (piece = WhitePawn; piece <= BlackKing; piece++) {
7937 /* Begin with empty mask */
7938 if(!xpmDone) // [HGM] pieces: keep using existing
7939 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7940 squareSize, squareSize, 1);
7941 XSetFunction(xDisplay, maskGC, GXclear);
7942 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7943 0, 0, squareSize, squareSize);
7945 /* Take a copy of the piece */
7950 XSetFunction(xDisplay, bufGC, GXcopy);
7951 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
7953 0, 0, squareSize, squareSize, 0, 0);
7955 /* XOR the background (light) over the piece */
7956 XSetFunction(xDisplay, bufGC, GXxor);
7958 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7959 0, 0, squareSize, squareSize, 0, 0);
7961 XSetForeground(xDisplay, bufGC, lightSquareColor);
7962 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7965 /* We now have an inverted piece image with the background
7966 erased. Construct mask by just selecting all the non-zero
7967 pixels - no need to reconstruct the original image. */
7968 XSetFunction(xDisplay, maskGC, GXor);
7970 /* Might be quicker to download an XImage and create bitmap
7971 data from it rather than this N copies per piece, but it
7972 only takes a fraction of a second and there is a much
7973 longer delay for loading the pieces. */
7974 for (n = 0; n < pieceDepth; n ++) {
7975 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7976 0, 0, squareSize, squareSize,
7982 XFreePixmap(xDisplay, buf);
7983 XFreeGC(xDisplay, bufGC);
7984 XFreeGC(xDisplay, maskGC);
7988 InitAnimState (anim, info)
7990 XWindowAttributes * info;
7995 /* Each buffer is square size, same depth as window */
7996 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
7997 squareSize, squareSize, info->depth);
7998 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
7999 squareSize, squareSize, info->depth);
8001 /* Create a plain GC for blitting */
8002 mask = GCForeground | GCBackground | GCFunction |
8003 GCPlaneMask | GCGraphicsExposures;
8004 values.foreground = XBlackPixel(xDisplay, xScreen);
8005 values.background = XWhitePixel(xDisplay, xScreen);
8006 values.function = GXcopy;
8007 values.plane_mask = AllPlanes;
8008 values.graphics_exposures = False;
8009 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8011 /* Piece will be copied from an existing context at
8012 the start of each new animation/drag. */
8013 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8015 /* Outline will be a read-only copy of an existing */
8016 anim->outlineGC = None;
8022 XWindowAttributes info;
8024 if (xpmDone && gameInfo.variant == oldVariant) return;
8025 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
8026 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8028 InitAnimState(&game, &info);
8029 InitAnimState(&player, &info);
8031 /* For XPM pieces, we need bitmaps to use as masks. */
8033 CreateAnimMasks(info.depth), xpmDone = 1;
8038 static Boolean frameWaiting;
8040 static RETSIGTYPE FrameAlarm (sig)
8043 frameWaiting = False;
8044 /* In case System-V style signals. Needed?? */
8045 signal(SIGALRM, FrameAlarm);
8052 struct itimerval delay;
8054 XSync(xDisplay, False);
8057 frameWaiting = True;
8058 signal(SIGALRM, FrameAlarm);
8059 delay.it_interval.tv_sec =
8060 delay.it_value.tv_sec = time / 1000;
8061 delay.it_interval.tv_usec =
8062 delay.it_value.tv_usec = (time % 1000) * 1000;
8063 setitimer(ITIMER_REAL, &delay, NULL);
8064 while (frameWaiting) pause();
8065 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8066 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8067 setitimer(ITIMER_REAL, &delay, NULL);
8077 XSync(xDisplay, False);
8079 usleep(time * 1000);
8084 /* Convert board position to corner of screen rect and color */
8087 ScreenSquare(column, row, pt, color)
8088 int column; int row; XPoint * pt; int * color;
8091 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8092 pt->y = lineGap + row * (squareSize + lineGap);
8094 pt->x = lineGap + column * (squareSize + lineGap);
8095 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8097 *color = SquareColor(row, column);
8100 /* Convert window coords to square */
8103 BoardSquare(x, y, column, row)
8104 int x; int y; int * column; int * row;
8106 *column = EventToSquare(x, BOARD_WIDTH);
8107 if (flipView && *column >= 0)
8108 *column = BOARD_WIDTH - 1 - *column;
8109 *row = EventToSquare(y, BOARD_HEIGHT);
8110 if (!flipView && *row >= 0)
8111 *row = BOARD_HEIGHT - 1 - *row;
8116 #undef Max /* just in case */
8118 #define Max(a, b) ((a) > (b) ? (a) : (b))
8119 #define Min(a, b) ((a) < (b) ? (a) : (b))
8122 SetRect(rect, x, y, width, height)
8123 XRectangle * rect; int x; int y; int width; int height;
8127 rect->width = width;
8128 rect->height = height;
8131 /* Test if two frames overlap. If they do, return
8132 intersection rect within old and location of
8133 that rect within new. */
8136 Intersect(old, new, size, area, pt)
8137 XPoint * old; XPoint * new;
8138 int size; XRectangle * area; XPoint * pt;
8140 if (old->x > new->x + size || new->x > old->x + size ||
8141 old->y > new->y + size || new->y > old->y + size) {
8144 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8145 size - abs(old->x - new->x), size - abs(old->y - new->y));
8146 pt->x = Max(old->x - new->x, 0);
8147 pt->y = Max(old->y - new->y, 0);
8152 /* For two overlapping frames, return the rect(s)
8153 in the old that do not intersect with the new. */
8156 CalcUpdateRects(old, new, size, update, nUpdates)
8157 XPoint * old; XPoint * new; int size;
8158 XRectangle update[]; int * nUpdates;
8162 /* If old = new (shouldn't happen) then nothing to draw */
8163 if (old->x == new->x && old->y == new->y) {
8167 /* Work out what bits overlap. Since we know the rects
8168 are the same size we don't need a full intersect calc. */
8170 /* Top or bottom edge? */
8171 if (new->y > old->y) {
8172 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8174 } else if (old->y > new->y) {
8175 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8176 size, old->y - new->y);
8179 /* Left or right edge - don't overlap any update calculated above. */
8180 if (new->x > old->x) {
8181 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8182 new->x - old->x, size - abs(new->y - old->y));
8184 } else if (old->x > new->x) {
8185 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8186 old->x - new->x, size - abs(new->y - old->y));
8193 /* Generate a series of frame coords from start->mid->finish.
8194 The movement rate doubles until the half way point is
8195 reached, then halves back down to the final destination,
8196 which gives a nice slow in/out effect. The algorithmn
8197 may seem to generate too many intermediates for short
8198 moves, but remember that the purpose is to attract the
8199 viewers attention to the piece about to be moved and
8200 then to where it ends up. Too few frames would be less
8204 Tween(start, mid, finish, factor, frames, nFrames)
8205 XPoint * start; XPoint * mid;
8206 XPoint * finish; int factor;
8207 XPoint frames[]; int * nFrames;
8209 int fraction, n, count;
8213 /* Slow in, stepping 1/16th, then 1/8th, ... */
8215 for (n = 0; n < factor; n++)
8217 for (n = 0; n < factor; n++) {
8218 frames[count].x = start->x + (mid->x - start->x) / fraction;
8219 frames[count].y = start->y + (mid->y - start->y) / fraction;
8221 fraction = fraction / 2;
8225 frames[count] = *mid;
8228 /* Slow out, stepping 1/2, then 1/4, ... */
8230 for (n = 0; n < factor; n++) {
8231 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8232 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8234 fraction = fraction * 2;
8239 /* Draw a piece on the screen without disturbing what's there */
8242 SelectGCMask(piece, clip, outline, mask)
8243 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8247 /* Bitmap for piece being moved. */
8248 if (appData.monoMode) {
8249 *mask = *pieceToSolid(piece);
8250 } else if (useImages) {
8252 *mask = xpmMask[piece];
8254 *mask = ximMaskPm[piece];
8257 *mask = *pieceToSolid(piece);
8260 /* GC for piece being moved. Square color doesn't matter, but
8261 since it gets modified we make a copy of the original. */
8263 if (appData.monoMode)
8268 if (appData.monoMode)
8273 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8275 /* Outline only used in mono mode and is not modified */
8277 *outline = bwPieceGC;
8279 *outline = wbPieceGC;
8283 OverlayPiece(piece, clip, outline, dest)
8284 ChessSquare piece; GC clip; GC outline; Drawable dest;
8289 /* Draw solid rectangle which will be clipped to shape of piece */
8290 XFillRectangle(xDisplay, dest, clip,
8291 0, 0, squareSize, squareSize);
8292 if (appData.monoMode)
8293 /* Also draw outline in contrasting color for black
8294 on black / white on white cases */
8295 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8296 0, 0, squareSize, squareSize, 0, 0, 1);
8298 /* Copy the piece */
8303 if(appData.upsideDown && flipView) kind ^= 2;
8304 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8306 0, 0, squareSize, squareSize,
8311 /* Animate the movement of a single piece */
8314 BeginAnimation(anim, piece, startColor, start)
8322 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8323 /* The old buffer is initialised with the start square (empty) */
8324 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8325 anim->prevFrame = *start;
8327 /* The piece will be drawn using its own bitmap as a matte */
8328 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8329 XSetClipMask(xDisplay, anim->pieceGC, mask);
8333 AnimationFrame(anim, frame, piece)
8338 XRectangle updates[4];
8343 /* Save what we are about to draw into the new buffer */
8344 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8345 frame->x, frame->y, squareSize, squareSize,
8348 /* Erase bits of the previous frame */
8349 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8350 /* Where the new frame overlapped the previous,
8351 the contents in newBuf are wrong. */
8352 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8353 overlap.x, overlap.y,
8354 overlap.width, overlap.height,
8356 /* Repaint the areas in the old that don't overlap new */
8357 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8358 for (i = 0; i < count; i++)
8359 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8360 updates[i].x - anim->prevFrame.x,
8361 updates[i].y - anim->prevFrame.y,
8362 updates[i].width, updates[i].height,
8363 updates[i].x, updates[i].y);
8365 /* Easy when no overlap */
8366 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8367 0, 0, squareSize, squareSize,
8368 anim->prevFrame.x, anim->prevFrame.y);
8371 /* Save this frame for next time round */
8372 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8373 0, 0, squareSize, squareSize,
8375 anim->prevFrame = *frame;
8377 /* Draw piece over original screen contents, not current,
8378 and copy entire rect. Wipes out overlapping piece images. */
8379 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8380 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8381 0, 0, squareSize, squareSize,
8382 frame->x, frame->y);
8386 EndAnimation (anim, finish)
8390 XRectangle updates[4];
8395 /* The main code will redraw the final square, so we
8396 only need to erase the bits that don't overlap. */
8397 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8398 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8399 for (i = 0; i < count; i++)
8400 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8401 updates[i].x - anim->prevFrame.x,
8402 updates[i].y - anim->prevFrame.y,
8403 updates[i].width, updates[i].height,
8404 updates[i].x, updates[i].y);
8406 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8407 0, 0, squareSize, squareSize,
8408 anim->prevFrame.x, anim->prevFrame.y);
8413 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8415 ChessSquare piece; int startColor;
8416 XPoint * start; XPoint * finish;
8417 XPoint frames[]; int nFrames;
8421 BeginAnimation(anim, piece, startColor, start);
8422 for (n = 0; n < nFrames; n++) {
8423 AnimationFrame(anim, &(frames[n]), piece);
8424 FrameDelay(appData.animSpeed);
8426 EndAnimation(anim, finish);
8430 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8433 ChessSquare piece = board[fromY][toY];
8434 board[fromY][toY] = EmptySquare;
8435 DrawPosition(FALSE, board);
8437 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8438 y = lineGap + toY * (squareSize + lineGap);
8440 x = lineGap + toX * (squareSize + lineGap);
8441 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8443 for(i=1; i<4*kFactor; i++) {
8444 int r = squareSize * 9 * i/(20*kFactor - 5);
8445 XFillArc(xDisplay, xBoardWindow, highlineGC,
8446 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8447 FrameDelay(appData.animSpeed);
8449 board[fromY][toY] = piece;
8452 /* Main control logic for deciding what to animate and how */
8455 AnimateMove(board, fromX, fromY, toX, toY)
8464 XPoint start, finish, mid;
8465 XPoint frames[kFactor * 2 + 1];
8466 int nFrames, startColor, endColor;
8468 /* Are we animating? */
8469 if (!appData.animate || appData.blindfold)
8472 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8473 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8474 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8476 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8477 piece = board[fromY][fromX];
8478 if (piece >= EmptySquare) return;
8483 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8486 if (appData.debugMode) {
8487 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8488 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8489 piece, fromX, fromY, toX, toY); }
8491 ScreenSquare(fromX, fromY, &start, &startColor);
8492 ScreenSquare(toX, toY, &finish, &endColor);
8495 /* Knight: make straight movement then diagonal */
8496 if (abs(toY - fromY) < abs(toX - fromX)) {
8497 mid.x = start.x + (finish.x - start.x) / 2;
8501 mid.y = start.y + (finish.y - start.y) / 2;
8504 mid.x = start.x + (finish.x - start.x) / 2;
8505 mid.y = start.y + (finish.y - start.y) / 2;
8508 /* Don't use as many frames for very short moves */
8509 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8510 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8512 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8513 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8514 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8516 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8517 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8520 /* Be sure end square is redrawn */
8521 damage[0][toY][toX] = True;
8525 DragPieceBegin(x, y)
8528 int boardX, boardY, color;
8531 /* Are we animating? */
8532 if (!appData.animateDragging || appData.blindfold)
8535 /* Figure out which square we start in and the
8536 mouse position relative to top left corner. */
8537 BoardSquare(x, y, &boardX, &boardY);
8538 player.startBoardX = boardX;
8539 player.startBoardY = boardY;
8540 ScreenSquare(boardX, boardY, &corner, &color);
8541 player.startSquare = corner;
8542 player.startColor = color;
8543 /* As soon as we start dragging, the piece will jump slightly to
8544 be centered over the mouse pointer. */
8545 player.mouseDelta.x = squareSize/2;
8546 player.mouseDelta.y = squareSize/2;
8547 /* Initialise animation */
8548 player.dragPiece = PieceForSquare(boardX, boardY);
8550 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8551 player.dragActive = True;
8552 BeginAnimation(&player, player.dragPiece, color, &corner);
8553 /* Mark this square as needing to be redrawn. Note that
8554 we don't remove the piece though, since logically (ie
8555 as seen by opponent) the move hasn't been made yet. */
8556 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8557 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8558 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8559 corner.x, corner.y, squareSize, squareSize,
8560 0, 0); // [HGM] zh: unstack in stead of grab
8561 if(gatingPiece != EmptySquare) {
8562 /* Kludge alert: When gating we want the introduced
8563 piece to appear on the from square. To generate an
8564 image of it, we draw it on the board, copy the image,
8565 and draw the original piece again. */
8566 ChessSquare piece = boards[currentMove][boardY][boardX];
8567 DrawSquare(boardY, boardX, gatingPiece, 0);
8568 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8569 corner.x, corner.y, squareSize, squareSize, 0, 0);
8570 DrawSquare(boardY, boardX, piece, 0);
8572 damage[0][boardY][boardX] = True;
8574 player.dragActive = False;
8579 ChangeDragPiece(ChessSquare piece)
8582 player.dragPiece = piece;
8583 /* The piece will be drawn using its own bitmap as a matte */
8584 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8585 XSetClipMask(xDisplay, player.pieceGC, mask);
8594 /* Are we animating? */
8595 if (!appData.animateDragging || appData.blindfold)
8599 if (! player.dragActive)
8601 /* Move piece, maintaining same relative position
8602 of mouse within square */
8603 corner.x = x - player.mouseDelta.x;
8604 corner.y = y - player.mouseDelta.y;
8605 AnimationFrame(&player, &corner, player.dragPiece);
8607 if (appData.highlightDragging) {
8609 BoardSquare(x, y, &boardX, &boardY);
8610 SetHighlights(fromX, fromY, boardX, boardY);
8619 int boardX, boardY, color;
8622 /* Are we animating? */
8623 if (!appData.animateDragging || appData.blindfold)
8627 if (! player.dragActive)
8629 /* Last frame in sequence is square piece is
8630 placed on, which may not match mouse exactly. */
8631 BoardSquare(x, y, &boardX, &boardY);
8632 ScreenSquare(boardX, boardY, &corner, &color);
8633 EndAnimation(&player, &corner);
8635 /* Be sure end square is redrawn */
8636 damage[0][boardY][boardX] = True;
8638 /* This prevents weird things happening with fast successive
8639 clicks which on my Sun at least can cause motion events
8640 without corresponding press/release. */
8641 player.dragActive = False;
8644 /* Handle expose event while piece being dragged */
8649 if (!player.dragActive || appData.blindfold)
8652 /* What we're doing: logically, the move hasn't been made yet,
8653 so the piece is still in it's original square. But visually
8654 it's being dragged around the board. So we erase the square
8655 that the piece is on and draw it at the last known drag point. */
8656 BlankSquare(player.startSquare.x, player.startSquare.y,
8657 player.startColor, EmptySquare, xBoardWindow, 1);
8658 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8659 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8662 #include <sys/ioctl.h>
8663 int get_term_width()
8665 int fd, default_width;
8668 default_width = 79; // this is FICS default anyway...
8670 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8672 if (!ioctl(fd, TIOCGSIZE, &win))
8673 default_width = win.ts_cols;
8674 #elif defined(TIOCGWINSZ)
8676 if (!ioctl(fd, TIOCGWINSZ, &win))
8677 default_width = win.ws_col;
8679 return default_width;
8685 static int old_width = 0;
8686 int new_width = get_term_width();
8688 if (old_width != new_width)
8689 ics_printf("set width %d\n", new_width);
8690 old_width = new_width;
8693 void NotifyFrontendLogin()
8698 /* [AS] Arrow highlighting support */
8700 static double A_WIDTH = 5; /* Width of arrow body */
8702 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8703 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8705 static double Sqr( double x )
8710 static int Round( double x )
8712 return (int) (x + 0.5);
8715 void SquareToPos(int rank, int file, int *x, int *y)
8718 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8719 *y = lineGap + rank * (squareSize + lineGap);
8721 *x = lineGap + file * (squareSize + lineGap);
8722 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8726 /* Draw an arrow between two points using current settings */
8727 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8730 double dx, dy, j, k, x, y;
8733 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8735 arrow[0].x = s_x + A_WIDTH + 0.5;
8738 arrow[1].x = s_x + A_WIDTH + 0.5;
8739 arrow[1].y = d_y - h;
8741 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8742 arrow[2].y = d_y - h;
8747 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8748 arrow[5].y = d_y - h;
8750 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8751 arrow[4].y = d_y - h;
8753 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8756 else if( d_y == s_y ) {
8757 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8760 arrow[0].y = s_y + A_WIDTH + 0.5;
8762 arrow[1].x = d_x - w;
8763 arrow[1].y = s_y + A_WIDTH + 0.5;
8765 arrow[2].x = d_x - w;
8766 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8771 arrow[5].x = d_x - w;
8772 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8774 arrow[4].x = d_x - w;
8775 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8778 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8781 /* [AS] Needed a lot of paper for this! :-) */
8782 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8783 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8785 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8787 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8792 arrow[0].x = Round(x - j);
8793 arrow[0].y = Round(y + j*dx);
8795 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8796 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8799 x = (double) d_x - k;
8800 y = (double) d_y - k*dy;
8803 x = (double) d_x + k;
8804 y = (double) d_y + k*dy;
8807 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8809 arrow[6].x = Round(x - j);
8810 arrow[6].y = Round(y + j*dx);
8812 arrow[2].x = Round(arrow[6].x + 2*j);
8813 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8815 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8816 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8821 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8822 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8825 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8826 // Polygon( hdc, arrow, 7 );
8829 /* [AS] Draw an arrow between two squares */
8830 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
8832 int s_x, s_y, d_x, d_y, hor, vert, i;
8834 if( s_col == d_col && s_row == d_row ) {
8838 /* Get source and destination points */
8839 SquareToPos( s_row, s_col, &s_x, &s_y);
8840 SquareToPos( d_row, d_col, &d_x, &d_y);
8843 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8845 else if( d_y < s_y ) {
8846 d_y += squareSize / 2 + squareSize / 4;
8849 d_y += squareSize / 2;
8853 d_x += squareSize / 2 - squareSize / 4;
8855 else if( d_x < s_x ) {
8856 d_x += squareSize / 2 + squareSize / 4;
8859 d_x += squareSize / 2;
8862 s_x += squareSize / 2;
8863 s_y += squareSize / 2;
8866 A_WIDTH = squareSize / 14.; //[HGM] make float
8868 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8870 hor = 64*s_col + 32; vert = 64*s_row + 32;
8871 for(i=0; i<= 64; i++) {
8872 damage[0][vert+6>>6][hor+6>>6] = True;
8873 damage[0][vert-6>>6][hor+6>>6] = True;
8874 damage[0][vert+6>>6][hor-6>>6] = True;
8875 damage[0][vert-6>>6][hor-6>>6] = True;
8876 hor += d_col - s_col; vert += d_row - s_row;
8880 Boolean IsDrawArrowEnabled()
8882 return appData.highlightMoveWithArrow && squareSize >= 32;
8885 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
8887 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8888 DrawArrowBetweenSquares(fromX, fromY, toX, toY);