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 EditBookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
467 void GameListOptionsPopDown P(());
468 void ShufflePopDown P(());
469 void TimeControlPopDown P(());
470 void GenericPopDown P(());
471 void update_ics_width P(());
472 int get_term_width P(());
473 int CopyMemoProc P(());
474 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
475 Boolean IsDrawArrowEnabled P(());
478 * XBoard depends on Xt R4 or higher
480 int xtVersion = XtSpecificationRelease;
485 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
486 jailSquareColor, highlightSquareColor, premoveHighlightColor;
487 Pixel lowTimeWarningColor;
488 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
489 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
490 wjPieceGC, bjPieceGC, prelineGC, countGC;
491 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
492 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
493 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
494 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
495 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
496 ICSInputShell, fileNameShell, askQuestionShell;
497 Widget historyShell, evalGraphShell, gameListShell;
498 int hOffset; // [HGM] dual
499 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
500 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
501 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
502 Font clockFontID, coordFontID, countFontID;
503 XFontStruct *clockFontStruct, *coordFontStruct, *countFontStruct;
504 XtAppContext appContext;
506 char *oldICSInteractionTitle;
510 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
512 Position commentX = -1, commentY = -1;
513 Dimension commentW, commentH;
514 typedef unsigned int BoardSize;
516 Boolean chessProgram;
518 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
519 int squareSize, smallLayout = 0, tinyLayout = 0,
520 marginW, marginH, // [HGM] for run-time resizing
521 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
522 ICSInputBoxUp = False, askQuestionUp = False,
523 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
524 errorUp = False, errorExitStatus = -1, lineGap, defaultLineGap;
525 Pixel timerForegroundPixel, timerBackgroundPixel;
526 Pixel buttonForegroundPixel, buttonBackgroundPixel;
527 char *chessDir, *programName, *programVersion,
528 *gameCopyFilename, *gamePasteFilename;
529 Boolean alwaysOnTop = False;
530 Boolean saveSettingsOnExit;
531 char *settingsFileName;
532 char *icsTextMenuString;
534 char *firstChessProgramNames;
535 char *secondChessProgramNames;
537 WindowPlacement wpMain;
538 WindowPlacement wpConsole;
539 WindowPlacement wpComment;
540 WindowPlacement wpMoveHistory;
541 WindowPlacement wpEvalGraph;
542 WindowPlacement wpEngineOutput;
543 WindowPlacement wpGameList;
544 WindowPlacement wpTags;
546 extern Widget shells[];
547 extern Boolean shellUp[];
551 Pixmap pieceBitmap[2][(int)BlackPawn];
552 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
553 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
554 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
555 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
556 Pixmap xpmBoardBitmap[2];
557 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
558 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
559 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
560 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
561 XImage *ximLightSquare, *ximDarkSquare;
564 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
565 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
567 #define White(piece) ((int)(piece) < (int)BlackPawn)
569 /* Variables for doing smooth animation. This whole thing
570 would be much easier if the board was double-buffered,
571 but that would require a fairly major rewrite. */
576 GC blitGC, pieceGC, outlineGC;
577 XPoint startSquare, prevFrame, mouseDelta;
581 int startBoardX, startBoardY;
584 /* There can be two pieces being animated at once: a player
585 can begin dragging a piece before the remote opponent has moved. */
587 static AnimState game, player;
589 /* Bitmaps for use as masks when drawing XPM pieces.
590 Need one for each black and white piece. */
591 static Pixmap xpmMask[BlackKing + 1];
593 /* This magic number is the number of intermediate frames used
594 in each half of the animation. For short moves it's reduced
595 by 1. The total number of frames will be factor * 2 + 1. */
598 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
600 MenuItem fileMenu[] = {
601 {N_("New Game Ctrl+N"), "New Game", ResetProc},
602 {N_("New Shuffle Game ..."), "New Shuffle Game", ShuffleMenuProc},
603 {N_("New Variant ... Alt+Shift+V"), "New Variant", NewVariantProc}, // [HGM] variant: not functional yet
604 {"----", NULL, NothingProc},
605 {N_("Load Game Ctrl+O"), "Load Game", LoadGameProc},
606 {N_("Load Position Ctrl+Shift+O"), "Load Position", LoadPositionProc},
607 // {N_("Load Next Game"), "Load Next Game", LoadNextGameProc},
608 // {N_("Load Previous Game"), "Load Previous Game", LoadPrevGameProc},
609 // {N_("Reload Same Game"), "Reload Same Game", ReloadGameProc},
610 {N_("Next Position Shift+PgDn"), "Load Next Position", LoadNextPositionProc},
611 {N_("Prev Position Shift+PgUp"), "Load Previous Position", LoadPrevPositionProc},
612 {"----", NULL, NothingProc},
613 // {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
614 {N_("Save Game Ctrl+S"), "Save Game", SaveGameProc},
615 {N_("Save Position Ctrl+Shift+S"), "Save Position", SavePositionProc},
616 {"----", NULL, NothingProc},
617 {N_("Mail Move"), "Mail Move", MailMoveProc},
618 {N_("Reload CMail Message"), "Reload CMail Message", ReloadCmailMsgProc},
619 {"----", NULL, NothingProc},
620 {N_("Quit Ctr+Q"), "Exit", QuitProc},
624 MenuItem editMenu[] = {
625 {N_("Copy Game Ctrl+C"), "Copy Game", CopyGameProc},
626 {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
627 {N_("Copy Game List"), "Copy Game List", CopyGameListProc},
628 {"----", NULL, NothingProc},
629 {N_("Paste Game Ctrl+V"), "Paste Game", PasteGameProc},
630 {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
631 {"----", NULL, NothingProc},
632 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
633 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
634 {N_("Edit Tags"), "Edit Tags", EditTagsProc},
635 {N_("Edit Comment"), "Edit Comment", EditCommentProc},
636 {N_("Edit Book"), "Edit Book", EditBookProc},
637 {"----", NULL, NothingProc},
638 {N_("Revert Home"), "Revert", RevertProc},
639 {N_("Annotate"), "Annotate", AnnotateProc},
640 {N_("Truncate Game End"), "Truncate Game", TruncateGameProc},
641 {"----", NULL, NothingProc},
642 {N_("Backward Alt+Left"), "Backward", BackwardProc},
643 {N_("Forward Alt+Right"), "Forward", ForwardProc},
644 {N_("Back to Start Alt+Home"), "Back to Start", ToStartProc},
645 {N_("Forward to End Alt+End"), "Forward to End", ToEndProc},
649 MenuItem viewMenu[] = {
650 {N_("Flip View F2"), "Flip View", FlipViewProc},
651 {"----", NULL, NothingProc},
652 {N_("Engine Output Alt+Shift+O"), "Show Engine Output", EngineOutputProc},
653 {N_("Move History Alt+Shift+H"), "Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
654 {N_("Evaluation Graph Alt+Shift+E"), "Show Evaluation Graph", EvalGraphProc},
655 {N_("Game List Alt+Shift+G"), "Show Game List", ShowGameListProc},
656 {N_("ICS text menu"), "ICStex", IcsTextProc},
657 {"----", NULL, NothingProc},
658 {N_("Tags"), "Show Tags", EditTagsProc},
659 {N_("Comments"), "Show Comments", EditCommentProc},
660 {N_("ICS Input Box"), "ICS Input Box", IcsInputBoxProc},
661 {"----", NULL, NothingProc},
662 {N_("Board..."), "Board Options", BoardOptionsProc},
663 {N_("Game List Tags..."), "Game List", GameListOptionsPopUp},
667 MenuItem modeMenu[] = {
668 {N_("Machine White Ctrl+W"), "Machine White", MachineWhiteProc},
669 {N_("Machine Black Ctrl+B"), "Machine Black", MachineBlackProc},
670 {N_("Two Machines Ctrl+T"), "Two Machines", TwoMachinesProc},
671 {N_("Analysis Mode Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
672 {N_("Analyze File Ctrl+F"), "Analyze File", AnalyzeFileProc },
673 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
674 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
675 {N_("Training"), "Training", TrainingProc},
676 {N_("ICS Client"), "ICS Client", IcsClientProc},
677 {"----", NULL, NothingProc},
678 {N_("Machine Match"), "Machine Match", MatchProc},
679 {N_("Pause Pause"), "Pause", PauseProc},
683 MenuItem actionMenu[] = {
684 {N_("Accept F3"), "Accept", AcceptProc},
685 {N_("Decline F4"), "Decline", DeclineProc},
686 {N_("Rematch F12"), "Rematch", RematchProc},
687 {"----", NULL, NothingProc},
688 {N_("Call Flag F5"), "Call Flag", CallFlagProc},
689 {N_("Draw F6"), "Draw", DrawProc},
690 {N_("Adjourn F7"), "Adjourn", AdjournProc},
691 {N_("Abort F8"),"Abort", AbortProc},
692 {N_("Resign F9"), "Resign", ResignProc},
693 {"----", NULL, NothingProc},
694 {N_("Stop Observing F10"), "Stop Observing", StopObservingProc},
695 {N_("Stop Examining F11"), "Stop Examining", StopExaminingProc},
696 {N_("Upload to Examine"), "Upload to Examine", UploadProc},
697 {"----", NULL, NothingProc},
698 {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
699 {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
700 {N_("Adjudicate Draw"), "Adjudicate Draw", AdjuDrawProc},
704 MenuItem engineMenu[] = {
705 {N_("Load New Engine ..."), "Load Engine", LoadEngineProc},
706 {"----", NULL, NothingProc},
707 {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
708 {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
709 {"----", NULL, NothingProc},
710 {N_("Hint"), "Hint", HintProc},
711 {N_("Book"), "Book", BookProc},
712 {"----", NULL, NothingProc},
713 {N_("Move Now Ctrl+M"), "Move Now", MoveNowProc},
714 {N_("Retract Move Ctrl+X"), "Retract Move", RetractMoveProc},
718 MenuItem optionsMenu[] = {
719 #define OPTIONSDIALOG
721 {N_("General ..."), "General", OptionsProc},
723 {N_("Time Control ... Alt+Shift+T"), "Time Control", TimeControlProc},
724 {N_("Common Engine ... Alt+Shift+U"), "Common Engine", UciMenuProc},
725 {N_("Adjudications ... Alt+Shift+J"), "Adjudications", EngineMenuProc},
726 {N_("ICS ..."), "ICS", IcsOptionsProc},
727 {N_("Match ..."), "Match", MatchOptionsProc},
728 {N_("Load Game ..."), "Load Game", LoadOptionsProc},
729 {N_("Save Game ..."), "Save Game", SaveOptionsProc},
730 // {N_(" ..."), "", OptionsProc},
731 {N_("Game List ..."), "Game List", GameListOptionsPopUp},
732 {N_("Sounds ..."), "Sounds", SoundOptionsProc},
733 {"----", NULL, NothingProc},
734 #ifndef OPTIONSDIALOG
735 {N_("Always Queen Ctrl+Shift+Q"), "Always Queen", AlwaysQueenProc},
736 {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
737 {N_("Animate Moving Ctrl+Shift+A"), "Animate Moving", AnimateMovingProc},
738 {N_("Auto Flag Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
739 {N_("Auto Flip View"), "Auto Flip View", AutoflipProc},
740 {N_("Blindfold"), "Blindfold", BlindfoldProc},
741 {N_("Flash Moves"), "Flash Moves", FlashMovesProc},
743 {N_("Highlight Dragging"), "Highlight Dragging", HighlightDraggingProc},
745 {N_("Highlight Last Move"), "Highlight Last Move", HighlightLastMoveProc},
746 {N_("Highlight With Arrow"), "Arrow", HighlightArrowProc},
747 {N_("Move Sound"), "Move Sound", MoveSoundProc},
748 // {N_("ICS Alarm"), "ICS Alarm", IcsAlarmProc},
749 {N_("One-Click Moving"), "OneClick", OneClickProc},
750 {N_("Periodic Updates"), "Periodic Updates", PeriodicUpdatesProc},
751 {N_("Ponder Next Move Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
752 {N_("Popup Exit Message"), "Popup Exit Message", PopupExitMessageProc},
753 {N_("Popup Move Errors"), "Popup Move Errors", PopupMoveErrorsProc},
754 // {N_("Premove"), "Premove", PremoveProc},
755 {N_("Show Coords"), "Show Coords", ShowCoordsProc},
756 {N_("Hide Thinking Ctrl+Shift+H"), "Hide Thinking", HideThinkingProc},
757 {N_("Test Legality Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
758 {"----", NULL, NothingProc},
760 {N_("Save Settings Now"), "Save Settings Now", SaveSettingsProc},
761 {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
765 MenuItem helpMenu[] = {
766 {N_("Info XBoard"), "Info XBoard", InfoProc},
767 {N_("Man XBoard F1"), "Man XBoard", ManProc},
768 {"----", NULL, NothingProc},
769 {N_("About XBoard"), "About XBoard", AboutProc},
774 {N_("File"), "File", fileMenu},
775 {N_("Edit"), "Edit", editMenu},
776 {N_("View"), "View", viewMenu},
777 {N_("Mode"), "Mode", modeMenu},
778 {N_("Action"), "Action", actionMenu},
779 {N_("Engine"), "Engine", engineMenu},
780 {N_("Options"), "Options", optionsMenu},
781 {N_("Help"), "Help", helpMenu},
785 #define PAUSE_BUTTON "P"
786 MenuItem buttonBar[] = {
787 {"<<", "<<", ToStartProc},
788 {"<", "<", BackwardProc},
789 {PAUSE_BUTTON, PAUSE_BUTTON, PauseProc},
790 {">", ">", ForwardProc},
791 {">>", ">>", ToEndProc},
795 #define PIECE_MENU_SIZE 18
796 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
797 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
798 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
799 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
800 N_("Empty square"), N_("Clear board") },
801 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
802 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
803 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
804 N_("Empty square"), N_("Clear board") }
806 /* must be in same order as PieceMenuStrings! */
807 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
808 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
809 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
810 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
811 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
812 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
813 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
814 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
815 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
818 #define DROP_MENU_SIZE 6
819 String dropMenuStrings[DROP_MENU_SIZE] = {
820 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
822 /* must be in same order as PieceMenuStrings! */
823 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
824 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
825 WhiteRook, WhiteQueen
833 DropMenuEnables dmEnables[] = {
851 { XtNborderWidth, 0 },
852 { XtNdefaultDistance, 0 },
856 { XtNborderWidth, 0 },
857 { XtNresizable, (XtArgVal) True },
861 { XtNborderWidth, 0 },
867 { XtNjustify, (XtArgVal) XtJustifyRight },
868 { XtNlabel, (XtArgVal) "..." },
869 { XtNresizable, (XtArgVal) True },
870 { XtNresize, (XtArgVal) False }
873 Arg messageArgs[] = {
874 { XtNjustify, (XtArgVal) XtJustifyLeft },
875 { XtNlabel, (XtArgVal) "..." },
876 { XtNresizable, (XtArgVal) True },
877 { XtNresize, (XtArgVal) False }
881 { XtNborderWidth, 0 },
882 { XtNjustify, (XtArgVal) XtJustifyLeft }
885 XtResource clientResources[] = {
886 { "flashCount", "flashCount", XtRInt, sizeof(int),
887 XtOffset(AppDataPtr, flashCount), XtRImmediate,
888 (XtPointer) FLASH_COUNT },
891 XrmOptionDescRec shellOptions[] = {
892 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
893 { "-flash", "flashCount", XrmoptionNoArg, "3" },
894 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
897 XtActionsRec boardActions[] = {
898 { "DrawPosition", DrawPositionProc },
899 { "HandleUserMove", HandleUserMove },
900 { "AnimateUserMove", AnimateUserMove },
901 { "HandlePV", HandlePV },
902 { "SelectPV", SelectPV },
903 { "StopPV", StopPV },
904 { "FileNameAction", FileNameAction },
905 { "AskQuestionProc", AskQuestionProc },
906 { "AskQuestionReplyAction", AskQuestionReplyAction },
907 { "PieceMenuPopup", PieceMenuPopup },
908 { "WhiteClock", WhiteClock },
909 { "BlackClock", BlackClock },
910 { "Iconify", Iconify },
911 { "ResetProc", ResetProc },
912 { "NewVariantProc", NewVariantProc },
913 { "LoadGameProc", LoadGameProc },
914 { "LoadNextGameProc", LoadNextGameProc },
915 { "LoadPrevGameProc", LoadPrevGameProc },
916 { "LoadSelectedProc", LoadSelectedProc },
917 { "SetFilterProc", SetFilterProc },
918 { "ReloadGameProc", ReloadGameProc },
919 { "LoadPositionProc", LoadPositionProc },
920 { "LoadNextPositionProc", LoadNextPositionProc },
921 { "LoadPrevPositionProc", LoadPrevPositionProc },
922 { "ReloadPositionProc", ReloadPositionProc },
923 { "CopyPositionProc", CopyPositionProc },
924 { "PastePositionProc", PastePositionProc },
925 { "CopyGameProc", CopyGameProc },
926 { "CopyGameListProc", CopyGameListProc },
927 { "PasteGameProc", PasteGameProc },
928 { "SaveGameProc", SaveGameProc },
929 { "SavePositionProc", SavePositionProc },
930 { "MailMoveProc", MailMoveProc },
931 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
932 { "QuitProc", QuitProc },
933 { "MachineWhiteProc", MachineWhiteProc },
934 { "MachineBlackProc", MachineBlackProc },
935 { "AnalysisModeProc", AnalyzeModeProc },
936 { "AnalyzeFileProc", AnalyzeFileProc },
937 { "TwoMachinesProc", TwoMachinesProc },
938 { "IcsClientProc", IcsClientProc },
939 { "EditGameProc", EditGameProc },
940 { "EditPositionProc", EditPositionProc },
941 { "TrainingProc", EditPositionProc },
942 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
943 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
944 { "ShowGameListProc", ShowGameListProc },
945 { "ShowMoveListProc", HistoryShowProc},
946 { "EditTagsProc", EditCommentProc },
947 { "EditBookProc", EditBookProc },
948 { "EditCommentProc", EditCommentProc },
949 { "IcsInputBoxProc", IcsInputBoxProc },
950 { "PauseProc", PauseProc },
951 { "AcceptProc", AcceptProc },
952 { "DeclineProc", DeclineProc },
953 { "RematchProc", RematchProc },
954 { "CallFlagProc", CallFlagProc },
955 { "DrawProc", DrawProc },
956 { "AdjournProc", AdjournProc },
957 { "AbortProc", AbortProc },
958 { "ResignProc", ResignProc },
959 { "AdjuWhiteProc", AdjuWhiteProc },
960 { "AdjuBlackProc", AdjuBlackProc },
961 { "AdjuDrawProc", AdjuDrawProc },
962 { "TypeInProc", TypeInProc },
963 { "EnterKeyProc", EnterKeyProc },
964 { "UpKeyProc", UpKeyProc },
965 { "DownKeyProc", DownKeyProc },
966 { "StopObservingProc", StopObservingProc },
967 { "StopExaminingProc", StopExaminingProc },
968 { "UploadProc", UploadProc },
969 { "BackwardProc", BackwardProc },
970 { "ForwardProc", ForwardProc },
971 { "ToStartProc", ToStartProc },
972 { "ToEndProc", ToEndProc },
973 { "RevertProc", RevertProc },
974 { "AnnotateProc", AnnotateProc },
975 { "TruncateGameProc", TruncateGameProc },
976 { "MoveNowProc", MoveNowProc },
977 { "RetractMoveProc", RetractMoveProc },
978 { "EngineMenuProc", (XtActionProc) EngineMenuProc },
979 { "UciMenuProc", (XtActionProc) UciMenuProc },
980 { "TimeControlProc", (XtActionProc) TimeControlProc },
981 { "FlipViewProc", FlipViewProc },
982 { "PonderNextMoveProc", PonderNextMoveProc },
983 #ifndef OPTIONSDIALOG
984 { "AlwaysQueenProc", AlwaysQueenProc },
985 { "AnimateDraggingProc", AnimateDraggingProc },
986 { "AnimateMovingProc", AnimateMovingProc },
987 { "AutoflagProc", AutoflagProc },
988 { "AutoflipProc", AutoflipProc },
989 { "BlindfoldProc", BlindfoldProc },
990 { "FlashMovesProc", FlashMovesProc },
992 { "HighlightDraggingProc", HighlightDraggingProc },
994 { "HighlightLastMoveProc", HighlightLastMoveProc },
995 // { "IcsAlarmProc", IcsAlarmProc },
996 { "MoveSoundProc", MoveSoundProc },
997 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
998 { "PopupExitMessageProc", PopupExitMessageProc },
999 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
1000 // { "PremoveProc", PremoveProc },
1001 { "ShowCoordsProc", ShowCoordsProc },
1002 { "ShowThinkingProc", ShowThinkingProc },
1003 { "HideThinkingProc", HideThinkingProc },
1004 { "TestLegalityProc", TestLegalityProc },
1006 { "SaveSettingsProc", SaveSettingsProc },
1007 { "SaveOnExitProc", SaveOnExitProc },
1008 { "InfoProc", InfoProc },
1009 { "ManProc", ManProc },
1010 { "HintProc", HintProc },
1011 { "BookProc", BookProc },
1012 { "AboutGameProc", AboutGameProc },
1013 { "AboutProc", AboutProc },
1014 { "DebugProc", DebugProc },
1015 { "NothingProc", NothingProc },
1016 { "CommentClick", (XtActionProc) CommentClick },
1017 { "CommentPopDown", (XtActionProc) CommentPopDown },
1018 { "TagsPopDown", (XtActionProc) TagsPopDown },
1019 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1020 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1021 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1022 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1023 { "GameListPopDown", (XtActionProc) GameListPopDown },
1024 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1025 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1026 { "HistoryPopDown", (XtActionProc) HistoryPopDown },
1027 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1028 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1029 { "ShufflePopDown", (XtActionProc) ShufflePopDown },
1030 { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
1031 { "GenericPopDown", (XtActionProc) GenericPopDown },
1032 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1035 char globalTranslations[] =
1036 ":<Key>F9: ResignProc() \n \
1037 :Ctrl<Key>n: ResetProc() \n \
1038 :Meta<Key>V: NewVariantProc() \n \
1039 :Ctrl<Key>o: LoadGameProc() \n \
1040 :Meta<Key>Next: LoadNextGameProc() \n \
1041 :Meta<Key>Prior: LoadPrevGameProc() \n \
1042 :Ctrl<Key>s: SaveGameProc() \n \
1043 :Ctrl<Key>c: CopyGameProc() \n \
1044 :Ctrl<Key>v: PasteGameProc() \n \
1045 :Ctrl<Key>O: LoadPositionProc() \n \
1046 :Shift<Key>Next: LoadNextPositionProc() \n \
1047 :Shift<Key>Prior: LoadPrevPositionProc() \n \
1048 :Ctrl<Key>S: SavePositionProc() \n \
1049 :Ctrl<Key>C: CopyPositionProc() \n \
1050 :Ctrl<Key>V: PastePositionProc() \n \
1051 :Ctrl<Key>q: QuitProc() \n \
1052 :Ctrl<Key>w: MachineWhiteProc() \n \
1053 :Ctrl<Key>b: MachineBlackProc() \n \
1054 :Ctrl<Key>t: TwoMachinesProc() \n \
1055 :Ctrl<Key>a: AnalysisModeProc() \n \
1056 :Ctrl<Key>f: AnalyzeFileProc() \n \
1057 :Ctrl<Key>e: EditGameProc() \n \
1058 :Ctrl<Key>E: EditPositionProc() \n \
1059 :Meta<Key>O: EngineOutputProc() \n \
1060 :Meta<Key>E: EvalGraphProc() \n \
1061 :Meta<Key>G: ShowGameListProc() \n \
1062 :Meta<Key>H: ShowMoveListProc() \n \
1063 :<Key>Pause: PauseProc() \n \
1064 :<Key>F3: AcceptProc() \n \
1065 :<Key>F4: DeclineProc() \n \
1066 :<Key>F12: RematchProc() \n \
1067 :<Key>F5: CallFlagProc() \n \
1068 :<Key>F6: DrawProc() \n \
1069 :<Key>F7: AdjournProc() \n \
1070 :<Key>F8: AbortProc() \n \
1071 :<Key>F10: StopObservingProc() \n \
1072 :<Key>F11: StopExaminingProc() \n \
1073 :Meta Ctrl<Key>F12: DebugProc() \n \
1074 :Meta<Key>End: ToEndProc() \n \
1075 :Meta<Key>Right: ForwardProc() \n \
1076 :Meta<Key>Home: ToStartProc() \n \
1077 :Meta<Key>Left: BackwardProc() \n \
1078 :<Key>Home: RevertProc() \n \
1079 :<Key>End: TruncateGameProc() \n \
1080 :Ctrl<Key>m: MoveNowProc() \n \
1081 :Ctrl<Key>x: RetractMoveProc() \n \
1082 :Meta<Key>J: EngineMenuProc() \n \
1083 :Meta<Key>U: UciMenuProc() \n \
1084 :Meta<Key>T: TimeControlProc() \n \
1085 :Ctrl<Key>P: PonderNextMoveProc() \n "
1086 #ifndef OPTIONSDIALOG
1088 :Ctrl<Key>Q: AlwaysQueenProc() \n \
1089 :Ctrl<Key>F: AutoflagProc() \n \
1090 :Ctrl<Key>A: AnimateMovingProc() \n \
1091 :Ctrl<Key>L: TestLegalityProc() \n \
1092 :Ctrl<Key>H: HideThinkingProc() \n "
1095 :<Key>-: Iconify() \n \
1096 :<Key>F1: ManProc() \n \
1097 :<Key>F2: FlipViewProc() \n \
1098 <KeyDown>.: BackwardProc() \n \
1099 <KeyUp>.: ForwardProc() \n \
1100 Shift<Key>1: AskQuestionProc(\"Direct command\",\
1101 \"Send to chess program:\",,1) \n \
1102 Shift<Key>2: AskQuestionProc(\"Direct command\",\
1103 \"Send to second chess program:\",,2) \n";
1105 char boardTranslations[] =
1106 "<Btn1Down>: HandleUserMove(0) \n \
1107 Shift<Btn1Up>: HandleUserMove(1) \n \
1108 <Btn1Up>: HandleUserMove(0) \n \
1109 <Btn1Motion>: AnimateUserMove() \n \
1110 <Btn3Motion>: HandlePV() \n \
1111 <Btn3Up>: PieceMenuPopup(menuB) \n \
1112 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1113 PieceMenuPopup(menuB) \n \
1114 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1115 PieceMenuPopup(menuW) \n \
1116 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1117 PieceMenuPopup(menuW) \n \
1118 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1119 PieceMenuPopup(menuB) \n";
1121 char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
1122 char blackTranslations[] = "<BtnDown>: BlackClock()\n";
1124 char ICSInputTranslations[] =
1125 "<Key>Up: UpKeyProc() \n "
1126 "<Key>Down: DownKeyProc() \n "
1127 "<Key>Return: EnterKeyProc() \n";
1129 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1130 // as the widget is destroyed before the up-click can call extend-end
1131 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1133 String xboardResources[] = {
1134 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1135 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1136 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1141 /* Max possible square size */
1142 #define MAXSQSIZE 256
1144 static int xpm_avail[MAXSQSIZE];
1146 #ifdef HAVE_DIR_STRUCT
1148 /* Extract piece size from filename */
1150 xpm_getsize(name, len, ext)
1161 if ((p=strchr(name, '.')) == NULL ||
1162 StrCaseCmp(p+1, ext) != 0)
1168 while (*p && isdigit(*p))
1175 /* Setup xpm_avail */
1177 xpm_getavail(dirname, ext)
1185 for (i=0; i<MAXSQSIZE; ++i)
1188 if (appData.debugMode)
1189 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1191 dir = opendir(dirname);
1194 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1195 programName, dirname);
1199 while ((ent=readdir(dir)) != NULL) {
1200 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1201 if (i > 0 && i < MAXSQSIZE)
1211 xpm_print_avail(fp, ext)
1217 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1218 for (i=1; i<MAXSQSIZE; ++i) {
1224 /* Return XPM piecesize closest to size */
1226 xpm_closest_to(dirname, size, ext)
1232 int sm_diff = MAXSQSIZE;
1236 xpm_getavail(dirname, ext);
1238 if (appData.debugMode)
1239 xpm_print_avail(stderr, ext);
1241 for (i=1; i<MAXSQSIZE; ++i) {
1244 diff = (diff<0) ? -diff : diff;
1245 if (diff < sm_diff) {
1253 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1259 #else /* !HAVE_DIR_STRUCT */
1260 /* If we are on a system without a DIR struct, we can't
1261 read the directory, so we can't collect a list of
1262 filenames, etc., so we can't do any size-fitting. */
1264 xpm_closest_to(dirname, size, ext)
1269 fprintf(stderr, _("\
1270 Warning: No DIR structure found on this system --\n\
1271 Unable to autosize for XPM/XIM pieces.\n\
1272 Please report this error to %s.\n\
1273 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
1276 #endif /* HAVE_DIR_STRUCT */
1278 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1279 "magenta", "cyan", "white" };
1283 TextColors textColors[(int)NColorClasses];
1285 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1287 parse_color(str, which)
1291 char *p, buf[100], *d;
1294 if (strlen(str) > 99) /* watch bounds on buf */
1299 for (i=0; i<which; ++i) {
1306 /* Could be looking at something like:
1308 .. in which case we want to stop on a comma also */
1309 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1313 return -1; /* Use default for empty field */
1316 if (which == 2 || isdigit(*p))
1319 while (*p && isalpha(*p))
1324 for (i=0; i<8; ++i) {
1325 if (!StrCaseCmp(buf, cnames[i]))
1326 return which? (i+40) : (i+30);
1328 if (!StrCaseCmp(buf, "default")) return -1;
1330 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1335 parse_cpair(cc, str)
1339 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1340 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1345 /* bg and attr are optional */
1346 textColors[(int)cc].bg = parse_color(str, 1);
1347 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1348 textColors[(int)cc].attr = 0;
1354 /* Arrange to catch delete-window events */
1355 Atom wm_delete_window;
1357 CatchDeleteWindow(Widget w, String procname)
1360 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1361 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1362 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1369 XtSetArg(args[0], XtNiconic, False);
1370 XtSetValues(shellWidget, args, 1);
1372 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1375 //---------------------------------------------------------------------------------------------------------
1376 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1379 #define CW_USEDEFAULT (1<<31)
1380 #define ICS_TEXT_MENU_SIZE 90
1381 #define DEBUG_FILE "xboard.debug"
1382 #define SetCurrentDirectory chdir
1383 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1387 // these two must some day move to frontend.h, when they are implemented
1388 Boolean GameListIsUp();
1390 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1393 // front-end part of option handling
1395 // [HGM] This platform-dependent table provides the location for storing the color info
1396 extern char *crWhite, * crBlack;
1400 &appData.whitePieceColor,
1401 &appData.blackPieceColor,
1402 &appData.lightSquareColor,
1403 &appData.darkSquareColor,
1404 &appData.highlightSquareColor,
1405 &appData.premoveHighlightColor,
1406 &appData.lowTimeWarningColor,
1417 // [HGM] font: keep a font for each square size, even non-stndard ones
1418 #define NUM_SIZES 18
1419 #define MAX_SIZE 130
1420 Boolean fontSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1421 char *fontTable[NUM_FONTS][MAX_SIZE];
1424 ParseFont(char *name, int number)
1425 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1427 if(sscanf(name, "size%d:", &size)) {
1428 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1429 // defer processing it until we know if it matches our board size
1430 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1431 fontTable[number][size] = strdup(strchr(name, ':')+1);
1432 fontValid[number][size] = True;
1437 case 0: // CLOCK_FONT
1438 appData.clockFont = strdup(name);
1440 case 1: // MESSAGE_FONT
1441 appData.font = strdup(name);
1443 case 2: // COORD_FONT
1444 appData.coordFont = strdup(name);
1449 fontSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1454 { // only 2 fonts currently
1455 appData.clockFont = CLOCK_FONT_NAME;
1456 appData.coordFont = COORD_FONT_NAME;
1457 appData.font = DEFAULT_FONT_NAME;
1462 { // no-op, until we identify the code for this already in XBoard and move it here
1466 ParseColor(int n, char *name)
1467 { // in XBoard, just copy the color-name string
1468 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1472 ParseTextAttribs(ColorClass cc, char *s)
1474 (&appData.colorShout)[cc] = strdup(s);
1478 ParseBoardSize(void *addr, char *name)
1480 appData.boardSize = strdup(name);
1485 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1489 SetCommPortDefaults()
1490 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1493 // [HGM] args: these three cases taken out to stay in front-end
1495 SaveFontArg(FILE *f, ArgDescriptor *ad)
1498 int i, n = (int)(intptr_t)ad->argLoc;
1500 case 0: // CLOCK_FONT
1501 name = appData.clockFont;
1503 case 1: // MESSAGE_FONT
1504 name = appData.font;
1506 case 2: // COORD_FONT
1507 name = appData.coordFont;
1512 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1513 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1514 fontTable[n][squareSize] = strdup(name);
1515 fontValid[n][squareSize] = True;
1518 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1519 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1524 { // nothing to do, as the sounds are at all times represented by their text-string names already
1528 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1529 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1530 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1534 SaveColor(FILE *f, ArgDescriptor *ad)
1535 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1536 if(colorVariable[(int)(intptr_t)ad->argLoc])
1537 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1541 SaveBoardSize(FILE *f, char *name, void *addr)
1542 { // wrapper to shield back-end from BoardSize & sizeInfo
1543 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1547 ParseCommPortSettings(char *s)
1548 { // no such option in XBoard (yet)
1551 extern Widget engineOutputShell;
1554 GetActualPlacement(Widget wg, WindowPlacement *wp)
1564 XtSetArg(args[i], XtNx, &x); i++;
1565 XtSetArg(args[i], XtNy, &y); i++;
1566 XtSetArg(args[i], XtNwidth, &w); i++;
1567 XtSetArg(args[i], XtNheight, &h); i++;
1568 XtGetValues(wg, args, i);
1577 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1578 // In XBoard this will have to wait until awareness of window parameters is implemented
1579 GetActualPlacement(shellWidget, &wpMain);
1580 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1581 if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1582 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1583 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1584 if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1585 if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1589 PrintCommPortSettings(FILE *f, char *name)
1590 { // This option does not exist in XBoard
1594 MySearchPath(char *installDir, char *name, char *fullname)
1595 { // just append installDir and name. Perhaps ExpandPath should be used here?
1596 name = ExpandPathName(name);
1597 if(name && name[0] == '/')
1598 safeStrCpy(fullname, name, MSG_SIZ );
1600 sprintf(fullname, "%s%c%s", installDir, '/', name);
1606 MyGetFullPathName(char *name, char *fullname)
1607 { // should use ExpandPath?
1608 name = ExpandPathName(name);
1609 safeStrCpy(fullname, name, MSG_SIZ );
1614 EnsureOnScreen(int *x, int *y, int minX, int minY)
1621 { // [HGM] args: allows testing if main window is realized from back-end
1622 return xBoardWindow != 0;
1626 PopUpStartupDialog()
1627 { // start menu not implemented in XBoard
1631 ConvertToLine(int argc, char **argv)
1633 static char line[128*1024], buf[1024];
1637 for(i=1; i<argc; i++)
1639 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1640 && argv[i][0] != '{' )
1641 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1643 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1644 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1647 line[strlen(line)-1] = NULLCHAR;
1651 //--------------------------------------------------------------------------------------------
1653 extern Boolean twoBoards, partnerUp;
1656 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1658 #define BoardSize int
1659 void InitDrawingSizes(BoardSize boardSize, int flags)
1660 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1661 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1663 XtGeometryResult gres;
1666 if(!formWidget) return;
1669 * Enable shell resizing.
1671 shellArgs[0].value = (XtArgVal) &w;
1672 shellArgs[1].value = (XtArgVal) &h;
1673 XtGetValues(shellWidget, shellArgs, 2);
1675 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1676 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1677 XtSetValues(shellWidget, &shellArgs[2], 4);
1679 XtSetArg(args[0], XtNdefaultDistance, &sep);
1680 XtGetValues(formWidget, args, 1);
1682 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1683 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1684 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1686 hOffset = boardWidth + 10;
1687 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1688 secondSegments[i] = gridSegments[i];
1689 secondSegments[i].x1 += hOffset;
1690 secondSegments[i].x2 += hOffset;
1693 XtSetArg(args[0], XtNwidth, boardWidth);
1694 XtSetArg(args[1], XtNheight, boardHeight);
1695 XtSetValues(boardWidget, args, 2);
1697 timerWidth = (boardWidth - sep) / 2;
1698 XtSetArg(args[0], XtNwidth, timerWidth);
1699 XtSetValues(whiteTimerWidget, args, 1);
1700 XtSetValues(blackTimerWidget, args, 1);
1702 XawFormDoLayout(formWidget, False);
1704 if (appData.titleInWindow) {
1706 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1707 XtSetArg(args[i], XtNheight, &h); i++;
1708 XtGetValues(titleWidget, args, i);
1710 w = boardWidth - 2*bor;
1712 XtSetArg(args[0], XtNwidth, &w);
1713 XtGetValues(menuBarWidget, args, 1);
1714 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1717 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1718 if (gres != XtGeometryYes && appData.debugMode) {
1720 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1721 programName, gres, w, h, wr, hr);
1725 XawFormDoLayout(formWidget, True);
1728 * Inhibit shell resizing.
1730 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1731 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1732 shellArgs[4].value = shellArgs[2].value = w;
1733 shellArgs[5].value = shellArgs[3].value = h;
1734 XtSetValues(shellWidget, &shellArgs[0], 6);
1736 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1739 for(i=0; i<4; i++) {
1741 for(p=0; p<=(int)WhiteKing; p++)
1742 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1743 if(gameInfo.variant == VariantShogi) {
1744 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1745 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1746 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1747 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1748 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1751 if(gameInfo.variant == VariantGothic) {
1752 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1755 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1756 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1757 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1760 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1761 for(p=0; p<=(int)WhiteKing; p++)
1762 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1763 if(gameInfo.variant == VariantShogi) {
1764 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1765 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1766 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1767 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1768 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1771 if(gameInfo.variant == VariantGothic) {
1772 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1775 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1776 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1777 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1782 for(i=0; i<2; i++) {
1784 for(p=0; p<=(int)WhiteKing; p++)
1785 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1786 if(gameInfo.variant == VariantShogi) {
1787 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1788 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1789 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1790 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1791 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1794 if(gameInfo.variant == VariantGothic) {
1795 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1798 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1799 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1800 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1810 void ParseIcsTextColors()
1811 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1812 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1813 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1814 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1815 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1816 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1817 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1818 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1819 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1820 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1821 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1823 if (appData.colorize) {
1825 _("%s: can't parse color names; disabling colorization\n"),
1828 appData.colorize = FALSE;
1833 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1834 XrmValue vFrom, vTo;
1835 int forceMono = False;
1837 if (!appData.monoMode) {
1838 vFrom.addr = (caddr_t) appData.lightSquareColor;
1839 vFrom.size = strlen(appData.lightSquareColor);
1840 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1841 if (vTo.addr == NULL) {
1842 appData.monoMode = True;
1845 lightSquareColor = *(Pixel *) vTo.addr;
1848 if (!appData.monoMode) {
1849 vFrom.addr = (caddr_t) appData.darkSquareColor;
1850 vFrom.size = strlen(appData.darkSquareColor);
1851 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1852 if (vTo.addr == NULL) {
1853 appData.monoMode = True;
1856 darkSquareColor = *(Pixel *) vTo.addr;
1859 if (!appData.monoMode) {
1860 vFrom.addr = (caddr_t) appData.whitePieceColor;
1861 vFrom.size = strlen(appData.whitePieceColor);
1862 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1863 if (vTo.addr == NULL) {
1864 appData.monoMode = True;
1867 whitePieceColor = *(Pixel *) vTo.addr;
1870 if (!appData.monoMode) {
1871 vFrom.addr = (caddr_t) appData.blackPieceColor;
1872 vFrom.size = strlen(appData.blackPieceColor);
1873 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1874 if (vTo.addr == NULL) {
1875 appData.monoMode = True;
1878 blackPieceColor = *(Pixel *) vTo.addr;
1882 if (!appData.monoMode) {
1883 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1884 vFrom.size = strlen(appData.highlightSquareColor);
1885 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1886 if (vTo.addr == NULL) {
1887 appData.monoMode = True;
1890 highlightSquareColor = *(Pixel *) vTo.addr;
1894 if (!appData.monoMode) {
1895 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1896 vFrom.size = strlen(appData.premoveHighlightColor);
1897 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1898 if (vTo.addr == NULL) {
1899 appData.monoMode = True;
1902 premoveHighlightColor = *(Pixel *) vTo.addr;
1910 { // [HGM] taken out of main
1912 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1913 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1914 appData.bitmapDirectory = DEF_BITMAP_DIR;
1916 if (appData.bitmapDirectory[0] != NULLCHAR) {
1920 CreateXPMBoard(appData.liteBackTextureFile, 1);
1921 CreateXPMBoard(appData.darkBackTextureFile, 0);
1925 /* Create regular pieces */
1926 if (!useImages) CreatePieces();
1935 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1936 XSetWindowAttributes window_attributes;
1938 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1939 XrmValue vFrom, vTo;
1940 XtGeometryResult gres;
1943 int forceMono = False;
1945 srandom(time(0)); // [HGM] book: make random truly random
1947 setbuf(stdout, NULL);
1948 setbuf(stderr, NULL);
1951 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1952 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1956 programName = strrchr(argv[0], '/');
1957 if (programName == NULL)
1958 programName = argv[0];
1963 XtSetLanguageProc(NULL, NULL, NULL);
1964 bindtextdomain(PACKAGE, LOCALEDIR);
1965 textdomain(PACKAGE);
1969 XtAppInitialize(&appContext, "XBoard", shellOptions,
1970 XtNumber(shellOptions),
1971 &argc, argv, xboardResources, NULL, 0);
1972 appData.boardSize = "";
1973 InitAppData(ConvertToLine(argc, argv));
1975 if (p == NULL) p = "/tmp";
1976 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1977 gameCopyFilename = (char*) malloc(i);
1978 gamePasteFilename = (char*) malloc(i);
1979 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1980 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1982 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1983 clientResources, XtNumber(clientResources),
1986 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1987 static char buf[MSG_SIZ];
1988 EscapeExpand(buf, appData.firstInitString);
1989 appData.firstInitString = strdup(buf);
1990 EscapeExpand(buf, appData.secondInitString);
1991 appData.secondInitString = strdup(buf);
1992 EscapeExpand(buf, appData.firstComputerString);
1993 appData.firstComputerString = strdup(buf);
1994 EscapeExpand(buf, appData.secondComputerString);
1995 appData.secondComputerString = strdup(buf);
1998 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
2001 if (chdir(chessDir) != 0) {
2002 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2008 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2009 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2010 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
2011 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2014 setbuf(debugFP, NULL);
2017 /* [HGM,HR] make sure board size is acceptable */
2018 if(appData.NrFiles > BOARD_FILES ||
2019 appData.NrRanks > BOARD_RANKS )
2020 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
2023 /* This feature does not work; animation needs a rewrite */
2024 appData.highlightDragging = FALSE;
2028 xDisplay = XtDisplay(shellWidget);
2029 xScreen = DefaultScreen(xDisplay);
2030 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2032 gameInfo.variant = StringToVariant(appData.variant);
2033 InitPosition(FALSE);
2036 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2038 if (isdigit(appData.boardSize[0])) {
2039 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2040 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2041 &fontPxlSize, &smallLayout, &tinyLayout);
2043 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2044 programName, appData.boardSize);
2048 /* Find some defaults; use the nearest known size */
2049 SizeDefaults *szd, *nearest;
2050 int distance = 99999;
2051 nearest = szd = sizeDefaults;
2052 while (szd->name != NULL) {
2053 if (abs(szd->squareSize - squareSize) < distance) {
2055 distance = abs(szd->squareSize - squareSize);
2056 if (distance == 0) break;
2060 if (i < 2) lineGap = nearest->lineGap;
2061 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2062 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2063 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2064 if (i < 6) smallLayout = nearest->smallLayout;
2065 if (i < 7) tinyLayout = nearest->tinyLayout;
2068 SizeDefaults *szd = sizeDefaults;
2069 if (*appData.boardSize == NULLCHAR) {
2070 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2071 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2074 if (szd->name == NULL) szd--;
2075 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2077 while (szd->name != NULL &&
2078 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2079 if (szd->name == NULL) {
2080 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2081 programName, appData.boardSize);
2085 squareSize = szd->squareSize;
2086 lineGap = szd->lineGap;
2087 clockFontPxlSize = szd->clockFontPxlSize;
2088 coordFontPxlSize = szd->coordFontPxlSize;
2089 fontPxlSize = szd->fontPxlSize;
2090 smallLayout = szd->smallLayout;
2091 tinyLayout = szd->tinyLayout;
2092 // [HGM] font: use defaults from settings file if available and not overruled
2094 if(!fontSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2095 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2096 if(!fontSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2097 appData.font = fontTable[MESSAGE_FONT][squareSize];
2098 if(!fontSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2099 appData.coordFont = fontTable[COORD_FONT][squareSize];
2101 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2102 if (strlen(appData.pixmapDirectory) > 0) {
2103 p = ExpandPathName(appData.pixmapDirectory);
2105 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2106 appData.pixmapDirectory);
2109 if (appData.debugMode) {
2110 fprintf(stderr, _("\
2111 XBoard square size (hint): %d\n\
2112 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2114 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2115 if (appData.debugMode) {
2116 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2119 defaultLineGap = lineGap;
2120 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2122 /* [HR] height treated separately (hacked) */
2123 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2124 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2125 if (appData.showJail == 1) {
2126 /* Jail on top and bottom */
2127 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2128 XtSetArg(boardArgs[2], XtNheight,
2129 boardHeight + 2*(lineGap + squareSize));
2130 } else if (appData.showJail == 2) {
2132 XtSetArg(boardArgs[1], XtNwidth,
2133 boardWidth + 2*(lineGap + squareSize));
2134 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2137 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2138 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2142 * Determine what fonts to use.
2144 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2145 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2146 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2147 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2148 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2149 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2150 appData.font = FindFont(appData.font, fontPxlSize);
2151 countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
2152 countFontStruct = XQueryFont(xDisplay, countFontID);
2153 // appData.font = FindFont(appData.font, fontPxlSize);
2155 xdb = XtDatabase(xDisplay);
2156 XrmPutStringResource(&xdb, "*font", appData.font);
2159 * Detect if there are not enough colors available and adapt.
2161 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2162 appData.monoMode = True;
2165 forceMono = MakeColors();
2168 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2170 appData.monoMode = True;
2173 if (appData.lowTimeWarning && !appData.monoMode) {
2174 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2175 vFrom.size = strlen(appData.lowTimeWarningColor);
2176 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2177 if (vTo.addr == NULL)
2178 appData.monoMode = True;
2180 lowTimeWarningColor = *(Pixel *) vTo.addr;
2183 if (appData.monoMode && appData.debugMode) {
2184 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2185 (unsigned long) XWhitePixel(xDisplay, xScreen),
2186 (unsigned long) XBlackPixel(xDisplay, xScreen));
2189 ParseIcsTextColors();
2190 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2191 textColors[ColorNone].attr = 0;
2193 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2199 layoutName = "tinyLayout";
2200 } else if (smallLayout) {
2201 layoutName = "smallLayout";
2203 layoutName = "normalLayout";
2205 /* Outer layoutWidget is there only to provide a name for use in
2206 resources that depend on the layout style */
2208 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2209 layoutArgs, XtNumber(layoutArgs));
2211 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2212 formArgs, XtNumber(formArgs));
2213 XtSetArg(args[0], XtNdefaultDistance, &sep);
2214 XtGetValues(formWidget, args, 1);
2217 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2218 XtSetArg(args[0], XtNtop, XtChainTop);
2219 XtSetArg(args[1], XtNbottom, XtChainTop);
2220 XtSetArg(args[2], XtNright, XtChainLeft);
2221 XtSetValues(menuBarWidget, args, 3);
2223 widgetList[j++] = whiteTimerWidget =
2224 XtCreateWidget("whiteTime", labelWidgetClass,
2225 formWidget, timerArgs, XtNumber(timerArgs));
2226 XtSetArg(args[0], XtNfont, clockFontStruct);
2227 XtSetArg(args[1], XtNtop, XtChainTop);
2228 XtSetArg(args[2], XtNbottom, XtChainTop);
2229 XtSetValues(whiteTimerWidget, args, 3);
2231 widgetList[j++] = blackTimerWidget =
2232 XtCreateWidget("blackTime", labelWidgetClass,
2233 formWidget, timerArgs, XtNumber(timerArgs));
2234 XtSetArg(args[0], XtNfont, clockFontStruct);
2235 XtSetArg(args[1], XtNtop, XtChainTop);
2236 XtSetArg(args[2], XtNbottom, XtChainTop);
2237 XtSetValues(blackTimerWidget, args, 3);
2239 if (appData.titleInWindow) {
2240 widgetList[j++] = titleWidget =
2241 XtCreateWidget("title", labelWidgetClass, formWidget,
2242 titleArgs, XtNumber(titleArgs));
2243 XtSetArg(args[0], XtNtop, XtChainTop);
2244 XtSetArg(args[1], XtNbottom, XtChainTop);
2245 XtSetValues(titleWidget, args, 2);
2248 if (appData.showButtonBar) {
2249 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2250 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2251 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2252 XtSetArg(args[2], XtNtop, XtChainTop);
2253 XtSetArg(args[3], XtNbottom, XtChainTop);
2254 XtSetValues(buttonBarWidget, args, 4);
2257 widgetList[j++] = messageWidget =
2258 XtCreateWidget("message", labelWidgetClass, formWidget,
2259 messageArgs, XtNumber(messageArgs));
2260 XtSetArg(args[0], XtNtop, XtChainTop);
2261 XtSetArg(args[1], XtNbottom, XtChainTop);
2262 XtSetValues(messageWidget, args, 2);
2264 widgetList[j++] = boardWidget =
2265 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2266 XtNumber(boardArgs));
2268 XtManageChildren(widgetList, j);
2270 timerWidth = (boardWidth - sep) / 2;
2271 XtSetArg(args[0], XtNwidth, timerWidth);
2272 XtSetValues(whiteTimerWidget, args, 1);
2273 XtSetValues(blackTimerWidget, args, 1);
2275 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2276 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2277 XtGetValues(whiteTimerWidget, args, 2);
2279 if (appData.showButtonBar) {
2280 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2281 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2282 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2286 * formWidget uses these constraints but they are stored
2290 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2291 XtSetValues(menuBarWidget, args, i);
2292 if (appData.titleInWindow) {
2295 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2296 XtSetValues(whiteTimerWidget, args, i);
2298 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2299 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2300 XtSetValues(blackTimerWidget, args, i);
2302 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2303 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2304 XtSetValues(titleWidget, args, i);
2306 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2307 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2308 XtSetValues(messageWidget, args, i);
2309 if (appData.showButtonBar) {
2311 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2312 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2313 XtSetValues(buttonBarWidget, args, i);
2317 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2318 XtSetValues(whiteTimerWidget, args, i);
2320 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2321 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2322 XtSetValues(blackTimerWidget, args, i);
2324 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2325 XtSetValues(titleWidget, args, i);
2327 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2328 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2329 XtSetValues(messageWidget, args, i);
2330 if (appData.showButtonBar) {
2332 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2333 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2334 XtSetValues(buttonBarWidget, args, i);
2339 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2340 XtSetValues(whiteTimerWidget, args, i);
2342 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2343 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2344 XtSetValues(blackTimerWidget, args, i);
2346 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2347 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2348 XtSetValues(messageWidget, args, i);
2349 if (appData.showButtonBar) {
2351 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2352 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2353 XtSetValues(buttonBarWidget, args, i);
2357 XtSetArg(args[0], XtNfromVert, messageWidget);
2358 XtSetArg(args[1], XtNtop, XtChainTop);
2359 XtSetArg(args[2], XtNbottom, XtChainBottom);
2360 XtSetArg(args[3], XtNleft, XtChainLeft);
2361 XtSetArg(args[4], XtNright, XtChainRight);
2362 XtSetValues(boardWidget, args, 5);
2364 XtRealizeWidget(shellWidget);
2367 XtSetArg(args[0], XtNx, wpMain.x);
2368 XtSetArg(args[1], XtNy, wpMain.y);
2369 XtSetValues(shellWidget, args, 2);
2373 * Correct the width of the message and title widgets.
2374 * It is not known why some systems need the extra fudge term.
2375 * The value "2" is probably larger than needed.
2377 XawFormDoLayout(formWidget, False);
2379 #define WIDTH_FUDGE 2
2381 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2382 XtSetArg(args[i], XtNheight, &h); i++;
2383 XtGetValues(messageWidget, args, i);
2384 if (appData.showButtonBar) {
2386 XtSetArg(args[i], XtNwidth, &w); i++;
2387 XtGetValues(buttonBarWidget, args, i);
2388 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2390 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2393 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2394 if (gres != XtGeometryYes && appData.debugMode) {
2395 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2396 programName, gres, w, h, wr, hr);
2399 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2400 /* The size used for the child widget in layout lags one resize behind
2401 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2403 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2404 if (gres != XtGeometryYes && appData.debugMode) {
2405 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2406 programName, gres, w, h, wr, hr);
2409 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2410 XtSetArg(args[1], XtNright, XtChainRight);
2411 XtSetValues(messageWidget, args, 2);
2413 if (appData.titleInWindow) {
2415 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2416 XtSetArg(args[i], XtNheight, &h); i++;
2417 XtGetValues(titleWidget, args, i);
2419 w = boardWidth - 2*bor;
2421 XtSetArg(args[0], XtNwidth, &w);
2422 XtGetValues(menuBarWidget, args, 1);
2423 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2426 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2427 if (gres != XtGeometryYes && appData.debugMode) {
2429 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2430 programName, gres, w, h, wr, hr);
2433 XawFormDoLayout(formWidget, True);
2435 xBoardWindow = XtWindow(boardWidget);
2437 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2438 // not need to go into InitDrawingSizes().
2442 * Create X checkmark bitmap and initialize option menu checks.
2444 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2445 checkmark_bits, checkmark_width, checkmark_height);
2446 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2447 #ifndef OPTIONSDIALOG
2448 if (appData.alwaysPromoteToQueen) {
2449 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2452 if (appData.animateDragging) {
2453 XtSetValues(XtNameToWidget(menuBarWidget,
2454 "menuOptions.Animate Dragging"),
2457 if (appData.animate) {
2458 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2461 if (appData.autoCallFlag) {
2462 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2465 if (appData.autoFlipView) {
2466 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2469 if (appData.blindfold) {
2470 XtSetValues(XtNameToWidget(menuBarWidget,
2471 "menuOptions.Blindfold"), args, 1);
2473 if (appData.flashCount > 0) {
2474 XtSetValues(XtNameToWidget(menuBarWidget,
2475 "menuOptions.Flash Moves"),
2479 if (appData.highlightDragging) {
2480 XtSetValues(XtNameToWidget(menuBarWidget,
2481 "menuOptions.Highlight Dragging"),
2485 if (appData.highlightLastMove) {
2486 XtSetValues(XtNameToWidget(menuBarWidget,
2487 "menuOptions.Highlight Last Move"),
2490 if (appData.highlightMoveWithArrow) {
2491 XtSetValues(XtNameToWidget(menuBarWidget,
2492 "menuOptions.Arrow"),
2495 // if (appData.icsAlarm) {
2496 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2499 if (appData.ringBellAfterMoves) {
2500 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2503 if (appData.oneClick) {
2504 XtSetValues(XtNameToWidget(menuBarWidget,
2505 "menuOptions.OneClick"), args, 1);
2507 if (appData.periodicUpdates) {
2508 XtSetValues(XtNameToWidget(menuBarWidget,
2509 "menuOptions.Periodic Updates"), args, 1);
2511 if (appData.ponderNextMove) {
2512 XtSetValues(XtNameToWidget(menuBarWidget,
2513 "menuOptions.Ponder Next Move"), args, 1);
2515 if (appData.popupExitMessage) {
2516 XtSetValues(XtNameToWidget(menuBarWidget,
2517 "menuOptions.Popup Exit Message"), args, 1);
2519 if (appData.popupMoveErrors) {
2520 XtSetValues(XtNameToWidget(menuBarWidget,
2521 "menuOptions.Popup Move Errors"), args, 1);
2523 // if (appData.premove) {
2524 // XtSetValues(XtNameToWidget(menuBarWidget,
2525 // "menuOptions.Premove"), args, 1);
2527 if (appData.showCoords) {
2528 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2531 if (appData.hideThinkingFromHuman) {
2532 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2535 if (appData.testLegality) {
2536 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2540 if (saveSettingsOnExit) {
2541 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2548 ReadBitmap(&wIconPixmap, "icon_white.bm",
2549 icon_white_bits, icon_white_width, icon_white_height);
2550 ReadBitmap(&bIconPixmap, "icon_black.bm",
2551 icon_black_bits, icon_black_width, icon_black_height);
2552 iconPixmap = wIconPixmap;
2554 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2555 XtSetValues(shellWidget, args, i);
2558 * Create a cursor for the board widget.
2560 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2561 XChangeWindowAttributes(xDisplay, xBoardWindow,
2562 CWCursor, &window_attributes);
2565 * Inhibit shell resizing.
2567 shellArgs[0].value = (XtArgVal) &w;
2568 shellArgs[1].value = (XtArgVal) &h;
2569 XtGetValues(shellWidget, shellArgs, 2);
2570 shellArgs[4].value = shellArgs[2].value = w;
2571 shellArgs[5].value = shellArgs[3].value = h;
2572 XtSetValues(shellWidget, &shellArgs[2], 4);
2573 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2574 marginH = h - boardHeight;
2576 CatchDeleteWindow(shellWidget, "QuitProc");
2584 if (appData.animate || appData.animateDragging)
2587 XtAugmentTranslations(formWidget,
2588 XtParseTranslationTable(globalTranslations));
2589 XtAugmentTranslations(boardWidget,
2590 XtParseTranslationTable(boardTranslations));
2591 XtAugmentTranslations(whiteTimerWidget,
2592 XtParseTranslationTable(whiteTranslations));
2593 XtAugmentTranslations(blackTimerWidget,
2594 XtParseTranslationTable(blackTranslations));
2596 /* Why is the following needed on some versions of X instead
2597 * of a translation? */
2598 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2599 (XtEventHandler) EventProc, NULL);
2601 XtAddEventHandler(formWidget, KeyPressMask, False,
2602 (XtEventHandler) MoveTypeInProc, NULL);
2604 /* [AS] Restore layout */
2605 if( wpMoveHistory.visible ) {
2609 if( wpEvalGraph.visible )
2614 if( wpEngineOutput.visible ) {
2615 EngineOutputPopUp();
2620 if (errorExitStatus == -1) {
2621 if (appData.icsActive) {
2622 /* We now wait until we see "login:" from the ICS before
2623 sending the logon script (problems with timestamp otherwise) */
2624 /*ICSInitScript();*/
2625 if (appData.icsInputBox) ICSInputBoxPopUp();
2629 signal(SIGWINCH, TermSizeSigHandler);
2631 signal(SIGINT, IntSigHandler);
2632 signal(SIGTERM, IntSigHandler);
2633 if (*appData.cmailGameName != NULLCHAR) {
2634 signal(SIGUSR1, CmailSigHandler);
2637 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2639 // XtSetKeyboardFocus(shellWidget, formWidget);
2640 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2642 XtAppMainLoop(appContext);
2643 if (appData.debugMode) fclose(debugFP); // [DM] debug
2650 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2651 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2653 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2654 unlink(gameCopyFilename);
2655 unlink(gamePasteFilename);
2658 RETSIGTYPE TermSizeSigHandler(int sig)
2671 CmailSigHandler(sig)
2677 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2679 /* Activate call-back function CmailSigHandlerCallBack() */
2680 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2682 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2686 CmailSigHandlerCallBack(isr, closure, message, count, error)
2694 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2696 /**** end signal code ****/
2702 /* try to open the icsLogon script, either in the location given
2703 * or in the users HOME directory
2710 f = fopen(appData.icsLogon, "r");
2713 homedir = getenv("HOME");
2714 if (homedir != NULL)
2716 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2717 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2718 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2719 f = fopen(buf, "r");
2724 ProcessICSInitScript(f);
2726 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2749 if (!menuBarWidget) return;
2750 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2752 DisplayError("menuEdit.Revert", 0);
2754 XtSetSensitive(w, !grey);
2756 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2758 DisplayError("menuEdit.Annotate", 0);
2760 XtSetSensitive(w, !grey);
2765 SetMenuEnables(enab)
2769 if (!menuBarWidget) return;
2770 while (enab->name != NULL) {
2771 w = XtNameToWidget(menuBarWidget, enab->name);
2773 DisplayError(enab->name, 0);
2775 XtSetSensitive(w, enab->value);
2781 Enables icsEnables[] = {
2782 { "menuFile.Mail Move", False },
2783 { "menuFile.Reload CMail Message", False },
2784 { "menuMode.Machine Black", False },
2785 { "menuMode.Machine White", False },
2786 { "menuMode.Analysis Mode", False },
2787 { "menuMode.Analyze File", False },
2788 { "menuMode.Two Machines", False },
2789 { "menuMode.Machine Match", False },
2791 { "menuEngine.Hint", False },
2792 { "menuEngine.Book", False },
2793 { "menuEngine.Move Now", False },
2794 #ifndef OPTIONSDIALOG
2795 { "menuOptions.Periodic Updates", False },
2796 { "menuOptions.Hide Thinking", False },
2797 { "menuOptions.Ponder Next Move", False },
2800 { "menuEngine.Engine #1 Settings", False },
2801 { "menuEngine.Engine #2 Settings", False },
2802 { "menuEngine.Load Engine", False },
2803 { "menuEdit.Annotate", False },
2804 { "menuOptions.Match", False },
2808 Enables ncpEnables[] = {
2809 { "menuFile.Mail Move", False },
2810 { "menuFile.Reload CMail Message", False },
2811 { "menuMode.Machine White", False },
2812 { "menuMode.Machine Black", False },
2813 { "menuMode.Analysis Mode", False },
2814 { "menuMode.Analyze File", False },
2815 { "menuMode.Two Machines", False },
2816 { "menuMode.Machine Match", False },
2817 { "menuMode.ICS Client", False },
2818 { "menuView.ICStex", False },
2819 { "menuView.ICS Input Box", False },
2820 { "Action", False },
2821 { "menuEdit.Revert", False },
2822 { "menuEdit.Annotate", False },
2823 { "menuEngine.Engine #1 Settings", False },
2824 { "menuEngine.Engine #2 Settings", False },
2825 { "menuEngine.Move Now", False },
2826 { "menuEngine.Retract Move", False },
2827 { "menuOptions.ICS", False },
2828 #ifndef OPTIONSDIALOG
2829 { "menuOptions.Auto Flag", False },
2830 { "menuOptions.Auto Flip View", False },
2831 // { "menuOptions.ICS Alarm", False },
2832 { "menuOptions.Move Sound", False },
2833 { "menuOptions.Hide Thinking", False },
2834 { "menuOptions.Periodic Updates", False },
2835 { "menuOptions.Ponder Next Move", False },
2837 { "menuEngine.Hint", False },
2838 { "menuEngine.Book", False },
2842 Enables gnuEnables[] = {
2843 { "menuMode.ICS Client", False },
2844 { "menuView.ICStex", False },
2845 { "menuView.ICS Input Box", False },
2846 { "menuAction.Accept", False },
2847 { "menuAction.Decline", False },
2848 { "menuAction.Rematch", False },
2849 { "menuAction.Adjourn", False },
2850 { "menuAction.Stop Examining", False },
2851 { "menuAction.Stop Observing", False },
2852 { "menuAction.Upload to Examine", False },
2853 { "menuEdit.Revert", False },
2854 { "menuEdit.Annotate", False },
2855 { "menuOptions.ICS", False },
2857 /* The next two options rely on SetCmailMode being called *after* */
2858 /* SetGNUMode so that when GNU is being used to give hints these */
2859 /* menu options are still available */
2861 { "menuFile.Mail Move", False },
2862 { "menuFile.Reload CMail Message", False },
2863 // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2864 { "menuMode.Machine White", True },
2865 { "menuMode.Machine Black", True },
2866 { "menuMode.Analysis Mode", True },
2867 { "menuMode.Analyze File", True },
2868 { "menuMode.Two Machines", True },
2869 { "menuMode.Machine Match", True },
2870 { "menuEngine.Engine #1 Settings", True },
2871 { "menuEngine.Engine #2 Settings", True },
2872 { "menuEngine.Hint", True },
2873 { "menuEngine.Book", True },
2874 { "menuEngine.Move Now", True },
2875 { "menuEngine.Retract Move", True },
2880 Enables cmailEnables[] = {
2882 { "menuAction.Call Flag", False },
2883 { "menuAction.Draw", True },
2884 { "menuAction.Adjourn", False },
2885 { "menuAction.Abort", False },
2886 { "menuAction.Stop Observing", False },
2887 { "menuAction.Stop Examining", False },
2888 { "menuFile.Mail Move", True },
2889 { "menuFile.Reload CMail Message", True },
2893 Enables trainingOnEnables[] = {
2894 { "menuMode.Edit Comment", False },
2895 { "menuMode.Pause", False },
2896 { "menuEdit.Forward", False },
2897 { "menuEdit.Backward", False },
2898 { "menuEdit.Forward to End", False },
2899 { "menuEdit.Back to Start", False },
2900 { "menuEngine.Move Now", False },
2901 { "menuEdit.Truncate Game", False },
2905 Enables trainingOffEnables[] = {
2906 { "menuMode.Edit Comment", True },
2907 { "menuMode.Pause", True },
2908 { "menuEdit.Forward", True },
2909 { "menuEdit.Backward", True },
2910 { "menuEdit.Forward to End", True },
2911 { "menuEdit.Back to Start", True },
2912 { "menuEngine.Move Now", True },
2913 { "menuEdit.Truncate Game", True },
2917 Enables machineThinkingEnables[] = {
2918 { "menuFile.Load Game", False },
2919 // { "menuFile.Load Next Game", False },
2920 // { "menuFile.Load Previous Game", False },
2921 // { "menuFile.Reload Same Game", False },
2922 { "menuEdit.Paste Game", False },
2923 { "menuFile.Load Position", False },
2924 // { "menuFile.Load Next Position", False },
2925 // { "menuFile.Load Previous Position", False },
2926 // { "menuFile.Reload Same Position", False },
2927 { "menuEdit.Paste Position", False },
2928 { "menuMode.Machine White", False },
2929 { "menuMode.Machine Black", False },
2930 { "menuMode.Two Machines", False },
2931 // { "menuMode.Machine Match", False },
2932 { "menuEngine.Retract Move", False },
2936 Enables userThinkingEnables[] = {
2937 { "menuFile.Load Game", True },
2938 // { "menuFile.Load Next Game", True },
2939 // { "menuFile.Load Previous Game", True },
2940 // { "menuFile.Reload Same Game", True },
2941 { "menuEdit.Paste Game", True },
2942 { "menuFile.Load Position", True },
2943 // { "menuFile.Load Next Position", True },
2944 // { "menuFile.Load Previous Position", True },
2945 // { "menuFile.Reload Same Position", True },
2946 { "menuEdit.Paste Position", True },
2947 { "menuMode.Machine White", True },
2948 { "menuMode.Machine Black", True },
2949 { "menuMode.Two Machines", True },
2950 // { "menuMode.Machine Match", True },
2951 { "menuEngine.Retract Move", True },
2957 SetMenuEnables(icsEnables);
2960 if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
2961 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2962 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
2970 SetMenuEnables(ncpEnables);
2976 SetMenuEnables(gnuEnables);
2982 SetMenuEnables(cmailEnables);
2988 SetMenuEnables(trainingOnEnables);
2989 if (appData.showButtonBar) {
2990 XtSetSensitive(buttonBarWidget, False);
2996 SetTrainingModeOff()
2998 SetMenuEnables(trainingOffEnables);
2999 if (appData.showButtonBar) {
3000 XtSetSensitive(buttonBarWidget, True);
3005 SetUserThinkingEnables()
3007 if (appData.noChessProgram) return;
3008 SetMenuEnables(userThinkingEnables);
3012 SetMachineThinkingEnables()
3014 if (appData.noChessProgram) return;
3015 SetMenuEnables(machineThinkingEnables);
3017 case MachinePlaysBlack:
3018 case MachinePlaysWhite:
3019 case TwoMachinesPlay:
3020 XtSetSensitive(XtNameToWidget(menuBarWidget,
3021 ModeToWidgetName(gameMode)), True);
3028 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3029 #define HISTORY_SIZE 64
3030 static char *history[HISTORY_SIZE];
3031 int histIn = 0, histP = 0;
3034 SaveInHistory(char *cmd)
3036 if (history[histIn] != NULL) {
3037 free(history[histIn]);
3038 history[histIn] = NULL;
3040 if (*cmd == NULLCHAR) return;
3041 history[histIn] = StrSave(cmd);
3042 histIn = (histIn + 1) % HISTORY_SIZE;
3043 if (history[histIn] != NULL) {
3044 free(history[histIn]);
3045 history[histIn] = NULL;
3051 PrevInHistory(char *cmd)
3054 if (histP == histIn) {
3055 if (history[histIn] != NULL) free(history[histIn]);
3056 history[histIn] = StrSave(cmd);
3058 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3059 if (newhp == histIn || history[newhp] == NULL) return NULL;
3061 return history[histP];
3067 if (histP == histIn) return NULL;
3068 histP = (histP + 1) % HISTORY_SIZE;
3069 return history[histP];
3071 // end of borrowed code
3073 #define Abs(n) ((n)<0 ? -(n) : (n))
3076 * Find a font that matches "pattern" that is as close as
3077 * possible to the targetPxlSize. Prefer fonts that are k
3078 * pixels smaller to fonts that are k pixels larger. The
3079 * pattern must be in the X Consortium standard format,
3080 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3081 * The return value should be freed with XtFree when no
3085 FindFont(pattern, targetPxlSize)
3089 char **fonts, *p, *best, *scalable, *scalableTail;
3090 int i, j, nfonts, minerr, err, pxlSize;
3093 char **missing_list;
3095 char *def_string, *base_fnt_lst, strInt[3];
3097 XFontStruct **fnt_list;
3098 base_fnt_lst = calloc(1, strlen(pattern) + 3);
3099 snprintf(strInt, sizeof(strInt)/sizeof(strInt[0]), "%d", targetPxlSize);
3100 p = strstr(pattern, "--");
3101 strncpy(base_fnt_lst, pattern, p - pattern + 2);
3102 strcat(base_fnt_lst, strInt);
3103 strcat(base_fnt_lst, strchr(p + 2, '-'));
3105 if ((fntSet = XCreateFontSet(xDisplay,
3109 &def_string)) == NULL) {
3111 fprintf(stderr, _("Unable to create font set.\n"));
3115 nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
3117 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3119 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3120 programName, pattern);
3128 for (i=0; i<nfonts; i++) {
3131 if (*p != '-') continue;
3133 if (*p == NULLCHAR) break;
3134 if (*p++ == '-') j++;
3136 if (j < 7) continue;
3139 scalable = fonts[i];
3142 err = pxlSize - targetPxlSize;
3143 if (Abs(err) < Abs(minerr) ||
3144 (minerr > 0 && err < 0 && -err == minerr)) {
3150 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3151 /* If the error is too big and there is a scalable font,
3152 use the scalable font. */
3153 int headlen = scalableTail - scalable;
3154 p = (char *) XtMalloc(strlen(scalable) + 10);
3155 while (isdigit(*scalableTail)) scalableTail++;
3156 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3158 p = (char *) XtMalloc(strlen(best) + 2);
3159 safeStrCpy(p, best, strlen(best)+1 );
3161 if (appData.debugMode) {
3162 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3163 pattern, targetPxlSize, p);
3166 if (missing_count > 0)
3167 XFreeStringList(missing_list);
3168 XFreeFontSet(xDisplay, fntSet);
3170 XFreeFontNames(fonts);
3176 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3177 // must be called before all non-first callse to CreateGCs()
3178 XtReleaseGC(shellWidget, highlineGC);
3179 XtReleaseGC(shellWidget, lightSquareGC);
3180 XtReleaseGC(shellWidget, darkSquareGC);
3181 XtReleaseGC(shellWidget, lineGC);
3182 if (appData.monoMode) {
3183 if (DefaultDepth(xDisplay, xScreen) == 1) {
3184 XtReleaseGC(shellWidget, wbPieceGC);
3186 XtReleaseGC(shellWidget, bwPieceGC);
3189 XtReleaseGC(shellWidget, prelineGC);
3190 XtReleaseGC(shellWidget, jailSquareGC);
3191 XtReleaseGC(shellWidget, wdPieceGC);
3192 XtReleaseGC(shellWidget, wlPieceGC);
3193 XtReleaseGC(shellWidget, wjPieceGC);
3194 XtReleaseGC(shellWidget, bdPieceGC);
3195 XtReleaseGC(shellWidget, blPieceGC);
3196 XtReleaseGC(shellWidget, bjPieceGC);
3200 void CreateGCs(int redo)
3202 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3203 | GCBackground | GCFunction | GCPlaneMask;
3204 XGCValues gc_values;
3207 gc_values.plane_mask = AllPlanes;
3208 gc_values.line_width = lineGap;
3209 gc_values.line_style = LineSolid;
3210 gc_values.function = GXcopy;
3213 DeleteGCs(); // called a second time; clean up old GCs first
3214 } else { // [HGM] grid and font GCs created on first call only
3215 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3216 gc_values.background = XWhitePixel(xDisplay, xScreen);
3217 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3218 XSetFont(xDisplay, coordGC, coordFontID);
3220 // [HGM] make font for holdings counts (white on black)
3221 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3222 gc_values.background = XBlackPixel(xDisplay, xScreen);
3223 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3224 XSetFont(xDisplay, countGC, countFontID);
3226 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3227 gc_values.background = XBlackPixel(xDisplay, xScreen);
3228 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3230 if (appData.monoMode) {
3231 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3232 gc_values.background = XWhitePixel(xDisplay, xScreen);
3233 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3235 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3236 gc_values.background = XBlackPixel(xDisplay, xScreen);
3237 lightSquareGC = wbPieceGC
3238 = XtGetGC(shellWidget, value_mask, &gc_values);
3240 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3241 gc_values.background = XWhitePixel(xDisplay, xScreen);
3242 darkSquareGC = bwPieceGC
3243 = XtGetGC(shellWidget, value_mask, &gc_values);
3245 if (DefaultDepth(xDisplay, xScreen) == 1) {
3246 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3247 gc_values.function = GXcopyInverted;
3248 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3249 gc_values.function = GXcopy;
3250 if (XBlackPixel(xDisplay, xScreen) == 1) {
3251 bwPieceGC = darkSquareGC;
3252 wbPieceGC = copyInvertedGC;
3254 bwPieceGC = copyInvertedGC;
3255 wbPieceGC = lightSquareGC;
3259 gc_values.foreground = highlightSquareColor;
3260 gc_values.background = highlightSquareColor;
3261 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3263 gc_values.foreground = premoveHighlightColor;
3264 gc_values.background = premoveHighlightColor;
3265 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3267 gc_values.foreground = lightSquareColor;
3268 gc_values.background = darkSquareColor;
3269 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3271 gc_values.foreground = darkSquareColor;
3272 gc_values.background = lightSquareColor;
3273 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3275 gc_values.foreground = jailSquareColor;
3276 gc_values.background = jailSquareColor;
3277 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3279 gc_values.foreground = whitePieceColor;
3280 gc_values.background = darkSquareColor;
3281 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3283 gc_values.foreground = whitePieceColor;
3284 gc_values.background = lightSquareColor;
3285 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3287 gc_values.foreground = whitePieceColor;
3288 gc_values.background = jailSquareColor;
3289 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3291 gc_values.foreground = blackPieceColor;
3292 gc_values.background = darkSquareColor;
3293 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3295 gc_values.foreground = blackPieceColor;
3296 gc_values.background = lightSquareColor;
3297 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3299 gc_values.foreground = blackPieceColor;
3300 gc_values.background = jailSquareColor;
3301 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3305 void loadXIM(xim, xmask, filename, dest, mask)
3318 fp = fopen(filename, "rb");
3320 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3327 for (y=0; y<h; ++y) {
3328 for (x=0; x<h; ++x) {
3333 XPutPixel(xim, x, y, blackPieceColor);
3335 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3338 XPutPixel(xim, x, y, darkSquareColor);
3340 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3343 XPutPixel(xim, x, y, whitePieceColor);
3345 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3348 XPutPixel(xim, x, y, lightSquareColor);
3350 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3358 /* create Pixmap of piece */
3359 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3361 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3364 /* create Pixmap of clipmask
3365 Note: We assume the white/black pieces have the same
3366 outline, so we make only 6 masks. This is okay
3367 since the XPM clipmask routines do the same. */
3369 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3371 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3374 /* now create the 1-bit version */
3375 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3378 values.foreground = 1;
3379 values.background = 0;
3381 /* Don't use XtGetGC, not read only */
3382 maskGC = XCreateGC(xDisplay, *mask,
3383 GCForeground | GCBackground, &values);
3384 XCopyPlane(xDisplay, temp, *mask, maskGC,
3385 0, 0, squareSize, squareSize, 0, 0, 1);
3386 XFreePixmap(xDisplay, temp);
3391 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3393 void CreateXIMPieces()
3398 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3403 /* The XSynchronize calls were copied from CreatePieces.
3404 Not sure if needed, but can't hurt */
3405 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3408 /* temp needed by loadXIM() */
3409 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3410 0, 0, ss, ss, AllPlanes, XYPixmap);
3412 if (strlen(appData.pixmapDirectory) == 0) {
3416 if (appData.monoMode) {
3417 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3421 fprintf(stderr, _("\nLoading XIMs...\n"));
3423 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3424 fprintf(stderr, "%d", piece+1);
3425 for (kind=0; kind<4; kind++) {
3426 fprintf(stderr, ".");
3427 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3428 ExpandPathName(appData.pixmapDirectory),
3429 piece <= (int) WhiteKing ? "" : "w",
3430 pieceBitmapNames[piece],
3432 ximPieceBitmap[kind][piece] =
3433 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3434 0, 0, ss, ss, AllPlanes, XYPixmap);
3435 if (appData.debugMode)
3436 fprintf(stderr, _("(File:%s:) "), buf);
3437 loadXIM(ximPieceBitmap[kind][piece],
3439 &(xpmPieceBitmap2[kind][piece]),
3440 &(ximMaskPm2[piece]));
3441 if(piece <= (int)WhiteKing)
3442 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3444 fprintf(stderr," ");
3446 /* Load light and dark squares */
3447 /* If the LSQ and DSQ pieces don't exist, we will
3448 draw them with solid squares. */
3449 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3450 if (access(buf, 0) != 0) {
3454 fprintf(stderr, _("light square "));
3456 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3457 0, 0, ss, ss, AllPlanes, XYPixmap);
3458 if (appData.debugMode)
3459 fprintf(stderr, _("(File:%s:) "), buf);
3461 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3462 fprintf(stderr, _("dark square "));
3463 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3464 ExpandPathName(appData.pixmapDirectory), ss);
3465 if (appData.debugMode)
3466 fprintf(stderr, _("(File:%s:) "), buf);
3468 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3469 0, 0, ss, ss, AllPlanes, XYPixmap);
3470 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3471 xpmJailSquare = xpmLightSquare;
3473 fprintf(stderr, _("Done.\n"));
3475 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3478 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3481 void CreateXPMBoard(char *s, int kind)
3485 if(s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3486 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3487 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3491 void FreeXPMPieces()
3492 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3493 // thisroutine has to be called t free the old piece pixmaps
3495 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3496 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3498 XFreePixmap(xDisplay, xpmLightSquare);
3499 XFreePixmap(xDisplay, xpmDarkSquare);
3503 void CreateXPMPieces()
3507 u_int ss = squareSize;
3509 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3510 XpmColorSymbol symbols[4];
3511 static int redo = False;
3513 if(redo) FreeXPMPieces(); else redo = 1;
3515 /* The XSynchronize calls were copied from CreatePieces.
3516 Not sure if needed, but can't hurt */
3517 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3519 /* Setup translations so piece colors match square colors */
3520 symbols[0].name = "light_piece";
3521 symbols[0].value = appData.whitePieceColor;
3522 symbols[1].name = "dark_piece";
3523 symbols[1].value = appData.blackPieceColor;
3524 symbols[2].name = "light_square";
3525 symbols[2].value = appData.lightSquareColor;
3526 symbols[3].name = "dark_square";
3527 symbols[3].value = appData.darkSquareColor;
3529 attr.valuemask = XpmColorSymbols;
3530 attr.colorsymbols = symbols;
3531 attr.numsymbols = 4;
3533 if (appData.monoMode) {
3534 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3538 if (strlen(appData.pixmapDirectory) == 0) {
3539 XpmPieces* pieces = builtInXpms;
3542 while (pieces->size != squareSize && pieces->size) pieces++;
3543 if (!pieces->size) {
3544 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3547 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3548 for (kind=0; kind<4; kind++) {
3550 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3551 pieces->xpm[piece][kind],
3552 &(xpmPieceBitmap2[kind][piece]),
3553 NULL, &attr)) != 0) {
3554 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3558 if(piece <= (int) WhiteKing)
3559 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3563 xpmJailSquare = xpmLightSquare;
3567 fprintf(stderr, _("\nLoading XPMs...\n"));
3570 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3571 fprintf(stderr, "%d ", piece+1);
3572 for (kind=0; kind<4; kind++) {
3573 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3574 ExpandPathName(appData.pixmapDirectory),
3575 piece > (int) WhiteKing ? "w" : "",
3576 pieceBitmapNames[piece],
3578 if (appData.debugMode) {
3579 fprintf(stderr, _("(File:%s:) "), buf);
3581 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3582 &(xpmPieceBitmap2[kind][piece]),
3583 NULL, &attr)) != 0) {
3584 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3585 // [HGM] missing: read of unorthodox piece failed; substitute King.
3586 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3587 ExpandPathName(appData.pixmapDirectory),
3589 if (appData.debugMode) {
3590 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3592 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3593 &(xpmPieceBitmap2[kind][piece]),
3597 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3602 if(piece <= (int) WhiteKing)
3603 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3606 /* Load light and dark squares */
3607 /* If the LSQ and DSQ pieces don't exist, we will
3608 draw them with solid squares. */
3609 fprintf(stderr, _("light square "));
3610 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3611 if (access(buf, 0) != 0) {
3615 if (appData.debugMode)
3616 fprintf(stderr, _("(File:%s:) "), buf);
3618 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3619 &xpmLightSquare, NULL, &attr)) != 0) {
3620 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3623 fprintf(stderr, _("dark square "));
3624 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3625 ExpandPathName(appData.pixmapDirectory), ss);
3626 if (appData.debugMode) {
3627 fprintf(stderr, _("(File:%s:) "), buf);
3629 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3630 &xpmDarkSquare, NULL, &attr)) != 0) {
3631 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3635 xpmJailSquare = xpmLightSquare;
3636 fprintf(stderr, _("Done.\n"));
3638 oldVariant = -1; // kludge to force re-makig of animation masks
3639 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3642 #endif /* HAVE_LIBXPM */
3645 /* No built-in bitmaps */
3650 u_int ss = squareSize;
3652 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3655 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3656 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3657 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3658 pieceBitmapNames[piece],
3659 ss, kind == SOLID ? 's' : 'o');
3660 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3661 if(piece <= (int)WhiteKing)
3662 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3666 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3670 /* With built-in bitmaps */
3673 BuiltInBits* bib = builtInBits;
3676 u_int ss = squareSize;
3678 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3681 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3683 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3684 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3685 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3686 pieceBitmapNames[piece],
3687 ss, kind == SOLID ? 's' : 'o');
3688 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3689 bib->bits[kind][piece], ss, ss);
3690 if(piece <= (int)WhiteKing)
3691 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3695 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3700 void ReadBitmap(pm, name, bits, wreq, hreq)
3703 unsigned char bits[];
3709 char msg[MSG_SIZ], fullname[MSG_SIZ];
3711 if (*appData.bitmapDirectory != NULLCHAR) {
3712 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3713 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3714 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3715 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3716 &w, &h, pm, &x_hot, &y_hot);
3717 fprintf(stderr, "load %s\n", name);
3718 if (errcode != BitmapSuccess) {
3720 case BitmapOpenFailed:
3721 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3723 case BitmapFileInvalid:
3724 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3726 case BitmapNoMemory:
3727 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3731 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3735 fprintf(stderr, _("%s: %s...using built-in\n"),
3737 } else if (w != wreq || h != hreq) {
3739 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3740 programName, fullname, w, h, wreq, hreq);
3746 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3755 if (lineGap == 0) return;
3757 /* [HR] Split this into 2 loops for non-square boards. */
3759 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3760 gridSegments[i].x1 = 0;
3761 gridSegments[i].x2 =
3762 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3763 gridSegments[i].y1 = gridSegments[i].y2
3764 = lineGap / 2 + (i * (squareSize + lineGap));
3767 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3768 gridSegments[j + i].y1 = 0;
3769 gridSegments[j + i].y2 =
3770 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3771 gridSegments[j + i].x1 = gridSegments[j + i].x2
3772 = lineGap / 2 + (j * (squareSize + lineGap));
3776 static void MenuBarSelect(w, addr, index)
3781 XtActionProc proc = (XtActionProc) addr;
3783 (proc)(NULL, NULL, NULL, NULL);
3786 void CreateMenuBarPopup(parent, name, mb)
3796 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3799 XtSetArg(args[j], XtNleftMargin, 20); j++;
3800 XtSetArg(args[j], XtNrightMargin, 20); j++;
3802 while (mi->string != NULL) {
3803 if (strcmp(mi->string, "----") == 0) {
3804 entry = XtCreateManagedWidget(_(mi->string), smeLineObjectClass,
3807 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3808 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3810 XtAddCallback(entry, XtNcallback,
3811 (XtCallbackProc) MenuBarSelect,
3812 (caddr_t) mi->proc);
3818 Widget CreateMenuBar(mb)
3822 Widget anchor, menuBar;
3824 char menuName[MSG_SIZ];
3827 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3828 XtSetArg(args[j], XtNvSpace, 0); j++;
3829 XtSetArg(args[j], XtNborderWidth, 0); j++;
3830 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3831 formWidget, args, j);
3833 while (mb->name != NULL) {
3834 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3835 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3837 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3840 shortName[0] = mb->name[0];
3841 shortName[1] = NULLCHAR;
3842 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3845 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3848 XtSetArg(args[j], XtNborderWidth, 0); j++;
3849 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3851 CreateMenuBarPopup(menuBar, menuName, mb);
3857 Widget CreateButtonBar(mi)
3861 Widget button, buttonBar;
3865 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3867 XtSetArg(args[j], XtNhSpace, 0); j++;
3869 XtSetArg(args[j], XtNborderWidth, 0); j++;
3870 XtSetArg(args[j], XtNvSpace, 0); j++;
3871 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3872 formWidget, args, j);
3874 while (mi->string != NULL) {
3877 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3878 XtSetArg(args[j], XtNborderWidth, 0); j++;
3880 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3881 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3882 buttonBar, args, j);
3883 XtAddCallback(button, XtNcallback,
3884 (XtCallbackProc) MenuBarSelect,
3885 (caddr_t) mi->proc);
3892 CreatePieceMenu(name, color)
3899 ChessSquare selection;
3901 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3902 boardWidget, args, 0);
3904 for (i = 0; i < PIECE_MENU_SIZE; i++) {
3905 String item = pieceMenuStrings[color][i];
3907 if (strcmp(item, "----") == 0) {
3908 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3911 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3912 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3914 selection = pieceMenuTranslation[color][i];
3915 XtAddCallback(entry, XtNcallback,
3916 (XtCallbackProc) PieceMenuSelect,
3917 (caddr_t) selection);
3918 if (selection == WhitePawn || selection == BlackPawn) {
3919 XtSetArg(args[0], XtNpopupOnEntry, entry);
3920 XtSetValues(menu, args, 1);
3933 ChessSquare selection;
3935 whitePieceMenu = CreatePieceMenu("menuW", 0);
3936 blackPieceMenu = CreatePieceMenu("menuB", 1);
3938 XtRegisterGrabAction(PieceMenuPopup, True,
3939 (unsigned)(ButtonPressMask|ButtonReleaseMask),
3940 GrabModeAsync, GrabModeAsync);
3942 XtSetArg(args[0], XtNlabel, _("Drop"));
3943 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3944 boardWidget, args, 1);
3945 for (i = 0; i < DROP_MENU_SIZE; i++) {
3946 String item = dropMenuStrings[i];
3948 if (strcmp(item, "----") == 0) {
3949 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3952 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3953 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3955 selection = dropMenuTranslation[i];
3956 XtAddCallback(entry, XtNcallback,
3957 (XtCallbackProc) DropMenuSelect,
3958 (caddr_t) selection);
3963 void SetupDropMenu()
3971 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3972 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3973 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3974 dmEnables[i].piece);
3975 XtSetSensitive(entry, p != NULL || !appData.testLegality
3976 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3977 && !appData.icsActive));
3979 while (p && *p++ == dmEnables[i].piece) count++;
3980 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3982 XtSetArg(args[j], XtNlabel, label); j++;
3983 XtSetValues(entry, args, j);
3987 void PieceMenuPopup(w, event, params, num_params)
3991 Cardinal *num_params;
3993 String whichMenu; int menuNr = -2;
3994 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3995 if (event->type == ButtonRelease)
3996 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3997 else if (event->type == ButtonPress)
3998 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4000 case 0: whichMenu = params[0]; break;
4001 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
4003 case -1: if (errorUp) ErrorPopDown();
4006 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
4009 static void PieceMenuSelect(w, piece, junk)
4014 if (pmFromX < 0 || pmFromY < 0) return;
4015 EditPositionMenuEvent(piece, pmFromX, pmFromY);
4018 static void DropMenuSelect(w, piece, junk)
4023 if (pmFromX < 0 || pmFromY < 0) return;
4024 DropMenuEvent(piece, pmFromX, pmFromY);
4027 void WhiteClock(w, event, prms, nprms)
4036 void BlackClock(w, event, prms, nprms)
4047 * If the user selects on a border boundary, return -1; if off the board,
4048 * return -2. Otherwise map the event coordinate to the square.
4050 int EventToSquare(x, limit)
4058 if ((x % (squareSize + lineGap)) >= squareSize)
4060 x /= (squareSize + lineGap);
4066 static void do_flash_delay(msec)
4072 static void drawHighlight(file, rank, gc)
4078 if (lineGap == 0) return;
4081 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4082 (squareSize + lineGap);
4083 y = lineGap/2 + rank * (squareSize + lineGap);
4085 x = lineGap/2 + file * (squareSize + lineGap);
4086 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4087 (squareSize + lineGap);
4090 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4091 squareSize+lineGap, squareSize+lineGap);
4094 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4095 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4098 SetHighlights(fromX, fromY, toX, toY)
4099 int fromX, fromY, toX, toY;
4101 if (hi1X != fromX || hi1Y != fromY) {
4102 if (hi1X >= 0 && hi1Y >= 0) {
4103 drawHighlight(hi1X, hi1Y, lineGC);
4105 } // [HGM] first erase both, then draw new!
4106 if (hi2X != toX || hi2Y != toY) {
4107 if (hi2X >= 0 && hi2Y >= 0) {
4108 drawHighlight(hi2X, hi2Y, lineGC);
4111 if (hi1X != fromX || hi1Y != fromY) {
4112 if (fromX >= 0 && fromY >= 0) {
4113 drawHighlight(fromX, fromY, highlineGC);
4116 if (hi2X != toX || hi2Y != toY) {
4117 if (toX >= 0 && toY >= 0) {
4118 drawHighlight(toX, toY, highlineGC);
4130 SetHighlights(-1, -1, -1, -1);
4135 SetPremoveHighlights(fromX, fromY, toX, toY)
4136 int fromX, fromY, toX, toY;
4138 if (pm1X != fromX || pm1Y != fromY) {
4139 if (pm1X >= 0 && pm1Y >= 0) {
4140 drawHighlight(pm1X, pm1Y, lineGC);
4142 if (fromX >= 0 && fromY >= 0) {
4143 drawHighlight(fromX, fromY, prelineGC);
4146 if (pm2X != toX || pm2Y != toY) {
4147 if (pm2X >= 0 && pm2Y >= 0) {
4148 drawHighlight(pm2X, pm2Y, lineGC);
4150 if (toX >= 0 && toY >= 0) {
4151 drawHighlight(toX, toY, prelineGC);
4161 ClearPremoveHighlights()
4163 SetPremoveHighlights(-1, -1, -1, -1);
4166 static int CutOutSquare(x, y, x0, y0, kind)
4167 int x, y, *x0, *y0, kind;
4169 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4170 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4172 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4173 if(textureW[kind] < W*squareSize)
4174 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4176 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4177 if(textureH[kind] < H*squareSize)
4178 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4180 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4184 static void BlankSquare(x, y, color, piece, dest, fac)
4185 int x, y, color, fac;
4188 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4190 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4191 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4192 squareSize, squareSize, x*fac, y*fac);
4194 if (useImages && useImageSqs) {
4198 pm = xpmLightSquare;
4203 case 2: /* neutral */
4208 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4209 squareSize, squareSize, x*fac, y*fac);
4219 case 2: /* neutral */
4224 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4229 I split out the routines to draw a piece so that I could
4230 make a generic flash routine.
4232 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4234 int square_color, x, y;
4237 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4238 switch (square_color) {
4240 case 2: /* neutral */
4242 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4243 ? *pieceToOutline(piece)
4244 : *pieceToSolid(piece),
4245 dest, bwPieceGC, 0, 0,
4246 squareSize, squareSize, x, y);
4249 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4250 ? *pieceToSolid(piece)
4251 : *pieceToOutline(piece),
4252 dest, wbPieceGC, 0, 0,
4253 squareSize, squareSize, x, y);
4258 static void monoDrawPiece(piece, square_color, x, y, dest)
4260 int square_color, x, y;
4263 switch (square_color) {
4265 case 2: /* neutral */
4267 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4268 ? *pieceToOutline(piece)
4269 : *pieceToSolid(piece),
4270 dest, bwPieceGC, 0, 0,
4271 squareSize, squareSize, x, y, 1);
4274 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4275 ? *pieceToSolid(piece)
4276 : *pieceToOutline(piece),
4277 dest, wbPieceGC, 0, 0,
4278 squareSize, squareSize, x, y, 1);
4283 static void colorDrawPiece(piece, square_color, x, y, dest)
4285 int square_color, x, y;
4288 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4289 switch (square_color) {
4291 XCopyPlane(xDisplay, *pieceToSolid(piece),
4292 dest, (int) piece < (int) BlackPawn
4293 ? wlPieceGC : blPieceGC, 0, 0,
4294 squareSize, squareSize, x, y, 1);
4297 XCopyPlane(xDisplay, *pieceToSolid(piece),
4298 dest, (int) piece < (int) BlackPawn
4299 ? wdPieceGC : bdPieceGC, 0, 0,
4300 squareSize, squareSize, x, y, 1);
4302 case 2: /* neutral */
4304 XCopyPlane(xDisplay, *pieceToSolid(piece),
4305 dest, (int) piece < (int) BlackPawn
4306 ? wjPieceGC : bjPieceGC, 0, 0,
4307 squareSize, squareSize, x, y, 1);
4312 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4314 int square_color, x, y;
4317 int kind, p = piece;
4319 switch (square_color) {
4321 case 2: /* neutral */
4323 if ((int)piece < (int) BlackPawn) {
4331 if ((int)piece < (int) BlackPawn) {
4339 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4340 if(useTexture & square_color+1) {
4341 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4342 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4343 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4344 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4345 XSetClipMask(xDisplay, wlPieceGC, None);
4346 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4348 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4349 dest, wlPieceGC, 0, 0,
4350 squareSize, squareSize, x, y);
4353 typedef void (*DrawFunc)();
4355 DrawFunc ChooseDrawFunc()
4357 if (appData.monoMode) {
4358 if (DefaultDepth(xDisplay, xScreen) == 1) {
4359 return monoDrawPiece_1bit;
4361 return monoDrawPiece;
4365 return colorDrawPieceImage;
4367 return colorDrawPiece;
4371 /* [HR] determine square color depending on chess variant. */
4372 static int SquareColor(row, column)
4377 if (gameInfo.variant == VariantXiangqi) {
4378 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4380 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4382 } else if (row <= 4) {
4388 square_color = ((column + row) % 2) == 1;
4391 /* [hgm] holdings: next line makes all holdings squares light */
4392 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4394 return square_color;
4397 void DrawSquare(row, column, piece, do_flash)
4398 int row, column, do_flash;
4401 int square_color, x, y, direction, font_ascent, font_descent;
4404 XCharStruct overall;
4408 /* Calculate delay in milliseconds (2-delays per complete flash) */
4409 flash_delay = 500 / appData.flashRate;
4412 x = lineGap + ((BOARD_WIDTH-1)-column) *
4413 (squareSize + lineGap);
4414 y = lineGap + row * (squareSize + lineGap);
4416 x = lineGap + column * (squareSize + lineGap);
4417 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4418 (squareSize + lineGap);
4421 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4423 square_color = SquareColor(row, column);
4425 if ( // [HGM] holdings: blank out area between board and holdings
4426 column == BOARD_LEFT-1 || column == BOARD_RGHT
4427 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4428 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4429 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4431 // [HGM] print piece counts next to holdings
4432 string[1] = NULLCHAR;
4433 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4434 string[0] = '0' + piece;
4435 XTextExtents(countFontStruct, string, 1, &direction,
4436 &font_ascent, &font_descent, &overall);
4437 if (appData.monoMode) {
4438 XDrawImageString(xDisplay, xBoardWindow, countGC,
4439 x + squareSize - overall.width - 2,
4440 y + font_ascent + 1, string, 1);
4442 XDrawString(xDisplay, xBoardWindow, countGC,
4443 x + squareSize - overall.width - 2,
4444 y + font_ascent + 1, string, 1);
4447 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4448 string[0] = '0' + piece;
4449 XTextExtents(countFontStruct, string, 1, &direction,
4450 &font_ascent, &font_descent, &overall);
4451 if (appData.monoMode) {
4452 XDrawImageString(xDisplay, xBoardWindow, countGC,
4453 x + 2, y + font_ascent + 1, string, 1);
4455 XDrawString(xDisplay, xBoardWindow, countGC,
4456 x + 2, y + font_ascent + 1, string, 1);
4460 if (piece == EmptySquare || appData.blindfold) {
4461 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4463 drawfunc = ChooseDrawFunc();
4465 if (do_flash && appData.flashCount > 0) {
4466 for (i=0; i<appData.flashCount; ++i) {
4467 drawfunc(piece, square_color, x, y, xBoardWindow);
4468 XSync(xDisplay, False);
4469 do_flash_delay(flash_delay);
4471 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4472 XSync(xDisplay, False);
4473 do_flash_delay(flash_delay);
4476 drawfunc(piece, square_color, x, y, xBoardWindow);
4480 string[1] = NULLCHAR;
4481 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4482 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4483 string[0] = 'a' + column - BOARD_LEFT;
4484 XTextExtents(coordFontStruct, string, 1, &direction,
4485 &font_ascent, &font_descent, &overall);
4486 if (appData.monoMode) {
4487 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4488 x + squareSize - overall.width - 2,
4489 y + squareSize - font_descent - 1, string, 1);
4491 XDrawString(xDisplay, xBoardWindow, coordGC,
4492 x + squareSize - overall.width - 2,
4493 y + squareSize - font_descent - 1, string, 1);
4496 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4497 string[0] = ONE + row;
4498 XTextExtents(coordFontStruct, string, 1, &direction,
4499 &font_ascent, &font_descent, &overall);
4500 if (appData.monoMode) {
4501 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4502 x + 2, y + font_ascent + 1, string, 1);
4504 XDrawString(xDisplay, xBoardWindow, coordGC,
4505 x + 2, y + font_ascent + 1, string, 1);
4508 if(!partnerUp && marker[row][column]) {
4509 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4510 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4515 /* Why is this needed on some versions of X? */
4516 void EventProc(widget, unused, event)
4521 if (!XtIsRealized(widget))
4524 switch (event->type) {
4526 if (event->xexpose.count > 0) return; /* no clipping is done */
4527 XDrawPosition(widget, True, NULL);
4528 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4529 flipView = !flipView; partnerUp = !partnerUp;
4530 XDrawPosition(widget, True, NULL);
4531 flipView = !flipView; partnerUp = !partnerUp;
4535 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4542 void DrawPosition(fullRedraw, board)
4543 /*Boolean*/int fullRedraw;
4546 XDrawPosition(boardWidget, fullRedraw, board);
4549 /* Returns 1 if there are "too many" differences between b1 and b2
4550 (i.e. more than 1 move was made) */
4551 static int too_many_diffs(b1, b2)
4557 for (i=0; i<BOARD_HEIGHT; ++i) {
4558 for (j=0; j<BOARD_WIDTH; ++j) {
4559 if (b1[i][j] != b2[i][j]) {
4560 if (++c > 4) /* Castling causes 4 diffs */
4568 /* Matrix describing castling maneuvers */
4569 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4570 static int castling_matrix[4][5] = {
4571 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4572 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4573 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4574 { 7, 7, 4, 5, 6 } /* 0-0, black */
4577 /* Checks whether castling occurred. If it did, *rrow and *rcol
4578 are set to the destination (row,col) of the rook that moved.
4580 Returns 1 if castling occurred, 0 if not.
4582 Note: Only handles a max of 1 castling move, so be sure
4583 to call too_many_diffs() first.
4585 static int check_castle_draw(newb, oldb, rrow, rcol)
4592 /* For each type of castling... */
4593 for (i=0; i<4; ++i) {
4594 r = castling_matrix[i];
4596 /* Check the 4 squares involved in the castling move */
4598 for (j=1; j<=4; ++j) {
4599 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4606 /* All 4 changed, so it must be a castling move */
4615 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4616 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4618 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4621 void DrawSeekBackground( int left, int top, int right, int bottom )
4623 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4626 void DrawSeekText(char *buf, int x, int y)
4628 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4631 void DrawSeekDot(int x, int y, int colorNr)
4633 int square = colorNr & 0x80;
4636 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4638 XFillRectangle(xDisplay, xBoardWindow, color,
4639 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4641 XFillArc(xDisplay, xBoardWindow, color,
4642 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4645 static int damage[2][BOARD_RANKS][BOARD_FILES];
4648 * event handler for redrawing the board
4650 void XDrawPosition(w, repaint, board)
4652 /*Boolean*/int repaint;
4656 static int lastFlipView = 0;
4657 static int lastBoardValid[2] = {0, 0};
4658 static Board lastBoard[2];
4661 int nr = twoBoards*partnerUp;
4663 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4665 if (board == NULL) {
4666 if (!lastBoardValid[nr]) return;
4667 board = lastBoard[nr];
4669 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4670 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4671 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4676 * It would be simpler to clear the window with XClearWindow()
4677 * but this causes a very distracting flicker.
4680 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4682 if ( lineGap && IsDrawArrowEnabled())
4683 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4684 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4686 /* If too much changes (begin observing new game, etc.), don't
4688 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4690 /* Special check for castling so we don't flash both the king
4691 and the rook (just flash the king). */
4693 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4694 /* Draw rook with NO flashing. King will be drawn flashing later */
4695 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4696 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4700 /* First pass -- Draw (newly) empty squares and repair damage.
4701 This prevents you from having a piece show up twice while it
4702 is flashing on its new square */
4703 for (i = 0; i < BOARD_HEIGHT; i++)
4704 for (j = 0; j < BOARD_WIDTH; j++)
4705 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4706 || damage[nr][i][j]) {
4707 DrawSquare(i, j, board[i][j], 0);
4708 damage[nr][i][j] = False;
4711 /* Second pass -- Draw piece(s) in new position and flash them */
4712 for (i = 0; i < BOARD_HEIGHT; i++)
4713 for (j = 0; j < BOARD_WIDTH; j++)
4714 if (board[i][j] != lastBoard[nr][i][j]) {
4715 DrawSquare(i, j, board[i][j], do_flash);
4719 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4720 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4721 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4723 for (i = 0; i < BOARD_HEIGHT; i++)
4724 for (j = 0; j < BOARD_WIDTH; j++) {
4725 DrawSquare(i, j, board[i][j], 0);
4726 damage[nr][i][j] = False;
4730 CopyBoard(lastBoard[nr], board);
4731 lastBoardValid[nr] = 1;
4732 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4733 lastFlipView = flipView;
4735 /* Draw highlights */
4736 if (pm1X >= 0 && pm1Y >= 0) {
4737 drawHighlight(pm1X, pm1Y, prelineGC);
4739 if (pm2X >= 0 && pm2Y >= 0) {
4740 drawHighlight(pm2X, pm2Y, prelineGC);
4742 if (hi1X >= 0 && hi1Y >= 0) {
4743 drawHighlight(hi1X, hi1Y, highlineGC);
4745 if (hi2X >= 0 && hi2Y >= 0) {
4746 drawHighlight(hi2X, hi2Y, highlineGC);
4748 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4750 /* If piece being dragged around board, must redraw that too */
4753 XSync(xDisplay, False);
4758 * event handler for redrawing the board
4760 void DrawPositionProc(w, event, prms, nprms)
4766 XDrawPosition(w, True, NULL);
4771 * event handler for parsing user moves
4773 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4774 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4775 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4776 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4777 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4778 // and at the end FinishMove() to perform the move after optional promotion popups.
4779 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4780 void HandleUserMove(w, event, prms, nprms)
4786 if (w != boardWidget || errorExitStatus != -1) return;
4787 if(nprms) shiftKey = !strcmp(prms[0], "1");
4790 if (event->type == ButtonPress) {
4791 XtPopdown(promotionShell);
4792 XtDestroyWidget(promotionShell);
4793 promotionUp = False;
4801 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4802 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4803 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4806 void AnimateUserMove (Widget w, XEvent * event,
4807 String * params, Cardinal * nParams)
4809 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4810 DragPieceMove(event->xmotion.x, event->xmotion.y);
4813 void HandlePV (Widget w, XEvent * event,
4814 String * params, Cardinal * nParams)
4815 { // [HGM] pv: walk PV
4816 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4819 static int savedIndex; /* gross that this is global */
4821 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4824 XawTextPosition index, dummy;
4827 XawTextGetSelectionPos(w, &index, &dummy);
4828 XtSetArg(arg, XtNstring, &val);
4829 XtGetValues(w, &arg, 1);
4830 ReplaceComment(savedIndex, val);
4831 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4832 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4835 void EditCommentPopUp(index, title, text)
4840 if (text == NULL) text = "";
4841 NewCommentPopup(title, text, index);
4844 void ICSInputBoxPopUp()
4849 extern Option boxOptions[];
4851 void ICSInputSendText()
4858 edit = boxOptions[0].handle;
4860 XtSetArg(args[j], XtNstring, &val); j++;
4861 XtGetValues(edit, args, j);
4863 SendMultiLineToICS(val);
4864 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4865 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4868 void ICSInputBoxPopDown()
4873 void CommentPopUp(title, text)
4876 savedIndex = currentMove; // [HGM] vari
4877 NewCommentPopup(title, text, currentMove);
4880 void CommentPopDown()
4885 void FileNamePopUp(label, def, filter, proc, openMode)
4892 fileProc = proc; /* I can't see a way not */
4893 fileOpenMode = openMode; /* to use globals here */
4894 { // [HGM] use file-selector dialog stolen from Ghostview
4896 int index; // this is not supported yet
4898 if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
4899 (def[0] ? def : NULL), filter, openMode, NULL, &name))
4900 (void) (*fileProc)(f, index=0, name);
4904 void FileNamePopDown()
4906 if (!filenameUp) return;
4907 XtPopdown(fileNameShell);
4908 XtDestroyWidget(fileNameShell);
4913 void FileNameCallback(w, client_data, call_data)
4915 XtPointer client_data, call_data;
4920 XtSetArg(args[0], XtNlabel, &name);
4921 XtGetValues(w, args, 1);
4923 if (strcmp(name, _("cancel")) == 0) {
4928 FileNameAction(w, NULL, NULL, NULL);
4931 void FileNameAction(w, event, prms, nprms)
4943 name = XawDialogGetValueString(w = XtParent(w));
4945 if ((name != NULL) && (*name != NULLCHAR)) {
4946 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
4947 XtPopdown(w = XtParent(XtParent(w)));
4951 p = strrchr(buf, ' ');
4958 fullname = ExpandPathName(buf);
4960 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
4963 f = fopen(fullname, fileOpenMode);
4965 DisplayError(_("Failed to open file"), errno);
4967 (void) (*fileProc)(f, index, buf);
4974 XtPopdown(w = XtParent(XtParent(w)));
4980 void PromotionPopUp()
4983 Widget dialog, layout;
4985 Dimension bw_width, pw_width;
4989 XtSetArg(args[j], XtNwidth, &bw_width); j++;
4990 XtGetValues(boardWidget, args, j);
4993 XtSetArg(args[j], XtNresizable, True); j++;
4994 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
4996 XtCreatePopupShell("Promotion", transientShellWidgetClass,
4997 shellWidget, args, j);
4999 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5000 layoutArgs, XtNumber(layoutArgs));
5003 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5004 XtSetArg(args[j], XtNborderWidth, 0); j++;
5005 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5008 if(gameInfo.variant != VariantShogi) {
5009 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5010 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback,
5011 (XtPointer) dialog);
5012 XawDialogAddButton(dialog, _("General"), PromotionCallback,
5013 (XtPointer) dialog);
5014 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback,
5015 (XtPointer) dialog);
5016 XawDialogAddButton(dialog, _("Captain"), PromotionCallback,
5017 (XtPointer) dialog);
5019 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
5020 (XtPointer) dialog);
5021 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
5022 (XtPointer) dialog);
5023 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
5024 (XtPointer) dialog);
5025 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
5026 (XtPointer) dialog);
5028 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5029 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
5030 gameInfo.variant == VariantGiveaway) {
5031 XawDialogAddButton(dialog, _("King"), PromotionCallback,
5032 (XtPointer) dialog);
5034 if(gameInfo.variant == VariantCapablanca ||
5035 gameInfo.variant == VariantGothic ||
5036 gameInfo.variant == VariantCapaRandom) {
5037 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5038 (XtPointer) dialog);
5039 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5040 (XtPointer) dialog);
5042 } else // [HGM] shogi
5044 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5045 (XtPointer) dialog);
5046 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5047 (XtPointer) dialog);
5049 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5050 (XtPointer) dialog);
5052 XtRealizeWidget(promotionShell);
5053 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5056 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5057 XtGetValues(promotionShell, args, j);
5059 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5060 lineGap + squareSize/3 +
5061 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5062 0 : 6*(squareSize + lineGap)), &x, &y);
5065 XtSetArg(args[j], XtNx, x); j++;
5066 XtSetArg(args[j], XtNy, y); j++;
5067 XtSetValues(promotionShell, args, j);
5069 XtPopup(promotionShell, XtGrabNone);
5074 void PromotionPopDown()
5076 if (!promotionUp) return;
5077 XtPopdown(promotionShell);
5078 XtDestroyWidget(promotionShell);
5079 promotionUp = False;
5082 void PromotionCallback(w, client_data, call_data)
5084 XtPointer client_data, call_data;
5090 XtSetArg(args[0], XtNlabel, &name);
5091 XtGetValues(w, args, 1);
5095 if (fromX == -1) return;
5097 if (strcmp(name, _("cancel")) == 0) {
5101 } else if (strcmp(name, _("Knight")) == 0) {
5103 } else if (strcmp(name, _("Promote")) == 0) {
5105 } else if (strcmp(name, _("Defer")) == 0) {
5108 promoChar = ToLower(name[0]);
5111 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5113 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5114 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5119 void ErrorCallback(w, client_data, call_data)
5121 XtPointer client_data, call_data;
5124 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5126 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5132 if (!errorUp) return;
5134 XtPopdown(errorShell);
5135 XtDestroyWidget(errorShell);
5136 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5139 void ErrorPopUp(title, label, modal)
5140 char *title, *label;
5144 Widget dialog, layout;
5148 Dimension bw_width, pw_width;
5149 Dimension pw_height;
5153 XtSetArg(args[i], XtNresizable, True); i++;
5154 XtSetArg(args[i], XtNtitle, title); i++;
5156 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5157 shellWidget, args, i);
5159 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5160 layoutArgs, XtNumber(layoutArgs));
5163 XtSetArg(args[i], XtNlabel, label); i++;
5164 XtSetArg(args[i], XtNborderWidth, 0); i++;
5165 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5168 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5170 XtRealizeWidget(errorShell);
5171 CatchDeleteWindow(errorShell, "ErrorPopDown");
5174 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5175 XtGetValues(boardWidget, args, i);
5177 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5178 XtSetArg(args[i], XtNheight, &pw_height); i++;
5179 XtGetValues(errorShell, args, i);
5182 /* This code seems to tickle an X bug if it is executed too soon
5183 after xboard starts up. The coordinates get transformed as if
5184 the main window was positioned at (0, 0).
5186 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5187 0 - pw_height + squareSize / 3, &x, &y);
5189 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5190 RootWindowOfScreen(XtScreen(boardWidget)),
5191 (bw_width - pw_width) / 2,
5192 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5196 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5199 XtSetArg(args[i], XtNx, x); i++;
5200 XtSetArg(args[i], XtNy, y); i++;
5201 XtSetValues(errorShell, args, i);
5204 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5207 /* Disable all user input other than deleting the window */
5208 static int frozen = 0;
5212 /* Grab by a widget that doesn't accept input */
5213 XtAddGrab(messageWidget, TRUE, FALSE);
5217 /* Undo a FreezeUI */
5220 if (!frozen) return;
5221 XtRemoveGrab(messageWidget);
5225 char *ModeToWidgetName(mode)
5229 case BeginningOfGame:
5230 if (appData.icsActive)
5231 return "menuMode.ICS Client";
5232 else if (appData.noChessProgram ||
5233 *appData.cmailGameName != NULLCHAR)
5234 return "menuMode.Edit Game";
5236 return "menuMode.Machine Black";
5237 case MachinePlaysBlack:
5238 return "menuMode.Machine Black";
5239 case MachinePlaysWhite:
5240 return "menuMode.Machine White";
5242 return "menuMode.Analysis Mode";
5244 return "menuMode.Analyze File";
5245 case TwoMachinesPlay:
5246 return "menuMode.Two Machines";
5248 return "menuMode.Edit Game";
5249 case PlayFromGameFile:
5250 return "menuFile.Load Game";
5252 return "menuMode.Edit Position";
5254 return "menuMode.Training";
5255 case IcsPlayingWhite:
5256 case IcsPlayingBlack:
5260 return "menuMode.ICS Client";
5267 void ModeHighlight()
5270 static int oldPausing = FALSE;
5271 static GameMode oldmode = (GameMode) -1;
5274 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5276 if (pausing != oldPausing) {
5277 oldPausing = pausing;
5279 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5281 XtSetArg(args[0], XtNleftBitmap, None);
5283 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5286 if (appData.showButtonBar) {
5287 /* Always toggle, don't set. Previous code messes up when
5288 invoked while the button is pressed, as releasing it
5289 toggles the state again. */
5292 XtSetArg(args[0], XtNbackground, &oldbg);
5293 XtSetArg(args[1], XtNforeground, &oldfg);
5294 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5296 XtSetArg(args[0], XtNbackground, oldfg);
5297 XtSetArg(args[1], XtNforeground, oldbg);
5299 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5303 wname = ModeToWidgetName(oldmode);
5304 if (wname != NULL) {
5305 XtSetArg(args[0], XtNleftBitmap, None);
5306 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5308 wname = ModeToWidgetName(gameMode);
5309 if (wname != NULL) {
5310 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5311 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5314 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5315 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5317 /* Maybe all the enables should be handled here, not just this one */
5318 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5319 gameMode == Training || gameMode == PlayFromGameFile);
5324 * Button/menu procedures
5326 void ResetProc(w, event, prms, nprms)
5335 int LoadGamePopUp(f, gameNumber, title)
5340 cmailMsgLoaded = FALSE;
5341 if (gameNumber == 0) {
5342 int error = GameListBuild(f);
5344 DisplayError(_("Cannot build game list"), error);
5345 } else if (!ListEmpty(&gameList) &&
5346 ((ListGame *) gameList.tailPred)->number > 1) {
5347 GameListPopUp(f, title);
5353 return LoadGame(f, gameNumber, title, FALSE);
5356 void LoadGameProc(w, event, prms, nprms)
5362 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5365 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5368 void LoadNextGameProc(w, event, prms, nprms)
5377 void LoadPrevGameProc(w, event, prms, nprms)
5386 void ReloadGameProc(w, event, prms, nprms)
5395 void LoadNextPositionProc(w, event, prms, nprms)
5404 void LoadPrevPositionProc(w, event, prms, nprms)
5413 void ReloadPositionProc(w, event, prms, nprms)
5422 void LoadPositionProc(w, event, prms, nprms)
5428 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5431 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5434 void SaveGameProc(w, event, prms, nprms)
5440 FileNamePopUp(_("Save game file name?"),
5441 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5442 appData.oldSaveStyle ? ".game" : ".pgn",
5446 void SavePositionProc(w, event, prms, nprms)
5452 FileNamePopUp(_("Save position file name?"),
5453 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5454 appData.oldSaveStyle ? ".pos" : ".fen",
5458 void ReloadCmailMsgProc(w, event, prms, nprms)
5464 ReloadCmailMsgEvent(FALSE);
5467 void MailMoveProc(w, event, prms, nprms)
5476 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5477 char *selected_fen_position=NULL;
5480 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5481 Atom *type_return, XtPointer *value_return,
5482 unsigned long *length_return, int *format_return)
5484 char *selection_tmp;
5486 if (!selected_fen_position) return False; /* should never happen */
5487 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5488 /* note: since no XtSelectionDoneProc was registered, Xt will
5489 * automatically call XtFree on the value returned. So have to
5490 * make a copy of it allocated with XtMalloc */
5491 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5492 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5494 *value_return=selection_tmp;
5495 *length_return=strlen(selection_tmp);
5496 *type_return=*target;
5497 *format_return = 8; /* bits per byte */
5499 } else if (*target == XA_TARGETS(xDisplay)) {
5500 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5501 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5502 targets_tmp[1] = XA_STRING;
5503 *value_return = targets_tmp;
5504 *type_return = XA_ATOM;
5506 *format_return = 8 * sizeof(Atom);
5507 if (*format_return > 32) {
5508 *length_return *= *format_return / 32;
5509 *format_return = 32;
5517 /* note: when called from menu all parameters are NULL, so no clue what the
5518 * Widget which was clicked on was, or what the click event was
5520 void CopyPositionProc(w, event, prms, nprms)
5527 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5528 * have a notion of a position that is selected but not copied.
5529 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5531 if(gameMode == EditPosition) EditPositionDone(TRUE);
5532 if (selected_fen_position) free(selected_fen_position);
5533 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5534 if (!selected_fen_position) return;
5535 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5537 SendPositionSelection,
5538 NULL/* lose_ownership_proc */ ,
5539 NULL/* transfer_done_proc */);
5540 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5542 SendPositionSelection,
5543 NULL/* lose_ownership_proc */ ,
5544 NULL/* transfer_done_proc */);
5547 /* function called when the data to Paste is ready */
5549 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5550 Atom *type, XtPointer value, unsigned long *len, int *format)
5553 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5554 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5555 EditPositionPasteFEN(fenstr);
5559 /* called when Paste Position button is pressed,
5560 * all parameters will be NULL */
5561 void PastePositionProc(w, event, prms, nprms)
5567 XtGetSelectionValue(menuBarWidget,
5568 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5569 /* (XtSelectionCallbackProc) */ PastePositionCB,
5570 NULL, /* client_data passed to PastePositionCB */
5572 /* better to use the time field from the event that triggered the
5573 * call to this function, but that isn't trivial to get
5581 SendGameSelection(Widget w, Atom *selection, Atom *target,
5582 Atom *type_return, XtPointer *value_return,
5583 unsigned long *length_return, int *format_return)
5585 char *selection_tmp;
5587 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5588 FILE* f = fopen(gameCopyFilename, "r");
5591 if (f == NULL) return False;
5595 selection_tmp = XtMalloc(len + 1);
5596 count = fread(selection_tmp, 1, len, f);
5599 XtFree(selection_tmp);
5602 selection_tmp[len] = NULLCHAR;
5603 *value_return = selection_tmp;
5604 *length_return = len;
5605 *type_return = *target;
5606 *format_return = 8; /* bits per byte */
5608 } else if (*target == XA_TARGETS(xDisplay)) {
5609 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5610 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5611 targets_tmp[1] = XA_STRING;
5612 *value_return = targets_tmp;
5613 *type_return = XA_ATOM;
5615 *format_return = 8 * sizeof(Atom);
5616 if (*format_return > 32) {
5617 *length_return *= *format_return / 32;
5618 *format_return = 32;
5626 void CopySomething()
5631 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5632 * have a notion of a game that is selected but not copied.
5633 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5635 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5638 NULL/* lose_ownership_proc */ ,
5639 NULL/* transfer_done_proc */);
5640 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5643 NULL/* lose_ownership_proc */ ,
5644 NULL/* transfer_done_proc */);
5647 /* note: when called from menu all parameters are NULL, so no clue what the
5648 * Widget which was clicked on was, or what the click event was
5650 void CopyGameProc(w, event, prms, nprms)
5658 ret = SaveGameToFile(gameCopyFilename, FALSE);
5664 void CopyGameListProc(w, event, prms, nprms)
5670 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5674 /* function called when the data to Paste is ready */
5676 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5677 Atom *type, XtPointer value, unsigned long *len, int *format)
5680 if (value == NULL || *len == 0) {
5681 return; /* nothing had been selected to copy */
5683 f = fopen(gamePasteFilename, "w");
5685 DisplayError(_("Can't open temp file"), errno);
5688 fwrite(value, 1, *len, f);
5691 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5694 /* called when Paste Game button is pressed,
5695 * all parameters will be NULL */
5696 void PasteGameProc(w, event, prms, nprms)
5702 XtGetSelectionValue(menuBarWidget,
5703 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5704 /* (XtSelectionCallbackProc) */ PasteGameCB,
5705 NULL, /* client_data passed to PasteGameCB */
5707 /* better to use the time field from the event that triggered the
5708 * call to this function, but that isn't trivial to get
5718 SaveGameProc(NULL, NULL, NULL, NULL);
5722 void QuitProc(w, event, prms, nprms)
5731 void PauseProc(w, event, prms, nprms)
5741 void MachineBlackProc(w, event, prms, nprms)
5747 MachineBlackEvent();
5750 void MachineWhiteProc(w, event, prms, nprms)
5756 MachineWhiteEvent();
5759 void AnalyzeModeProc(w, event, prms, nprms)
5767 if (!first.analysisSupport) {
5768 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5769 DisplayError(buf, 0);
5772 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5773 if (appData.icsActive) {
5774 if (gameMode != IcsObserving) {
5775 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5776 DisplayError(buf, 0);
5778 if (appData.icsEngineAnalyze) {
5779 if (appData.debugMode)
5780 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5786 /* if enable, use want disable icsEngineAnalyze */
5787 if (appData.icsEngineAnalyze) {
5792 appData.icsEngineAnalyze = TRUE;
5793 if (appData.debugMode)
5794 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5796 #ifndef OPTIONSDIALOG
5797 if (!appData.showThinking)
5798 ShowThinkingProc(w,event,prms,nprms);
5804 void AnalyzeFileProc(w, event, prms, nprms)
5810 if (!first.analysisSupport) {
5812 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5813 DisplayError(buf, 0);
5817 #ifndef OPTIONSDIALOG
5818 if (!appData.showThinking)
5819 ShowThinkingProc(w,event,prms,nprms);
5822 FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5823 AnalysisPeriodicEvent(1);
5826 void TwoMachinesProc(w, event, prms, nprms)
5835 void MatchProc(w, event, prms, nprms)
5844 void IcsClientProc(w, event, prms, nprms)
5853 void EditGameProc(w, event, prms, nprms)
5862 void EditPositionProc(w, event, prms, nprms)
5868 EditPositionEvent();
5871 void TrainingProc(w, event, prms, nprms)
5880 void EditCommentProc(w, event, prms, nprms)
5888 if (PopDown(1)) { // popdown succesful
5890 XtSetArg(args[j], XtNleftBitmap, None); j++;
5891 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
5892 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
5893 } else // was not up
5897 void IcsInputBoxProc(w, event, prms, nprms)
5903 if (!PopDown(4)) ICSInputBoxPopUp();
5906 void AcceptProc(w, event, prms, nprms)
5915 void DeclineProc(w, event, prms, nprms)
5924 void RematchProc(w, event, prms, nprms)
5933 void CallFlagProc(w, event, prms, nprms)
5942 void DrawProc(w, event, prms, nprms)
5951 void AbortProc(w, event, prms, nprms)
5960 void AdjournProc(w, event, prms, nprms)
5969 void ResignProc(w, event, prms, nprms)
5978 void AdjuWhiteProc(w, event, prms, nprms)
5984 UserAdjudicationEvent(+1);
5987 void AdjuBlackProc(w, event, prms, nprms)
5993 UserAdjudicationEvent(-1);
5996 void AdjuDrawProc(w, event, prms, nprms)
6002 UserAdjudicationEvent(0);
6005 void EnterKeyProc(w, event, prms, nprms)
6011 if (shellUp[4] == True)
6015 void UpKeyProc(w, event, prms, nprms)
6020 { // [HGM] input: let up-arrow recall previous line from history
6027 if (!shellUp[4]) return;
6028 edit = boxOptions[0].handle;
6030 XtSetArg(args[j], XtNstring, &val); j++;
6031 XtGetValues(edit, args, j);
6032 val = PrevInHistory(val);
6033 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6034 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6036 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6037 XawTextReplace(edit, 0, 0, &t);
6038 XawTextSetInsertionPoint(edit, 9999);
6042 void DownKeyProc(w, event, prms, nprms)
6047 { // [HGM] input: let down-arrow recall next line from history
6052 if (!shellUp[4]) return;
6053 edit = boxOptions[0].handle;
6054 val = NextInHistory();
6055 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6056 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6058 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6059 XawTextReplace(edit, 0, 0, &t);
6060 XawTextSetInsertionPoint(edit, 9999);
6064 void StopObservingProc(w, event, prms, nprms)
6070 StopObservingEvent();
6073 void StopExaminingProc(w, event, prms, nprms)
6079 StopExaminingEvent();
6082 void UploadProc(w, event, prms, nprms)
6092 void ForwardProc(w, event, prms, nprms)
6102 void BackwardProc(w, event, prms, nprms)
6111 void ToStartProc(w, event, prms, nprms)
6120 void ToEndProc(w, event, prms, nprms)
6129 void RevertProc(w, event, prms, nprms)
6138 void AnnotateProc(w, event, prms, nprms)
6147 void TruncateGameProc(w, event, prms, nprms)
6153 TruncateGameEvent();
6155 void RetractMoveProc(w, event, prms, nprms)
6164 void MoveNowProc(w, event, prms, nprms)
6173 void FlipViewProc(w, event, prms, nprms)
6179 flipView = !flipView;
6180 DrawPosition(True, NULL);
6183 void PonderNextMoveProc(w, event, prms, nprms)
6191 PonderNextMoveEvent(!appData.ponderNextMove);
6192 #ifndef OPTIONSDIALOG
6193 if (appData.ponderNextMove) {
6194 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6196 XtSetArg(args[0], XtNleftBitmap, None);
6198 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6203 #ifndef OPTIONSDIALOG
6204 void AlwaysQueenProc(w, event, prms, nprms)
6212 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6214 if (appData.alwaysPromoteToQueen) {
6215 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6217 XtSetArg(args[0], XtNleftBitmap, None);
6219 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6223 void AnimateDraggingProc(w, event, prms, nprms)
6231 appData.animateDragging = !appData.animateDragging;
6233 if (appData.animateDragging) {
6234 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6237 XtSetArg(args[0], XtNleftBitmap, None);
6239 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6243 void AnimateMovingProc(w, event, prms, nprms)
6251 appData.animate = !appData.animate;
6253 if (appData.animate) {
6254 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6257 XtSetArg(args[0], XtNleftBitmap, None);
6259 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6263 void AutoflagProc(w, event, prms, nprms)
6271 appData.autoCallFlag = !appData.autoCallFlag;
6273 if (appData.autoCallFlag) {
6274 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6276 XtSetArg(args[0], XtNleftBitmap, None);
6278 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6282 void AutoflipProc(w, event, prms, nprms)
6290 appData.autoFlipView = !appData.autoFlipView;
6292 if (appData.autoFlipView) {
6293 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6295 XtSetArg(args[0], XtNleftBitmap, None);
6297 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6301 void BlindfoldProc(w, event, prms, nprms)
6309 appData.blindfold = !appData.blindfold;
6311 if (appData.blindfold) {
6312 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6314 XtSetArg(args[0], XtNleftBitmap, None);
6316 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6319 DrawPosition(True, NULL);
6322 void TestLegalityProc(w, event, prms, nprms)
6330 appData.testLegality = !appData.testLegality;
6332 if (appData.testLegality) {
6333 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6335 XtSetArg(args[0], XtNleftBitmap, None);
6337 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6342 void FlashMovesProc(w, event, prms, nprms)
6350 if (appData.flashCount == 0) {
6351 appData.flashCount = 3;
6353 appData.flashCount = -appData.flashCount;
6356 if (appData.flashCount > 0) {
6357 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6359 XtSetArg(args[0], XtNleftBitmap, None);
6361 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6366 void HighlightDraggingProc(w, event, prms, nprms)
6374 appData.highlightDragging = !appData.highlightDragging;
6376 if (appData.highlightDragging) {
6377 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6379 XtSetArg(args[0], XtNleftBitmap, None);
6381 XtSetValues(XtNameToWidget(menuBarWidget,
6382 "menuOptions.Highlight Dragging"), args, 1);
6386 void HighlightLastMoveProc(w, event, prms, nprms)
6394 appData.highlightLastMove = !appData.highlightLastMove;
6396 if (appData.highlightLastMove) {
6397 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6399 XtSetArg(args[0], XtNleftBitmap, None);
6401 XtSetValues(XtNameToWidget(menuBarWidget,
6402 "menuOptions.Highlight Last Move"), args, 1);
6405 void HighlightArrowProc(w, event, prms, nprms)
6413 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6415 if (appData.highlightMoveWithArrow) {
6416 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6418 XtSetArg(args[0], XtNleftBitmap, None);
6420 XtSetValues(XtNameToWidget(menuBarWidget,
6421 "menuOptions.Arrow"), args, 1);
6425 void IcsAlarmProc(w, event, prms, nprms)
6433 appData.icsAlarm = !appData.icsAlarm;
6435 if (appData.icsAlarm) {
6436 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6438 XtSetArg(args[0], XtNleftBitmap, None);
6440 XtSetValues(XtNameToWidget(menuBarWidget,
6441 "menuOptions.ICS Alarm"), args, 1);
6445 void MoveSoundProc(w, event, prms, nprms)
6453 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6455 if (appData.ringBellAfterMoves) {
6456 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6458 XtSetArg(args[0], XtNleftBitmap, None);
6460 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6464 void OneClickProc(w, event, prms, nprms)
6472 appData.oneClick = !appData.oneClick;
6474 if (appData.oneClick) {
6475 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6477 XtSetArg(args[0], XtNleftBitmap, None);
6479 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6483 void PeriodicUpdatesProc(w, event, prms, nprms)
6491 PeriodicUpdatesEvent(!appData.periodicUpdates);
6493 if (appData.periodicUpdates) {
6494 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6496 XtSetArg(args[0], XtNleftBitmap, None);
6498 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6502 void PopupExitMessageProc(w, event, prms, nprms)
6510 appData.popupExitMessage = !appData.popupExitMessage;
6512 if (appData.popupExitMessage) {
6513 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6515 XtSetArg(args[0], XtNleftBitmap, None);
6517 XtSetValues(XtNameToWidget(menuBarWidget,
6518 "menuOptions.Popup Exit Message"), args, 1);
6521 void PopupMoveErrorsProc(w, event, prms, nprms)
6529 appData.popupMoveErrors = !appData.popupMoveErrors;
6531 if (appData.popupMoveErrors) {
6532 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6534 XtSetArg(args[0], XtNleftBitmap, None);
6536 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6541 void PremoveProc(w, event, prms, nprms)
6549 appData.premove = !appData.premove;
6551 if (appData.premove) {
6552 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6554 XtSetArg(args[0], XtNleftBitmap, None);
6556 XtSetValues(XtNameToWidget(menuBarWidget,
6557 "menuOptions.Premove"), args, 1);
6561 void ShowCoordsProc(w, event, prms, nprms)
6569 appData.showCoords = !appData.showCoords;
6571 if (appData.showCoords) {
6572 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6574 XtSetArg(args[0], XtNleftBitmap, None);
6576 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6579 DrawPosition(True, NULL);
6582 void ShowThinkingProc(w, event, prms, nprms)
6588 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6589 ShowThinkingEvent();
6592 void HideThinkingProc(w, event, prms, nprms)
6600 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6601 ShowThinkingEvent();
6603 if (appData.hideThinkingFromHuman) {
6604 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6606 XtSetArg(args[0], XtNleftBitmap, None);
6608 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6613 void SaveOnExitProc(w, event, prms, nprms)
6621 saveSettingsOnExit = !saveSettingsOnExit;
6623 if (saveSettingsOnExit) {
6624 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6626 XtSetArg(args[0], XtNleftBitmap, None);
6628 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6632 void SaveSettingsProc(w, event, prms, nprms)
6638 SaveSettings(settingsFileName);
6641 void InfoProc(w, event, prms, nprms)
6648 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6653 void ManProc(w, event, prms, nprms)
6661 if (nprms && *nprms > 0)
6665 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6669 void HintProc(w, event, prms, nprms)
6678 void BookProc(w, event, prms, nprms)
6687 void AboutProc(w, event, prms, nprms)
6695 char *zippy = " (with Zippy code)";
6699 snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
6700 programVersion, zippy,
6701 "Copyright 1991 Digital Equipment Corporation",
6702 "Enhancements Copyright 1992-2009 Free Software Foundation",
6703 "Enhancements Copyright 2005 Alessandro Scotti",
6704 PACKAGE, " is free software and carries NO WARRANTY;",
6705 "see the file COPYING for more information.");
6706 ErrorPopUp(_("About XBoard"), buf, FALSE);
6709 void DebugProc(w, event, prms, nprms)
6715 appData.debugMode = !appData.debugMode;
6718 void AboutGameProc(w, event, prms, nprms)
6727 void NothingProc(w, event, prms, nprms)
6736 void Iconify(w, event, prms, nprms)
6745 XtSetArg(args[0], XtNiconic, True);
6746 XtSetValues(shellWidget, args, 1);
6749 void DisplayMessage(message, extMessage)
6750 char *message, *extMessage;
6752 /* display a message in the message widget */
6761 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6766 message = extMessage;
6770 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6772 /* need to test if messageWidget already exists, since this function
6773 can also be called during the startup, if for example a Xresource
6774 is not set up correctly */
6777 XtSetArg(arg, XtNlabel, message);
6778 XtSetValues(messageWidget, &arg, 1);
6784 void DisplayTitle(text)
6789 char title[MSG_SIZ];
6792 if (text == NULL) text = "";
6794 if (appData.titleInWindow) {
6796 XtSetArg(args[i], XtNlabel, text); i++;
6797 XtSetValues(titleWidget, args, i);
6800 if (*text != NULLCHAR) {
6801 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6802 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6803 } else if (appData.icsActive) {
6804 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6805 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6806 } else if (appData.cmailGameName[0] != NULLCHAR) {
6807 snprintf(icon, sizeof(icon), "%s", "CMail");
6808 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6810 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6811 } else if (gameInfo.variant == VariantGothic) {
6812 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6813 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6816 } else if (gameInfo.variant == VariantFalcon) {
6817 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6818 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6820 } else if (appData.noChessProgram) {
6821 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6822 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6824 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6825 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6828 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6829 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6830 XtSetValues(shellWidget, args, i);
6835 DisplayError(message, error)
6842 if (appData.debugMode || appData.matchMode) {
6843 fprintf(stderr, "%s: %s\n", programName, message);
6846 if (appData.debugMode || appData.matchMode) {
6847 fprintf(stderr, "%s: %s: %s\n",
6848 programName, message, strerror(error));
6850 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6853 ErrorPopUp(_("Error"), message, FALSE);
6857 void DisplayMoveError(message)
6862 DrawPosition(FALSE, NULL);
6863 if (appData.debugMode || appData.matchMode) {
6864 fprintf(stderr, "%s: %s\n", programName, message);
6866 if (appData.popupMoveErrors) {
6867 ErrorPopUp(_("Error"), message, FALSE);
6869 DisplayMessage(message, "");
6874 void DisplayFatalError(message, error, status)
6880 errorExitStatus = status;
6882 fprintf(stderr, "%s: %s\n", programName, message);
6884 fprintf(stderr, "%s: %s: %s\n",
6885 programName, message, strerror(error));
6886 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6889 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6890 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6896 void DisplayInformation(message)
6900 ErrorPopUp(_("Information"), message, TRUE);
6903 void DisplayNote(message)
6907 ErrorPopUp(_("Note"), message, FALSE);
6911 NullXErrorCheck(dpy, error_event)
6913 XErrorEvent *error_event;
6918 void DisplayIcsInteractionTitle(message)
6921 if (oldICSInteractionTitle == NULL) {
6922 /* Magic to find the old window title, adapted from vim */
6923 char *wina = getenv("WINDOWID");
6925 Window win = (Window) atoi(wina);
6926 Window root, parent, *children;
6927 unsigned int nchildren;
6928 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6930 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6931 if (!XQueryTree(xDisplay, win, &root, &parent,
6932 &children, &nchildren)) break;
6933 if (children) XFree((void *)children);
6934 if (parent == root || parent == 0) break;
6937 XSetErrorHandler(oldHandler);
6939 if (oldICSInteractionTitle == NULL) {
6940 oldICSInteractionTitle = "xterm";
6943 printf("\033]0;%s\007", message);
6947 char pendingReplyPrefix[MSG_SIZ];
6948 ProcRef pendingReplyPR;
6950 void AskQuestionProc(w, event, prms, nprms)
6957 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
6961 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6964 void AskQuestionPopDown()
6966 if (!askQuestionUp) return;
6967 XtPopdown(askQuestionShell);
6968 XtDestroyWidget(askQuestionShell);
6969 askQuestionUp = False;
6972 void AskQuestionReplyAction(w, event, prms, nprms)
6982 reply = XawDialogGetValueString(w = XtParent(w));
6983 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
6984 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
6985 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
6986 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
6987 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6988 AskQuestionPopDown();
6990 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
6993 void AskQuestionCallback(w, client_data, call_data)
6995 XtPointer client_data, call_data;
7000 XtSetArg(args[0], XtNlabel, &name);
7001 XtGetValues(w, args, 1);
7003 if (strcmp(name, _("cancel")) == 0) {
7004 AskQuestionPopDown();
7006 AskQuestionReplyAction(w, NULL, NULL, NULL);
7010 void AskQuestion(title, question, replyPrefix, pr)
7011 char *title, *question, *replyPrefix;
7015 Widget popup, layout, dialog, edit;
7021 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7022 pendingReplyPR = pr;
7025 XtSetArg(args[i], XtNresizable, True); i++;
7026 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7027 askQuestionShell = popup =
7028 XtCreatePopupShell(title, transientShellWidgetClass,
7029 shellWidget, args, i);
7032 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7033 layoutArgs, XtNumber(layoutArgs));
7036 XtSetArg(args[i], XtNlabel, question); i++;
7037 XtSetArg(args[i], XtNvalue, ""); i++;
7038 XtSetArg(args[i], XtNborderWidth, 0); i++;
7039 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7042 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7043 (XtPointer) dialog);
7044 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7045 (XtPointer) dialog);
7047 XtRealizeWidget(popup);
7048 CatchDeleteWindow(popup, "AskQuestionPopDown");
7050 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7051 &x, &y, &win_x, &win_y, &mask);
7053 XtSetArg(args[0], XtNx, x - 10);
7054 XtSetArg(args[1], XtNy, y - 30);
7055 XtSetValues(popup, args, 2);
7057 XtPopup(popup, XtGrabExclusive);
7058 askQuestionUp = True;
7060 edit = XtNameToWidget(dialog, "*value");
7061 XtSetKeyboardFocus(popup, edit);
7069 if (*name == NULLCHAR) {
7071 } else if (strcmp(name, "$") == 0) {
7072 putc(BELLCHAR, stderr);
7075 char *prefix = "", *sep = "";
7076 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7077 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7085 PlaySound(appData.soundMove);
7091 PlaySound(appData.soundIcsWin);
7097 PlaySound(appData.soundIcsLoss);
7103 PlaySound(appData.soundIcsDraw);
7107 PlayIcsUnfinishedSound()
7109 PlaySound(appData.soundIcsUnfinished);
7115 PlaySound(appData.soundIcsAlarm);
7121 system("stty echo");
7127 system("stty -echo");
7131 Colorize(cc, continuation)
7136 int count, outCount, error;
7138 if (textColors[(int)cc].bg > 0) {
7139 if (textColors[(int)cc].fg > 0) {
7140 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7141 textColors[(int)cc].fg, textColors[(int)cc].bg);
7143 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7144 textColors[(int)cc].bg);
7147 if (textColors[(int)cc].fg > 0) {
7148 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7149 textColors[(int)cc].fg);
7151 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7154 count = strlen(buf);
7155 outCount = OutputToProcess(NoProc, buf, count, &error);
7156 if (outCount < count) {
7157 DisplayFatalError(_("Error writing to display"), error, 1);
7160 if (continuation) return;
7163 PlaySound(appData.soundShout);
7166 PlaySound(appData.soundSShout);
7169 PlaySound(appData.soundChannel1);
7172 PlaySound(appData.soundChannel);
7175 PlaySound(appData.soundKibitz);
7178 PlaySound(appData.soundTell);
7180 case ColorChallenge:
7181 PlaySound(appData.soundChallenge);
7184 PlaySound(appData.soundRequest);
7187 PlaySound(appData.soundSeek);
7198 return getpwuid(getuid())->pw_name;
7202 ExpandPathName(path)
7205 static char static_buf[4*MSG_SIZ];
7206 char *d, *s, buf[4*MSG_SIZ];
7212 while (*s && isspace(*s))
7221 if (*(s+1) == '/') {
7222 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7226 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7227 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7228 pwd = getpwnam(buf);
7231 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7235 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7236 strcat(d, strchr(s+1, '/'));
7240 safeStrCpy(d, s, 4*MSG_SIZ );
7247 static char host_name[MSG_SIZ];
7249 #if HAVE_GETHOSTNAME
7250 gethostname(host_name, MSG_SIZ);
7252 #else /* not HAVE_GETHOSTNAME */
7253 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7254 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7256 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7258 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7259 #endif /* not HAVE_GETHOSTNAME */
7262 XtIntervalId delayedEventTimerXID = 0;
7263 DelayedEventCallback delayedEventCallback = 0;
7268 delayedEventTimerXID = 0;
7269 delayedEventCallback();
7273 ScheduleDelayedEvent(cb, millisec)
7274 DelayedEventCallback cb; long millisec;
7276 if(delayedEventTimerXID && delayedEventCallback == cb)
7277 // [HGM] alive: replace, rather than add or flush identical event
7278 XtRemoveTimeOut(delayedEventTimerXID);
7279 delayedEventCallback = cb;
7280 delayedEventTimerXID =
7281 XtAppAddTimeOut(appContext, millisec,
7282 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7285 DelayedEventCallback
7288 if (delayedEventTimerXID) {
7289 return delayedEventCallback;
7296 CancelDelayedEvent()
7298 if (delayedEventTimerXID) {
7299 XtRemoveTimeOut(delayedEventTimerXID);
7300 delayedEventTimerXID = 0;
7304 XtIntervalId loadGameTimerXID = 0;
7306 int LoadGameTimerRunning()
7308 return loadGameTimerXID != 0;
7311 int StopLoadGameTimer()
7313 if (loadGameTimerXID != 0) {
7314 XtRemoveTimeOut(loadGameTimerXID);
7315 loadGameTimerXID = 0;
7323 LoadGameTimerCallback(arg, id)
7327 loadGameTimerXID = 0;
7332 StartLoadGameTimer(millisec)
7336 XtAppAddTimeOut(appContext, millisec,
7337 (XtTimerCallbackProc) LoadGameTimerCallback,
7341 XtIntervalId analysisClockXID = 0;
7344 AnalysisClockCallback(arg, id)
7348 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7349 || appData.icsEngineAnalyze) { // [DM]
7350 AnalysisPeriodicEvent(0);
7351 StartAnalysisClock();
7356 StartAnalysisClock()
7359 XtAppAddTimeOut(appContext, 2000,
7360 (XtTimerCallbackProc) AnalysisClockCallback,
7364 XtIntervalId clockTimerXID = 0;
7366 int ClockTimerRunning()
7368 return clockTimerXID != 0;
7371 int StopClockTimer()
7373 if (clockTimerXID != 0) {
7374 XtRemoveTimeOut(clockTimerXID);
7383 ClockTimerCallback(arg, id)
7392 StartClockTimer(millisec)
7396 XtAppAddTimeOut(appContext, millisec,
7397 (XtTimerCallbackProc) ClockTimerCallback,
7402 DisplayTimerLabel(w, color, timer, highlight)
7411 /* check for low time warning */
7412 Pixel foregroundOrWarningColor = timerForegroundPixel;
7415 appData.lowTimeWarning &&
7416 (timer / 1000) < appData.icsAlarmTime)
7417 foregroundOrWarningColor = lowTimeWarningColor;
7419 if (appData.clockMode) {
7420 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7421 XtSetArg(args[0], XtNlabel, buf);
7423 snprintf(buf, MSG_SIZ, "%s ", color);
7424 XtSetArg(args[0], XtNlabel, buf);
7429 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7430 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7432 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7433 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7436 XtSetValues(w, args, 3);
7440 DisplayWhiteClock(timeRemaining, highlight)
7446 if(appData.noGUI) return;
7447 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7448 if (highlight && iconPixmap == bIconPixmap) {
7449 iconPixmap = wIconPixmap;
7450 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7451 XtSetValues(shellWidget, args, 1);
7456 DisplayBlackClock(timeRemaining, highlight)
7462 if(appData.noGUI) return;
7463 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7464 if (highlight && iconPixmap == wIconPixmap) {
7465 iconPixmap = bIconPixmap;
7466 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7467 XtSetValues(shellWidget, args, 1);
7485 int StartChildProcess(cmdLine, dir, pr)
7492 int to_prog[2], from_prog[2];
7496 if (appData.debugMode) {
7497 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7500 /* We do NOT feed the cmdLine to the shell; we just
7501 parse it into blank-separated arguments in the
7502 most simple-minded way possible.
7505 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7508 while(*p == ' ') p++;
7510 if(*p == '"' || *p == '\'')
7511 p = strchr(++argv[i-1], *p);
7512 else p = strchr(p, ' ');
7513 if (p == NULL) break;
7518 SetUpChildIO(to_prog, from_prog);
7520 if ((pid = fork()) == 0) {
7522 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7523 close(to_prog[1]); // first close the unused pipe ends
7524 close(from_prog[0]);
7525 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7526 dup2(from_prog[1], 1);
7527 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7528 close(from_prog[1]); // and closing again loses one of the pipes!
7529 if(fileno(stderr) >= 2) // better safe than sorry...
7530 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7532 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7537 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7539 execvp(argv[0], argv);
7541 /* If we get here, exec failed */
7546 /* Parent process */
7548 close(from_prog[1]);
7550 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7553 cp->fdFrom = from_prog[0];
7554 cp->fdTo = to_prog[1];
7559 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7560 static RETSIGTYPE AlarmCallBack(int n)
7566 DestroyChildProcess(pr, signalType)
7570 ChildProc *cp = (ChildProc *) pr;
7572 if (cp->kind != CPReal) return;
7574 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7575 signal(SIGALRM, AlarmCallBack);
7577 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7578 kill(cp->pid, SIGKILL); // kill it forcefully
7579 wait((int *) 0); // and wait again
7583 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7585 /* Process is exiting either because of the kill or because of
7586 a quit command sent by the backend; either way, wait for it to die.
7595 InterruptChildProcess(pr)
7598 ChildProc *cp = (ChildProc *) pr;
7600 if (cp->kind != CPReal) return;
7601 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7604 int OpenTelnet(host, port, pr)
7609 char cmdLine[MSG_SIZ];
7611 if (port[0] == NULLCHAR) {
7612 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7614 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7616 return StartChildProcess(cmdLine, "", pr);
7619 int OpenTCP(host, port, pr)
7625 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7626 #else /* !OMIT_SOCKETS */
7627 struct addrinfo hints;
7628 struct addrinfo *ais, *ai;
7633 memset(&hints, 0, sizeof(hints));
7634 hints.ai_family = AF_UNSPEC;
7635 hints.ai_socktype = SOCK_STREAM;
7637 error = getaddrinfo(host, port, &hints, &ais);
7639 /* a getaddrinfo error is not an errno, so can't return it */
7640 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7641 host, port, gai_strerror(error));
7645 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7646 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7650 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7663 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7669 #endif /* !OMIT_SOCKETS */
7674 int OpenCommPort(name, pr)
7681 fd = open(name, 2, 0);
7682 if (fd < 0) return errno;
7684 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7694 int OpenLoopback(pr)
7700 SetUpChildIO(to, from);
7702 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7705 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7712 int OpenRcmd(host, user, cmd, pr)
7713 char *host, *user, *cmd;
7716 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7720 #define INPUT_SOURCE_BUF_SIZE 8192
7729 char buf[INPUT_SOURCE_BUF_SIZE];
7734 DoInputCallback(closure, source, xid)
7739 InputSource *is = (InputSource *) closure;
7744 if (is->lineByLine) {
7745 count = read(is->fd, is->unused,
7746 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7748 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7751 is->unused += count;
7753 while (p < is->unused) {
7754 q = memchr(p, '\n', is->unused - p);
7755 if (q == NULL) break;
7757 (is->func)(is, is->closure, p, q - p, 0);
7761 while (p < is->unused) {
7766 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7771 (is->func)(is, is->closure, is->buf, count, error);
7775 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7782 ChildProc *cp = (ChildProc *) pr;
7784 is = (InputSource *) calloc(1, sizeof(InputSource));
7785 is->lineByLine = lineByLine;
7789 is->fd = fileno(stdin);
7791 is->kind = cp->kind;
7792 is->fd = cp->fdFrom;
7795 is->unused = is->buf;
7798 is->xid = XtAppAddInput(appContext, is->fd,
7799 (XtPointer) (XtInputReadMask),
7800 (XtInputCallbackProc) DoInputCallback,
7802 is->closure = closure;
7803 return (InputSourceRef) is;
7807 RemoveInputSource(isr)
7810 InputSource *is = (InputSource *) isr;
7812 if (is->xid == 0) return;
7813 XtRemoveInput(is->xid);
7817 int OutputToProcess(pr, message, count, outError)
7823 static int line = 0;
7824 ChildProc *cp = (ChildProc *) pr;
7829 if (appData.noJoin || !appData.useInternalWrap)
7830 outCount = fwrite(message, 1, count, stdout);
7833 int width = get_term_width();
7834 int len = wrap(NULL, message, count, width, &line);
7835 char *msg = malloc(len);
7839 outCount = fwrite(message, 1, count, stdout);
7842 dbgchk = wrap(msg, message, count, width, &line);
7843 if (dbgchk != len && appData.debugMode)
7844 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7845 outCount = fwrite(msg, 1, dbgchk, stdout);
7851 outCount = write(cp->fdTo, message, count);
7861 /* Output message to process, with "ms" milliseconds of delay
7862 between each character. This is needed when sending the logon
7863 script to ICC, which for some reason doesn't like the
7864 instantaneous send. */
7865 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
7872 ChildProc *cp = (ChildProc *) pr;
7877 r = write(cp->fdTo, message++, 1);
7890 /**** Animation code by Hugh Fisher, DCS, ANU.
7892 Known problem: if a window overlapping the board is
7893 moved away while a piece is being animated underneath,
7894 the newly exposed area won't be updated properly.
7895 I can live with this.
7897 Known problem: if you look carefully at the animation
7898 of pieces in mono mode, they are being drawn as solid
7899 shapes without interior detail while moving. Fixing
7900 this would be a major complication for minimal return.
7903 /* Masks for XPM pieces. Black and white pieces can have
7904 different shapes, but in the interest of retaining my
7905 sanity pieces must have the same outline on both light
7906 and dark squares, and all pieces must use the same
7907 background square colors/images. */
7909 static int xpmDone = 0;
7912 CreateAnimMasks (pieceDepth)
7919 unsigned long plane;
7922 /* Need a bitmap just to get a GC with right depth */
7923 buf = XCreatePixmap(xDisplay, xBoardWindow,
7925 values.foreground = 1;
7926 values.background = 0;
7927 /* Don't use XtGetGC, not read only */
7928 maskGC = XCreateGC(xDisplay, buf,
7929 GCForeground | GCBackground, &values);
7930 XFreePixmap(xDisplay, buf);
7932 buf = XCreatePixmap(xDisplay, xBoardWindow,
7933 squareSize, squareSize, pieceDepth);
7934 values.foreground = XBlackPixel(xDisplay, xScreen);
7935 values.background = XWhitePixel(xDisplay, xScreen);
7936 bufGC = XCreateGC(xDisplay, buf,
7937 GCForeground | GCBackground, &values);
7939 for (piece = WhitePawn; piece <= BlackKing; piece++) {
7940 /* Begin with empty mask */
7941 if(!xpmDone) // [HGM] pieces: keep using existing
7942 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7943 squareSize, squareSize, 1);
7944 XSetFunction(xDisplay, maskGC, GXclear);
7945 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7946 0, 0, squareSize, squareSize);
7948 /* Take a copy of the piece */
7953 XSetFunction(xDisplay, bufGC, GXcopy);
7954 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
7956 0, 0, squareSize, squareSize, 0, 0);
7958 /* XOR the background (light) over the piece */
7959 XSetFunction(xDisplay, bufGC, GXxor);
7961 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7962 0, 0, squareSize, squareSize, 0, 0);
7964 XSetForeground(xDisplay, bufGC, lightSquareColor);
7965 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7968 /* We now have an inverted piece image with the background
7969 erased. Construct mask by just selecting all the non-zero
7970 pixels - no need to reconstruct the original image. */
7971 XSetFunction(xDisplay, maskGC, GXor);
7973 /* Might be quicker to download an XImage and create bitmap
7974 data from it rather than this N copies per piece, but it
7975 only takes a fraction of a second and there is a much
7976 longer delay for loading the pieces. */
7977 for (n = 0; n < pieceDepth; n ++) {
7978 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7979 0, 0, squareSize, squareSize,
7985 XFreePixmap(xDisplay, buf);
7986 XFreeGC(xDisplay, bufGC);
7987 XFreeGC(xDisplay, maskGC);
7991 InitAnimState (anim, info)
7993 XWindowAttributes * info;
7998 /* Each buffer is square size, same depth as window */
7999 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8000 squareSize, squareSize, info->depth);
8001 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8002 squareSize, squareSize, info->depth);
8004 /* Create a plain GC for blitting */
8005 mask = GCForeground | GCBackground | GCFunction |
8006 GCPlaneMask | GCGraphicsExposures;
8007 values.foreground = XBlackPixel(xDisplay, xScreen);
8008 values.background = XWhitePixel(xDisplay, xScreen);
8009 values.function = GXcopy;
8010 values.plane_mask = AllPlanes;
8011 values.graphics_exposures = False;
8012 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8014 /* Piece will be copied from an existing context at
8015 the start of each new animation/drag. */
8016 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8018 /* Outline will be a read-only copy of an existing */
8019 anim->outlineGC = None;
8025 XWindowAttributes info;
8027 if (xpmDone && gameInfo.variant == oldVariant) return;
8028 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
8029 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8031 InitAnimState(&game, &info);
8032 InitAnimState(&player, &info);
8034 /* For XPM pieces, we need bitmaps to use as masks. */
8036 CreateAnimMasks(info.depth), xpmDone = 1;
8041 static Boolean frameWaiting;
8043 static RETSIGTYPE FrameAlarm (sig)
8046 frameWaiting = False;
8047 /* In case System-V style signals. Needed?? */
8048 signal(SIGALRM, FrameAlarm);
8055 struct itimerval delay;
8057 XSync(xDisplay, False);
8060 frameWaiting = True;
8061 signal(SIGALRM, FrameAlarm);
8062 delay.it_interval.tv_sec =
8063 delay.it_value.tv_sec = time / 1000;
8064 delay.it_interval.tv_usec =
8065 delay.it_value.tv_usec = (time % 1000) * 1000;
8066 setitimer(ITIMER_REAL, &delay, NULL);
8067 while (frameWaiting) pause();
8068 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8069 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8070 setitimer(ITIMER_REAL, &delay, NULL);
8080 XSync(xDisplay, False);
8082 usleep(time * 1000);
8087 /* Convert board position to corner of screen rect and color */
8090 ScreenSquare(column, row, pt, color)
8091 int column; int row; XPoint * pt; int * color;
8094 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8095 pt->y = lineGap + row * (squareSize + lineGap);
8097 pt->x = lineGap + column * (squareSize + lineGap);
8098 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8100 *color = SquareColor(row, column);
8103 /* Convert window coords to square */
8106 BoardSquare(x, y, column, row)
8107 int x; int y; int * column; int * row;
8109 *column = EventToSquare(x, BOARD_WIDTH);
8110 if (flipView && *column >= 0)
8111 *column = BOARD_WIDTH - 1 - *column;
8112 *row = EventToSquare(y, BOARD_HEIGHT);
8113 if (!flipView && *row >= 0)
8114 *row = BOARD_HEIGHT - 1 - *row;
8119 #undef Max /* just in case */
8121 #define Max(a, b) ((a) > (b) ? (a) : (b))
8122 #define Min(a, b) ((a) < (b) ? (a) : (b))
8125 SetRect(rect, x, y, width, height)
8126 XRectangle * rect; int x; int y; int width; int height;
8130 rect->width = width;
8131 rect->height = height;
8134 /* Test if two frames overlap. If they do, return
8135 intersection rect within old and location of
8136 that rect within new. */
8139 Intersect(old, new, size, area, pt)
8140 XPoint * old; XPoint * new;
8141 int size; XRectangle * area; XPoint * pt;
8143 if (old->x > new->x + size || new->x > old->x + size ||
8144 old->y > new->y + size || new->y > old->y + size) {
8147 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8148 size - abs(old->x - new->x), size - abs(old->y - new->y));
8149 pt->x = Max(old->x - new->x, 0);
8150 pt->y = Max(old->y - new->y, 0);
8155 /* For two overlapping frames, return the rect(s)
8156 in the old that do not intersect with the new. */
8159 CalcUpdateRects(old, new, size, update, nUpdates)
8160 XPoint * old; XPoint * new; int size;
8161 XRectangle update[]; int * nUpdates;
8165 /* If old = new (shouldn't happen) then nothing to draw */
8166 if (old->x == new->x && old->y == new->y) {
8170 /* Work out what bits overlap. Since we know the rects
8171 are the same size we don't need a full intersect calc. */
8173 /* Top or bottom edge? */
8174 if (new->y > old->y) {
8175 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8177 } else if (old->y > new->y) {
8178 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8179 size, old->y - new->y);
8182 /* Left or right edge - don't overlap any update calculated above. */
8183 if (new->x > old->x) {
8184 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8185 new->x - old->x, size - abs(new->y - old->y));
8187 } else if (old->x > new->x) {
8188 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8189 old->x - new->x, size - abs(new->y - old->y));
8196 /* Generate a series of frame coords from start->mid->finish.
8197 The movement rate doubles until the half way point is
8198 reached, then halves back down to the final destination,
8199 which gives a nice slow in/out effect. The algorithmn
8200 may seem to generate too many intermediates for short
8201 moves, but remember that the purpose is to attract the
8202 viewers attention to the piece about to be moved and
8203 then to where it ends up. Too few frames would be less
8207 Tween(start, mid, finish, factor, frames, nFrames)
8208 XPoint * start; XPoint * mid;
8209 XPoint * finish; int factor;
8210 XPoint frames[]; int * nFrames;
8212 int fraction, n, count;
8216 /* Slow in, stepping 1/16th, then 1/8th, ... */
8218 for (n = 0; n < factor; n++)
8220 for (n = 0; n < factor; n++) {
8221 frames[count].x = start->x + (mid->x - start->x) / fraction;
8222 frames[count].y = start->y + (mid->y - start->y) / fraction;
8224 fraction = fraction / 2;
8228 frames[count] = *mid;
8231 /* Slow out, stepping 1/2, then 1/4, ... */
8233 for (n = 0; n < factor; n++) {
8234 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8235 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8237 fraction = fraction * 2;
8242 /* Draw a piece on the screen without disturbing what's there */
8245 SelectGCMask(piece, clip, outline, mask)
8246 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8250 /* Bitmap for piece being moved. */
8251 if (appData.monoMode) {
8252 *mask = *pieceToSolid(piece);
8253 } else if (useImages) {
8255 *mask = xpmMask[piece];
8257 *mask = ximMaskPm[piece];
8260 *mask = *pieceToSolid(piece);
8263 /* GC for piece being moved. Square color doesn't matter, but
8264 since it gets modified we make a copy of the original. */
8266 if (appData.monoMode)
8271 if (appData.monoMode)
8276 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8278 /* Outline only used in mono mode and is not modified */
8280 *outline = bwPieceGC;
8282 *outline = wbPieceGC;
8286 OverlayPiece(piece, clip, outline, dest)
8287 ChessSquare piece; GC clip; GC outline; Drawable dest;
8292 /* Draw solid rectangle which will be clipped to shape of piece */
8293 XFillRectangle(xDisplay, dest, clip,
8294 0, 0, squareSize, squareSize);
8295 if (appData.monoMode)
8296 /* Also draw outline in contrasting color for black
8297 on black / white on white cases */
8298 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8299 0, 0, squareSize, squareSize, 0, 0, 1);
8301 /* Copy the piece */
8306 if(appData.upsideDown && flipView) kind ^= 2;
8307 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8309 0, 0, squareSize, squareSize,
8314 /* Animate the movement of a single piece */
8317 BeginAnimation(anim, piece, startColor, start)
8325 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8326 /* The old buffer is initialised with the start square (empty) */
8327 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8328 anim->prevFrame = *start;
8330 /* The piece will be drawn using its own bitmap as a matte */
8331 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8332 XSetClipMask(xDisplay, anim->pieceGC, mask);
8336 AnimationFrame(anim, frame, piece)
8341 XRectangle updates[4];
8346 /* Save what we are about to draw into the new buffer */
8347 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8348 frame->x, frame->y, squareSize, squareSize,
8351 /* Erase bits of the previous frame */
8352 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8353 /* Where the new frame overlapped the previous,
8354 the contents in newBuf are wrong. */
8355 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8356 overlap.x, overlap.y,
8357 overlap.width, overlap.height,
8359 /* Repaint the areas in the old that don't overlap new */
8360 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8361 for (i = 0; i < count; i++)
8362 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8363 updates[i].x - anim->prevFrame.x,
8364 updates[i].y - anim->prevFrame.y,
8365 updates[i].width, updates[i].height,
8366 updates[i].x, updates[i].y);
8368 /* Easy when no overlap */
8369 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8370 0, 0, squareSize, squareSize,
8371 anim->prevFrame.x, anim->prevFrame.y);
8374 /* Save this frame for next time round */
8375 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8376 0, 0, squareSize, squareSize,
8378 anim->prevFrame = *frame;
8380 /* Draw piece over original screen contents, not current,
8381 and copy entire rect. Wipes out overlapping piece images. */
8382 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8383 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8384 0, 0, squareSize, squareSize,
8385 frame->x, frame->y);
8389 EndAnimation (anim, finish)
8393 XRectangle updates[4];
8398 /* The main code will redraw the final square, so we
8399 only need to erase the bits that don't overlap. */
8400 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8401 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8402 for (i = 0; i < count; i++)
8403 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8404 updates[i].x - anim->prevFrame.x,
8405 updates[i].y - anim->prevFrame.y,
8406 updates[i].width, updates[i].height,
8407 updates[i].x, updates[i].y);
8409 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8410 0, 0, squareSize, squareSize,
8411 anim->prevFrame.x, anim->prevFrame.y);
8416 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8418 ChessSquare piece; int startColor;
8419 XPoint * start; XPoint * finish;
8420 XPoint frames[]; int nFrames;
8424 BeginAnimation(anim, piece, startColor, start);
8425 for (n = 0; n < nFrames; n++) {
8426 AnimationFrame(anim, &(frames[n]), piece);
8427 FrameDelay(appData.animSpeed);
8429 EndAnimation(anim, finish);
8433 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8436 ChessSquare piece = board[fromY][toY];
8437 board[fromY][toY] = EmptySquare;
8438 DrawPosition(FALSE, board);
8440 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8441 y = lineGap + toY * (squareSize + lineGap);
8443 x = lineGap + toX * (squareSize + lineGap);
8444 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8446 for(i=1; i<4*kFactor; i++) {
8447 int r = squareSize * 9 * i/(20*kFactor - 5);
8448 XFillArc(xDisplay, xBoardWindow, highlineGC,
8449 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8450 FrameDelay(appData.animSpeed);
8452 board[fromY][toY] = piece;
8455 /* Main control logic for deciding what to animate and how */
8458 AnimateMove(board, fromX, fromY, toX, toY)
8467 XPoint start, finish, mid;
8468 XPoint frames[kFactor * 2 + 1];
8469 int nFrames, startColor, endColor;
8471 /* Are we animating? */
8472 if (!appData.animate || appData.blindfold)
8475 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8476 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8477 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8479 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8480 piece = board[fromY][fromX];
8481 if (piece >= EmptySquare) return;
8486 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8489 if (appData.debugMode) {
8490 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8491 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8492 piece, fromX, fromY, toX, toY); }
8494 ScreenSquare(fromX, fromY, &start, &startColor);
8495 ScreenSquare(toX, toY, &finish, &endColor);
8498 /* Knight: make straight movement then diagonal */
8499 if (abs(toY - fromY) < abs(toX - fromX)) {
8500 mid.x = start.x + (finish.x - start.x) / 2;
8504 mid.y = start.y + (finish.y - start.y) / 2;
8507 mid.x = start.x + (finish.x - start.x) / 2;
8508 mid.y = start.y + (finish.y - start.y) / 2;
8511 /* Don't use as many frames for very short moves */
8512 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8513 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8515 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8516 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8517 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8519 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8520 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8523 /* Be sure end square is redrawn */
8524 damage[0][toY][toX] = True;
8528 DragPieceBegin(x, y)
8531 int boardX, boardY, color;
8534 /* Are we animating? */
8535 if (!appData.animateDragging || appData.blindfold)
8538 /* Figure out which square we start in and the
8539 mouse position relative to top left corner. */
8540 BoardSquare(x, y, &boardX, &boardY);
8541 player.startBoardX = boardX;
8542 player.startBoardY = boardY;
8543 ScreenSquare(boardX, boardY, &corner, &color);
8544 player.startSquare = corner;
8545 player.startColor = color;
8546 /* As soon as we start dragging, the piece will jump slightly to
8547 be centered over the mouse pointer. */
8548 player.mouseDelta.x = squareSize/2;
8549 player.mouseDelta.y = squareSize/2;
8550 /* Initialise animation */
8551 player.dragPiece = PieceForSquare(boardX, boardY);
8553 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8554 player.dragActive = True;
8555 BeginAnimation(&player, player.dragPiece, color, &corner);
8556 /* Mark this square as needing to be redrawn. Note that
8557 we don't remove the piece though, since logically (ie
8558 as seen by opponent) the move hasn't been made yet. */
8559 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8560 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8561 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8562 corner.x, corner.y, squareSize, squareSize,
8563 0, 0); // [HGM] zh: unstack in stead of grab
8564 if(gatingPiece != EmptySquare) {
8565 /* Kludge alert: When gating we want the introduced
8566 piece to appear on the from square. To generate an
8567 image of it, we draw it on the board, copy the image,
8568 and draw the original piece again. */
8569 ChessSquare piece = boards[currentMove][boardY][boardX];
8570 DrawSquare(boardY, boardX, gatingPiece, 0);
8571 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8572 corner.x, corner.y, squareSize, squareSize, 0, 0);
8573 DrawSquare(boardY, boardX, piece, 0);
8575 damage[0][boardY][boardX] = True;
8577 player.dragActive = False;
8582 ChangeDragPiece(ChessSquare piece)
8585 player.dragPiece = piece;
8586 /* The piece will be drawn using its own bitmap as a matte */
8587 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8588 XSetClipMask(xDisplay, player.pieceGC, mask);
8597 /* Are we animating? */
8598 if (!appData.animateDragging || appData.blindfold)
8602 if (! player.dragActive)
8604 /* Move piece, maintaining same relative position
8605 of mouse within square */
8606 corner.x = x - player.mouseDelta.x;
8607 corner.y = y - player.mouseDelta.y;
8608 AnimationFrame(&player, &corner, player.dragPiece);
8610 if (appData.highlightDragging) {
8612 BoardSquare(x, y, &boardX, &boardY);
8613 SetHighlights(fromX, fromY, boardX, boardY);
8622 int boardX, boardY, color;
8625 /* Are we animating? */
8626 if (!appData.animateDragging || appData.blindfold)
8630 if (! player.dragActive)
8632 /* Last frame in sequence is square piece is
8633 placed on, which may not match mouse exactly. */
8634 BoardSquare(x, y, &boardX, &boardY);
8635 ScreenSquare(boardX, boardY, &corner, &color);
8636 EndAnimation(&player, &corner);
8638 /* Be sure end square is redrawn */
8639 damage[0][boardY][boardX] = True;
8641 /* This prevents weird things happening with fast successive
8642 clicks which on my Sun at least can cause motion events
8643 without corresponding press/release. */
8644 player.dragActive = False;
8647 /* Handle expose event while piece being dragged */
8652 if (!player.dragActive || appData.blindfold)
8655 /* What we're doing: logically, the move hasn't been made yet,
8656 so the piece is still in it's original square. But visually
8657 it's being dragged around the board. So we erase the square
8658 that the piece is on and draw it at the last known drag point. */
8659 BlankSquare(player.startSquare.x, player.startSquare.y,
8660 player.startColor, EmptySquare, xBoardWindow, 1);
8661 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8662 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8665 #include <sys/ioctl.h>
8666 int get_term_width()
8668 int fd, default_width;
8671 default_width = 79; // this is FICS default anyway...
8673 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8675 if (!ioctl(fd, TIOCGSIZE, &win))
8676 default_width = win.ts_cols;
8677 #elif defined(TIOCGWINSZ)
8679 if (!ioctl(fd, TIOCGWINSZ, &win))
8680 default_width = win.ws_col;
8682 return default_width;
8688 static int old_width = 0;
8689 int new_width = get_term_width();
8691 if (old_width != new_width)
8692 ics_printf("set width %d\n", new_width);
8693 old_width = new_width;
8696 void NotifyFrontendLogin()
8701 /* [AS] Arrow highlighting support */
8703 static double A_WIDTH = 5; /* Width of arrow body */
8705 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8706 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8708 static double Sqr( double x )
8713 static int Round( double x )
8715 return (int) (x + 0.5);
8718 void SquareToPos(int rank, int file, int *x, int *y)
8721 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8722 *y = lineGap + rank * (squareSize + lineGap);
8724 *x = lineGap + file * (squareSize + lineGap);
8725 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8729 /* Draw an arrow between two points using current settings */
8730 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8733 double dx, dy, j, k, x, y;
8736 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8738 arrow[0].x = s_x + A_WIDTH + 0.5;
8741 arrow[1].x = s_x + A_WIDTH + 0.5;
8742 arrow[1].y = d_y - h;
8744 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8745 arrow[2].y = d_y - h;
8750 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8751 arrow[5].y = d_y - h;
8753 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8754 arrow[4].y = d_y - h;
8756 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8759 else if( d_y == s_y ) {
8760 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8763 arrow[0].y = s_y + A_WIDTH + 0.5;
8765 arrow[1].x = d_x - w;
8766 arrow[1].y = s_y + A_WIDTH + 0.5;
8768 arrow[2].x = d_x - w;
8769 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8774 arrow[5].x = d_x - w;
8775 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8777 arrow[4].x = d_x - w;
8778 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8781 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8784 /* [AS] Needed a lot of paper for this! :-) */
8785 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8786 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8788 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8790 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8795 arrow[0].x = Round(x - j);
8796 arrow[0].y = Round(y + j*dx);
8798 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8799 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8802 x = (double) d_x - k;
8803 y = (double) d_y - k*dy;
8806 x = (double) d_x + k;
8807 y = (double) d_y + k*dy;
8810 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8812 arrow[6].x = Round(x - j);
8813 arrow[6].y = Round(y + j*dx);
8815 arrow[2].x = Round(arrow[6].x + 2*j);
8816 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8818 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8819 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8824 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8825 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8828 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8829 // Polygon( hdc, arrow, 7 );
8832 /* [AS] Draw an arrow between two squares */
8833 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
8835 int s_x, s_y, d_x, d_y, hor, vert, i;
8837 if( s_col == d_col && s_row == d_row ) {
8841 /* Get source and destination points */
8842 SquareToPos( s_row, s_col, &s_x, &s_y);
8843 SquareToPos( d_row, d_col, &d_x, &d_y);
8846 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8848 else if( d_y < s_y ) {
8849 d_y += squareSize / 2 + squareSize / 4;
8852 d_y += squareSize / 2;
8856 d_x += squareSize / 2 - squareSize / 4;
8858 else if( d_x < s_x ) {
8859 d_x += squareSize / 2 + squareSize / 4;
8862 d_x += squareSize / 2;
8865 s_x += squareSize / 2;
8866 s_y += squareSize / 2;
8869 A_WIDTH = squareSize / 14.; //[HGM] make float
8871 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8873 hor = 64*s_col + 32; vert = 64*s_row + 32;
8874 for(i=0; i<= 64; i++) {
8875 damage[0][vert+6>>6][hor+6>>6] = True;
8876 damage[0][vert-6>>6][hor+6>>6] = True;
8877 damage[0][vert+6>>6][hor-6>>6] = True;
8878 damage[0][vert-6>>6][hor-6>>6] = True;
8879 hor += d_col - s_col; vert += d_row - s_row;
8883 Boolean IsDrawArrowEnabled()
8885 return appData.highlightMoveWithArrow && squareSize >= 32;
8888 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
8890 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8891 DrawArrowBetweenSquares(fromX, fromY, toX, toY);