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 frankm@hiwaay.net.\n\
1270 Include system type & operating system in message.\n"));
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 },
2804 Enables ncpEnables[] = {
2805 { "menuFile.Mail Move", False },
2806 { "menuFile.Reload CMail Message", False },
2807 { "menuMode.Machine White", False },
2808 { "menuMode.Machine Black", False },
2809 { "menuMode.Analysis Mode", False },
2810 { "menuMode.Analyze File", False },
2811 { "menuMode.Two Machines", False },
2812 { "menuMode.Machine Match", False },
2813 { "menuMode.ICS Client", False },
2814 { "menuView.ICStex", False },
2815 { "menuView.ICS Input Box", False },
2816 { "Action", False },
2817 { "menuEdit.Revert", False },
2818 { "menuEdit.Annotate", False },
2819 { "menuEngine.Engine #1 Settings", False },
2820 { "menuEngine.Engine #2 Settings", False },
2821 { "menuEngine.Move Now", False },
2822 { "menuEngine.Retract Move", False },
2823 { "menuOptions.ICS", False },
2824 #ifndef OPTIONSDIALOG
2825 { "menuOptions.Auto Flag", False },
2826 { "menuOptions.Auto Flip View", False },
2827 // { "menuOptions.ICS Alarm", False },
2828 { "menuOptions.Move Sound", False },
2829 { "menuOptions.Hide Thinking", False },
2830 { "menuOptions.Periodic Updates", False },
2831 { "menuOptions.Ponder Next Move", False },
2833 { "menuEngine.Hint", False },
2834 { "menuEngine.Book", False },
2838 Enables gnuEnables[] = {
2839 { "menuMode.ICS Client", False },
2840 { "menuView.ICStex", False },
2841 { "menuView.ICS Input Box", False },
2842 { "menuAction.Accept", False },
2843 { "menuAction.Decline", False },
2844 { "menuAction.Rematch", False },
2845 { "menuAction.Adjourn", False },
2846 { "menuAction.Stop Examining", False },
2847 { "menuAction.Stop Observing", False },
2848 { "menuAction.Upload to Examine", False },
2849 { "menuEdit.Revert", False },
2850 { "menuEdit.Annotate", False },
2851 { "menuOptions.ICS", False },
2853 /* The next two options rely on SetCmailMode being called *after* */
2854 /* SetGNUMode so that when GNU is being used to give hints these */
2855 /* menu options are still available */
2857 { "menuFile.Mail Move", False },
2858 { "menuFile.Reload CMail Message", False },
2859 // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2860 { "menuMode.Machine White", True },
2861 { "menuMode.Machine Black", True },
2862 { "menuMode.Analysis Mode", True },
2863 { "menuMode.Analyze File", True },
2864 { "menuMode.Two Machines", True },
2865 { "menuMode.Machine Match", True },
2866 { "menuEngine.Engine #1 Settings", True },
2867 { "menuEngine.Engine #2 Settings", True },
2868 { "menuEngine.Hint", True },
2869 { "menuEngine.Book", True },
2870 { "menuEngine.Move Now", True },
2871 { "menuEngine.Retract Move", True },
2876 Enables cmailEnables[] = {
2878 { "menuAction.Call Flag", False },
2879 { "menuAction.Draw", True },
2880 { "menuAction.Adjourn", False },
2881 { "menuAction.Abort", False },
2882 { "menuAction.Stop Observing", False },
2883 { "menuAction.Stop Examining", False },
2884 { "menuFile.Mail Move", True },
2885 { "menuFile.Reload CMail Message", True },
2889 Enables trainingOnEnables[] = {
2890 { "menuMode.Edit Comment", False },
2891 { "menuMode.Pause", False },
2892 { "menuEdit.Forward", False },
2893 { "menuEdit.Backward", False },
2894 { "menuEdit.Forward to End", False },
2895 { "menuEdit.Back to Start", False },
2896 { "menuEngine.Move Now", False },
2897 { "menuEdit.Truncate Game", False },
2901 Enables trainingOffEnables[] = {
2902 { "menuMode.Edit Comment", True },
2903 { "menuMode.Pause", True },
2904 { "menuEdit.Forward", True },
2905 { "menuEdit.Backward", True },
2906 { "menuEdit.Forward to End", True },
2907 { "menuEdit.Back to Start", True },
2908 { "menuEngine.Move Now", True },
2909 { "menuEdit.Truncate Game", True },
2913 Enables machineThinkingEnables[] = {
2914 { "menuFile.Load Game", False },
2915 // { "menuFile.Load Next Game", False },
2916 // { "menuFile.Load Previous Game", False },
2917 // { "menuFile.Reload Same Game", False },
2918 { "menuEdit.Paste Game", False },
2919 { "menuFile.Load Position", False },
2920 // { "menuFile.Load Next Position", False },
2921 // { "menuFile.Load Previous Position", False },
2922 // { "menuFile.Reload Same Position", False },
2923 { "menuEdit.Paste Position", False },
2924 { "menuMode.Machine White", False },
2925 { "menuMode.Machine Black", False },
2926 { "menuMode.Two Machines", False },
2927 { "menuMode.Machine Match", False },
2928 { "menuEngine.Retract Move", False },
2932 Enables userThinkingEnables[] = {
2933 { "menuFile.Load Game", True },
2934 // { "menuFile.Load Next Game", True },
2935 // { "menuFile.Load Previous Game", True },
2936 // { "menuFile.Reload Same Game", True },
2937 { "menuEdit.Paste Game", True },
2938 { "menuFile.Load Position", True },
2939 // { "menuFile.Load Next Position", True },
2940 // { "menuFile.Load Previous Position", True },
2941 // { "menuFile.Reload Same Position", True },
2942 { "menuEdit.Paste Position", True },
2943 { "menuMode.Machine White", True },
2944 { "menuMode.Machine Black", True },
2945 { "menuMode.Two Machines", True },
2946 { "menuMode.Machine Match", True },
2947 { "menuEngine.Retract Move", True },
2953 SetMenuEnables(icsEnables);
2956 if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
2957 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2958 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
2966 SetMenuEnables(ncpEnables);
2972 SetMenuEnables(gnuEnables);
2978 SetMenuEnables(cmailEnables);
2984 SetMenuEnables(trainingOnEnables);
2985 if (appData.showButtonBar) {
2986 XtSetSensitive(buttonBarWidget, False);
2992 SetTrainingModeOff()
2994 SetMenuEnables(trainingOffEnables);
2995 if (appData.showButtonBar) {
2996 XtSetSensitive(buttonBarWidget, True);
3001 SetUserThinkingEnables()
3003 if (appData.noChessProgram) return;
3004 SetMenuEnables(userThinkingEnables);
3008 SetMachineThinkingEnables()
3010 if (appData.noChessProgram) return;
3011 SetMenuEnables(machineThinkingEnables);
3013 case MachinePlaysBlack:
3014 case MachinePlaysWhite:
3015 case TwoMachinesPlay:
3016 XtSetSensitive(XtNameToWidget(menuBarWidget,
3017 ModeToWidgetName(gameMode)), True);
3024 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3025 #define HISTORY_SIZE 64
3026 static char *history[HISTORY_SIZE];
3027 int histIn = 0, histP = 0;
3030 SaveInHistory(char *cmd)
3032 if (history[histIn] != NULL) {
3033 free(history[histIn]);
3034 history[histIn] = NULL;
3036 if (*cmd == NULLCHAR) return;
3037 history[histIn] = StrSave(cmd);
3038 histIn = (histIn + 1) % HISTORY_SIZE;
3039 if (history[histIn] != NULL) {
3040 free(history[histIn]);
3041 history[histIn] = NULL;
3047 PrevInHistory(char *cmd)
3050 if (histP == histIn) {
3051 if (history[histIn] != NULL) free(history[histIn]);
3052 history[histIn] = StrSave(cmd);
3054 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3055 if (newhp == histIn || history[newhp] == NULL) return NULL;
3057 return history[histP];
3063 if (histP == histIn) return NULL;
3064 histP = (histP + 1) % HISTORY_SIZE;
3065 return history[histP];
3067 // end of borrowed code
3069 #define Abs(n) ((n)<0 ? -(n) : (n))
3072 * Find a font that matches "pattern" that is as close as
3073 * possible to the targetPxlSize. Prefer fonts that are k
3074 * pixels smaller to fonts that are k pixels larger. The
3075 * pattern must be in the X Consortium standard format,
3076 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3077 * The return value should be freed with XtFree when no
3081 FindFont(pattern, targetPxlSize)
3085 char **fonts, *p, *best, *scalable, *scalableTail;
3086 int i, j, nfonts, minerr, err, pxlSize;
3089 char **missing_list;
3091 char *def_string, *base_fnt_lst, strInt[3];
3093 XFontStruct **fnt_list;
3094 base_fnt_lst = calloc(1, strlen(pattern) + 3);
3095 snprintf(strInt, sizeof(strInt)/sizeof(strInt[0]), "%d", targetPxlSize);
3096 p = strstr(pattern, "--");
3097 strncpy(base_fnt_lst, pattern, p - pattern + 2);
3098 strcat(base_fnt_lst, strInt);
3099 strcat(base_fnt_lst, strchr(p + 2, '-'));
3101 if ((fntSet = XCreateFontSet(xDisplay,
3105 &def_string)) == NULL) {
3107 fprintf(stderr, _("Unable to create font set.\n"));
3111 nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
3113 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3115 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3116 programName, pattern);
3124 for (i=0; i<nfonts; i++) {
3127 if (*p != '-') continue;
3129 if (*p == NULLCHAR) break;
3130 if (*p++ == '-') j++;
3132 if (j < 7) continue;
3135 scalable = fonts[i];
3138 err = pxlSize - targetPxlSize;
3139 if (Abs(err) < Abs(minerr) ||
3140 (minerr > 0 && err < 0 && -err == minerr)) {
3146 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3147 /* If the error is too big and there is a scalable font,
3148 use the scalable font. */
3149 int headlen = scalableTail - scalable;
3150 p = (char *) XtMalloc(strlen(scalable) + 10);
3151 while (isdigit(*scalableTail)) scalableTail++;
3152 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3154 p = (char *) XtMalloc(strlen(best) + 2);
3155 safeStrCpy(p, best, strlen(best)+1 );
3157 if (appData.debugMode) {
3158 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3159 pattern, targetPxlSize, p);
3162 if (missing_count > 0)
3163 XFreeStringList(missing_list);
3164 XFreeFontSet(xDisplay, fntSet);
3166 XFreeFontNames(fonts);
3172 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3173 // must be called before all non-first callse to CreateGCs()
3174 XtReleaseGC(shellWidget, highlineGC);
3175 XtReleaseGC(shellWidget, lightSquareGC);
3176 XtReleaseGC(shellWidget, darkSquareGC);
3177 XtReleaseGC(shellWidget, lineGC);
3178 if (appData.monoMode) {
3179 if (DefaultDepth(xDisplay, xScreen) == 1) {
3180 XtReleaseGC(shellWidget, wbPieceGC);
3182 XtReleaseGC(shellWidget, bwPieceGC);
3185 XtReleaseGC(shellWidget, prelineGC);
3186 XtReleaseGC(shellWidget, jailSquareGC);
3187 XtReleaseGC(shellWidget, wdPieceGC);
3188 XtReleaseGC(shellWidget, wlPieceGC);
3189 XtReleaseGC(shellWidget, wjPieceGC);
3190 XtReleaseGC(shellWidget, bdPieceGC);
3191 XtReleaseGC(shellWidget, blPieceGC);
3192 XtReleaseGC(shellWidget, bjPieceGC);
3196 void CreateGCs(int redo)
3198 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3199 | GCBackground | GCFunction | GCPlaneMask;
3200 XGCValues gc_values;
3203 gc_values.plane_mask = AllPlanes;
3204 gc_values.line_width = lineGap;
3205 gc_values.line_style = LineSolid;
3206 gc_values.function = GXcopy;
3209 DeleteGCs(); // called a second time; clean up old GCs first
3210 } else { // [HGM] grid and font GCs created on first call only
3211 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3212 gc_values.background = XWhitePixel(xDisplay, xScreen);
3213 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3214 XSetFont(xDisplay, coordGC, coordFontID);
3216 // [HGM] make font for holdings counts (white on black)
3217 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3218 gc_values.background = XBlackPixel(xDisplay, xScreen);
3219 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3220 XSetFont(xDisplay, countGC, countFontID);
3222 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3223 gc_values.background = XBlackPixel(xDisplay, xScreen);
3224 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3226 if (appData.monoMode) {
3227 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3228 gc_values.background = XWhitePixel(xDisplay, xScreen);
3229 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3231 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3232 gc_values.background = XBlackPixel(xDisplay, xScreen);
3233 lightSquareGC = wbPieceGC
3234 = XtGetGC(shellWidget, value_mask, &gc_values);
3236 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3237 gc_values.background = XWhitePixel(xDisplay, xScreen);
3238 darkSquareGC = bwPieceGC
3239 = XtGetGC(shellWidget, value_mask, &gc_values);
3241 if (DefaultDepth(xDisplay, xScreen) == 1) {
3242 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3243 gc_values.function = GXcopyInverted;
3244 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3245 gc_values.function = GXcopy;
3246 if (XBlackPixel(xDisplay, xScreen) == 1) {
3247 bwPieceGC = darkSquareGC;
3248 wbPieceGC = copyInvertedGC;
3250 bwPieceGC = copyInvertedGC;
3251 wbPieceGC = lightSquareGC;
3255 gc_values.foreground = highlightSquareColor;
3256 gc_values.background = highlightSquareColor;
3257 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3259 gc_values.foreground = premoveHighlightColor;
3260 gc_values.background = premoveHighlightColor;
3261 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3263 gc_values.foreground = lightSquareColor;
3264 gc_values.background = darkSquareColor;
3265 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3267 gc_values.foreground = darkSquareColor;
3268 gc_values.background = lightSquareColor;
3269 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3271 gc_values.foreground = jailSquareColor;
3272 gc_values.background = jailSquareColor;
3273 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3275 gc_values.foreground = whitePieceColor;
3276 gc_values.background = darkSquareColor;
3277 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3279 gc_values.foreground = whitePieceColor;
3280 gc_values.background = lightSquareColor;
3281 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3283 gc_values.foreground = whitePieceColor;
3284 gc_values.background = jailSquareColor;
3285 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3287 gc_values.foreground = blackPieceColor;
3288 gc_values.background = darkSquareColor;
3289 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3291 gc_values.foreground = blackPieceColor;
3292 gc_values.background = lightSquareColor;
3293 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3295 gc_values.foreground = blackPieceColor;
3296 gc_values.background = jailSquareColor;
3297 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3301 void loadXIM(xim, xmask, filename, dest, mask)
3314 fp = fopen(filename, "rb");
3316 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3323 for (y=0; y<h; ++y) {
3324 for (x=0; x<h; ++x) {
3329 XPutPixel(xim, x, y, blackPieceColor);
3331 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3334 XPutPixel(xim, x, y, darkSquareColor);
3336 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3339 XPutPixel(xim, x, y, whitePieceColor);
3341 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3344 XPutPixel(xim, x, y, lightSquareColor);
3346 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3354 /* create Pixmap of piece */
3355 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3357 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3360 /* create Pixmap of clipmask
3361 Note: We assume the white/black pieces have the same
3362 outline, so we make only 6 masks. This is okay
3363 since the XPM clipmask routines do the same. */
3365 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3367 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3370 /* now create the 1-bit version */
3371 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3374 values.foreground = 1;
3375 values.background = 0;
3377 /* Don't use XtGetGC, not read only */
3378 maskGC = XCreateGC(xDisplay, *mask,
3379 GCForeground | GCBackground, &values);
3380 XCopyPlane(xDisplay, temp, *mask, maskGC,
3381 0, 0, squareSize, squareSize, 0, 0, 1);
3382 XFreePixmap(xDisplay, temp);
3387 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3389 void CreateXIMPieces()
3394 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3399 /* The XSynchronize calls were copied from CreatePieces.
3400 Not sure if needed, but can't hurt */
3401 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3404 /* temp needed by loadXIM() */
3405 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3406 0, 0, ss, ss, AllPlanes, XYPixmap);
3408 if (strlen(appData.pixmapDirectory) == 0) {
3412 if (appData.monoMode) {
3413 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3417 fprintf(stderr, _("\nLoading XIMs...\n"));
3419 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3420 fprintf(stderr, "%d", piece+1);
3421 for (kind=0; kind<4; kind++) {
3422 fprintf(stderr, ".");
3423 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3424 ExpandPathName(appData.pixmapDirectory),
3425 piece <= (int) WhiteKing ? "" : "w",
3426 pieceBitmapNames[piece],
3428 ximPieceBitmap[kind][piece] =
3429 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3430 0, 0, ss, ss, AllPlanes, XYPixmap);
3431 if (appData.debugMode)
3432 fprintf(stderr, _("(File:%s:) "), buf);
3433 loadXIM(ximPieceBitmap[kind][piece],
3435 &(xpmPieceBitmap2[kind][piece]),
3436 &(ximMaskPm2[piece]));
3437 if(piece <= (int)WhiteKing)
3438 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3440 fprintf(stderr," ");
3442 /* Load light and dark squares */
3443 /* If the LSQ and DSQ pieces don't exist, we will
3444 draw them with solid squares. */
3445 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3446 if (access(buf, 0) != 0) {
3450 fprintf(stderr, _("light square "));
3452 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3453 0, 0, ss, ss, AllPlanes, XYPixmap);
3454 if (appData.debugMode)
3455 fprintf(stderr, _("(File:%s:) "), buf);
3457 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3458 fprintf(stderr, _("dark square "));
3459 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3460 ExpandPathName(appData.pixmapDirectory), ss);
3461 if (appData.debugMode)
3462 fprintf(stderr, _("(File:%s:) "), buf);
3464 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3465 0, 0, ss, ss, AllPlanes, XYPixmap);
3466 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3467 xpmJailSquare = xpmLightSquare;
3469 fprintf(stderr, _("Done.\n"));
3471 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3474 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3477 void CreateXPMBoard(char *s, int kind)
3481 if(s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3482 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3483 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3487 void FreeXPMPieces()
3488 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3489 // thisroutine has to be called t free the old piece pixmaps
3491 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3492 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3494 XFreePixmap(xDisplay, xpmLightSquare);
3495 XFreePixmap(xDisplay, xpmDarkSquare);
3499 void CreateXPMPieces()
3503 u_int ss = squareSize;
3505 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3506 XpmColorSymbol symbols[4];
3507 static int redo = False;
3509 if(redo) FreeXPMPieces(); else redo = 1;
3511 /* The XSynchronize calls were copied from CreatePieces.
3512 Not sure if needed, but can't hurt */
3513 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3515 /* Setup translations so piece colors match square colors */
3516 symbols[0].name = "light_piece";
3517 symbols[0].value = appData.whitePieceColor;
3518 symbols[1].name = "dark_piece";
3519 symbols[1].value = appData.blackPieceColor;
3520 symbols[2].name = "light_square";
3521 symbols[2].value = appData.lightSquareColor;
3522 symbols[3].name = "dark_square";
3523 symbols[3].value = appData.darkSquareColor;
3525 attr.valuemask = XpmColorSymbols;
3526 attr.colorsymbols = symbols;
3527 attr.numsymbols = 4;
3529 if (appData.monoMode) {
3530 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3534 if (strlen(appData.pixmapDirectory) == 0) {
3535 XpmPieces* pieces = builtInXpms;
3538 while (pieces->size != squareSize && pieces->size) pieces++;
3539 if (!pieces->size) {
3540 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3543 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3544 for (kind=0; kind<4; kind++) {
3546 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3547 pieces->xpm[piece][kind],
3548 &(xpmPieceBitmap2[kind][piece]),
3549 NULL, &attr)) != 0) {
3550 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3554 if(piece <= (int) WhiteKing)
3555 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3559 xpmJailSquare = xpmLightSquare;
3563 fprintf(stderr, _("\nLoading XPMs...\n"));
3566 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3567 fprintf(stderr, "%d ", piece+1);
3568 for (kind=0; kind<4; kind++) {
3569 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3570 ExpandPathName(appData.pixmapDirectory),
3571 piece > (int) WhiteKing ? "w" : "",
3572 pieceBitmapNames[piece],
3574 if (appData.debugMode) {
3575 fprintf(stderr, _("(File:%s:) "), buf);
3577 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3578 &(xpmPieceBitmap2[kind][piece]),
3579 NULL, &attr)) != 0) {
3580 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3581 // [HGM] missing: read of unorthodox piece failed; substitute King.
3582 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3583 ExpandPathName(appData.pixmapDirectory),
3585 if (appData.debugMode) {
3586 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3588 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3589 &(xpmPieceBitmap2[kind][piece]),
3593 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3598 if(piece <= (int) WhiteKing)
3599 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3602 /* Load light and dark squares */
3603 /* If the LSQ and DSQ pieces don't exist, we will
3604 draw them with solid squares. */
3605 fprintf(stderr, _("light square "));
3606 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3607 if (access(buf, 0) != 0) {
3611 if (appData.debugMode)
3612 fprintf(stderr, _("(File:%s:) "), buf);
3614 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3615 &xpmLightSquare, NULL, &attr)) != 0) {
3616 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3619 fprintf(stderr, _("dark square "));
3620 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3621 ExpandPathName(appData.pixmapDirectory), ss);
3622 if (appData.debugMode) {
3623 fprintf(stderr, _("(File:%s:) "), buf);
3625 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3626 &xpmDarkSquare, NULL, &attr)) != 0) {
3627 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3631 xpmJailSquare = xpmLightSquare;
3632 fprintf(stderr, _("Done.\n"));
3634 oldVariant = -1; // kludge to force re-makig of animation masks
3635 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3638 #endif /* HAVE_LIBXPM */
3641 /* No built-in bitmaps */
3646 u_int ss = squareSize;
3648 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3651 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3652 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3653 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3654 pieceBitmapNames[piece],
3655 ss, kind == SOLID ? 's' : 'o');
3656 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3657 if(piece <= (int)WhiteKing)
3658 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3662 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3666 /* With built-in bitmaps */
3669 BuiltInBits* bib = builtInBits;
3672 u_int ss = squareSize;
3674 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3677 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3679 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3680 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3681 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3682 pieceBitmapNames[piece],
3683 ss, kind == SOLID ? 's' : 'o');
3684 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3685 bib->bits[kind][piece], ss, ss);
3686 if(piece <= (int)WhiteKing)
3687 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3691 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3696 void ReadBitmap(pm, name, bits, wreq, hreq)
3699 unsigned char bits[];
3705 char msg[MSG_SIZ], fullname[MSG_SIZ];
3707 if (*appData.bitmapDirectory != NULLCHAR) {
3708 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3709 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3710 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3711 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3712 &w, &h, pm, &x_hot, &y_hot);
3713 fprintf(stderr, "load %s\n", name);
3714 if (errcode != BitmapSuccess) {
3716 case BitmapOpenFailed:
3717 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3719 case BitmapFileInvalid:
3720 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3722 case BitmapNoMemory:
3723 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3727 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3731 fprintf(stderr, _("%s: %s...using built-in\n"),
3733 } else if (w != wreq || h != hreq) {
3735 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3736 programName, fullname, w, h, wreq, hreq);
3742 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3751 if (lineGap == 0) return;
3753 /* [HR] Split this into 2 loops for non-square boards. */
3755 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3756 gridSegments[i].x1 = 0;
3757 gridSegments[i].x2 =
3758 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3759 gridSegments[i].y1 = gridSegments[i].y2
3760 = lineGap / 2 + (i * (squareSize + lineGap));
3763 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3764 gridSegments[j + i].y1 = 0;
3765 gridSegments[j + i].y2 =
3766 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3767 gridSegments[j + i].x1 = gridSegments[j + i].x2
3768 = lineGap / 2 + (j * (squareSize + lineGap));
3772 static void MenuBarSelect(w, addr, index)
3777 XtActionProc proc = (XtActionProc) addr;
3779 (proc)(NULL, NULL, NULL, NULL);
3782 void CreateMenuBarPopup(parent, name, mb)
3792 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3795 XtSetArg(args[j], XtNleftMargin, 20); j++;
3796 XtSetArg(args[j], XtNrightMargin, 20); j++;
3798 while (mi->string != NULL) {
3799 if (strcmp(mi->string, "----") == 0) {
3800 entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
3803 XtSetArg(args[j], XtNlabel, XtNewString(mi->string));
3804 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3806 XtAddCallback(entry, XtNcallback,
3807 (XtCallbackProc) MenuBarSelect,
3808 (caddr_t) mi->proc);
3814 Widget CreateMenuBar(mb)
3818 Widget anchor, menuBar;
3820 char menuName[MSG_SIZ];
3823 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3824 XtSetArg(args[j], XtNvSpace, 0); j++;
3825 XtSetArg(args[j], XtNborderWidth, 0); j++;
3826 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3827 formWidget, args, j);
3829 while (mb->name != NULL) {
3830 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3831 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3833 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3836 shortName[0] = mb->name[0];
3837 shortName[1] = NULLCHAR;
3838 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3841 XtSetArg(args[j], XtNlabel, XtNewString(mb->name)); j++;
3844 XtSetArg(args[j], XtNborderWidth, 0); j++;
3845 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3847 CreateMenuBarPopup(menuBar, menuName, mb);
3853 Widget CreateButtonBar(mi)
3857 Widget button, buttonBar;
3861 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3863 XtSetArg(args[j], XtNhSpace, 0); j++;
3865 XtSetArg(args[j], XtNborderWidth, 0); j++;
3866 XtSetArg(args[j], XtNvSpace, 0); j++;
3867 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3868 formWidget, args, j);
3870 while (mi->string != NULL) {
3873 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3874 XtSetArg(args[j], XtNborderWidth, 0); j++;
3876 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3877 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3878 buttonBar, args, j);
3879 XtAddCallback(button, XtNcallback,
3880 (XtCallbackProc) MenuBarSelect,
3881 (caddr_t) mi->proc);
3888 CreatePieceMenu(name, color)
3895 ChessSquare selection;
3897 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3898 boardWidget, args, 0);
3900 for (i = 0; i < PIECE_MENU_SIZE; i++) {
3901 String item = pieceMenuStrings[color][i];
3903 if (strcmp(item, "----") == 0) {
3904 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3907 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3908 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3910 selection = pieceMenuTranslation[color][i];
3911 XtAddCallback(entry, XtNcallback,
3912 (XtCallbackProc) PieceMenuSelect,
3913 (caddr_t) selection);
3914 if (selection == WhitePawn || selection == BlackPawn) {
3915 XtSetArg(args[0], XtNpopupOnEntry, entry);
3916 XtSetValues(menu, args, 1);
3929 ChessSquare selection;
3931 whitePieceMenu = CreatePieceMenu("menuW", 0);
3932 blackPieceMenu = CreatePieceMenu("menuB", 1);
3934 XtRegisterGrabAction(PieceMenuPopup, True,
3935 (unsigned)(ButtonPressMask|ButtonReleaseMask),
3936 GrabModeAsync, GrabModeAsync);
3938 XtSetArg(args[0], XtNlabel, _("Drop"));
3939 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3940 boardWidget, args, 1);
3941 for (i = 0; i < DROP_MENU_SIZE; i++) {
3942 String item = dropMenuStrings[i];
3944 if (strcmp(item, "----") == 0) {
3945 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3948 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3949 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3951 selection = dropMenuTranslation[i];
3952 XtAddCallback(entry, XtNcallback,
3953 (XtCallbackProc) DropMenuSelect,
3954 (caddr_t) selection);
3959 void SetupDropMenu()
3967 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3968 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3969 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3970 dmEnables[i].piece);
3971 XtSetSensitive(entry, p != NULL || !appData.testLegality
3972 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3973 && !appData.icsActive));
3975 while (p && *p++ == dmEnables[i].piece) count++;
3976 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3978 XtSetArg(args[j], XtNlabel, label); j++;
3979 XtSetValues(entry, args, j);
3983 void PieceMenuPopup(w, event, params, num_params)
3987 Cardinal *num_params;
3989 String whichMenu; int menuNr;
3990 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3991 if (event->type == ButtonRelease)
3992 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3993 else if (event->type == ButtonPress)
3994 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3996 case 0: whichMenu = params[0]; break;
3997 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3999 case -1: if (errorUp) ErrorPopDown();
4002 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
4005 static void PieceMenuSelect(w, piece, junk)
4010 if (pmFromX < 0 || pmFromY < 0) return;
4011 EditPositionMenuEvent(piece, pmFromX, pmFromY);
4014 static void DropMenuSelect(w, piece, junk)
4019 if (pmFromX < 0 || pmFromY < 0) return;
4020 DropMenuEvent(piece, pmFromX, pmFromY);
4023 void WhiteClock(w, event, prms, nprms)
4032 void BlackClock(w, event, prms, nprms)
4043 * If the user selects on a border boundary, return -1; if off the board,
4044 * return -2. Otherwise map the event coordinate to the square.
4046 int EventToSquare(x, limit)
4054 if ((x % (squareSize + lineGap)) >= squareSize)
4056 x /= (squareSize + lineGap);
4062 static void do_flash_delay(msec)
4068 static void drawHighlight(file, rank, gc)
4074 if (lineGap == 0) return;
4077 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4078 (squareSize + lineGap);
4079 y = lineGap/2 + rank * (squareSize + lineGap);
4081 x = lineGap/2 + file * (squareSize + lineGap);
4082 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4083 (squareSize + lineGap);
4086 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4087 squareSize+lineGap, squareSize+lineGap);
4090 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4091 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4094 SetHighlights(fromX, fromY, toX, toY)
4095 int fromX, fromY, toX, toY;
4097 if (hi1X != fromX || hi1Y != fromY) {
4098 if (hi1X >= 0 && hi1Y >= 0) {
4099 drawHighlight(hi1X, hi1Y, lineGC);
4101 } // [HGM] first erase both, then draw new!
4102 if (hi2X != toX || hi2Y != toY) {
4103 if (hi2X >= 0 && hi2Y >= 0) {
4104 drawHighlight(hi2X, hi2Y, lineGC);
4107 if (hi1X != fromX || hi1Y != fromY) {
4108 if (fromX >= 0 && fromY >= 0) {
4109 drawHighlight(fromX, fromY, highlineGC);
4112 if (hi2X != toX || hi2Y != toY) {
4113 if (toX >= 0 && toY >= 0) {
4114 drawHighlight(toX, toY, highlineGC);
4126 SetHighlights(-1, -1, -1, -1);
4131 SetPremoveHighlights(fromX, fromY, toX, toY)
4132 int fromX, fromY, toX, toY;
4134 if (pm1X != fromX || pm1Y != fromY) {
4135 if (pm1X >= 0 && pm1Y >= 0) {
4136 drawHighlight(pm1X, pm1Y, lineGC);
4138 if (fromX >= 0 && fromY >= 0) {
4139 drawHighlight(fromX, fromY, prelineGC);
4142 if (pm2X != toX || pm2Y != toY) {
4143 if (pm2X >= 0 && pm2Y >= 0) {
4144 drawHighlight(pm2X, pm2Y, lineGC);
4146 if (toX >= 0 && toY >= 0) {
4147 drawHighlight(toX, toY, prelineGC);
4157 ClearPremoveHighlights()
4159 SetPremoveHighlights(-1, -1, -1, -1);
4162 static int CutOutSquare(x, y, x0, y0, kind)
4163 int x, y, *x0, *y0, kind;
4165 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4166 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4168 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4169 if(textureW[kind] < W*squareSize)
4170 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4172 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4173 if(textureH[kind] < H*squareSize)
4174 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4176 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4180 static void BlankSquare(x, y, color, piece, dest, fac)
4181 int x, y, color, fac;
4184 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4186 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4187 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4188 squareSize, squareSize, x*fac, y*fac);
4190 if (useImages && useImageSqs) {
4194 pm = xpmLightSquare;
4199 case 2: /* neutral */
4204 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4205 squareSize, squareSize, x*fac, y*fac);
4215 case 2: /* neutral */
4220 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4225 I split out the routines to draw a piece so that I could
4226 make a generic flash routine.
4228 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4230 int square_color, x, y;
4233 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4234 switch (square_color) {
4236 case 2: /* neutral */
4238 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4239 ? *pieceToOutline(piece)
4240 : *pieceToSolid(piece),
4241 dest, bwPieceGC, 0, 0,
4242 squareSize, squareSize, x, y);
4245 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4246 ? *pieceToSolid(piece)
4247 : *pieceToOutline(piece),
4248 dest, wbPieceGC, 0, 0,
4249 squareSize, squareSize, x, y);
4254 static void monoDrawPiece(piece, square_color, x, y, dest)
4256 int square_color, x, y;
4259 switch (square_color) {
4261 case 2: /* neutral */
4263 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4264 ? *pieceToOutline(piece)
4265 : *pieceToSolid(piece),
4266 dest, bwPieceGC, 0, 0,
4267 squareSize, squareSize, x, y, 1);
4270 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4271 ? *pieceToSolid(piece)
4272 : *pieceToOutline(piece),
4273 dest, wbPieceGC, 0, 0,
4274 squareSize, squareSize, x, y, 1);
4279 static void colorDrawPiece(piece, square_color, x, y, dest)
4281 int square_color, x, y;
4284 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4285 switch (square_color) {
4287 XCopyPlane(xDisplay, *pieceToSolid(piece),
4288 dest, (int) piece < (int) BlackPawn
4289 ? wlPieceGC : blPieceGC, 0, 0,
4290 squareSize, squareSize, x, y, 1);
4293 XCopyPlane(xDisplay, *pieceToSolid(piece),
4294 dest, (int) piece < (int) BlackPawn
4295 ? wdPieceGC : bdPieceGC, 0, 0,
4296 squareSize, squareSize, x, y, 1);
4298 case 2: /* neutral */
4300 XCopyPlane(xDisplay, *pieceToSolid(piece),
4301 dest, (int) piece < (int) BlackPawn
4302 ? wjPieceGC : bjPieceGC, 0, 0,
4303 squareSize, squareSize, x, y, 1);
4308 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4310 int square_color, x, y;
4313 int kind, p = piece;
4315 switch (square_color) {
4317 case 2: /* neutral */
4319 if ((int)piece < (int) BlackPawn) {
4327 if ((int)piece < (int) BlackPawn) {
4335 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4336 if(useTexture & square_color+1) {
4337 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4338 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4339 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4340 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4341 XSetClipMask(xDisplay, wlPieceGC, None);
4342 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4344 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4345 dest, wlPieceGC, 0, 0,
4346 squareSize, squareSize, x, y);
4349 typedef void (*DrawFunc)();
4351 DrawFunc ChooseDrawFunc()
4353 if (appData.monoMode) {
4354 if (DefaultDepth(xDisplay, xScreen) == 1) {
4355 return monoDrawPiece_1bit;
4357 return monoDrawPiece;
4361 return colorDrawPieceImage;
4363 return colorDrawPiece;
4367 /* [HR] determine square color depending on chess variant. */
4368 static int SquareColor(row, column)
4373 if (gameInfo.variant == VariantXiangqi) {
4374 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4376 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4378 } else if (row <= 4) {
4384 square_color = ((column + row) % 2) == 1;
4387 /* [hgm] holdings: next line makes all holdings squares light */
4388 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4390 return square_color;
4393 void DrawSquare(row, column, piece, do_flash)
4394 int row, column, do_flash;
4397 int square_color, x, y, direction, font_ascent, font_descent;
4400 XCharStruct overall;
4404 /* Calculate delay in milliseconds (2-delays per complete flash) */
4405 flash_delay = 500 / appData.flashRate;
4408 x = lineGap + ((BOARD_WIDTH-1)-column) *
4409 (squareSize + lineGap);
4410 y = lineGap + row * (squareSize + lineGap);
4412 x = lineGap + column * (squareSize + lineGap);
4413 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4414 (squareSize + lineGap);
4417 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4419 square_color = SquareColor(row, column);
4421 if ( // [HGM] holdings: blank out area between board and holdings
4422 column == BOARD_LEFT-1 || column == BOARD_RGHT
4423 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4424 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4425 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4427 // [HGM] print piece counts next to holdings
4428 string[1] = NULLCHAR;
4429 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4430 string[0] = '0' + piece;
4431 XTextExtents(countFontStruct, string, 1, &direction,
4432 &font_ascent, &font_descent, &overall);
4433 if (appData.monoMode) {
4434 XDrawImageString(xDisplay, xBoardWindow, countGC,
4435 x + squareSize - overall.width - 2,
4436 y + font_ascent + 1, string, 1);
4438 XDrawString(xDisplay, xBoardWindow, countGC,
4439 x + squareSize - overall.width - 2,
4440 y + font_ascent + 1, string, 1);
4443 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4444 string[0] = '0' + piece;
4445 XTextExtents(countFontStruct, string, 1, &direction,
4446 &font_ascent, &font_descent, &overall);
4447 if (appData.monoMode) {
4448 XDrawImageString(xDisplay, xBoardWindow, countGC,
4449 x + 2, y + font_ascent + 1, string, 1);
4451 XDrawString(xDisplay, xBoardWindow, countGC,
4452 x + 2, y + font_ascent + 1, string, 1);
4456 if (piece == EmptySquare || appData.blindfold) {
4457 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4459 drawfunc = ChooseDrawFunc();
4461 if (do_flash && appData.flashCount > 0) {
4462 for (i=0; i<appData.flashCount; ++i) {
4463 drawfunc(piece, square_color, x, y, xBoardWindow);
4464 XSync(xDisplay, False);
4465 do_flash_delay(flash_delay);
4467 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4468 XSync(xDisplay, False);
4469 do_flash_delay(flash_delay);
4472 drawfunc(piece, square_color, x, y, xBoardWindow);
4476 string[1] = NULLCHAR;
4477 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4478 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4479 string[0] = 'a' + column - BOARD_LEFT;
4480 XTextExtents(coordFontStruct, string, 1, &direction,
4481 &font_ascent, &font_descent, &overall);
4482 if (appData.monoMode) {
4483 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4484 x + squareSize - overall.width - 2,
4485 y + squareSize - font_descent - 1, string, 1);
4487 XDrawString(xDisplay, xBoardWindow, coordGC,
4488 x + squareSize - overall.width - 2,
4489 y + squareSize - font_descent - 1, string, 1);
4492 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4493 string[0] = ONE + row;
4494 XTextExtents(coordFontStruct, string, 1, &direction,
4495 &font_ascent, &font_descent, &overall);
4496 if (appData.monoMode) {
4497 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4498 x + 2, y + font_ascent + 1, string, 1);
4500 XDrawString(xDisplay, xBoardWindow, coordGC,
4501 x + 2, y + font_ascent + 1, string, 1);
4504 if(!partnerUp && marker[row][column]) {
4505 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4506 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4511 /* Why is this needed on some versions of X? */
4512 void EventProc(widget, unused, event)
4517 if (!XtIsRealized(widget))
4520 switch (event->type) {
4522 if (event->xexpose.count > 0) return; /* no clipping is done */
4523 XDrawPosition(widget, True, NULL);
4524 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4525 flipView = !flipView; partnerUp = !partnerUp;
4526 XDrawPosition(widget, True, NULL);
4527 flipView = !flipView; partnerUp = !partnerUp;
4531 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4538 void DrawPosition(fullRedraw, board)
4539 /*Boolean*/int fullRedraw;
4542 XDrawPosition(boardWidget, fullRedraw, board);
4545 /* Returns 1 if there are "too many" differences between b1 and b2
4546 (i.e. more than 1 move was made) */
4547 static int too_many_diffs(b1, b2)
4553 for (i=0; i<BOARD_HEIGHT; ++i) {
4554 for (j=0; j<BOARD_WIDTH; ++j) {
4555 if (b1[i][j] != b2[i][j]) {
4556 if (++c > 4) /* Castling causes 4 diffs */
4564 /* Matrix describing castling maneuvers */
4565 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4566 static int castling_matrix[4][5] = {
4567 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4568 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4569 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4570 { 7, 7, 4, 5, 6 } /* 0-0, black */
4573 /* Checks whether castling occurred. If it did, *rrow and *rcol
4574 are set to the destination (row,col) of the rook that moved.
4576 Returns 1 if castling occurred, 0 if not.
4578 Note: Only handles a max of 1 castling move, so be sure
4579 to call too_many_diffs() first.
4581 static int check_castle_draw(newb, oldb, rrow, rcol)
4588 /* For each type of castling... */
4589 for (i=0; i<4; ++i) {
4590 r = castling_matrix[i];
4592 /* Check the 4 squares involved in the castling move */
4594 for (j=1; j<=4; ++j) {
4595 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4602 /* All 4 changed, so it must be a castling move */
4611 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4612 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4614 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4617 void DrawSeekBackground( int left, int top, int right, int bottom )
4619 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4622 void DrawSeekText(char *buf, int x, int y)
4624 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4627 void DrawSeekDot(int x, int y, int colorNr)
4629 int square = colorNr & 0x80;
4632 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4634 XFillRectangle(xDisplay, xBoardWindow, color,
4635 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4637 XFillArc(xDisplay, xBoardWindow, color,
4638 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4641 static int damage[2][BOARD_RANKS][BOARD_FILES];
4644 * event handler for redrawing the board
4646 void XDrawPosition(w, repaint, board)
4648 /*Boolean*/int repaint;
4652 static int lastFlipView = 0;
4653 static int lastBoardValid[2] = {0, 0};
4654 static Board lastBoard[2];
4657 int nr = twoBoards*partnerUp;
4659 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4661 if (board == NULL) {
4662 if (!lastBoardValid[nr]) return;
4663 board = lastBoard[nr];
4665 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4666 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4667 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4672 * It would be simpler to clear the window with XClearWindow()
4673 * but this causes a very distracting flicker.
4676 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4678 if ( lineGap && IsDrawArrowEnabled())
4679 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4680 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4682 /* If too much changes (begin observing new game, etc.), don't
4684 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4686 /* Special check for castling so we don't flash both the king
4687 and the rook (just flash the king). */
4689 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4690 /* Draw rook with NO flashing. King will be drawn flashing later */
4691 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4692 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4696 /* First pass -- Draw (newly) empty squares and repair damage.
4697 This prevents you from having a piece show up twice while it
4698 is flashing on its new square */
4699 for (i = 0; i < BOARD_HEIGHT; i++)
4700 for (j = 0; j < BOARD_WIDTH; j++)
4701 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4702 || damage[nr][i][j]) {
4703 DrawSquare(i, j, board[i][j], 0);
4704 damage[nr][i][j] = False;
4707 /* Second pass -- Draw piece(s) in new position and flash them */
4708 for (i = 0; i < BOARD_HEIGHT; i++)
4709 for (j = 0; j < BOARD_WIDTH; j++)
4710 if (board[i][j] != lastBoard[nr][i][j]) {
4711 DrawSquare(i, j, board[i][j], do_flash);
4715 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4716 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4717 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4719 for (i = 0; i < BOARD_HEIGHT; i++)
4720 for (j = 0; j < BOARD_WIDTH; j++) {
4721 DrawSquare(i, j, board[i][j], 0);
4722 damage[nr][i][j] = False;
4726 CopyBoard(lastBoard[nr], board);
4727 lastBoardValid[nr] = 1;
4728 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4729 lastFlipView = flipView;
4731 /* Draw highlights */
4732 if (pm1X >= 0 && pm1Y >= 0) {
4733 drawHighlight(pm1X, pm1Y, prelineGC);
4735 if (pm2X >= 0 && pm2Y >= 0) {
4736 drawHighlight(pm2X, pm2Y, prelineGC);
4738 if (hi1X >= 0 && hi1Y >= 0) {
4739 drawHighlight(hi1X, hi1Y, highlineGC);
4741 if (hi2X >= 0 && hi2Y >= 0) {
4742 drawHighlight(hi2X, hi2Y, highlineGC);
4744 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4746 /* If piece being dragged around board, must redraw that too */
4749 XSync(xDisplay, False);
4754 * event handler for redrawing the board
4756 void DrawPositionProc(w, event, prms, nprms)
4762 XDrawPosition(w, True, NULL);
4767 * event handler for parsing user moves
4769 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4770 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4771 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4772 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4773 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4774 // and at the end FinishMove() to perform the move after optional promotion popups.
4775 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4776 void HandleUserMove(w, event, prms, nprms)
4782 if (w != boardWidget || errorExitStatus != -1) return;
4783 if(nprms) shiftKey = !strcmp(prms[0], "1");
4786 if (event->type == ButtonPress) {
4787 XtPopdown(promotionShell);
4788 XtDestroyWidget(promotionShell);
4789 promotionUp = False;
4797 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4798 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4799 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4802 void AnimateUserMove (Widget w, XEvent * event,
4803 String * params, Cardinal * nParams)
4805 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4806 DragPieceMove(event->xmotion.x, event->xmotion.y);
4809 void HandlePV (Widget w, XEvent * event,
4810 String * params, Cardinal * nParams)
4811 { // [HGM] pv: walk PV
4812 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4815 static int savedIndex; /* gross that this is global */
4817 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4820 XawTextPosition index, dummy;
4823 XawTextGetSelectionPos(w, &index, &dummy);
4824 XtSetArg(arg, XtNstring, &val);
4825 XtGetValues(w, &arg, 1);
4826 ReplaceComment(savedIndex, val);
4827 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4828 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4831 void EditCommentPopUp(index, title, text)
4836 if (text == NULL) text = "";
4837 NewCommentPopup(title, text, index);
4840 void ICSInputBoxPopUp()
4845 extern Option boxOptions[];
4847 void ICSInputSendText()
4854 edit = boxOptions[0].handle;
4856 XtSetArg(args[j], XtNstring, &val); j++;
4857 XtGetValues(edit, args, j);
4859 SendMultiLineToICS(val);
4860 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4861 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4864 void ICSInputBoxPopDown()
4869 void CommentPopUp(title, text)
4872 savedIndex = currentMove; // [HGM] vari
4873 NewCommentPopup(title, text, currentMove);
4876 void CommentPopDown()
4881 void FileNamePopUp(label, def, filter, proc, openMode)
4888 fileProc = proc; /* I can't see a way not */
4889 fileOpenMode = openMode; /* to use globals here */
4890 { // [HGM] use file-selector dialog stolen from Ghostview
4892 int index; // this is not supported yet
4894 if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
4895 (def[0] ? def : NULL), filter, openMode, NULL, &name))
4896 (void) (*fileProc)(f, index=0, name);
4900 void FileNamePopDown()
4902 if (!filenameUp) return;
4903 XtPopdown(fileNameShell);
4904 XtDestroyWidget(fileNameShell);
4909 void FileNameCallback(w, client_data, call_data)
4911 XtPointer client_data, call_data;
4916 XtSetArg(args[0], XtNlabel, &name);
4917 XtGetValues(w, args, 1);
4919 if (strcmp(name, _("cancel")) == 0) {
4924 FileNameAction(w, NULL, NULL, NULL);
4927 void FileNameAction(w, event, prms, nprms)
4939 name = XawDialogGetValueString(w = XtParent(w));
4941 if ((name != NULL) && (*name != NULLCHAR)) {
4942 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
4943 XtPopdown(w = XtParent(XtParent(w)));
4947 p = strrchr(buf, ' ');
4954 fullname = ExpandPathName(buf);
4956 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
4959 f = fopen(fullname, fileOpenMode);
4961 DisplayError(_("Failed to open file"), errno);
4963 (void) (*fileProc)(f, index, buf);
4970 XtPopdown(w = XtParent(XtParent(w)));
4976 void PromotionPopUp()
4979 Widget dialog, layout;
4981 Dimension bw_width, pw_width;
4985 XtSetArg(args[j], XtNwidth, &bw_width); j++;
4986 XtGetValues(boardWidget, args, j);
4989 XtSetArg(args[j], XtNresizable, True); j++;
4990 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
4992 XtCreatePopupShell("Promotion", transientShellWidgetClass,
4993 shellWidget, args, j);
4995 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
4996 layoutArgs, XtNumber(layoutArgs));
4999 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5000 XtSetArg(args[j], XtNborderWidth, 0); j++;
5001 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5004 if(gameInfo.variant != VariantShogi) {
5005 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5006 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback,
5007 (XtPointer) dialog);
5008 XawDialogAddButton(dialog, _("General"), PromotionCallback,
5009 (XtPointer) dialog);
5010 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback,
5011 (XtPointer) dialog);
5012 XawDialogAddButton(dialog, _("Captain"), PromotionCallback,
5013 (XtPointer) dialog);
5015 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
5016 (XtPointer) dialog);
5017 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
5018 (XtPointer) dialog);
5019 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
5020 (XtPointer) dialog);
5021 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
5022 (XtPointer) dialog);
5024 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5025 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
5026 gameInfo.variant == VariantGiveaway) {
5027 XawDialogAddButton(dialog, _("King"), PromotionCallback,
5028 (XtPointer) dialog);
5030 if(gameInfo.variant == VariantCapablanca ||
5031 gameInfo.variant == VariantGothic ||
5032 gameInfo.variant == VariantCapaRandom) {
5033 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5034 (XtPointer) dialog);
5035 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5036 (XtPointer) dialog);
5038 } else // [HGM] shogi
5040 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5041 (XtPointer) dialog);
5042 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5043 (XtPointer) dialog);
5045 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5046 (XtPointer) dialog);
5048 XtRealizeWidget(promotionShell);
5049 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5052 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5053 XtGetValues(promotionShell, args, j);
5055 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5056 lineGap + squareSize/3 +
5057 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5058 0 : 6*(squareSize + lineGap)), &x, &y);
5061 XtSetArg(args[j], XtNx, x); j++;
5062 XtSetArg(args[j], XtNy, y); j++;
5063 XtSetValues(promotionShell, args, j);
5065 XtPopup(promotionShell, XtGrabNone);
5070 void PromotionPopDown()
5072 if (!promotionUp) return;
5073 XtPopdown(promotionShell);
5074 XtDestroyWidget(promotionShell);
5075 promotionUp = False;
5078 void PromotionCallback(w, client_data, call_data)
5080 XtPointer client_data, call_data;
5086 XtSetArg(args[0], XtNlabel, &name);
5087 XtGetValues(w, args, 1);
5091 if (fromX == -1) return;
5093 if (strcmp(name, _("cancel")) == 0) {
5097 } else if (strcmp(name, _("Knight")) == 0) {
5099 } else if (strcmp(name, _("Promote")) == 0) {
5101 } else if (strcmp(name, _("Defer")) == 0) {
5104 promoChar = ToLower(name[0]);
5107 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5109 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5110 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5115 void ErrorCallback(w, client_data, call_data)
5117 XtPointer client_data, call_data;
5120 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5122 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5128 if (!errorUp) return;
5130 XtPopdown(errorShell);
5131 XtDestroyWidget(errorShell);
5132 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5135 void ErrorPopUp(title, label, modal)
5136 char *title, *label;
5140 Widget dialog, layout;
5144 Dimension bw_width, pw_width;
5145 Dimension pw_height;
5149 XtSetArg(args[i], XtNresizable, True); i++;
5150 XtSetArg(args[i], XtNtitle, title); i++;
5152 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5153 shellWidget, args, i);
5155 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5156 layoutArgs, XtNumber(layoutArgs));
5159 XtSetArg(args[i], XtNlabel, label); i++;
5160 XtSetArg(args[i], XtNborderWidth, 0); i++;
5161 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5164 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5166 XtRealizeWidget(errorShell);
5167 CatchDeleteWindow(errorShell, "ErrorPopDown");
5170 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5171 XtGetValues(boardWidget, args, i);
5173 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5174 XtSetArg(args[i], XtNheight, &pw_height); i++;
5175 XtGetValues(errorShell, args, i);
5178 /* This code seems to tickle an X bug if it is executed too soon
5179 after xboard starts up. The coordinates get transformed as if
5180 the main window was positioned at (0, 0).
5182 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5183 0 - pw_height + squareSize / 3, &x, &y);
5185 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5186 RootWindowOfScreen(XtScreen(boardWidget)),
5187 (bw_width - pw_width) / 2,
5188 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5192 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5195 XtSetArg(args[i], XtNx, x); i++;
5196 XtSetArg(args[i], XtNy, y); i++;
5197 XtSetValues(errorShell, args, i);
5200 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5203 /* Disable all user input other than deleting the window */
5204 static int frozen = 0;
5208 /* Grab by a widget that doesn't accept input */
5209 XtAddGrab(messageWidget, TRUE, FALSE);
5213 /* Undo a FreezeUI */
5216 if (!frozen) return;
5217 XtRemoveGrab(messageWidget);
5221 char *ModeToWidgetName(mode)
5225 case BeginningOfGame:
5226 if (appData.icsActive)
5227 return "menuMode.ICS Client";
5228 else if (appData.noChessProgram ||
5229 *appData.cmailGameName != NULLCHAR)
5230 return "menuMode.Edit Game";
5232 return "menuMode.Machine Black";
5233 case MachinePlaysBlack:
5234 return "menuMode.Machine Black";
5235 case MachinePlaysWhite:
5236 return "menuMode.Machine White";
5238 return "menuMode.Analysis Mode";
5240 return "menuMode.Analyze File";
5241 case TwoMachinesPlay:
5242 return "menuMode.Two Machines";
5244 return "menuMode.Edit Game";
5245 case PlayFromGameFile:
5246 return "menuFile.Load Game";
5248 return "menuMode.Edit Position";
5250 return "menuMode.Training";
5251 case IcsPlayingWhite:
5252 case IcsPlayingBlack:
5256 return "menuMode.ICS Client";
5263 void ModeHighlight()
5266 static int oldPausing = FALSE;
5267 static GameMode oldmode = (GameMode) -1;
5270 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5272 if (pausing != oldPausing) {
5273 oldPausing = pausing;
5275 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5277 XtSetArg(args[0], XtNleftBitmap, None);
5279 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5282 if (appData.showButtonBar) {
5283 /* Always toggle, don't set. Previous code messes up when
5284 invoked while the button is pressed, as releasing it
5285 toggles the state again. */
5288 XtSetArg(args[0], XtNbackground, &oldbg);
5289 XtSetArg(args[1], XtNforeground, &oldfg);
5290 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5292 XtSetArg(args[0], XtNbackground, oldfg);
5293 XtSetArg(args[1], XtNforeground, oldbg);
5295 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5299 wname = ModeToWidgetName(oldmode);
5300 if (wname != NULL) {
5301 XtSetArg(args[0], XtNleftBitmap, None);
5302 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5304 wname = ModeToWidgetName(gameMode);
5305 if (wname != NULL) {
5306 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5307 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5311 /* Maybe all the enables should be handled here, not just this one */
5312 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5313 gameMode == Training || gameMode == PlayFromGameFile);
5318 * Button/menu procedures
5320 void ResetProc(w, event, prms, nprms)
5329 int LoadGamePopUp(f, gameNumber, title)
5334 cmailMsgLoaded = FALSE;
5335 if (gameNumber == 0) {
5336 int error = GameListBuild(f);
5338 DisplayError(_("Cannot build game list"), error);
5339 } else if (!ListEmpty(&gameList) &&
5340 ((ListGame *) gameList.tailPred)->number > 1) {
5341 GameListPopUp(f, title);
5347 return LoadGame(f, gameNumber, title, FALSE);
5350 void LoadGameProc(w, event, prms, nprms)
5356 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5359 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5362 void LoadNextGameProc(w, event, prms, nprms)
5371 void LoadPrevGameProc(w, event, prms, nprms)
5380 void ReloadGameProc(w, event, prms, nprms)
5389 void LoadNextPositionProc(w, event, prms, nprms)
5398 void LoadPrevPositionProc(w, event, prms, nprms)
5407 void ReloadPositionProc(w, event, prms, nprms)
5416 void LoadPositionProc(w, event, prms, nprms)
5422 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5425 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5428 void SaveGameProc(w, event, prms, nprms)
5434 FileNamePopUp(_("Save game file name?"),
5435 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5436 appData.oldSaveStyle ? ".game" : ".pgn",
5440 void SavePositionProc(w, event, prms, nprms)
5446 FileNamePopUp(_("Save position file name?"),
5447 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5448 appData.oldSaveStyle ? ".pos" : ".fen",
5452 void ReloadCmailMsgProc(w, event, prms, nprms)
5458 ReloadCmailMsgEvent(FALSE);
5461 void MailMoveProc(w, event, prms, nprms)
5470 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5471 char *selected_fen_position=NULL;
5474 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5475 Atom *type_return, XtPointer *value_return,
5476 unsigned long *length_return, int *format_return)
5478 char *selection_tmp;
5480 if (!selected_fen_position) return False; /* should never happen */
5481 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5482 /* note: since no XtSelectionDoneProc was registered, Xt will
5483 * automatically call XtFree on the value returned. So have to
5484 * make a copy of it allocated with XtMalloc */
5485 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5486 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5488 *value_return=selection_tmp;
5489 *length_return=strlen(selection_tmp);
5490 *type_return=*target;
5491 *format_return = 8; /* bits per byte */
5493 } else if (*target == XA_TARGETS(xDisplay)) {
5494 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5495 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5496 targets_tmp[1] = XA_STRING;
5497 *value_return = targets_tmp;
5498 *type_return = XA_ATOM;
5500 *format_return = 8 * sizeof(Atom);
5501 if (*format_return > 32) {
5502 *length_return *= *format_return / 32;
5503 *format_return = 32;
5511 /* note: when called from menu all parameters are NULL, so no clue what the
5512 * Widget which was clicked on was, or what the click event was
5514 void CopyPositionProc(w, event, prms, nprms)
5521 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5522 * have a notion of a position that is selected but not copied.
5523 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5525 if(gameMode == EditPosition) EditPositionDone(TRUE);
5526 if (selected_fen_position) free(selected_fen_position);
5527 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5528 if (!selected_fen_position) return;
5529 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5531 SendPositionSelection,
5532 NULL/* lose_ownership_proc */ ,
5533 NULL/* transfer_done_proc */);
5534 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5536 SendPositionSelection,
5537 NULL/* lose_ownership_proc */ ,
5538 NULL/* transfer_done_proc */);
5541 /* function called when the data to Paste is ready */
5543 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5544 Atom *type, XtPointer value, unsigned long *len, int *format)
5547 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5548 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5549 EditPositionPasteFEN(fenstr);
5553 /* called when Paste Position button is pressed,
5554 * all parameters will be NULL */
5555 void PastePositionProc(w, event, prms, nprms)
5561 XtGetSelectionValue(menuBarWidget,
5562 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5563 /* (XtSelectionCallbackProc) */ PastePositionCB,
5564 NULL, /* client_data passed to PastePositionCB */
5566 /* better to use the time field from the event that triggered the
5567 * call to this function, but that isn't trivial to get
5575 SendGameSelection(Widget w, Atom *selection, Atom *target,
5576 Atom *type_return, XtPointer *value_return,
5577 unsigned long *length_return, int *format_return)
5579 char *selection_tmp;
5581 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5582 FILE* f = fopen(gameCopyFilename, "r");
5585 if (f == NULL) return False;
5589 selection_tmp = XtMalloc(len + 1);
5590 count = fread(selection_tmp, 1, len, f);
5593 XtFree(selection_tmp);
5596 selection_tmp[len] = NULLCHAR;
5597 *value_return = selection_tmp;
5598 *length_return = len;
5599 *type_return = *target;
5600 *format_return = 8; /* bits per byte */
5602 } else if (*target == XA_TARGETS(xDisplay)) {
5603 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5604 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5605 targets_tmp[1] = XA_STRING;
5606 *value_return = targets_tmp;
5607 *type_return = XA_ATOM;
5609 *format_return = 8 * sizeof(Atom);
5610 if (*format_return > 32) {
5611 *length_return *= *format_return / 32;
5612 *format_return = 32;
5620 void CopySomething()
5625 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5626 * have a notion of a game that is selected but not copied.
5627 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5629 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5632 NULL/* lose_ownership_proc */ ,
5633 NULL/* transfer_done_proc */);
5634 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5637 NULL/* lose_ownership_proc */ ,
5638 NULL/* transfer_done_proc */);
5641 /* note: when called from menu all parameters are NULL, so no clue what the
5642 * Widget which was clicked on was, or what the click event was
5644 void CopyGameProc(w, event, prms, nprms)
5652 ret = SaveGameToFile(gameCopyFilename, FALSE);
5658 void CopyGameListProc(w, event, prms, nprms)
5664 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5668 /* function called when the data to Paste is ready */
5670 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5671 Atom *type, XtPointer value, unsigned long *len, int *format)
5674 if (value == NULL || *len == 0) {
5675 return; /* nothing had been selected to copy */
5677 f = fopen(gamePasteFilename, "w");
5679 DisplayError(_("Can't open temp file"), errno);
5682 fwrite(value, 1, *len, f);
5685 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5688 /* called when Paste Game button is pressed,
5689 * all parameters will be NULL */
5690 void PasteGameProc(w, event, prms, nprms)
5696 XtGetSelectionValue(menuBarWidget,
5697 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5698 /* (XtSelectionCallbackProc) */ PasteGameCB,
5699 NULL, /* client_data passed to PasteGameCB */
5701 /* better to use the time field from the event that triggered the
5702 * call to this function, but that isn't trivial to get
5712 SaveGameProc(NULL, NULL, NULL, NULL);
5716 void QuitProc(w, event, prms, nprms)
5725 void PauseProc(w, event, prms, nprms)
5735 void MachineBlackProc(w, event, prms, nprms)
5741 MachineBlackEvent();
5744 void MachineWhiteProc(w, event, prms, nprms)
5750 MachineWhiteEvent();
5753 void AnalyzeModeProc(w, event, prms, nprms)
5761 if (!first.analysisSupport) {
5762 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5763 DisplayError(buf, 0);
5766 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5767 if (appData.icsActive) {
5768 if (gameMode != IcsObserving) {
5769 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5770 DisplayError(buf, 0);
5772 if (appData.icsEngineAnalyze) {
5773 if (appData.debugMode)
5774 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5780 /* if enable, use want disable icsEngineAnalyze */
5781 if (appData.icsEngineAnalyze) {
5786 appData.icsEngineAnalyze = TRUE;
5787 if (appData.debugMode)
5788 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5790 #ifndef OPTIONSDIALOG
5791 if (!appData.showThinking)
5792 ShowThinkingProc(w,event,prms,nprms);
5798 void AnalyzeFileProc(w, event, prms, nprms)
5804 if (!first.analysisSupport) {
5806 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5807 DisplayError(buf, 0);
5811 #ifndef OPTIONSDIALOG
5812 if (!appData.showThinking)
5813 ShowThinkingProc(w,event,prms,nprms);
5816 FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5817 AnalysisPeriodicEvent(1);
5820 void TwoMachinesProc(w, event, prms, nprms)
5829 void MatchProc(w, event, prms, nprms)
5835 if(gameMode != BeginningOfGame) { DisplayError(_("You can only start a match from the initial position."), 0); return; }
5836 appData.matchGames = appData.defaultMatchGames;
5840 void IcsClientProc(w, event, prms, nprms)
5849 void EditGameProc(w, event, prms, nprms)
5858 void EditPositionProc(w, event, prms, nprms)
5864 EditPositionEvent();
5867 void TrainingProc(w, event, prms, nprms)
5876 void EditCommentProc(w, event, prms, nprms)
5884 if (PopDown(1)) { // popdown succesful
5886 XtSetArg(args[j], XtNleftBitmap, None); j++;
5887 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
5888 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
5889 } else // was not up
5893 void IcsInputBoxProc(w, event, prms, nprms)
5899 if (!PopDown(4)) ICSInputBoxPopUp();
5902 void AcceptProc(w, event, prms, nprms)
5911 void DeclineProc(w, event, prms, nprms)
5920 void RematchProc(w, event, prms, nprms)
5929 void CallFlagProc(w, event, prms, nprms)
5938 void DrawProc(w, event, prms, nprms)
5947 void AbortProc(w, event, prms, nprms)
5956 void AdjournProc(w, event, prms, nprms)
5965 void ResignProc(w, event, prms, nprms)
5974 void AdjuWhiteProc(w, event, prms, nprms)
5980 UserAdjudicationEvent(+1);
5983 void AdjuBlackProc(w, event, prms, nprms)
5989 UserAdjudicationEvent(-1);
5992 void AdjuDrawProc(w, event, prms, nprms)
5998 UserAdjudicationEvent(0);
6001 void EnterKeyProc(w, event, prms, nprms)
6007 if (shellUp[4] == True)
6011 void UpKeyProc(w, event, prms, nprms)
6016 { // [HGM] input: let up-arrow recall previous line from history
6023 if (!shellUp[4]) return;
6024 edit = boxOptions[0].handle;
6026 XtSetArg(args[j], XtNstring, &val); j++;
6027 XtGetValues(edit, args, j);
6028 val = PrevInHistory(val);
6029 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6030 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6032 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6033 XawTextReplace(edit, 0, 0, &t);
6034 XawTextSetInsertionPoint(edit, 9999);
6038 void DownKeyProc(w, event, prms, nprms)
6043 { // [HGM] input: let down-arrow recall next line from history
6048 if (!shellUp[4]) return;
6049 edit = boxOptions[0].handle;
6050 val = NextInHistory();
6051 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6052 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6054 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6055 XawTextReplace(edit, 0, 0, &t);
6056 XawTextSetInsertionPoint(edit, 9999);
6060 void StopObservingProc(w, event, prms, nprms)
6066 StopObservingEvent();
6069 void StopExaminingProc(w, event, prms, nprms)
6075 StopExaminingEvent();
6078 void UploadProc(w, event, prms, nprms)
6088 void ForwardProc(w, event, prms, nprms)
6098 void BackwardProc(w, event, prms, nprms)
6107 void ToStartProc(w, event, prms, nprms)
6116 void ToEndProc(w, event, prms, nprms)
6125 void RevertProc(w, event, prms, nprms)
6134 void AnnotateProc(w, event, prms, nprms)
6143 void TruncateGameProc(w, event, prms, nprms)
6149 TruncateGameEvent();
6151 void RetractMoveProc(w, event, prms, nprms)
6160 void MoveNowProc(w, event, prms, nprms)
6169 void FlipViewProc(w, event, prms, nprms)
6175 flipView = !flipView;
6176 DrawPosition(True, NULL);
6179 void PonderNextMoveProc(w, event, prms, nprms)
6187 PonderNextMoveEvent(!appData.ponderNextMove);
6188 #ifndef OPTIONSDIALOG
6189 if (appData.ponderNextMove) {
6190 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6192 XtSetArg(args[0], XtNleftBitmap, None);
6194 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6199 #ifndef OPTIONSDIALOG
6200 void AlwaysQueenProc(w, event, prms, nprms)
6208 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6210 if (appData.alwaysPromoteToQueen) {
6211 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6213 XtSetArg(args[0], XtNleftBitmap, None);
6215 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6219 void AnimateDraggingProc(w, event, prms, nprms)
6227 appData.animateDragging = !appData.animateDragging;
6229 if (appData.animateDragging) {
6230 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6233 XtSetArg(args[0], XtNleftBitmap, None);
6235 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6239 void AnimateMovingProc(w, event, prms, nprms)
6247 appData.animate = !appData.animate;
6249 if (appData.animate) {
6250 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6253 XtSetArg(args[0], XtNleftBitmap, None);
6255 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6259 void AutoflagProc(w, event, prms, nprms)
6267 appData.autoCallFlag = !appData.autoCallFlag;
6269 if (appData.autoCallFlag) {
6270 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6272 XtSetArg(args[0], XtNleftBitmap, None);
6274 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6278 void AutoflipProc(w, event, prms, nprms)
6286 appData.autoFlipView = !appData.autoFlipView;
6288 if (appData.autoFlipView) {
6289 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6291 XtSetArg(args[0], XtNleftBitmap, None);
6293 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6297 void BlindfoldProc(w, event, prms, nprms)
6305 appData.blindfold = !appData.blindfold;
6307 if (appData.blindfold) {
6308 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6310 XtSetArg(args[0], XtNleftBitmap, None);
6312 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6315 DrawPosition(True, NULL);
6318 void TestLegalityProc(w, event, prms, nprms)
6326 appData.testLegality = !appData.testLegality;
6328 if (appData.testLegality) {
6329 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6331 XtSetArg(args[0], XtNleftBitmap, None);
6333 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6338 void FlashMovesProc(w, event, prms, nprms)
6346 if (appData.flashCount == 0) {
6347 appData.flashCount = 3;
6349 appData.flashCount = -appData.flashCount;
6352 if (appData.flashCount > 0) {
6353 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6355 XtSetArg(args[0], XtNleftBitmap, None);
6357 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6362 void HighlightDraggingProc(w, event, prms, nprms)
6370 appData.highlightDragging = !appData.highlightDragging;
6372 if (appData.highlightDragging) {
6373 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6375 XtSetArg(args[0], XtNleftBitmap, None);
6377 XtSetValues(XtNameToWidget(menuBarWidget,
6378 "menuOptions.Highlight Dragging"), args, 1);
6382 void HighlightLastMoveProc(w, event, prms, nprms)
6390 appData.highlightLastMove = !appData.highlightLastMove;
6392 if (appData.highlightLastMove) {
6393 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6395 XtSetArg(args[0], XtNleftBitmap, None);
6397 XtSetValues(XtNameToWidget(menuBarWidget,
6398 "menuOptions.Highlight Last Move"), args, 1);
6401 void HighlightArrowProc(w, event, prms, nprms)
6409 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6411 if (appData.highlightMoveWithArrow) {
6412 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6414 XtSetArg(args[0], XtNleftBitmap, None);
6416 XtSetValues(XtNameToWidget(menuBarWidget,
6417 "menuOptions.Arrow"), args, 1);
6421 void IcsAlarmProc(w, event, prms, nprms)
6429 appData.icsAlarm = !appData.icsAlarm;
6431 if (appData.icsAlarm) {
6432 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6434 XtSetArg(args[0], XtNleftBitmap, None);
6436 XtSetValues(XtNameToWidget(menuBarWidget,
6437 "menuOptions.ICS Alarm"), args, 1);
6441 void MoveSoundProc(w, event, prms, nprms)
6449 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6451 if (appData.ringBellAfterMoves) {
6452 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6454 XtSetArg(args[0], XtNleftBitmap, None);
6456 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6460 void OneClickProc(w, event, prms, nprms)
6468 appData.oneClick = !appData.oneClick;
6470 if (appData.oneClick) {
6471 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6473 XtSetArg(args[0], XtNleftBitmap, None);
6475 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6479 void PeriodicUpdatesProc(w, event, prms, nprms)
6487 PeriodicUpdatesEvent(!appData.periodicUpdates);
6489 if (appData.periodicUpdates) {
6490 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6492 XtSetArg(args[0], XtNleftBitmap, None);
6494 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6498 void PopupExitMessageProc(w, event, prms, nprms)
6506 appData.popupExitMessage = !appData.popupExitMessage;
6508 if (appData.popupExitMessage) {
6509 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6511 XtSetArg(args[0], XtNleftBitmap, None);
6513 XtSetValues(XtNameToWidget(menuBarWidget,
6514 "menuOptions.Popup Exit Message"), args, 1);
6517 void PopupMoveErrorsProc(w, event, prms, nprms)
6525 appData.popupMoveErrors = !appData.popupMoveErrors;
6527 if (appData.popupMoveErrors) {
6528 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6530 XtSetArg(args[0], XtNleftBitmap, None);
6532 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6537 void PremoveProc(w, event, prms, nprms)
6545 appData.premove = !appData.premove;
6547 if (appData.premove) {
6548 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6550 XtSetArg(args[0], XtNleftBitmap, None);
6552 XtSetValues(XtNameToWidget(menuBarWidget,
6553 "menuOptions.Premove"), args, 1);
6557 void ShowCoordsProc(w, event, prms, nprms)
6565 appData.showCoords = !appData.showCoords;
6567 if (appData.showCoords) {
6568 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6570 XtSetArg(args[0], XtNleftBitmap, None);
6572 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6575 DrawPosition(True, NULL);
6578 void ShowThinkingProc(w, event, prms, nprms)
6584 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6585 ShowThinkingEvent();
6588 void HideThinkingProc(w, event, prms, nprms)
6596 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6597 ShowThinkingEvent();
6599 if (appData.hideThinkingFromHuman) {
6600 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6602 XtSetArg(args[0], XtNleftBitmap, None);
6604 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6609 void SaveOnExitProc(w, event, prms, nprms)
6617 saveSettingsOnExit = !saveSettingsOnExit;
6619 if (saveSettingsOnExit) {
6620 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6622 XtSetArg(args[0], XtNleftBitmap, None);
6624 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6628 void SaveSettingsProc(w, event, prms, nprms)
6634 SaveSettings(settingsFileName);
6637 void InfoProc(w, event, prms, nprms)
6644 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6649 void ManProc(w, event, prms, nprms)
6657 if (nprms && *nprms > 0)
6661 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6665 void HintProc(w, event, prms, nprms)
6674 void BookProc(w, event, prms, nprms)
6683 void AboutProc(w, event, prms, nprms)
6691 char *zippy = " (with Zippy code)";
6695 snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
6696 programVersion, zippy,
6697 "Copyright 1991 Digital Equipment Corporation",
6698 "Enhancements Copyright 1992-2009 Free Software Foundation",
6699 "Enhancements Copyright 2005 Alessandro Scotti",
6700 PACKAGE, " is free software and carries NO WARRANTY;",
6701 "see the file COPYING for more information.");
6702 ErrorPopUp(_("About XBoard"), buf, FALSE);
6705 void DebugProc(w, event, prms, nprms)
6711 appData.debugMode = !appData.debugMode;
6714 void AboutGameProc(w, event, prms, nprms)
6723 void NothingProc(w, event, prms, nprms)
6732 void Iconify(w, event, prms, nprms)
6741 XtSetArg(args[0], XtNiconic, True);
6742 XtSetValues(shellWidget, args, 1);
6745 void DisplayMessage(message, extMessage)
6746 char *message, *extMessage;
6748 /* display a message in the message widget */
6757 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6762 message = extMessage;
6766 /* need to test if messageWidget already exists, since this function
6767 can also be called during the startup, if for example a Xresource
6768 is not set up correctly */
6771 XtSetArg(arg, XtNlabel, message);
6772 XtSetValues(messageWidget, &arg, 1);
6778 void DisplayTitle(text)
6783 char title[MSG_SIZ];
6786 if (text == NULL) text = "";
6788 if (appData.titleInWindow) {
6790 XtSetArg(args[i], XtNlabel, text); i++;
6791 XtSetValues(titleWidget, args, i);
6794 if (*text != NULLCHAR) {
6795 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6796 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6797 } else if (appData.icsActive) {
6798 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6799 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6800 } else if (appData.cmailGameName[0] != NULLCHAR) {
6801 snprintf(icon, sizeof(icon), "%s", "CMail");
6802 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6804 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6805 } else if (gameInfo.variant == VariantGothic) {
6806 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6807 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6810 } else if (gameInfo.variant == VariantFalcon) {
6811 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6812 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6814 } else if (appData.noChessProgram) {
6815 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6816 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6818 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6819 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6822 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6823 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6824 XtSetValues(shellWidget, args, i);
6829 DisplayError(message, error)
6836 if (appData.debugMode || appData.matchMode) {
6837 fprintf(stderr, "%s: %s\n", programName, message);
6840 if (appData.debugMode || appData.matchMode) {
6841 fprintf(stderr, "%s: %s: %s\n",
6842 programName, message, strerror(error));
6844 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6847 ErrorPopUp(_("Error"), message, FALSE);
6851 void DisplayMoveError(message)
6856 DrawPosition(FALSE, NULL);
6857 if (appData.debugMode || appData.matchMode) {
6858 fprintf(stderr, "%s: %s\n", programName, message);
6860 if (appData.popupMoveErrors) {
6861 ErrorPopUp(_("Error"), message, FALSE);
6863 DisplayMessage(message, "");
6868 void DisplayFatalError(message, error, status)
6874 errorExitStatus = status;
6876 fprintf(stderr, "%s: %s\n", programName, message);
6878 fprintf(stderr, "%s: %s: %s\n",
6879 programName, message, strerror(error));
6880 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6883 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6884 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6890 void DisplayInformation(message)
6894 ErrorPopUp(_("Information"), message, TRUE);
6897 void DisplayNote(message)
6901 ErrorPopUp(_("Note"), message, FALSE);
6905 NullXErrorCheck(dpy, error_event)
6907 XErrorEvent *error_event;
6912 void DisplayIcsInteractionTitle(message)
6915 if (oldICSInteractionTitle == NULL) {
6916 /* Magic to find the old window title, adapted from vim */
6917 char *wina = getenv("WINDOWID");
6919 Window win = (Window) atoi(wina);
6920 Window root, parent, *children;
6921 unsigned int nchildren;
6922 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6924 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6925 if (!XQueryTree(xDisplay, win, &root, &parent,
6926 &children, &nchildren)) break;
6927 if (children) XFree((void *)children);
6928 if (parent == root || parent == 0) break;
6931 XSetErrorHandler(oldHandler);
6933 if (oldICSInteractionTitle == NULL) {
6934 oldICSInteractionTitle = "xterm";
6937 printf("\033]0;%s\007", message);
6941 char pendingReplyPrefix[MSG_SIZ];
6942 ProcRef pendingReplyPR;
6944 void AskQuestionProc(w, event, prms, nprms)
6951 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
6955 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6958 void AskQuestionPopDown()
6960 if (!askQuestionUp) return;
6961 XtPopdown(askQuestionShell);
6962 XtDestroyWidget(askQuestionShell);
6963 askQuestionUp = False;
6966 void AskQuestionReplyAction(w, event, prms, nprms)
6976 reply = XawDialogGetValueString(w = XtParent(w));
6977 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
6978 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
6979 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
6980 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
6981 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6982 AskQuestionPopDown();
6984 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
6987 void AskQuestionCallback(w, client_data, call_data)
6989 XtPointer client_data, call_data;
6994 XtSetArg(args[0], XtNlabel, &name);
6995 XtGetValues(w, args, 1);
6997 if (strcmp(name, _("cancel")) == 0) {
6998 AskQuestionPopDown();
7000 AskQuestionReplyAction(w, NULL, NULL, NULL);
7004 void AskQuestion(title, question, replyPrefix, pr)
7005 char *title, *question, *replyPrefix;
7009 Widget popup, layout, dialog, edit;
7015 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7016 pendingReplyPR = pr;
7019 XtSetArg(args[i], XtNresizable, True); i++;
7020 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7021 askQuestionShell = popup =
7022 XtCreatePopupShell(title, transientShellWidgetClass,
7023 shellWidget, args, i);
7026 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7027 layoutArgs, XtNumber(layoutArgs));
7030 XtSetArg(args[i], XtNlabel, question); i++;
7031 XtSetArg(args[i], XtNvalue, ""); i++;
7032 XtSetArg(args[i], XtNborderWidth, 0); i++;
7033 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7036 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7037 (XtPointer) dialog);
7038 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7039 (XtPointer) dialog);
7041 XtRealizeWidget(popup);
7042 CatchDeleteWindow(popup, "AskQuestionPopDown");
7044 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7045 &x, &y, &win_x, &win_y, &mask);
7047 XtSetArg(args[0], XtNx, x - 10);
7048 XtSetArg(args[1], XtNy, y - 30);
7049 XtSetValues(popup, args, 2);
7051 XtPopup(popup, XtGrabExclusive);
7052 askQuestionUp = True;
7054 edit = XtNameToWidget(dialog, "*value");
7055 XtSetKeyboardFocus(popup, edit);
7063 if (*name == NULLCHAR) {
7065 } else if (strcmp(name, "$") == 0) {
7066 putc(BELLCHAR, stderr);
7069 char *prefix = "", *sep = "";
7070 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7071 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7079 PlaySound(appData.soundMove);
7085 PlaySound(appData.soundIcsWin);
7091 PlaySound(appData.soundIcsLoss);
7097 PlaySound(appData.soundIcsDraw);
7101 PlayIcsUnfinishedSound()
7103 PlaySound(appData.soundIcsUnfinished);
7109 PlaySound(appData.soundIcsAlarm);
7115 system("stty echo");
7121 system("stty -echo");
7125 Colorize(cc, continuation)
7130 int count, outCount, error;
7132 if (textColors[(int)cc].bg > 0) {
7133 if (textColors[(int)cc].fg > 0) {
7134 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7135 textColors[(int)cc].fg, textColors[(int)cc].bg);
7137 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7138 textColors[(int)cc].bg);
7141 if (textColors[(int)cc].fg > 0) {
7142 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7143 textColors[(int)cc].fg);
7145 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7148 count = strlen(buf);
7149 outCount = OutputToProcess(NoProc, buf, count, &error);
7150 if (outCount < count) {
7151 DisplayFatalError(_("Error writing to display"), error, 1);
7154 if (continuation) return;
7157 PlaySound(appData.soundShout);
7160 PlaySound(appData.soundSShout);
7163 PlaySound(appData.soundChannel1);
7166 PlaySound(appData.soundChannel);
7169 PlaySound(appData.soundKibitz);
7172 PlaySound(appData.soundTell);
7174 case ColorChallenge:
7175 PlaySound(appData.soundChallenge);
7178 PlaySound(appData.soundRequest);
7181 PlaySound(appData.soundSeek);
7192 return getpwuid(getuid())->pw_name;
7196 ExpandPathName(path)
7199 static char static_buf[4*MSG_SIZ];
7200 char *d, *s, buf[4*MSG_SIZ];
7206 while (*s && isspace(*s))
7215 if (*(s+1) == '/') {
7216 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7220 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7221 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7222 pwd = getpwnam(buf);
7225 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7229 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7230 strcat(d, strchr(s+1, '/'));
7234 safeStrCpy(d, s, 4*MSG_SIZ );
7241 static char host_name[MSG_SIZ];
7243 #if HAVE_GETHOSTNAME
7244 gethostname(host_name, MSG_SIZ);
7246 #else /* not HAVE_GETHOSTNAME */
7247 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7248 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7250 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7252 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7253 #endif /* not HAVE_GETHOSTNAME */
7256 XtIntervalId delayedEventTimerXID = 0;
7257 DelayedEventCallback delayedEventCallback = 0;
7262 delayedEventTimerXID = 0;
7263 delayedEventCallback();
7267 ScheduleDelayedEvent(cb, millisec)
7268 DelayedEventCallback cb; long millisec;
7270 if(delayedEventTimerXID && delayedEventCallback == cb)
7271 // [HGM] alive: replace, rather than add or flush identical event
7272 XtRemoveTimeOut(delayedEventTimerXID);
7273 delayedEventCallback = cb;
7274 delayedEventTimerXID =
7275 XtAppAddTimeOut(appContext, millisec,
7276 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7279 DelayedEventCallback
7282 if (delayedEventTimerXID) {
7283 return delayedEventCallback;
7290 CancelDelayedEvent()
7292 if (delayedEventTimerXID) {
7293 XtRemoveTimeOut(delayedEventTimerXID);
7294 delayedEventTimerXID = 0;
7298 XtIntervalId loadGameTimerXID = 0;
7300 int LoadGameTimerRunning()
7302 return loadGameTimerXID != 0;
7305 int StopLoadGameTimer()
7307 if (loadGameTimerXID != 0) {
7308 XtRemoveTimeOut(loadGameTimerXID);
7309 loadGameTimerXID = 0;
7317 LoadGameTimerCallback(arg, id)
7321 loadGameTimerXID = 0;
7326 StartLoadGameTimer(millisec)
7330 XtAppAddTimeOut(appContext, millisec,
7331 (XtTimerCallbackProc) LoadGameTimerCallback,
7335 XtIntervalId analysisClockXID = 0;
7338 AnalysisClockCallback(arg, id)
7342 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7343 || appData.icsEngineAnalyze) { // [DM]
7344 AnalysisPeriodicEvent(0);
7345 StartAnalysisClock();
7350 StartAnalysisClock()
7353 XtAppAddTimeOut(appContext, 2000,
7354 (XtTimerCallbackProc) AnalysisClockCallback,
7358 XtIntervalId clockTimerXID = 0;
7360 int ClockTimerRunning()
7362 return clockTimerXID != 0;
7365 int StopClockTimer()
7367 if (clockTimerXID != 0) {
7368 XtRemoveTimeOut(clockTimerXID);
7377 ClockTimerCallback(arg, id)
7386 StartClockTimer(millisec)
7390 XtAppAddTimeOut(appContext, millisec,
7391 (XtTimerCallbackProc) ClockTimerCallback,
7396 DisplayTimerLabel(w, color, timer, highlight)
7405 /* check for low time warning */
7406 Pixel foregroundOrWarningColor = timerForegroundPixel;
7409 appData.lowTimeWarning &&
7410 (timer / 1000) < appData.icsAlarmTime)
7411 foregroundOrWarningColor = lowTimeWarningColor;
7413 if (appData.clockMode) {
7414 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7415 XtSetArg(args[0], XtNlabel, buf);
7417 snprintf(buf, MSG_SIZ, "%s ", color);
7418 XtSetArg(args[0], XtNlabel, buf);
7423 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7424 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7426 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7427 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7430 XtSetValues(w, args, 3);
7434 DisplayWhiteClock(timeRemaining, highlight)
7440 if(appData.noGUI) return;
7441 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7442 if (highlight && iconPixmap == bIconPixmap) {
7443 iconPixmap = wIconPixmap;
7444 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7445 XtSetValues(shellWidget, args, 1);
7450 DisplayBlackClock(timeRemaining, highlight)
7456 if(appData.noGUI) return;
7457 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7458 if (highlight && iconPixmap == wIconPixmap) {
7459 iconPixmap = bIconPixmap;
7460 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7461 XtSetValues(shellWidget, args, 1);
7479 int StartChildProcess(cmdLine, dir, pr)
7486 int to_prog[2], from_prog[2];
7490 if (appData.debugMode) {
7491 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7494 /* We do NOT feed the cmdLine to the shell; we just
7495 parse it into blank-separated arguments in the
7496 most simple-minded way possible.
7499 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7502 while(*p == ' ') p++;
7504 if(*p == '"' || *p == '\'')
7505 p = strchr(++argv[i-1], *p);
7506 else p = strchr(p, ' ');
7507 if (p == NULL) break;
7512 SetUpChildIO(to_prog, from_prog);
7514 if ((pid = fork()) == 0) {
7516 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7517 close(to_prog[1]); // first close the unused pipe ends
7518 close(from_prog[0]);
7519 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7520 dup2(from_prog[1], 1);
7521 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7522 close(from_prog[1]); // and closing again loses one of the pipes!
7523 if(fileno(stderr) >= 2) // better safe than sorry...
7524 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7526 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7531 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7533 execvp(argv[0], argv);
7535 /* If we get here, exec failed */
7540 /* Parent process */
7542 close(from_prog[1]);
7544 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7547 cp->fdFrom = from_prog[0];
7548 cp->fdTo = to_prog[1];
7553 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7554 static RETSIGTYPE AlarmCallBack(int n)
7560 DestroyChildProcess(pr, signalType)
7564 ChildProc *cp = (ChildProc *) pr;
7566 if (cp->kind != CPReal) return;
7568 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7569 signal(SIGALRM, AlarmCallBack);
7571 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7572 kill(cp->pid, SIGKILL); // kill it forcefully
7573 wait((int *) 0); // and wait again
7577 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7579 /* Process is exiting either because of the kill or because of
7580 a quit command sent by the backend; either way, wait for it to die.
7589 InterruptChildProcess(pr)
7592 ChildProc *cp = (ChildProc *) pr;
7594 if (cp->kind != CPReal) return;
7595 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7598 int OpenTelnet(host, port, pr)
7603 char cmdLine[MSG_SIZ];
7605 if (port[0] == NULLCHAR) {
7606 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7608 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7610 return StartChildProcess(cmdLine, "", pr);
7613 int OpenTCP(host, port, pr)
7619 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7620 #else /* !OMIT_SOCKETS */
7622 struct sockaddr_in sa;
7624 unsigned short uport;
7627 if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
7631 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7632 sa.sin_family = AF_INET;
7633 sa.sin_addr.s_addr = INADDR_ANY;
7634 uport = (unsigned short) 0;
7635 sa.sin_port = htons(uport);
7636 if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
7640 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7641 if (!(hp = gethostbyname(host))) {
7643 if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
7644 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
7645 hp->h_addrtype = AF_INET;
7647 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
7648 hp->h_addr_list[0] = (char *) malloc(4);
7649 hp->h_addr_list[0][0] = b0;
7650 hp->h_addr_list[0][1] = b1;
7651 hp->h_addr_list[0][2] = b2;
7652 hp->h_addr_list[0][3] = b3;
7657 sa.sin_family = hp->h_addrtype;
7658 uport = (unsigned short) atoi(port);
7659 sa.sin_port = htons(uport);
7660 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
7662 if (connect(s, (struct sockaddr *) &sa,
7663 sizeof(struct sockaddr_in)) < 0) {
7667 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7674 #endif /* !OMIT_SOCKETS */
7679 int OpenCommPort(name, pr)
7686 fd = open(name, 2, 0);
7687 if (fd < 0) return errno;
7689 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7699 int OpenLoopback(pr)
7705 SetUpChildIO(to, from);
7707 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7710 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7717 int OpenRcmd(host, user, cmd, pr)
7718 char *host, *user, *cmd;
7721 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7725 #define INPUT_SOURCE_BUF_SIZE 8192
7734 char buf[INPUT_SOURCE_BUF_SIZE];
7739 DoInputCallback(closure, source, xid)
7744 InputSource *is = (InputSource *) closure;
7749 if (is->lineByLine) {
7750 count = read(is->fd, is->unused,
7751 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7753 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7756 is->unused += count;
7758 while (p < is->unused) {
7759 q = memchr(p, '\n', is->unused - p);
7760 if (q == NULL) break;
7762 (is->func)(is, is->closure, p, q - p, 0);
7766 while (p < is->unused) {
7771 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7776 (is->func)(is, is->closure, is->buf, count, error);
7780 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7787 ChildProc *cp = (ChildProc *) pr;
7789 is = (InputSource *) calloc(1, sizeof(InputSource));
7790 is->lineByLine = lineByLine;
7794 is->fd = fileno(stdin);
7796 is->kind = cp->kind;
7797 is->fd = cp->fdFrom;
7800 is->unused = is->buf;
7803 is->xid = XtAppAddInput(appContext, is->fd,
7804 (XtPointer) (XtInputReadMask),
7805 (XtInputCallbackProc) DoInputCallback,
7807 is->closure = closure;
7808 return (InputSourceRef) is;
7812 RemoveInputSource(isr)
7815 InputSource *is = (InputSource *) isr;
7817 if (is->xid == 0) return;
7818 XtRemoveInput(is->xid);
7822 int OutputToProcess(pr, message, count, outError)
7828 static int line = 0;
7829 ChildProc *cp = (ChildProc *) pr;
7834 if (appData.noJoin || !appData.useInternalWrap)
7835 outCount = fwrite(message, 1, count, stdout);
7838 int width = get_term_width();
7839 int len = wrap(NULL, message, count, width, &line);
7840 char *msg = malloc(len);
7844 outCount = fwrite(message, 1, count, stdout);
7847 dbgchk = wrap(msg, message, count, width, &line);
7848 if (dbgchk != len && appData.debugMode)
7849 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7850 outCount = fwrite(msg, 1, dbgchk, stdout);
7856 outCount = write(cp->fdTo, message, count);
7866 /* Output message to process, with "ms" milliseconds of delay
7867 between each character. This is needed when sending the logon
7868 script to ICC, which for some reason doesn't like the
7869 instantaneous send. */
7870 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
7877 ChildProc *cp = (ChildProc *) pr;
7882 r = write(cp->fdTo, message++, 1);
7895 /**** Animation code by Hugh Fisher, DCS, ANU.
7897 Known problem: if a window overlapping the board is
7898 moved away while a piece is being animated underneath,
7899 the newly exposed area won't be updated properly.
7900 I can live with this.
7902 Known problem: if you look carefully at the animation
7903 of pieces in mono mode, they are being drawn as solid
7904 shapes without interior detail while moving. Fixing
7905 this would be a major complication for minimal return.
7908 /* Masks for XPM pieces. Black and white pieces can have
7909 different shapes, but in the interest of retaining my
7910 sanity pieces must have the same outline on both light
7911 and dark squares, and all pieces must use the same
7912 background square colors/images. */
7914 static int xpmDone = 0;
7917 CreateAnimMasks (pieceDepth)
7924 unsigned long plane;
7927 /* Need a bitmap just to get a GC with right depth */
7928 buf = XCreatePixmap(xDisplay, xBoardWindow,
7930 values.foreground = 1;
7931 values.background = 0;
7932 /* Don't use XtGetGC, not read only */
7933 maskGC = XCreateGC(xDisplay, buf,
7934 GCForeground | GCBackground, &values);
7935 XFreePixmap(xDisplay, buf);
7937 buf = XCreatePixmap(xDisplay, xBoardWindow,
7938 squareSize, squareSize, pieceDepth);
7939 values.foreground = XBlackPixel(xDisplay, xScreen);
7940 values.background = XWhitePixel(xDisplay, xScreen);
7941 bufGC = XCreateGC(xDisplay, buf,
7942 GCForeground | GCBackground, &values);
7944 for (piece = WhitePawn; piece <= BlackKing; piece++) {
7945 /* Begin with empty mask */
7946 if(!xpmDone) // [HGM] pieces: keep using existing
7947 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7948 squareSize, squareSize, 1);
7949 XSetFunction(xDisplay, maskGC, GXclear);
7950 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7951 0, 0, squareSize, squareSize);
7953 /* Take a copy of the piece */
7958 XSetFunction(xDisplay, bufGC, GXcopy);
7959 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
7961 0, 0, squareSize, squareSize, 0, 0);
7963 /* XOR the background (light) over the piece */
7964 XSetFunction(xDisplay, bufGC, GXxor);
7966 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7967 0, 0, squareSize, squareSize, 0, 0);
7969 XSetForeground(xDisplay, bufGC, lightSquareColor);
7970 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7973 /* We now have an inverted piece image with the background
7974 erased. Construct mask by just selecting all the non-zero
7975 pixels - no need to reconstruct the original image. */
7976 XSetFunction(xDisplay, maskGC, GXor);
7978 /* Might be quicker to download an XImage and create bitmap
7979 data from it rather than this N copies per piece, but it
7980 only takes a fraction of a second and there is a much
7981 longer delay for loading the pieces. */
7982 for (n = 0; n < pieceDepth; n ++) {
7983 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7984 0, 0, squareSize, squareSize,
7990 XFreePixmap(xDisplay, buf);
7991 XFreeGC(xDisplay, bufGC);
7992 XFreeGC(xDisplay, maskGC);
7996 InitAnimState (anim, info)
7998 XWindowAttributes * info;
8003 /* Each buffer is square size, same depth as window */
8004 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8005 squareSize, squareSize, info->depth);
8006 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8007 squareSize, squareSize, info->depth);
8009 /* Create a plain GC for blitting */
8010 mask = GCForeground | GCBackground | GCFunction |
8011 GCPlaneMask | GCGraphicsExposures;
8012 values.foreground = XBlackPixel(xDisplay, xScreen);
8013 values.background = XWhitePixel(xDisplay, xScreen);
8014 values.function = GXcopy;
8015 values.plane_mask = AllPlanes;
8016 values.graphics_exposures = False;
8017 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8019 /* Piece will be copied from an existing context at
8020 the start of each new animation/drag. */
8021 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8023 /* Outline will be a read-only copy of an existing */
8024 anim->outlineGC = None;
8030 XWindowAttributes info;
8032 if (xpmDone && gameInfo.variant == oldVariant) return;
8033 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
8034 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8036 InitAnimState(&game, &info);
8037 InitAnimState(&player, &info);
8039 /* For XPM pieces, we need bitmaps to use as masks. */
8041 CreateAnimMasks(info.depth), xpmDone = 1;
8046 static Boolean frameWaiting;
8048 static RETSIGTYPE FrameAlarm (sig)
8051 frameWaiting = False;
8052 /* In case System-V style signals. Needed?? */
8053 signal(SIGALRM, FrameAlarm);
8060 struct itimerval delay;
8062 XSync(xDisplay, False);
8065 frameWaiting = True;
8066 signal(SIGALRM, FrameAlarm);
8067 delay.it_interval.tv_sec =
8068 delay.it_value.tv_sec = time / 1000;
8069 delay.it_interval.tv_usec =
8070 delay.it_value.tv_usec = (time % 1000) * 1000;
8071 setitimer(ITIMER_REAL, &delay, NULL);
8072 while (frameWaiting) pause();
8073 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8074 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8075 setitimer(ITIMER_REAL, &delay, NULL);
8085 XSync(xDisplay, False);
8087 usleep(time * 1000);
8092 /* Convert board position to corner of screen rect and color */
8095 ScreenSquare(column, row, pt, color)
8096 int column; int row; XPoint * pt; int * color;
8099 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8100 pt->y = lineGap + row * (squareSize + lineGap);
8102 pt->x = lineGap + column * (squareSize + lineGap);
8103 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8105 *color = SquareColor(row, column);
8108 /* Convert window coords to square */
8111 BoardSquare(x, y, column, row)
8112 int x; int y; int * column; int * row;
8114 *column = EventToSquare(x, BOARD_WIDTH);
8115 if (flipView && *column >= 0)
8116 *column = BOARD_WIDTH - 1 - *column;
8117 *row = EventToSquare(y, BOARD_HEIGHT);
8118 if (!flipView && *row >= 0)
8119 *row = BOARD_HEIGHT - 1 - *row;
8124 #undef Max /* just in case */
8126 #define Max(a, b) ((a) > (b) ? (a) : (b))
8127 #define Min(a, b) ((a) < (b) ? (a) : (b))
8130 SetRect(rect, x, y, width, height)
8131 XRectangle * rect; int x; int y; int width; int height;
8135 rect->width = width;
8136 rect->height = height;
8139 /* Test if two frames overlap. If they do, return
8140 intersection rect within old and location of
8141 that rect within new. */
8144 Intersect(old, new, size, area, pt)
8145 XPoint * old; XPoint * new;
8146 int size; XRectangle * area; XPoint * pt;
8148 if (old->x > new->x + size || new->x > old->x + size ||
8149 old->y > new->y + size || new->y > old->y + size) {
8152 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8153 size - abs(old->x - new->x), size - abs(old->y - new->y));
8154 pt->x = Max(old->x - new->x, 0);
8155 pt->y = Max(old->y - new->y, 0);
8160 /* For two overlapping frames, return the rect(s)
8161 in the old that do not intersect with the new. */
8164 CalcUpdateRects(old, new, size, update, nUpdates)
8165 XPoint * old; XPoint * new; int size;
8166 XRectangle update[]; int * nUpdates;
8170 /* If old = new (shouldn't happen) then nothing to draw */
8171 if (old->x == new->x && old->y == new->y) {
8175 /* Work out what bits overlap. Since we know the rects
8176 are the same size we don't need a full intersect calc. */
8178 /* Top or bottom edge? */
8179 if (new->y > old->y) {
8180 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8182 } else if (old->y > new->y) {
8183 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8184 size, old->y - new->y);
8187 /* Left or right edge - don't overlap any update calculated above. */
8188 if (new->x > old->x) {
8189 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8190 new->x - old->x, size - abs(new->y - old->y));
8192 } else if (old->x > new->x) {
8193 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8194 old->x - new->x, size - abs(new->y - old->y));
8201 /* Generate a series of frame coords from start->mid->finish.
8202 The movement rate doubles until the half way point is
8203 reached, then halves back down to the final destination,
8204 which gives a nice slow in/out effect. The algorithmn
8205 may seem to generate too many intermediates for short
8206 moves, but remember that the purpose is to attract the
8207 viewers attention to the piece about to be moved and
8208 then to where it ends up. Too few frames would be less
8212 Tween(start, mid, finish, factor, frames, nFrames)
8213 XPoint * start; XPoint * mid;
8214 XPoint * finish; int factor;
8215 XPoint frames[]; int * nFrames;
8217 int fraction, n, count;
8221 /* Slow in, stepping 1/16th, then 1/8th, ... */
8223 for (n = 0; n < factor; n++)
8225 for (n = 0; n < factor; n++) {
8226 frames[count].x = start->x + (mid->x - start->x) / fraction;
8227 frames[count].y = start->y + (mid->y - start->y) / fraction;
8229 fraction = fraction / 2;
8233 frames[count] = *mid;
8236 /* Slow out, stepping 1/2, then 1/4, ... */
8238 for (n = 0; n < factor; n++) {
8239 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8240 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8242 fraction = fraction * 2;
8247 /* Draw a piece on the screen without disturbing what's there */
8250 SelectGCMask(piece, clip, outline, mask)
8251 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8255 /* Bitmap for piece being moved. */
8256 if (appData.monoMode) {
8257 *mask = *pieceToSolid(piece);
8258 } else if (useImages) {
8260 *mask = xpmMask[piece];
8262 *mask = ximMaskPm[piece];
8265 *mask = *pieceToSolid(piece);
8268 /* GC for piece being moved. Square color doesn't matter, but
8269 since it gets modified we make a copy of the original. */
8271 if (appData.monoMode)
8276 if (appData.monoMode)
8281 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8283 /* Outline only used in mono mode and is not modified */
8285 *outline = bwPieceGC;
8287 *outline = wbPieceGC;
8291 OverlayPiece(piece, clip, outline, dest)
8292 ChessSquare piece; GC clip; GC outline; Drawable dest;
8297 /* Draw solid rectangle which will be clipped to shape of piece */
8298 XFillRectangle(xDisplay, dest, clip,
8299 0, 0, squareSize, squareSize);
8300 if (appData.monoMode)
8301 /* Also draw outline in contrasting color for black
8302 on black / white on white cases */
8303 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8304 0, 0, squareSize, squareSize, 0, 0, 1);
8306 /* Copy the piece */
8311 if(appData.upsideDown && flipView) kind ^= 2;
8312 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8314 0, 0, squareSize, squareSize,
8319 /* Animate the movement of a single piece */
8322 BeginAnimation(anim, piece, startColor, start)
8330 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8331 /* The old buffer is initialised with the start square (empty) */
8332 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8333 anim->prevFrame = *start;
8335 /* The piece will be drawn using its own bitmap as a matte */
8336 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8337 XSetClipMask(xDisplay, anim->pieceGC, mask);
8341 AnimationFrame(anim, frame, piece)
8346 XRectangle updates[4];
8351 /* Save what we are about to draw into the new buffer */
8352 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8353 frame->x, frame->y, squareSize, squareSize,
8356 /* Erase bits of the previous frame */
8357 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8358 /* Where the new frame overlapped the previous,
8359 the contents in newBuf are wrong. */
8360 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8361 overlap.x, overlap.y,
8362 overlap.width, overlap.height,
8364 /* Repaint the areas in the old that don't overlap new */
8365 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8366 for (i = 0; i < count; i++)
8367 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8368 updates[i].x - anim->prevFrame.x,
8369 updates[i].y - anim->prevFrame.y,
8370 updates[i].width, updates[i].height,
8371 updates[i].x, updates[i].y);
8373 /* Easy when no overlap */
8374 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8375 0, 0, squareSize, squareSize,
8376 anim->prevFrame.x, anim->prevFrame.y);
8379 /* Save this frame for next time round */
8380 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8381 0, 0, squareSize, squareSize,
8383 anim->prevFrame = *frame;
8385 /* Draw piece over original screen contents, not current,
8386 and copy entire rect. Wipes out overlapping piece images. */
8387 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8388 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8389 0, 0, squareSize, squareSize,
8390 frame->x, frame->y);
8394 EndAnimation (anim, finish)
8398 XRectangle updates[4];
8403 /* The main code will redraw the final square, so we
8404 only need to erase the bits that don't overlap. */
8405 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8406 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8407 for (i = 0; i < count; i++)
8408 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8409 updates[i].x - anim->prevFrame.x,
8410 updates[i].y - anim->prevFrame.y,
8411 updates[i].width, updates[i].height,
8412 updates[i].x, updates[i].y);
8414 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8415 0, 0, squareSize, squareSize,
8416 anim->prevFrame.x, anim->prevFrame.y);
8421 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8423 ChessSquare piece; int startColor;
8424 XPoint * start; XPoint * finish;
8425 XPoint frames[]; int nFrames;
8429 BeginAnimation(anim, piece, startColor, start);
8430 for (n = 0; n < nFrames; n++) {
8431 AnimationFrame(anim, &(frames[n]), piece);
8432 FrameDelay(appData.animSpeed);
8434 EndAnimation(anim, finish);
8438 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8441 ChessSquare piece = board[fromY][toY];
8442 board[fromY][toY] = EmptySquare;
8443 DrawPosition(FALSE, board);
8445 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8446 y = lineGap + toY * (squareSize + lineGap);
8448 x = lineGap + toX * (squareSize + lineGap);
8449 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8451 for(i=1; i<4*kFactor; i++) {
8452 int r = squareSize * 9 * i/(20*kFactor - 5);
8453 XFillArc(xDisplay, xBoardWindow, highlineGC,
8454 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8455 FrameDelay(appData.animSpeed);
8457 board[fromY][toY] = piece;
8460 /* Main control logic for deciding what to animate and how */
8463 AnimateMove(board, fromX, fromY, toX, toY)
8472 XPoint start, finish, mid;
8473 XPoint frames[kFactor * 2 + 1];
8474 int nFrames, startColor, endColor;
8476 /* Are we animating? */
8477 if (!appData.animate || appData.blindfold)
8480 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8481 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8482 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8484 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8485 piece = board[fromY][fromX];
8486 if (piece >= EmptySquare) return;
8491 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8494 if (appData.debugMode) {
8495 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8496 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8497 piece, fromX, fromY, toX, toY); }
8499 ScreenSquare(fromX, fromY, &start, &startColor);
8500 ScreenSquare(toX, toY, &finish, &endColor);
8503 /* Knight: make straight movement then diagonal */
8504 if (abs(toY - fromY) < abs(toX - fromX)) {
8505 mid.x = start.x + (finish.x - start.x) / 2;
8509 mid.y = start.y + (finish.y - start.y) / 2;
8512 mid.x = start.x + (finish.x - start.x) / 2;
8513 mid.y = start.y + (finish.y - start.y) / 2;
8516 /* Don't use as many frames for very short moves */
8517 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8518 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8520 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8521 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8522 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8524 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8525 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8528 /* Be sure end square is redrawn */
8529 damage[0][toY][toX] = True;
8533 DragPieceBegin(x, y)
8536 int boardX, boardY, color;
8539 /* Are we animating? */
8540 if (!appData.animateDragging || appData.blindfold)
8543 /* Figure out which square we start in and the
8544 mouse position relative to top left corner. */
8545 BoardSquare(x, y, &boardX, &boardY);
8546 player.startBoardX = boardX;
8547 player.startBoardY = boardY;
8548 ScreenSquare(boardX, boardY, &corner, &color);
8549 player.startSquare = corner;
8550 player.startColor = color;
8551 /* As soon as we start dragging, the piece will jump slightly to
8552 be centered over the mouse pointer. */
8553 player.mouseDelta.x = squareSize/2;
8554 player.mouseDelta.y = squareSize/2;
8555 /* Initialise animation */
8556 player.dragPiece = PieceForSquare(boardX, boardY);
8558 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8559 player.dragActive = True;
8560 BeginAnimation(&player, player.dragPiece, color, &corner);
8561 /* Mark this square as needing to be redrawn. Note that
8562 we don't remove the piece though, since logically (ie
8563 as seen by opponent) the move hasn't been made yet. */
8564 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8565 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8566 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8567 corner.x, corner.y, squareSize, squareSize,
8568 0, 0); // [HGM] zh: unstack in stead of grab
8569 if(gatingPiece != EmptySquare) {
8570 /* Kludge alert: When gating we want the introduced
8571 piece to appear on the from square. To generate an
8572 image of it, we draw it on the board, copy the image,
8573 and draw the original piece again. */
8574 ChessSquare piece = boards[currentMove][boardY][boardX];
8575 DrawSquare(boardY, boardX, gatingPiece, 0);
8576 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8577 corner.x, corner.y, squareSize, squareSize, 0, 0);
8578 DrawSquare(boardY, boardX, piece, 0);
8580 damage[0][boardY][boardX] = True;
8582 player.dragActive = False;
8587 ChangeDragPiece(ChessSquare piece)
8590 player.dragPiece = piece;
8591 /* The piece will be drawn using its own bitmap as a matte */
8592 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8593 XSetClipMask(xDisplay, player.pieceGC, mask);
8602 /* Are we animating? */
8603 if (!appData.animateDragging || appData.blindfold)
8607 if (! player.dragActive)
8609 /* Move piece, maintaining same relative position
8610 of mouse within square */
8611 corner.x = x - player.mouseDelta.x;
8612 corner.y = y - player.mouseDelta.y;
8613 AnimationFrame(&player, &corner, player.dragPiece);
8615 if (appData.highlightDragging) {
8617 BoardSquare(x, y, &boardX, &boardY);
8618 SetHighlights(fromX, fromY, boardX, boardY);
8627 int boardX, boardY, color;
8630 /* Are we animating? */
8631 if (!appData.animateDragging || appData.blindfold)
8635 if (! player.dragActive)
8637 /* Last frame in sequence is square piece is
8638 placed on, which may not match mouse exactly. */
8639 BoardSquare(x, y, &boardX, &boardY);
8640 ScreenSquare(boardX, boardY, &corner, &color);
8641 EndAnimation(&player, &corner);
8643 /* Be sure end square is redrawn */
8644 damage[0][boardY][boardX] = True;
8646 /* This prevents weird things happening with fast successive
8647 clicks which on my Sun at least can cause motion events
8648 without corresponding press/release. */
8649 player.dragActive = False;
8652 /* Handle expose event while piece being dragged */
8657 if (!player.dragActive || appData.blindfold)
8660 /* What we're doing: logically, the move hasn't been made yet,
8661 so the piece is still in it's original square. But visually
8662 it's being dragged around the board. So we erase the square
8663 that the piece is on and draw it at the last known drag point. */
8664 BlankSquare(player.startSquare.x, player.startSquare.y,
8665 player.startColor, EmptySquare, xBoardWindow, 1);
8666 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8667 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8670 #include <sys/ioctl.h>
8671 int get_term_width()
8673 int fd, default_width;
8676 default_width = 79; // this is FICS default anyway...
8678 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8680 if (!ioctl(fd, TIOCGSIZE, &win))
8681 default_width = win.ts_cols;
8682 #elif defined(TIOCGWINSZ)
8684 if (!ioctl(fd, TIOCGWINSZ, &win))
8685 default_width = win.ws_col;
8687 return default_width;
8693 static int old_width = 0;
8694 int new_width = get_term_width();
8696 if (old_width != new_width)
8697 ics_printf("set width %d\n", new_width);
8698 old_width = new_width;
8701 void NotifyFrontendLogin()
8706 /* [AS] Arrow highlighting support */
8708 static double A_WIDTH = 5; /* Width of arrow body */
8710 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8711 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8713 static double Sqr( double x )
8718 static int Round( double x )
8720 return (int) (x + 0.5);
8723 void SquareToPos(int rank, int file, int *x, int *y)
8726 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8727 *y = lineGap + rank * (squareSize + lineGap);
8729 *x = lineGap + file * (squareSize + lineGap);
8730 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8734 /* Draw an arrow between two points using current settings */
8735 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8738 double dx, dy, j, k, x, y;
8741 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8743 arrow[0].x = s_x + A_WIDTH + 0.5;
8746 arrow[1].x = s_x + A_WIDTH + 0.5;
8747 arrow[1].y = d_y - h;
8749 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8750 arrow[2].y = d_y - h;
8755 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8756 arrow[5].y = d_y - h;
8758 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8759 arrow[4].y = d_y - h;
8761 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8764 else if( d_y == s_y ) {
8765 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8768 arrow[0].y = s_y + A_WIDTH + 0.5;
8770 arrow[1].x = d_x - w;
8771 arrow[1].y = s_y + A_WIDTH + 0.5;
8773 arrow[2].x = d_x - w;
8774 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8779 arrow[5].x = d_x - w;
8780 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8782 arrow[4].x = d_x - w;
8783 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8786 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8789 /* [AS] Needed a lot of paper for this! :-) */
8790 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8791 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8793 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8795 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8800 arrow[0].x = Round(x - j);
8801 arrow[0].y = Round(y + j*dx);
8803 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8804 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8807 x = (double) d_x - k;
8808 y = (double) d_y - k*dy;
8811 x = (double) d_x + k;
8812 y = (double) d_y + k*dy;
8815 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8817 arrow[6].x = Round(x - j);
8818 arrow[6].y = Round(y + j*dx);
8820 arrow[2].x = Round(arrow[6].x + 2*j);
8821 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8823 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8824 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8829 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8830 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8833 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8834 // Polygon( hdc, arrow, 7 );
8837 /* [AS] Draw an arrow between two squares */
8838 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
8840 int s_x, s_y, d_x, d_y, hor, vert, i;
8842 if( s_col == d_col && s_row == d_row ) {
8846 /* Get source and destination points */
8847 SquareToPos( s_row, s_col, &s_x, &s_y);
8848 SquareToPos( d_row, d_col, &d_x, &d_y);
8851 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8853 else if( d_y < s_y ) {
8854 d_y += squareSize / 2 + squareSize / 4;
8857 d_y += squareSize / 2;
8861 d_x += squareSize / 2 - squareSize / 4;
8863 else if( d_x < s_x ) {
8864 d_x += squareSize / 2 + squareSize / 4;
8867 d_x += squareSize / 2;
8870 s_x += squareSize / 2;
8871 s_y += squareSize / 2;
8874 A_WIDTH = squareSize / 14.; //[HGM] make float
8876 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8878 hor = 64*s_col + 32; vert = 64*s_row + 32;
8879 for(i=0; i<= 64; i++) {
8880 damage[0][vert+6>>6][hor+6>>6] = True;
8881 damage[0][vert-6>>6][hor+6>>6] = True;
8882 damage[0][vert+6>>6][hor-6>>6] = True;
8883 damage[0][vert-6>>6][hor-6>>6] = True;
8884 hor += d_col - s_col; vert += d_row - s_row;
8888 Boolean IsDrawArrowEnabled()
8890 return appData.highlightMoveWithArrow && squareSize >= 32;
8893 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
8895 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8896 DrawArrowBetweenSquares(fromX, fromY, toX, toY);