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));
255 char *InsertPxlSize P((char *pattern, int targetPxlSize));
256 XFontSet CreateFontSet P((char *base_fnt_lst));
258 char *FindFont P((char *pattern, int targetPxlSize));
260 void PieceMenuPopup P((Widget w, XEvent *event,
261 String *params, Cardinal *num_params));
262 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
263 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
264 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
265 u_int wreq, u_int hreq));
266 void CreateGrid P((void));
267 int EventToSquare P((int x, int limit));
268 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
269 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
270 void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
271 void HandleUserMove P((Widget w, XEvent *event,
272 String *prms, Cardinal *nprms));
273 void AnimateUserMove P((Widget w, XEvent * event,
274 String * params, Cardinal * nParams));
275 void HandlePV P((Widget w, XEvent * event,
276 String * params, Cardinal * nParams));
277 void SelectPV P((Widget w, XEvent * event,
278 String * params, Cardinal * nParams));
279 void StopPV P((Widget w, XEvent * event,
280 String * params, Cardinal * nParams));
281 void WhiteClock P((Widget w, XEvent *event,
282 String *prms, Cardinal *nprms));
283 void BlackClock P((Widget w, XEvent *event,
284 String *prms, Cardinal *nprms));
285 void DrawPositionProc P((Widget w, XEvent *event,
286 String *prms, Cardinal *nprms));
287 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
289 void CommentClick P((Widget w, XEvent * event,
290 String * params, Cardinal * nParams));
291 void CommentPopUp P((char *title, char *label));
292 void CommentPopDown P((void));
293 void ICSInputBoxPopUp P((void));
294 void ICSInputBoxPopDown P((void));
295 void FileNamePopUp P((char *label, char *def, char *filter,
296 FileProc proc, char *openMode));
297 void FileNamePopDown P((void));
298 void FileNameCallback P((Widget w, XtPointer client_data,
299 XtPointer call_data));
300 void FileNameAction P((Widget w, XEvent *event,
301 String *prms, Cardinal *nprms));
302 void AskQuestionReplyAction P((Widget w, XEvent *event,
303 String *prms, Cardinal *nprms));
304 void AskQuestionProc P((Widget w, XEvent *event,
305 String *prms, Cardinal *nprms));
306 void AskQuestionPopDown P((void));
307 void PromotionPopDown P((void));
308 void PromotionCallback P((Widget w, XtPointer client_data,
309 XtPointer call_data));
310 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
311 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
312 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
313 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
315 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
317 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
319 void LoadPositionProc P((Widget w, XEvent *event,
320 String *prms, Cardinal *nprms));
321 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
323 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
325 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
327 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
329 void PastePositionProc P((Widget w, XEvent *event, String *prms,
331 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
332 void CopyGameListProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
333 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
334 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
335 void SavePositionProc P((Widget w, XEvent *event,
336 String *prms, Cardinal *nprms));
337 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
338 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
340 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
341 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
342 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
344 void MachineWhiteProc P((Widget w, XEvent *event,
345 String *prms, Cardinal *nprms));
346 void AnalyzeModeProc P((Widget w, XEvent *event,
347 String *prms, Cardinal *nprms));
348 void AnalyzeFileProc P((Widget w, XEvent *event,
349 String *prms, Cardinal *nprms));
350 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
352 void MatchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
353 void MatchOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
354 void IcsClientProc P((Widget w, XEvent *event, String *prms,
356 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
357 void EditPositionProc P((Widget w, XEvent *event,
358 String *prms, Cardinal *nprms));
359 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
360 void EditCommentProc P((Widget w, XEvent *event,
361 String *prms, Cardinal *nprms));
362 void IcsInputBoxProc P((Widget w, XEvent *event,
363 String *prms, Cardinal *nprms));
364 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
365 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
366 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
367 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
368 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
369 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
370 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
371 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
372 void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
373 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
374 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
375 void TypeInProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
376 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
377 void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
378 void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
379 void StopObservingProc P((Widget w, XEvent *event, String *prms,
381 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
383 void UploadProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
384 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
385 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
386 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
387 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
388 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
389 void AnnotateProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
390 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
392 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
394 void MoveNowProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
395 void AlwaysQueenProc P((Widget w, XEvent *event, String *prms,
397 void AnimateDraggingProc P((Widget w, XEvent *event, String *prms,
399 void AnimateMovingProc P((Widget w, XEvent *event, String *prms,
401 void AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
402 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
403 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
404 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
406 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
407 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
409 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
411 void HighlightArrowProc P((Widget w, XEvent *event, String *prms,
413 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
414 //void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
415 void OneClickProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
416 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
418 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
420 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
422 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
424 //void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
425 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
427 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
429 void HideThinkingProc P((Widget w, XEvent *event, String *prms,
431 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
433 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
434 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
435 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
436 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
437 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
438 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
439 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
440 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
441 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
442 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
443 void Iconify P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
444 void DisplayMove P((int moveNumber));
445 void DisplayTitle P((char *title));
446 void ICSInitScript P((void));
447 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
448 void ErrorPopUp P((char *title, char *text, int modal));
449 void ErrorPopDown P((void));
450 static char *ExpandPathName P((char *path));
451 static void CreateAnimVars P((void));
452 static void DragPieceMove P((int x, int y));
453 static void DrawDragPiece P((void));
454 char *ModeToWidgetName P((GameMode mode));
455 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
456 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
457 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
458 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
459 void OptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
460 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
461 void IcsTextProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
462 void LoadEngineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
463 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
464 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
465 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
466 void IcsOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
467 void SoundOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
468 void BoardOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
469 void LoadOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
470 void SaveOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
471 void EditBookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
472 void GameListOptionsPopDown P(());
473 void ShufflePopDown P(());
474 void TimeControlPopDown P(());
475 void GenericPopDown P(());
476 void update_ics_width P(());
477 int get_term_width P(());
478 int CopyMemoProc P(());
479 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
480 Boolean IsDrawArrowEnabled P(());
483 * XBoard depends on Xt R4 or higher
485 int xtVersion = XtSpecificationRelease;
490 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
491 jailSquareColor, highlightSquareColor, premoveHighlightColor;
492 Pixel lowTimeWarningColor;
493 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
494 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
495 wjPieceGC, bjPieceGC, prelineGC, countGC;
496 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
497 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
498 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
499 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
500 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
501 ICSInputShell, fileNameShell, askQuestionShell;
502 Widget historyShell, evalGraphShell, gameListShell;
503 int hOffset; // [HGM] dual
504 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
505 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
506 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
508 XFontSet fontSet, clockFontSet;
511 XFontStruct *clockFontStruct;
513 Font coordFontID, countFontID;
514 XFontStruct *coordFontStruct, *countFontStruct;
515 XtAppContext appContext;
517 char *oldICSInteractionTitle;
521 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
523 Position commentX = -1, commentY = -1;
524 Dimension commentW, commentH;
525 typedef unsigned int BoardSize;
527 Boolean chessProgram;
529 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
530 int squareSize, smallLayout = 0, tinyLayout = 0,
531 marginW, marginH, // [HGM] for run-time resizing
532 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
533 ICSInputBoxUp = False, askQuestionUp = False,
534 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
535 errorUp = False, errorExitStatus = -1, lineGap, defaultLineGap;
536 Pixel timerForegroundPixel, timerBackgroundPixel;
537 Pixel buttonForegroundPixel, buttonBackgroundPixel;
538 char *chessDir, *programName, *programVersion,
539 *gameCopyFilename, *gamePasteFilename;
540 Boolean alwaysOnTop = False;
541 Boolean saveSettingsOnExit;
542 char *settingsFileName;
543 char *icsTextMenuString;
545 char *firstChessProgramNames;
546 char *secondChessProgramNames;
548 WindowPlacement wpMain;
549 WindowPlacement wpConsole;
550 WindowPlacement wpComment;
551 WindowPlacement wpMoveHistory;
552 WindowPlacement wpEvalGraph;
553 WindowPlacement wpEngineOutput;
554 WindowPlacement wpGameList;
555 WindowPlacement wpTags;
557 extern Widget shells[];
558 extern Boolean shellUp[];
562 Pixmap pieceBitmap[2][(int)BlackPawn];
563 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
564 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
565 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
566 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
567 Pixmap xpmBoardBitmap[2];
568 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
569 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
570 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
571 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
572 XImage *ximLightSquare, *ximDarkSquare;
575 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
576 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
578 #define White(piece) ((int)(piece) < (int)BlackPawn)
580 /* Variables for doing smooth animation. This whole thing
581 would be much easier if the board was double-buffered,
582 but that would require a fairly major rewrite. */
587 GC blitGC, pieceGC, outlineGC;
588 XPoint startSquare, prevFrame, mouseDelta;
592 int startBoardX, startBoardY;
595 /* There can be two pieces being animated at once: a player
596 can begin dragging a piece before the remote opponent has moved. */
598 static AnimState game, player;
600 /* Bitmaps for use as masks when drawing XPM pieces.
601 Need one for each black and white piece. */
602 static Pixmap xpmMask[BlackKing + 1];
604 /* This magic number is the number of intermediate frames used
605 in each half of the animation. For short moves it's reduced
606 by 1. The total number of frames will be factor * 2 + 1. */
609 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
611 MenuItem fileMenu[] = {
612 {N_("New Game Ctrl+N"), "New Game", ResetProc},
613 {N_("New Shuffle Game ..."), "New Shuffle Game", ShuffleMenuProc},
614 {N_("New Variant ... Alt+Shift+V"), "New Variant", NewVariantProc}, // [HGM] variant: not functional yet
615 {"----", NULL, NothingProc},
616 {N_("Load Game Ctrl+O"), "Load Game", LoadGameProc},
617 {N_("Load Position Ctrl+Shift+O"), "Load Position", LoadPositionProc},
618 // {N_("Load Next Game"), "Load Next Game", LoadNextGameProc},
619 // {N_("Load Previous Game"), "Load Previous Game", LoadPrevGameProc},
620 // {N_("Reload Same Game"), "Reload Same Game", ReloadGameProc},
621 {N_("Next Position Shift+PgDn"), "Load Next Position", LoadNextPositionProc},
622 {N_("Prev Position Shift+PgUp"), "Load Previous Position", LoadPrevPositionProc},
623 {"----", NULL, NothingProc},
624 // {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
625 {N_("Save Game Ctrl+S"), "Save Game", SaveGameProc},
626 {N_("Save Position Ctrl+Shift+S"), "Save Position", SavePositionProc},
627 {"----", NULL, NothingProc},
628 {N_("Mail Move"), "Mail Move", MailMoveProc},
629 {N_("Reload CMail Message"), "Reload CMail Message", ReloadCmailMsgProc},
630 {"----", NULL, NothingProc},
631 {N_("Quit Ctr+Q"), "Exit", QuitProc},
635 MenuItem editMenu[] = {
636 {N_("Copy Game Ctrl+C"), "Copy Game", CopyGameProc},
637 {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
638 {N_("Copy Game List"), "Copy Game List", CopyGameListProc},
639 {"----", NULL, NothingProc},
640 {N_("Paste Game Ctrl+V"), "Paste Game", PasteGameProc},
641 {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
642 {"----", NULL, NothingProc},
643 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
644 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
645 {N_("Edit Tags"), "Edit Tags", EditTagsProc},
646 {N_("Edit Comment"), "Edit Comment", EditCommentProc},
647 {N_("Edit Book"), "Edit Book", EditBookProc},
648 {"----", NULL, NothingProc},
649 {N_("Revert Home"), "Revert", RevertProc},
650 {N_("Annotate"), "Annotate", AnnotateProc},
651 {N_("Truncate Game End"), "Truncate Game", TruncateGameProc},
652 {"----", NULL, NothingProc},
653 {N_("Backward Alt+Left"), "Backward", BackwardProc},
654 {N_("Forward Alt+Right"), "Forward", ForwardProc},
655 {N_("Back to Start Alt+Home"), "Back to Start", ToStartProc},
656 {N_("Forward to End Alt+End"), "Forward to End", ToEndProc},
660 MenuItem viewMenu[] = {
661 {N_("Flip View F2"), "Flip View", FlipViewProc},
662 {"----", NULL, NothingProc},
663 {N_("Engine Output Alt+Shift+O"), "Show Engine Output", EngineOutputProc},
664 {N_("Move History Alt+Shift+H"), "Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
665 {N_("Evaluation Graph Alt+Shift+E"), "Show Evaluation Graph", EvalGraphProc},
666 {N_("Game List Alt+Shift+G"), "Show Game List", ShowGameListProc},
667 {N_("ICS text menu"), "ICStex", IcsTextProc},
668 {"----", NULL, NothingProc},
669 {N_("Tags"), "Show Tags", EditTagsProc},
670 {N_("Comments"), "Show Comments", EditCommentProc},
671 {N_("ICS Input Box"), "ICS Input Box", IcsInputBoxProc},
672 {"----", NULL, NothingProc},
673 {N_("Board..."), "Board Options", BoardOptionsProc},
674 {N_("Game List Tags..."), "Game List", GameListOptionsPopUp},
678 MenuItem modeMenu[] = {
679 {N_("Machine White Ctrl+W"), "Machine White", MachineWhiteProc},
680 {N_("Machine Black Ctrl+B"), "Machine Black", MachineBlackProc},
681 {N_("Two Machines Ctrl+T"), "Two Machines", TwoMachinesProc},
682 {N_("Analysis Mode Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
683 {N_("Analyze File Ctrl+F"), "Analyze File", AnalyzeFileProc },
684 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
685 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
686 {N_("Training"), "Training", TrainingProc},
687 {N_("ICS Client"), "ICS Client", IcsClientProc},
688 {"----", NULL, NothingProc},
689 {N_("Machine Match"), "Machine Match", MatchProc},
690 {N_("Pause Pause"), "Pause", PauseProc},
694 MenuItem actionMenu[] = {
695 {N_("Accept F3"), "Accept", AcceptProc},
696 {N_("Decline F4"), "Decline", DeclineProc},
697 {N_("Rematch F12"), "Rematch", RematchProc},
698 {"----", NULL, NothingProc},
699 {N_("Call Flag F5"), "Call Flag", CallFlagProc},
700 {N_("Draw F6"), "Draw", DrawProc},
701 {N_("Adjourn F7"), "Adjourn", AdjournProc},
702 {N_("Abort F8"),"Abort", AbortProc},
703 {N_("Resign F9"), "Resign", ResignProc},
704 {"----", NULL, NothingProc},
705 {N_("Stop Observing F10"), "Stop Observing", StopObservingProc},
706 {N_("Stop Examining F11"), "Stop Examining", StopExaminingProc},
707 {N_("Upload to Examine"), "Upload to Examine", UploadProc},
708 {"----", NULL, NothingProc},
709 {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
710 {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
711 {N_("Adjudicate Draw"), "Adjudicate Draw", AdjuDrawProc},
715 MenuItem engineMenu[] = {
716 {N_("Load New Engine ..."), "Load Engine", LoadEngineProc},
717 {"----", NULL, NothingProc},
718 {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
719 {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
720 {"----", NULL, NothingProc},
721 {N_("Hint"), "Hint", HintProc},
722 {N_("Book"), "Book", BookProc},
723 {"----", NULL, NothingProc},
724 {N_("Move Now Ctrl+M"), "Move Now", MoveNowProc},
725 {N_("Retract Move Ctrl+X"), "Retract Move", RetractMoveProc},
729 MenuItem optionsMenu[] = {
730 #define OPTIONSDIALOG
732 {N_("General ..."), "General", OptionsProc},
734 {N_("Time Control ... Alt+Shift+T"), "Time Control", TimeControlProc},
735 {N_("Common Engine ... Alt+Shift+U"), "Common Engine", UciMenuProc},
736 {N_("Adjudications ... Alt+Shift+J"), "Adjudications", EngineMenuProc},
737 {N_("ICS ..."), "ICS", IcsOptionsProc},
738 {N_("Match ..."), "Match", MatchOptionsProc},
739 {N_("Load Game ..."), "Load Game", LoadOptionsProc},
740 {N_("Save Game ..."), "Save Game", SaveOptionsProc},
741 // {N_(" ..."), "", OptionsProc},
742 {N_("Game List ..."), "Game List", GameListOptionsPopUp},
743 {N_("Sounds ..."), "Sounds", SoundOptionsProc},
744 {"----", NULL, NothingProc},
745 #ifndef OPTIONSDIALOG
746 {N_("Always Queen Ctrl+Shift+Q"), "Always Queen", AlwaysQueenProc},
747 {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
748 {N_("Animate Moving Ctrl+Shift+A"), "Animate Moving", AnimateMovingProc},
749 {N_("Auto Flag Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
750 {N_("Auto Flip View"), "Auto Flip View", AutoflipProc},
751 {N_("Blindfold"), "Blindfold", BlindfoldProc},
752 {N_("Flash Moves"), "Flash Moves", FlashMovesProc},
754 {N_("Highlight Dragging"), "Highlight Dragging", HighlightDraggingProc},
756 {N_("Highlight Last Move"), "Highlight Last Move", HighlightLastMoveProc},
757 {N_("Highlight With Arrow"), "Arrow", HighlightArrowProc},
758 {N_("Move Sound"), "Move Sound", MoveSoundProc},
759 // {N_("ICS Alarm"), "ICS Alarm", IcsAlarmProc},
760 {N_("One-Click Moving"), "OneClick", OneClickProc},
761 {N_("Periodic Updates"), "Periodic Updates", PeriodicUpdatesProc},
762 {N_("Ponder Next Move Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
763 {N_("Popup Exit Message"), "Popup Exit Message", PopupExitMessageProc},
764 {N_("Popup Move Errors"), "Popup Move Errors", PopupMoveErrorsProc},
765 // {N_("Premove"), "Premove", PremoveProc},
766 {N_("Show Coords"), "Show Coords", ShowCoordsProc},
767 {N_("Hide Thinking Ctrl+Shift+H"), "Hide Thinking", HideThinkingProc},
768 {N_("Test Legality Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
769 {"----", NULL, NothingProc},
771 {N_("Save Settings Now"), "Save Settings Now", SaveSettingsProc},
772 {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
776 MenuItem helpMenu[] = {
777 {N_("Info XBoard"), "Info XBoard", InfoProc},
778 {N_("Man XBoard F1"), "Man XBoard", ManProc},
779 {"----", NULL, NothingProc},
780 {N_("About XBoard"), "About XBoard", AboutProc},
785 {N_("File"), "File", fileMenu},
786 {N_("Edit"), "Edit", editMenu},
787 {N_("View"), "View", viewMenu},
788 {N_("Mode"), "Mode", modeMenu},
789 {N_("Action"), "Action", actionMenu},
790 {N_("Engine"), "Engine", engineMenu},
791 {N_("Options"), "Options", optionsMenu},
792 {N_("Help"), "Help", helpMenu},
796 #define PAUSE_BUTTON "P"
797 MenuItem buttonBar[] = {
798 {"<<", "<<", ToStartProc},
799 {"<", "<", BackwardProc},
800 {PAUSE_BUTTON, PAUSE_BUTTON, PauseProc},
801 {">", ">", ForwardProc},
802 {">>", ">>", ToEndProc},
806 #define PIECE_MENU_SIZE 18
807 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
808 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
809 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
810 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
811 N_("Empty square"), N_("Clear board") },
812 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
813 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
814 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
815 N_("Empty square"), N_("Clear board") }
817 /* must be in same order as PieceMenuStrings! */
818 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
819 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
820 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
821 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
822 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
823 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
824 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
825 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
826 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
829 #define DROP_MENU_SIZE 6
830 String dropMenuStrings[DROP_MENU_SIZE] = {
831 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
833 /* must be in same order as PieceMenuStrings! */
834 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
835 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
836 WhiteRook, WhiteQueen
844 DropMenuEnables dmEnables[] = {
862 { XtNborderWidth, 0 },
863 { XtNdefaultDistance, 0 },
867 { XtNborderWidth, 0 },
868 { XtNresizable, (XtArgVal) True },
872 { XtNborderWidth, 0 },
878 { XtNjustify, (XtArgVal) XtJustifyRight },
879 { XtNlabel, (XtArgVal) "..." },
880 { XtNresizable, (XtArgVal) True },
881 { XtNresize, (XtArgVal) False }
884 Arg messageArgs[] = {
885 { XtNjustify, (XtArgVal) XtJustifyLeft },
886 { XtNlabel, (XtArgVal) "..." },
887 { XtNresizable, (XtArgVal) True },
888 { XtNresize, (XtArgVal) False }
892 { XtNborderWidth, 0 },
893 { XtNjustify, (XtArgVal) XtJustifyLeft }
896 XtResource clientResources[] = {
897 { "flashCount", "flashCount", XtRInt, sizeof(int),
898 XtOffset(AppDataPtr, flashCount), XtRImmediate,
899 (XtPointer) FLASH_COUNT },
902 XrmOptionDescRec shellOptions[] = {
903 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
904 { "-flash", "flashCount", XrmoptionNoArg, "3" },
905 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
908 XtActionsRec boardActions[] = {
909 { "DrawPosition", DrawPositionProc },
910 { "HandleUserMove", HandleUserMove },
911 { "AnimateUserMove", AnimateUserMove },
912 { "HandlePV", HandlePV },
913 { "SelectPV", SelectPV },
914 { "StopPV", StopPV },
915 { "FileNameAction", FileNameAction },
916 { "AskQuestionProc", AskQuestionProc },
917 { "AskQuestionReplyAction", AskQuestionReplyAction },
918 { "PieceMenuPopup", PieceMenuPopup },
919 { "WhiteClock", WhiteClock },
920 { "BlackClock", BlackClock },
921 { "Iconify", Iconify },
922 { "ResetProc", ResetProc },
923 { "NewVariantProc", NewVariantProc },
924 { "LoadGameProc", LoadGameProc },
925 { "LoadNextGameProc", LoadNextGameProc },
926 { "LoadPrevGameProc", LoadPrevGameProc },
927 { "LoadSelectedProc", LoadSelectedProc },
928 { "SetFilterProc", SetFilterProc },
929 { "ReloadGameProc", ReloadGameProc },
930 { "LoadPositionProc", LoadPositionProc },
931 { "LoadNextPositionProc", LoadNextPositionProc },
932 { "LoadPrevPositionProc", LoadPrevPositionProc },
933 { "ReloadPositionProc", ReloadPositionProc },
934 { "CopyPositionProc", CopyPositionProc },
935 { "PastePositionProc", PastePositionProc },
936 { "CopyGameProc", CopyGameProc },
937 { "CopyGameListProc", CopyGameListProc },
938 { "PasteGameProc", PasteGameProc },
939 { "SaveGameProc", SaveGameProc },
940 { "SavePositionProc", SavePositionProc },
941 { "MailMoveProc", MailMoveProc },
942 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
943 { "QuitProc", QuitProc },
944 { "MachineWhiteProc", MachineWhiteProc },
945 { "MachineBlackProc", MachineBlackProc },
946 { "AnalysisModeProc", AnalyzeModeProc },
947 { "AnalyzeFileProc", AnalyzeFileProc },
948 { "TwoMachinesProc", TwoMachinesProc },
949 { "IcsClientProc", IcsClientProc },
950 { "EditGameProc", EditGameProc },
951 { "EditPositionProc", EditPositionProc },
952 { "TrainingProc", EditPositionProc },
953 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
954 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
955 { "ShowGameListProc", ShowGameListProc },
956 { "ShowMoveListProc", HistoryShowProc},
957 { "EditTagsProc", EditCommentProc },
958 { "EditBookProc", EditBookProc },
959 { "EditCommentProc", EditCommentProc },
960 { "IcsInputBoxProc", IcsInputBoxProc },
961 { "PauseProc", PauseProc },
962 { "AcceptProc", AcceptProc },
963 { "DeclineProc", DeclineProc },
964 { "RematchProc", RematchProc },
965 { "CallFlagProc", CallFlagProc },
966 { "DrawProc", DrawProc },
967 { "AdjournProc", AdjournProc },
968 { "AbortProc", AbortProc },
969 { "ResignProc", ResignProc },
970 { "AdjuWhiteProc", AdjuWhiteProc },
971 { "AdjuBlackProc", AdjuBlackProc },
972 { "AdjuDrawProc", AdjuDrawProc },
973 { "TypeInProc", TypeInProc },
974 { "EnterKeyProc", EnterKeyProc },
975 { "UpKeyProc", UpKeyProc },
976 { "DownKeyProc", DownKeyProc },
977 { "StopObservingProc", StopObservingProc },
978 { "StopExaminingProc", StopExaminingProc },
979 { "UploadProc", UploadProc },
980 { "BackwardProc", BackwardProc },
981 { "ForwardProc", ForwardProc },
982 { "ToStartProc", ToStartProc },
983 { "ToEndProc", ToEndProc },
984 { "RevertProc", RevertProc },
985 { "AnnotateProc", AnnotateProc },
986 { "TruncateGameProc", TruncateGameProc },
987 { "MoveNowProc", MoveNowProc },
988 { "RetractMoveProc", RetractMoveProc },
989 { "EngineMenuProc", (XtActionProc) EngineMenuProc },
990 { "UciMenuProc", (XtActionProc) UciMenuProc },
991 { "TimeControlProc", (XtActionProc) TimeControlProc },
992 { "FlipViewProc", FlipViewProc },
993 { "PonderNextMoveProc", PonderNextMoveProc },
994 #ifndef OPTIONSDIALOG
995 { "AlwaysQueenProc", AlwaysQueenProc },
996 { "AnimateDraggingProc", AnimateDraggingProc },
997 { "AnimateMovingProc", AnimateMovingProc },
998 { "AutoflagProc", AutoflagProc },
999 { "AutoflipProc", AutoflipProc },
1000 { "BlindfoldProc", BlindfoldProc },
1001 { "FlashMovesProc", FlashMovesProc },
1003 { "HighlightDraggingProc", HighlightDraggingProc },
1005 { "HighlightLastMoveProc", HighlightLastMoveProc },
1006 // { "IcsAlarmProc", IcsAlarmProc },
1007 { "MoveSoundProc", MoveSoundProc },
1008 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
1009 { "PopupExitMessageProc", PopupExitMessageProc },
1010 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
1011 // { "PremoveProc", PremoveProc },
1012 { "ShowCoordsProc", ShowCoordsProc },
1013 { "ShowThinkingProc", ShowThinkingProc },
1014 { "HideThinkingProc", HideThinkingProc },
1015 { "TestLegalityProc", TestLegalityProc },
1017 { "SaveSettingsProc", SaveSettingsProc },
1018 { "SaveOnExitProc", SaveOnExitProc },
1019 { "InfoProc", InfoProc },
1020 { "ManProc", ManProc },
1021 { "HintProc", HintProc },
1022 { "BookProc", BookProc },
1023 { "AboutGameProc", AboutGameProc },
1024 { "AboutProc", AboutProc },
1025 { "DebugProc", DebugProc },
1026 { "NothingProc", NothingProc },
1027 { "CommentClick", (XtActionProc) CommentClick },
1028 { "CommentPopDown", (XtActionProc) CommentPopDown },
1029 { "TagsPopDown", (XtActionProc) TagsPopDown },
1030 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1031 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1032 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1033 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1034 { "GameListPopDown", (XtActionProc) GameListPopDown },
1035 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1036 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1037 { "HistoryPopDown", (XtActionProc) HistoryPopDown },
1038 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1039 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1040 { "ShufflePopDown", (XtActionProc) ShufflePopDown },
1041 { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
1042 { "GenericPopDown", (XtActionProc) GenericPopDown },
1043 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1046 char globalTranslations[] =
1047 ":<Key>F9: ResignProc() \n \
1048 :Ctrl<Key>n: ResetProc() \n \
1049 :Meta<Key>V: NewVariantProc() \n \
1050 :Ctrl<Key>o: LoadGameProc() \n \
1051 :Meta<Key>Next: LoadNextGameProc() \n \
1052 :Meta<Key>Prior: LoadPrevGameProc() \n \
1053 :Ctrl<Key>s: SaveGameProc() \n \
1054 :Ctrl<Key>c: CopyGameProc() \n \
1055 :Ctrl<Key>v: PasteGameProc() \n \
1056 :Ctrl<Key>O: LoadPositionProc() \n \
1057 :Shift<Key>Next: LoadNextPositionProc() \n \
1058 :Shift<Key>Prior: LoadPrevPositionProc() \n \
1059 :Ctrl<Key>S: SavePositionProc() \n \
1060 :Ctrl<Key>C: CopyPositionProc() \n \
1061 :Ctrl<Key>V: PastePositionProc() \n \
1062 :Ctrl<Key>q: QuitProc() \n \
1063 :Ctrl<Key>w: MachineWhiteProc() \n \
1064 :Ctrl<Key>b: MachineBlackProc() \n \
1065 :Ctrl<Key>t: TwoMachinesProc() \n \
1066 :Ctrl<Key>a: AnalysisModeProc() \n \
1067 :Ctrl<Key>f: AnalyzeFileProc() \n \
1068 :Ctrl<Key>e: EditGameProc() \n \
1069 :Ctrl<Key>E: EditPositionProc() \n \
1070 :Meta<Key>O: EngineOutputProc() \n \
1071 :Meta<Key>E: EvalGraphProc() \n \
1072 :Meta<Key>G: ShowGameListProc() \n \
1073 :Meta<Key>H: ShowMoveListProc() \n \
1074 :<Key>Pause: PauseProc() \n \
1075 :<Key>F3: AcceptProc() \n \
1076 :<Key>F4: DeclineProc() \n \
1077 :<Key>F12: RematchProc() \n \
1078 :<Key>F5: CallFlagProc() \n \
1079 :<Key>F6: DrawProc() \n \
1080 :<Key>F7: AdjournProc() \n \
1081 :<Key>F8: AbortProc() \n \
1082 :<Key>F10: StopObservingProc() \n \
1083 :<Key>F11: StopExaminingProc() \n \
1084 :Meta Ctrl<Key>F12: DebugProc() \n \
1085 :Meta<Key>End: ToEndProc() \n \
1086 :Meta<Key>Right: ForwardProc() \n \
1087 :Meta<Key>Home: ToStartProc() \n \
1088 :Meta<Key>Left: BackwardProc() \n \
1089 :<Key>Home: RevertProc() \n \
1090 :<Key>End: TruncateGameProc() \n \
1091 :Ctrl<Key>m: MoveNowProc() \n \
1092 :Ctrl<Key>x: RetractMoveProc() \n \
1093 :Meta<Key>J: EngineMenuProc() \n \
1094 :Meta<Key>U: UciMenuProc() \n \
1095 :Meta<Key>T: TimeControlProc() \n \
1096 :Ctrl<Key>P: PonderNextMoveProc() \n "
1097 #ifndef OPTIONSDIALOG
1099 :Ctrl<Key>Q: AlwaysQueenProc() \n \
1100 :Ctrl<Key>F: AutoflagProc() \n \
1101 :Ctrl<Key>A: AnimateMovingProc() \n \
1102 :Ctrl<Key>L: TestLegalityProc() \n \
1103 :Ctrl<Key>H: HideThinkingProc() \n "
1106 :<Key>-: Iconify() \n \
1107 :<Key>F1: ManProc() \n \
1108 :<Key>F2: FlipViewProc() \n \
1109 <KeyDown>.: BackwardProc() \n \
1110 <KeyUp>.: ForwardProc() \n \
1111 Shift<Key>1: AskQuestionProc(\"Direct command\",\
1112 \"Send to chess program:\",,1) \n \
1113 Shift<Key>2: AskQuestionProc(\"Direct command\",\
1114 \"Send to second chess program:\",,2) \n";
1116 char boardTranslations[] =
1117 "<Btn1Down>: HandleUserMove(0) \n \
1118 Shift<Btn1Up>: HandleUserMove(1) \n \
1119 <Btn1Up>: HandleUserMove(0) \n \
1120 <Btn1Motion>: AnimateUserMove() \n \
1121 <Btn3Motion>: HandlePV() \n \
1122 <Btn3Up>: PieceMenuPopup(menuB) \n \
1123 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1124 PieceMenuPopup(menuB) \n \
1125 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1126 PieceMenuPopup(menuW) \n \
1127 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1128 PieceMenuPopup(menuW) \n \
1129 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1130 PieceMenuPopup(menuB) \n";
1132 char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
1133 char blackTranslations[] = "<BtnDown>: BlackClock()\n";
1135 char ICSInputTranslations[] =
1136 "<Key>Up: UpKeyProc() \n "
1137 "<Key>Down: DownKeyProc() \n "
1138 "<Key>Return: EnterKeyProc() \n";
1140 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1141 // as the widget is destroyed before the up-click can call extend-end
1142 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1144 String xboardResources[] = {
1145 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1146 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1147 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1152 /* Max possible square size */
1153 #define MAXSQSIZE 256
1155 static int xpm_avail[MAXSQSIZE];
1157 #ifdef HAVE_DIR_STRUCT
1159 /* Extract piece size from filename */
1161 xpm_getsize(name, len, ext)
1172 if ((p=strchr(name, '.')) == NULL ||
1173 StrCaseCmp(p+1, ext) != 0)
1179 while (*p && isdigit(*p))
1186 /* Setup xpm_avail */
1188 xpm_getavail(dirname, ext)
1196 for (i=0; i<MAXSQSIZE; ++i)
1199 if (appData.debugMode)
1200 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1202 dir = opendir(dirname);
1205 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1206 programName, dirname);
1210 while ((ent=readdir(dir)) != NULL) {
1211 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1212 if (i > 0 && i < MAXSQSIZE)
1222 xpm_print_avail(fp, ext)
1228 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1229 for (i=1; i<MAXSQSIZE; ++i) {
1235 /* Return XPM piecesize closest to size */
1237 xpm_closest_to(dirname, size, ext)
1243 int sm_diff = MAXSQSIZE;
1247 xpm_getavail(dirname, ext);
1249 if (appData.debugMode)
1250 xpm_print_avail(stderr, ext);
1252 for (i=1; i<MAXSQSIZE; ++i) {
1255 diff = (diff<0) ? -diff : diff;
1256 if (diff < sm_diff) {
1264 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1270 #else /* !HAVE_DIR_STRUCT */
1271 /* If we are on a system without a DIR struct, we can't
1272 read the directory, so we can't collect a list of
1273 filenames, etc., so we can't do any size-fitting. */
1275 xpm_closest_to(dirname, size, ext)
1280 fprintf(stderr, _("\
1281 Warning: No DIR structure found on this system --\n\
1282 Unable to autosize for XPM/XIM pieces.\n\
1283 Please report this error to %s.\n\
1284 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
1287 #endif /* HAVE_DIR_STRUCT */
1289 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1290 "magenta", "cyan", "white" };
1294 TextColors textColors[(int)NColorClasses];
1296 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1298 parse_color(str, which)
1302 char *p, buf[100], *d;
1305 if (strlen(str) > 99) /* watch bounds on buf */
1310 for (i=0; i<which; ++i) {
1317 /* Could be looking at something like:
1319 .. in which case we want to stop on a comma also */
1320 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1324 return -1; /* Use default for empty field */
1327 if (which == 2 || isdigit(*p))
1330 while (*p && isalpha(*p))
1335 for (i=0; i<8; ++i) {
1336 if (!StrCaseCmp(buf, cnames[i]))
1337 return which? (i+40) : (i+30);
1339 if (!StrCaseCmp(buf, "default")) return -1;
1341 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1346 parse_cpair(cc, str)
1350 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1351 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1356 /* bg and attr are optional */
1357 textColors[(int)cc].bg = parse_color(str, 1);
1358 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1359 textColors[(int)cc].attr = 0;
1365 /* Arrange to catch delete-window events */
1366 Atom wm_delete_window;
1368 CatchDeleteWindow(Widget w, String procname)
1371 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1372 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1373 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1380 XtSetArg(args[0], XtNiconic, False);
1381 XtSetValues(shellWidget, args, 1);
1383 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1386 //---------------------------------------------------------------------------------------------------------
1387 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1390 #define CW_USEDEFAULT (1<<31)
1391 #define ICS_TEXT_MENU_SIZE 90
1392 #define DEBUG_FILE "xboard.debug"
1393 #define SetCurrentDirectory chdir
1394 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1398 // these two must some day move to frontend.h, when they are implemented
1399 Boolean GameListIsUp();
1401 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1404 // front-end part of option handling
1406 // [HGM] This platform-dependent table provides the location for storing the color info
1407 extern char *crWhite, * crBlack;
1411 &appData.whitePieceColor,
1412 &appData.blackPieceColor,
1413 &appData.lightSquareColor,
1414 &appData.darkSquareColor,
1415 &appData.highlightSquareColor,
1416 &appData.premoveHighlightColor,
1417 &appData.lowTimeWarningColor,
1428 // [HGM] font: keep a font for each square size, even non-stndard ones
1429 #define NUM_SIZES 18
1430 #define MAX_SIZE 130
1431 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1432 char *fontTable[NUM_FONTS][MAX_SIZE];
1435 ParseFont(char *name, int number)
1436 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1438 if(sscanf(name, "size%d:", &size)) {
1439 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1440 // defer processing it until we know if it matches our board size
1441 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1442 fontTable[number][size] = strdup(strchr(name, ':')+1);
1443 fontValid[number][size] = True;
1448 case 0: // CLOCK_FONT
1449 appData.clockFont = strdup(name);
1451 case 1: // MESSAGE_FONT
1452 appData.font = strdup(name);
1454 case 2: // COORD_FONT
1455 appData.coordFont = strdup(name);
1460 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1465 { // only 2 fonts currently
1466 appData.clockFont = CLOCK_FONT_NAME;
1467 appData.coordFont = COORD_FONT_NAME;
1468 appData.font = DEFAULT_FONT_NAME;
1473 { // no-op, until we identify the code for this already in XBoard and move it here
1477 ParseColor(int n, char *name)
1478 { // in XBoard, just copy the color-name string
1479 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1483 ParseTextAttribs(ColorClass cc, char *s)
1485 (&appData.colorShout)[cc] = strdup(s);
1489 ParseBoardSize(void *addr, char *name)
1491 appData.boardSize = strdup(name);
1496 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1500 SetCommPortDefaults()
1501 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1504 // [HGM] args: these three cases taken out to stay in front-end
1506 SaveFontArg(FILE *f, ArgDescriptor *ad)
1509 int i, n = (int)(intptr_t)ad->argLoc;
1511 case 0: // CLOCK_FONT
1512 name = appData.clockFont;
1514 case 1: // MESSAGE_FONT
1515 name = appData.font;
1517 case 2: // COORD_FONT
1518 name = appData.coordFont;
1523 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1524 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1525 fontTable[n][squareSize] = strdup(name);
1526 fontValid[n][squareSize] = True;
1529 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1530 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1535 { // nothing to do, as the sounds are at all times represented by their text-string names already
1539 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1540 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1541 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1545 SaveColor(FILE *f, ArgDescriptor *ad)
1546 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1547 if(colorVariable[(int)(intptr_t)ad->argLoc])
1548 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1552 SaveBoardSize(FILE *f, char *name, void *addr)
1553 { // wrapper to shield back-end from BoardSize & sizeInfo
1554 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1558 ParseCommPortSettings(char *s)
1559 { // no such option in XBoard (yet)
1562 extern Widget engineOutputShell;
1565 GetActualPlacement(Widget wg, WindowPlacement *wp)
1575 XtSetArg(args[i], XtNx, &x); i++;
1576 XtSetArg(args[i], XtNy, &y); i++;
1577 XtSetArg(args[i], XtNwidth, &w); i++;
1578 XtSetArg(args[i], XtNheight, &h); i++;
1579 XtGetValues(wg, args, i);
1588 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1589 // In XBoard this will have to wait until awareness of window parameters is implemented
1590 GetActualPlacement(shellWidget, &wpMain);
1591 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1592 if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1593 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1594 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1595 if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1596 if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1600 PrintCommPortSettings(FILE *f, char *name)
1601 { // This option does not exist in XBoard
1605 MySearchPath(char *installDir, char *name, char *fullname)
1606 { // just append installDir and name. Perhaps ExpandPath should be used here?
1607 name = ExpandPathName(name);
1608 if(name && name[0] == '/')
1609 safeStrCpy(fullname, name, MSG_SIZ );
1611 sprintf(fullname, "%s%c%s", installDir, '/', name);
1617 MyGetFullPathName(char *name, char *fullname)
1618 { // should use ExpandPath?
1619 name = ExpandPathName(name);
1620 safeStrCpy(fullname, name, MSG_SIZ );
1625 EnsureOnScreen(int *x, int *y, int minX, int minY)
1632 { // [HGM] args: allows testing if main window is realized from back-end
1633 return xBoardWindow != 0;
1637 PopUpStartupDialog()
1638 { // start menu not implemented in XBoard
1642 ConvertToLine(int argc, char **argv)
1644 static char line[128*1024], buf[1024];
1648 for(i=1; i<argc; i++)
1650 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1651 && argv[i][0] != '{' )
1652 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1654 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1655 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1658 line[strlen(line)-1] = NULLCHAR;
1662 //--------------------------------------------------------------------------------------------
1664 extern Boolean twoBoards, partnerUp;
1667 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1669 #define BoardSize int
1670 void InitDrawingSizes(BoardSize boardSize, int flags)
1671 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1672 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1674 XtGeometryResult gres;
1677 if(!formWidget) return;
1680 * Enable shell resizing.
1682 shellArgs[0].value = (XtArgVal) &w;
1683 shellArgs[1].value = (XtArgVal) &h;
1684 XtGetValues(shellWidget, shellArgs, 2);
1686 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1687 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1688 XtSetValues(shellWidget, &shellArgs[2], 4);
1690 XtSetArg(args[0], XtNdefaultDistance, &sep);
1691 XtGetValues(formWidget, args, 1);
1693 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1694 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1695 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1697 hOffset = boardWidth + 10;
1698 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1699 secondSegments[i] = gridSegments[i];
1700 secondSegments[i].x1 += hOffset;
1701 secondSegments[i].x2 += hOffset;
1704 XtSetArg(args[0], XtNwidth, boardWidth);
1705 XtSetArg(args[1], XtNheight, boardHeight);
1706 XtSetValues(boardWidget, args, 2);
1708 timerWidth = (boardWidth - sep) / 2;
1709 XtSetArg(args[0], XtNwidth, timerWidth);
1710 XtSetValues(whiteTimerWidget, args, 1);
1711 XtSetValues(blackTimerWidget, args, 1);
1713 XawFormDoLayout(formWidget, False);
1715 if (appData.titleInWindow) {
1717 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1718 XtSetArg(args[i], XtNheight, &h); i++;
1719 XtGetValues(titleWidget, args, i);
1721 w = boardWidth - 2*bor;
1723 XtSetArg(args[0], XtNwidth, &w);
1724 XtGetValues(menuBarWidget, args, 1);
1725 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1728 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1729 if (gres != XtGeometryYes && appData.debugMode) {
1731 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1732 programName, gres, w, h, wr, hr);
1736 XawFormDoLayout(formWidget, True);
1739 * Inhibit shell resizing.
1741 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1742 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1743 shellArgs[4].value = shellArgs[2].value = w;
1744 shellArgs[5].value = shellArgs[3].value = h;
1745 XtSetValues(shellWidget, &shellArgs[0], 6);
1747 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1750 for(i=0; i<4; i++) {
1752 for(p=0; p<=(int)WhiteKing; p++)
1753 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1754 if(gameInfo.variant == VariantShogi) {
1755 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1756 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1757 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1758 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1759 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1762 if(gameInfo.variant == VariantGothic) {
1763 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1766 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1767 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1768 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1771 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1772 for(p=0; p<=(int)WhiteKing; p++)
1773 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1774 if(gameInfo.variant == VariantShogi) {
1775 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1776 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1777 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1778 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1779 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1782 if(gameInfo.variant == VariantGothic) {
1783 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1786 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1787 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1788 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1793 for(i=0; i<2; i++) {
1795 for(p=0; p<=(int)WhiteKing; p++)
1796 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1797 if(gameInfo.variant == VariantShogi) {
1798 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1799 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1800 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1801 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1802 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1805 if(gameInfo.variant == VariantGothic) {
1806 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1809 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1810 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1811 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1821 void ParseIcsTextColors()
1822 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1823 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1824 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1825 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1826 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1827 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1828 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1829 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1830 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1831 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1832 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1834 if (appData.colorize) {
1836 _("%s: can't parse color names; disabling colorization\n"),
1839 appData.colorize = FALSE;
1844 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1845 XrmValue vFrom, vTo;
1846 int forceMono = False;
1848 if (!appData.monoMode) {
1849 vFrom.addr = (caddr_t) appData.lightSquareColor;
1850 vFrom.size = strlen(appData.lightSquareColor);
1851 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1852 if (vTo.addr == NULL) {
1853 appData.monoMode = True;
1856 lightSquareColor = *(Pixel *) vTo.addr;
1859 if (!appData.monoMode) {
1860 vFrom.addr = (caddr_t) appData.darkSquareColor;
1861 vFrom.size = strlen(appData.darkSquareColor);
1862 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1863 if (vTo.addr == NULL) {
1864 appData.monoMode = True;
1867 darkSquareColor = *(Pixel *) vTo.addr;
1870 if (!appData.monoMode) {
1871 vFrom.addr = (caddr_t) appData.whitePieceColor;
1872 vFrom.size = strlen(appData.whitePieceColor);
1873 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1874 if (vTo.addr == NULL) {
1875 appData.monoMode = True;
1878 whitePieceColor = *(Pixel *) vTo.addr;
1881 if (!appData.monoMode) {
1882 vFrom.addr = (caddr_t) appData.blackPieceColor;
1883 vFrom.size = strlen(appData.blackPieceColor);
1884 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1885 if (vTo.addr == NULL) {
1886 appData.monoMode = True;
1889 blackPieceColor = *(Pixel *) vTo.addr;
1893 if (!appData.monoMode) {
1894 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1895 vFrom.size = strlen(appData.highlightSquareColor);
1896 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1897 if (vTo.addr == NULL) {
1898 appData.monoMode = True;
1901 highlightSquareColor = *(Pixel *) vTo.addr;
1905 if (!appData.monoMode) {
1906 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1907 vFrom.size = strlen(appData.premoveHighlightColor);
1908 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1909 if (vTo.addr == NULL) {
1910 appData.monoMode = True;
1913 premoveHighlightColor = *(Pixel *) vTo.addr;
1921 { // [HGM] taken out of main
1923 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1924 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1925 appData.bitmapDirectory = DEF_BITMAP_DIR;
1927 if (appData.bitmapDirectory[0] != NULLCHAR) {
1931 CreateXPMBoard(appData.liteBackTextureFile, 1);
1932 CreateXPMBoard(appData.darkBackTextureFile, 0);
1936 /* Create regular pieces */
1937 if (!useImages) CreatePieces();
1946 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1947 XSetWindowAttributes window_attributes;
1949 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1950 XrmValue vFrom, vTo;
1951 XtGeometryResult gres;
1954 int forceMono = False;
1956 srandom(time(0)); // [HGM] book: make random truly random
1958 setbuf(stdout, NULL);
1959 setbuf(stderr, NULL);
1962 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1963 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1967 programName = strrchr(argv[0], '/');
1968 if (programName == NULL)
1969 programName = argv[0];
1974 XtSetLanguageProc(NULL, NULL, NULL);
1975 bindtextdomain(PACKAGE, LOCALEDIR);
1976 textdomain(PACKAGE);
1980 XtAppInitialize(&appContext, "XBoard", shellOptions,
1981 XtNumber(shellOptions),
1982 &argc, argv, xboardResources, NULL, 0);
1983 appData.boardSize = "";
1984 InitAppData(ConvertToLine(argc, argv));
1986 if (p == NULL) p = "/tmp";
1987 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1988 gameCopyFilename = (char*) malloc(i);
1989 gamePasteFilename = (char*) malloc(i);
1990 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1991 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1993 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1994 clientResources, XtNumber(clientResources),
1997 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1998 static char buf[MSG_SIZ];
1999 EscapeExpand(buf, appData.firstInitString);
2000 appData.firstInitString = strdup(buf);
2001 EscapeExpand(buf, appData.secondInitString);
2002 appData.secondInitString = strdup(buf);
2003 EscapeExpand(buf, appData.firstComputerString);
2004 appData.firstComputerString = strdup(buf);
2005 EscapeExpand(buf, appData.secondComputerString);
2006 appData.secondComputerString = strdup(buf);
2009 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
2012 if (chdir(chessDir) != 0) {
2013 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2019 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2020 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2021 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
2022 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2025 setbuf(debugFP, NULL);
2028 /* [HGM,HR] make sure board size is acceptable */
2029 if(appData.NrFiles > BOARD_FILES ||
2030 appData.NrRanks > BOARD_RANKS )
2031 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
2034 /* This feature does not work; animation needs a rewrite */
2035 appData.highlightDragging = FALSE;
2039 xDisplay = XtDisplay(shellWidget);
2040 xScreen = DefaultScreen(xDisplay);
2041 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2043 gameInfo.variant = StringToVariant(appData.variant);
2044 InitPosition(FALSE);
2047 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2049 if (isdigit(appData.boardSize[0])) {
2050 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2051 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2052 &fontPxlSize, &smallLayout, &tinyLayout);
2054 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2055 programName, appData.boardSize);
2059 /* Find some defaults; use the nearest known size */
2060 SizeDefaults *szd, *nearest;
2061 int distance = 99999;
2062 nearest = szd = sizeDefaults;
2063 while (szd->name != NULL) {
2064 if (abs(szd->squareSize - squareSize) < distance) {
2066 distance = abs(szd->squareSize - squareSize);
2067 if (distance == 0) break;
2071 if (i < 2) lineGap = nearest->lineGap;
2072 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2073 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2074 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2075 if (i < 6) smallLayout = nearest->smallLayout;
2076 if (i < 7) tinyLayout = nearest->tinyLayout;
2079 SizeDefaults *szd = sizeDefaults;
2080 if (*appData.boardSize == NULLCHAR) {
2081 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2082 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2085 if (szd->name == NULL) szd--;
2086 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2088 while (szd->name != NULL &&
2089 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2090 if (szd->name == NULL) {
2091 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2092 programName, appData.boardSize);
2096 squareSize = szd->squareSize;
2097 lineGap = szd->lineGap;
2098 clockFontPxlSize = szd->clockFontPxlSize;
2099 coordFontPxlSize = szd->coordFontPxlSize;
2100 fontPxlSize = szd->fontPxlSize;
2101 smallLayout = szd->smallLayout;
2102 tinyLayout = szd->tinyLayout;
2103 // [HGM] font: use defaults from settings file if available and not overruled
2105 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2106 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2107 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2108 appData.font = fontTable[MESSAGE_FONT][squareSize];
2109 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2110 appData.coordFont = fontTable[COORD_FONT][squareSize];
2112 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2113 if (strlen(appData.pixmapDirectory) > 0) {
2114 p = ExpandPathName(appData.pixmapDirectory);
2116 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2117 appData.pixmapDirectory);
2120 if (appData.debugMode) {
2121 fprintf(stderr, _("\
2122 XBoard square size (hint): %d\n\
2123 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2125 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2126 if (appData.debugMode) {
2127 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2130 defaultLineGap = lineGap;
2131 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2133 /* [HR] height treated separately (hacked) */
2134 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2135 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2136 if (appData.showJail == 1) {
2137 /* Jail on top and bottom */
2138 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2139 XtSetArg(boardArgs[2], XtNheight,
2140 boardHeight + 2*(lineGap + squareSize));
2141 } else if (appData.showJail == 2) {
2143 XtSetArg(boardArgs[1], XtNwidth,
2144 boardWidth + 2*(lineGap + squareSize));
2145 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2148 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2149 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2153 * Determine what fonts to use.
2156 appData.font = InsertPxlSize(appData.font, fontPxlSize);
2157 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
2158 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
2159 fontSet = CreateFontSet(appData.font);
2160 clockFontSet = CreateFontSet(appData.clockFont);
2162 appData.font = FindFont(appData.font, fontPxlSize);
2163 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2164 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2165 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2166 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2168 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2169 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2170 countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
2171 countFontStruct = XQueryFont(xDisplay, countFontID);
2173 xdb = XtDatabase(xDisplay);
2175 XrmPutLineResource(&xdb, "*international: True");
2176 vTo.size = sizeof(XFontSet);
2177 vTo.addr = (XtPointer) &fontSet;
2178 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
2180 XrmPutStringResource(&xdb, "*font", appData.font);
2184 * Detect if there are not enough colors available and adapt.
2186 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2187 appData.monoMode = True;
2190 forceMono = MakeColors();
2193 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2195 appData.monoMode = True;
2198 if (appData.lowTimeWarning && !appData.monoMode) {
2199 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2200 vFrom.size = strlen(appData.lowTimeWarningColor);
2201 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2202 if (vTo.addr == NULL)
2203 appData.monoMode = True;
2205 lowTimeWarningColor = *(Pixel *) vTo.addr;
2208 if (appData.monoMode && appData.debugMode) {
2209 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2210 (unsigned long) XWhitePixel(xDisplay, xScreen),
2211 (unsigned long) XBlackPixel(xDisplay, xScreen));
2214 ParseIcsTextColors();
2215 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2216 textColors[ColorNone].attr = 0;
2218 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2224 layoutName = "tinyLayout";
2225 } else if (smallLayout) {
2226 layoutName = "smallLayout";
2228 layoutName = "normalLayout";
2230 /* Outer layoutWidget is there only to provide a name for use in
2231 resources that depend on the layout style */
2233 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2234 layoutArgs, XtNumber(layoutArgs));
2236 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2237 formArgs, XtNumber(formArgs));
2238 XtSetArg(args[0], XtNdefaultDistance, &sep);
2239 XtGetValues(formWidget, args, 1);
2242 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2243 XtSetArg(args[0], XtNtop, XtChainTop);
2244 XtSetArg(args[1], XtNbottom, XtChainTop);
2245 XtSetArg(args[2], XtNright, XtChainLeft);
2246 XtSetValues(menuBarWidget, args, 3);
2248 widgetList[j++] = whiteTimerWidget =
2249 XtCreateWidget("whiteTime", labelWidgetClass,
2250 formWidget, timerArgs, XtNumber(timerArgs));
2252 XtSetArg(args[0], XtNfontSet, clockFontSet);
2254 XtSetArg(args[0], XtNfont, clockFontStruct);
2256 XtSetArg(args[1], XtNtop, XtChainTop);
2257 XtSetArg(args[2], XtNbottom, XtChainTop);
2258 XtSetValues(whiteTimerWidget, args, 3);
2260 widgetList[j++] = blackTimerWidget =
2261 XtCreateWidget("blackTime", labelWidgetClass,
2262 formWidget, timerArgs, XtNumber(timerArgs));
2264 XtSetArg(args[0], XtNfontSet, clockFontSet);
2266 XtSetArg(args[0], XtNfont, clockFontStruct);
2268 XtSetArg(args[1], XtNtop, XtChainTop);
2269 XtSetArg(args[2], XtNbottom, XtChainTop);
2270 XtSetValues(blackTimerWidget, args, 3);
2272 if (appData.titleInWindow) {
2273 widgetList[j++] = titleWidget =
2274 XtCreateWidget("title", labelWidgetClass, formWidget,
2275 titleArgs, XtNumber(titleArgs));
2276 XtSetArg(args[0], XtNtop, XtChainTop);
2277 XtSetArg(args[1], XtNbottom, XtChainTop);
2278 XtSetValues(titleWidget, args, 2);
2281 if (appData.showButtonBar) {
2282 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2283 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2284 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2285 XtSetArg(args[2], XtNtop, XtChainTop);
2286 XtSetArg(args[3], XtNbottom, XtChainTop);
2287 XtSetValues(buttonBarWidget, args, 4);
2290 widgetList[j++] = messageWidget =
2291 XtCreateWidget("message", labelWidgetClass, formWidget,
2292 messageArgs, XtNumber(messageArgs));
2293 XtSetArg(args[0], XtNtop, XtChainTop);
2294 XtSetArg(args[1], XtNbottom, XtChainTop);
2295 XtSetValues(messageWidget, args, 2);
2297 widgetList[j++] = boardWidget =
2298 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2299 XtNumber(boardArgs));
2301 XtManageChildren(widgetList, j);
2303 timerWidth = (boardWidth - sep) / 2;
2304 XtSetArg(args[0], XtNwidth, timerWidth);
2305 XtSetValues(whiteTimerWidget, args, 1);
2306 XtSetValues(blackTimerWidget, args, 1);
2308 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2309 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2310 XtGetValues(whiteTimerWidget, args, 2);
2312 if (appData.showButtonBar) {
2313 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2314 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2315 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2319 * formWidget uses these constraints but they are stored
2323 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2324 XtSetValues(menuBarWidget, args, i);
2325 if (appData.titleInWindow) {
2328 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2329 XtSetValues(whiteTimerWidget, args, i);
2331 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2332 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2333 XtSetValues(blackTimerWidget, args, i);
2335 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2336 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2337 XtSetValues(titleWidget, args, i);
2339 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2340 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2341 XtSetValues(messageWidget, args, i);
2342 if (appData.showButtonBar) {
2344 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2345 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2346 XtSetValues(buttonBarWidget, args, i);
2350 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2351 XtSetValues(whiteTimerWidget, args, i);
2353 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2354 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2355 XtSetValues(blackTimerWidget, args, i);
2357 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2358 XtSetValues(titleWidget, args, i);
2360 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2361 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2362 XtSetValues(messageWidget, args, i);
2363 if (appData.showButtonBar) {
2365 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2366 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2367 XtSetValues(buttonBarWidget, args, i);
2372 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2373 XtSetValues(whiteTimerWidget, args, i);
2375 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2376 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2377 XtSetValues(blackTimerWidget, args, i);
2379 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2380 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2381 XtSetValues(messageWidget, args, i);
2382 if (appData.showButtonBar) {
2384 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2385 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2386 XtSetValues(buttonBarWidget, args, i);
2390 XtSetArg(args[0], XtNfromVert, messageWidget);
2391 XtSetArg(args[1], XtNtop, XtChainTop);
2392 XtSetArg(args[2], XtNbottom, XtChainBottom);
2393 XtSetArg(args[3], XtNleft, XtChainLeft);
2394 XtSetArg(args[4], XtNright, XtChainRight);
2395 XtSetValues(boardWidget, args, 5);
2397 XtRealizeWidget(shellWidget);
2400 XtSetArg(args[0], XtNx, wpMain.x);
2401 XtSetArg(args[1], XtNy, wpMain.y);
2402 XtSetValues(shellWidget, args, 2);
2406 * Correct the width of the message and title widgets.
2407 * It is not known why some systems need the extra fudge term.
2408 * The value "2" is probably larger than needed.
2410 XawFormDoLayout(formWidget, False);
2412 #define WIDTH_FUDGE 2
2414 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2415 XtSetArg(args[i], XtNheight, &h); i++;
2416 XtGetValues(messageWidget, args, i);
2417 if (appData.showButtonBar) {
2419 XtSetArg(args[i], XtNwidth, &w); i++;
2420 XtGetValues(buttonBarWidget, args, i);
2421 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2423 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2426 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2427 if (gres != XtGeometryYes && appData.debugMode) {
2428 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2429 programName, gres, w, h, wr, hr);
2432 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2433 /* The size used for the child widget in layout lags one resize behind
2434 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2436 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2437 if (gres != XtGeometryYes && appData.debugMode) {
2438 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2439 programName, gres, w, h, wr, hr);
2442 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2443 XtSetArg(args[1], XtNright, XtChainRight);
2444 XtSetValues(messageWidget, args, 2);
2446 if (appData.titleInWindow) {
2448 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2449 XtSetArg(args[i], XtNheight, &h); i++;
2450 XtGetValues(titleWidget, args, i);
2452 w = boardWidth - 2*bor;
2454 XtSetArg(args[0], XtNwidth, &w);
2455 XtGetValues(menuBarWidget, args, 1);
2456 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2459 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2460 if (gres != XtGeometryYes && appData.debugMode) {
2462 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2463 programName, gres, w, h, wr, hr);
2466 XawFormDoLayout(formWidget, True);
2468 xBoardWindow = XtWindow(boardWidget);
2470 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2471 // not need to go into InitDrawingSizes().
2475 * Create X checkmark bitmap and initialize option menu checks.
2477 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2478 checkmark_bits, checkmark_width, checkmark_height);
2479 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2480 #ifndef OPTIONSDIALOG
2481 if (appData.alwaysPromoteToQueen) {
2482 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2485 if (appData.animateDragging) {
2486 XtSetValues(XtNameToWidget(menuBarWidget,
2487 "menuOptions.Animate Dragging"),
2490 if (appData.animate) {
2491 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2494 if (appData.autoCallFlag) {
2495 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2498 if (appData.autoFlipView) {
2499 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2502 if (appData.blindfold) {
2503 XtSetValues(XtNameToWidget(menuBarWidget,
2504 "menuOptions.Blindfold"), args, 1);
2506 if (appData.flashCount > 0) {
2507 XtSetValues(XtNameToWidget(menuBarWidget,
2508 "menuOptions.Flash Moves"),
2512 if (appData.highlightDragging) {
2513 XtSetValues(XtNameToWidget(menuBarWidget,
2514 "menuOptions.Highlight Dragging"),
2518 if (appData.highlightLastMove) {
2519 XtSetValues(XtNameToWidget(menuBarWidget,
2520 "menuOptions.Highlight Last Move"),
2523 if (appData.highlightMoveWithArrow) {
2524 XtSetValues(XtNameToWidget(menuBarWidget,
2525 "menuOptions.Arrow"),
2528 // if (appData.icsAlarm) {
2529 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2532 if (appData.ringBellAfterMoves) {
2533 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2536 if (appData.oneClick) {
2537 XtSetValues(XtNameToWidget(menuBarWidget,
2538 "menuOptions.OneClick"), args, 1);
2540 if (appData.periodicUpdates) {
2541 XtSetValues(XtNameToWidget(menuBarWidget,
2542 "menuOptions.Periodic Updates"), args, 1);
2544 if (appData.ponderNextMove) {
2545 XtSetValues(XtNameToWidget(menuBarWidget,
2546 "menuOptions.Ponder Next Move"), args, 1);
2548 if (appData.popupExitMessage) {
2549 XtSetValues(XtNameToWidget(menuBarWidget,
2550 "menuOptions.Popup Exit Message"), args, 1);
2552 if (appData.popupMoveErrors) {
2553 XtSetValues(XtNameToWidget(menuBarWidget,
2554 "menuOptions.Popup Move Errors"), args, 1);
2556 // if (appData.premove) {
2557 // XtSetValues(XtNameToWidget(menuBarWidget,
2558 // "menuOptions.Premove"), args, 1);
2560 if (appData.showCoords) {
2561 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2564 if (appData.hideThinkingFromHuman) {
2565 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2568 if (appData.testLegality) {
2569 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2573 if (saveSettingsOnExit) {
2574 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2581 ReadBitmap(&wIconPixmap, "icon_white.bm",
2582 icon_white_bits, icon_white_width, icon_white_height);
2583 ReadBitmap(&bIconPixmap, "icon_black.bm",
2584 icon_black_bits, icon_black_width, icon_black_height);
2585 iconPixmap = wIconPixmap;
2587 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2588 XtSetValues(shellWidget, args, i);
2591 * Create a cursor for the board widget.
2593 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2594 XChangeWindowAttributes(xDisplay, xBoardWindow,
2595 CWCursor, &window_attributes);
2598 * Inhibit shell resizing.
2600 shellArgs[0].value = (XtArgVal) &w;
2601 shellArgs[1].value = (XtArgVal) &h;
2602 XtGetValues(shellWidget, shellArgs, 2);
2603 shellArgs[4].value = shellArgs[2].value = w;
2604 shellArgs[5].value = shellArgs[3].value = h;
2605 XtSetValues(shellWidget, &shellArgs[2], 4);
2606 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2607 marginH = h - boardHeight;
2609 CatchDeleteWindow(shellWidget, "QuitProc");
2617 if (appData.animate || appData.animateDragging)
2620 XtAugmentTranslations(formWidget,
2621 XtParseTranslationTable(globalTranslations));
2622 XtAugmentTranslations(boardWidget,
2623 XtParseTranslationTable(boardTranslations));
2624 XtAugmentTranslations(whiteTimerWidget,
2625 XtParseTranslationTable(whiteTranslations));
2626 XtAugmentTranslations(blackTimerWidget,
2627 XtParseTranslationTable(blackTranslations));
2629 /* Why is the following needed on some versions of X instead
2630 * of a translation? */
2631 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2632 (XtEventHandler) EventProc, NULL);
2634 XtAddEventHandler(formWidget, KeyPressMask, False,
2635 (XtEventHandler) MoveTypeInProc, NULL);
2637 /* [AS] Restore layout */
2638 if( wpMoveHistory.visible ) {
2642 if( wpEvalGraph.visible )
2647 if( wpEngineOutput.visible ) {
2648 EngineOutputPopUp();
2653 if (errorExitStatus == -1) {
2654 if (appData.icsActive) {
2655 /* We now wait until we see "login:" from the ICS before
2656 sending the logon script (problems with timestamp otherwise) */
2657 /*ICSInitScript();*/
2658 if (appData.icsInputBox) ICSInputBoxPopUp();
2662 signal(SIGWINCH, TermSizeSigHandler);
2664 signal(SIGINT, IntSigHandler);
2665 signal(SIGTERM, IntSigHandler);
2666 if (*appData.cmailGameName != NULLCHAR) {
2667 signal(SIGUSR1, CmailSigHandler);
2670 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2672 // XtSetKeyboardFocus(shellWidget, formWidget);
2673 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2675 XtAppMainLoop(appContext);
2676 if (appData.debugMode) fclose(debugFP); // [DM] debug
2683 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2684 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2686 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2687 unlink(gameCopyFilename);
2688 unlink(gamePasteFilename);
2691 RETSIGTYPE TermSizeSigHandler(int sig)
2704 CmailSigHandler(sig)
2710 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2712 /* Activate call-back function CmailSigHandlerCallBack() */
2713 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2715 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2719 CmailSigHandlerCallBack(isr, closure, message, count, error)
2727 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2729 /**** end signal code ****/
2735 /* try to open the icsLogon script, either in the location given
2736 * or in the users HOME directory
2743 f = fopen(appData.icsLogon, "r");
2746 homedir = getenv("HOME");
2747 if (homedir != NULL)
2749 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2750 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2751 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2752 f = fopen(buf, "r");
2757 ProcessICSInitScript(f);
2759 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2782 if (!menuBarWidget) return;
2783 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2785 DisplayError("menuEdit.Revert", 0);
2787 XtSetSensitive(w, !grey);
2789 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2791 DisplayError("menuEdit.Annotate", 0);
2793 XtSetSensitive(w, !grey);
2798 SetMenuEnables(enab)
2802 if (!menuBarWidget) return;
2803 while (enab->name != NULL) {
2804 w = XtNameToWidget(menuBarWidget, enab->name);
2806 DisplayError(enab->name, 0);
2808 XtSetSensitive(w, enab->value);
2814 Enables icsEnables[] = {
2815 { "menuFile.Mail Move", False },
2816 { "menuFile.Reload CMail Message", False },
2817 { "menuMode.Machine Black", False },
2818 { "menuMode.Machine White", False },
2819 { "menuMode.Analysis Mode", False },
2820 { "menuMode.Analyze File", False },
2821 { "menuMode.Two Machines", False },
2822 { "menuMode.Machine Match", False },
2824 { "menuEngine.Hint", False },
2825 { "menuEngine.Book", False },
2826 { "menuEngine.Move Now", False },
2827 #ifndef OPTIONSDIALOG
2828 { "menuOptions.Periodic Updates", False },
2829 { "menuOptions.Hide Thinking", False },
2830 { "menuOptions.Ponder Next Move", False },
2833 { "menuEngine.Engine #1 Settings", False },
2834 { "menuEngine.Engine #2 Settings", False },
2835 { "menuEngine.Load Engine", False },
2836 { "menuEdit.Annotate", False },
2837 { "menuOptions.Match", False },
2841 Enables ncpEnables[] = {
2842 { "menuFile.Mail Move", False },
2843 { "menuFile.Reload CMail Message", False },
2844 { "menuMode.Machine White", False },
2845 { "menuMode.Machine Black", False },
2846 { "menuMode.Analysis Mode", False },
2847 { "menuMode.Analyze File", False },
2848 { "menuMode.Two Machines", False },
2849 { "menuMode.Machine Match", False },
2850 { "menuMode.ICS Client", False },
2851 { "menuView.ICStex", False },
2852 { "menuView.ICS Input Box", False },
2853 { "Action", False },
2854 { "menuEdit.Revert", False },
2855 { "menuEdit.Annotate", False },
2856 { "menuEngine.Engine #1 Settings", False },
2857 { "menuEngine.Engine #2 Settings", False },
2858 { "menuEngine.Move Now", False },
2859 { "menuEngine.Retract Move", False },
2860 { "menuOptions.ICS", False },
2861 #ifndef OPTIONSDIALOG
2862 { "menuOptions.Auto Flag", False },
2863 { "menuOptions.Auto Flip View", False },
2864 // { "menuOptions.ICS Alarm", False },
2865 { "menuOptions.Move Sound", False },
2866 { "menuOptions.Hide Thinking", False },
2867 { "menuOptions.Periodic Updates", False },
2868 { "menuOptions.Ponder Next Move", False },
2870 { "menuEngine.Hint", False },
2871 { "menuEngine.Book", False },
2875 Enables gnuEnables[] = {
2876 { "menuMode.ICS Client", False },
2877 { "menuView.ICStex", False },
2878 { "menuView.ICS Input Box", False },
2879 { "menuAction.Accept", False },
2880 { "menuAction.Decline", False },
2881 { "menuAction.Rematch", False },
2882 { "menuAction.Adjourn", False },
2883 { "menuAction.Stop Examining", False },
2884 { "menuAction.Stop Observing", False },
2885 { "menuAction.Upload to Examine", False },
2886 { "menuEdit.Revert", False },
2887 { "menuEdit.Annotate", False },
2888 { "menuOptions.ICS", False },
2890 /* The next two options rely on SetCmailMode being called *after* */
2891 /* SetGNUMode so that when GNU is being used to give hints these */
2892 /* menu options are still available */
2894 { "menuFile.Mail Move", False },
2895 { "menuFile.Reload CMail Message", False },
2896 // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2897 { "menuMode.Machine White", True },
2898 { "menuMode.Machine Black", True },
2899 { "menuMode.Analysis Mode", True },
2900 { "menuMode.Analyze File", True },
2901 { "menuMode.Two Machines", True },
2902 { "menuMode.Machine Match", True },
2903 { "menuEngine.Engine #1 Settings", True },
2904 { "menuEngine.Engine #2 Settings", True },
2905 { "menuEngine.Hint", True },
2906 { "menuEngine.Book", True },
2907 { "menuEngine.Move Now", True },
2908 { "menuEngine.Retract Move", True },
2913 Enables cmailEnables[] = {
2915 { "menuAction.Call Flag", False },
2916 { "menuAction.Draw", True },
2917 { "menuAction.Adjourn", False },
2918 { "menuAction.Abort", False },
2919 { "menuAction.Stop Observing", False },
2920 { "menuAction.Stop Examining", False },
2921 { "menuFile.Mail Move", True },
2922 { "menuFile.Reload CMail Message", True },
2926 Enables trainingOnEnables[] = {
2927 { "menuMode.Edit Comment", False },
2928 { "menuMode.Pause", False },
2929 { "menuEdit.Forward", False },
2930 { "menuEdit.Backward", False },
2931 { "menuEdit.Forward to End", False },
2932 { "menuEdit.Back to Start", False },
2933 { "menuEngine.Move Now", False },
2934 { "menuEdit.Truncate Game", False },
2938 Enables trainingOffEnables[] = {
2939 { "menuMode.Edit Comment", True },
2940 { "menuMode.Pause", True },
2941 { "menuEdit.Forward", True },
2942 { "menuEdit.Backward", True },
2943 { "menuEdit.Forward to End", True },
2944 { "menuEdit.Back to Start", True },
2945 { "menuEngine.Move Now", True },
2946 { "menuEdit.Truncate Game", True },
2950 Enables machineThinkingEnables[] = {
2951 { "menuFile.Load Game", False },
2952 // { "menuFile.Load Next Game", False },
2953 // { "menuFile.Load Previous Game", False },
2954 // { "menuFile.Reload Same Game", False },
2955 { "menuEdit.Paste Game", False },
2956 { "menuFile.Load Position", False },
2957 // { "menuFile.Load Next Position", False },
2958 // { "menuFile.Load Previous Position", False },
2959 // { "menuFile.Reload Same Position", False },
2960 { "menuEdit.Paste Position", False },
2961 { "menuMode.Machine White", False },
2962 { "menuMode.Machine Black", False },
2963 { "menuMode.Two Machines", False },
2964 // { "menuMode.Machine Match", False },
2965 { "menuEngine.Retract Move", False },
2969 Enables userThinkingEnables[] = {
2970 { "menuFile.Load Game", True },
2971 // { "menuFile.Load Next Game", True },
2972 // { "menuFile.Load Previous Game", True },
2973 // { "menuFile.Reload Same Game", True },
2974 { "menuEdit.Paste Game", True },
2975 { "menuFile.Load Position", True },
2976 // { "menuFile.Load Next Position", True },
2977 // { "menuFile.Load Previous Position", True },
2978 // { "menuFile.Reload Same Position", True },
2979 { "menuEdit.Paste Position", True },
2980 { "menuMode.Machine White", True },
2981 { "menuMode.Machine Black", True },
2982 { "menuMode.Two Machines", True },
2983 // { "menuMode.Machine Match", True },
2984 { "menuEngine.Retract Move", True },
2990 SetMenuEnables(icsEnables);
2993 if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
2994 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2995 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
3003 SetMenuEnables(ncpEnables);
3009 SetMenuEnables(gnuEnables);
3015 SetMenuEnables(cmailEnables);
3021 SetMenuEnables(trainingOnEnables);
3022 if (appData.showButtonBar) {
3023 XtSetSensitive(buttonBarWidget, False);
3029 SetTrainingModeOff()
3031 SetMenuEnables(trainingOffEnables);
3032 if (appData.showButtonBar) {
3033 XtSetSensitive(buttonBarWidget, True);
3038 SetUserThinkingEnables()
3040 if (appData.noChessProgram) return;
3041 SetMenuEnables(userThinkingEnables);
3045 SetMachineThinkingEnables()
3047 if (appData.noChessProgram) return;
3048 SetMenuEnables(machineThinkingEnables);
3050 case MachinePlaysBlack:
3051 case MachinePlaysWhite:
3052 case TwoMachinesPlay:
3053 XtSetSensitive(XtNameToWidget(menuBarWidget,
3054 ModeToWidgetName(gameMode)), True);
3061 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3062 #define HISTORY_SIZE 64
3063 static char *history[HISTORY_SIZE];
3064 int histIn = 0, histP = 0;
3067 SaveInHistory(char *cmd)
3069 if (history[histIn] != NULL) {
3070 free(history[histIn]);
3071 history[histIn] = NULL;
3073 if (*cmd == NULLCHAR) return;
3074 history[histIn] = StrSave(cmd);
3075 histIn = (histIn + 1) % HISTORY_SIZE;
3076 if (history[histIn] != NULL) {
3077 free(history[histIn]);
3078 history[histIn] = NULL;
3084 PrevInHistory(char *cmd)
3087 if (histP == histIn) {
3088 if (history[histIn] != NULL) free(history[histIn]);
3089 history[histIn] = StrSave(cmd);
3091 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3092 if (newhp == histIn || history[newhp] == NULL) return NULL;
3094 return history[histP];
3100 if (histP == histIn) return NULL;
3101 histP = (histP + 1) % HISTORY_SIZE;
3102 return history[histP];
3104 // end of borrowed code
3106 #define Abs(n) ((n)<0 ? -(n) : (n))
3110 InsertPxlSize(pattern, targetPxlSize)
3114 char *base_fnt_lst, strInt[3], *p;
3116 base_fnt_lst = calloc(1, strlen(pattern) + 3);
3117 snprintf(strInt, sizeof(strInt)/sizeof(strInt[0]), "%d", targetPxlSize);
3118 p = strstr(pattern, "--");
3120 /* Can't insert size; use string as-is */
3123 strncpy(base_fnt_lst, pattern, p - pattern + 2);
3124 strcat(base_fnt_lst, strInt);
3125 strcat(base_fnt_lst, strchr(p + 2, '-'));
3126 return base_fnt_lst;
3130 CreateFontSet(base_fnt_lst)
3134 char **missing_list;
3138 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
3139 &missing_list, &missing_count, &def_string);
3140 if (missing_count > 0 && appData.debugMode) {
3142 for (i = 0; i < missing_count; i++) {
3143 fprintf(debugFP, _("Missing charset %s for %s (usually harmless)\n"),
3144 missing_list[i], base_fnt_lst);
3147 if (fntSet == NULL) {
3148 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
3153 #else // not ENABLE_NLS
3155 * Find a font that matches "pattern" that is as close as
3156 * possible to the targetPxlSize. Prefer fonts that are k
3157 * pixels smaller to fonts that are k pixels larger. The
3158 * pattern must be in the X Consortium standard format,
3159 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3160 * The return value should be freed with XtFree when no
3164 FindFont(pattern, targetPxlSize)
3168 char **fonts, *p, *best, *scalable, *scalableTail;
3169 int i, j, nfonts, minerr, err, pxlSize;
3171 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3173 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3174 programName, pattern);
3181 for (i=0; i<nfonts; i++) {
3184 if (*p != '-') continue;
3186 if (*p == NULLCHAR) break;
3187 if (*p++ == '-') j++;
3189 if (j < 7) continue;
3192 scalable = fonts[i];
3195 err = pxlSize - targetPxlSize;
3196 if (Abs(err) < Abs(minerr) ||
3197 (minerr > 0 && err < 0 && -err == minerr)) {
3203 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3204 /* If the error is too big and there is a scalable font,
3205 use the scalable font. */
3206 int headlen = scalableTail - scalable;
3207 p = (char *) XtMalloc(strlen(scalable) + 10);
3208 while (isdigit(*scalableTail)) scalableTail++;
3209 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3211 p = (char *) XtMalloc(strlen(best) + 2);
3212 safeStrCpy(p, best, strlen(best)+1 );
3214 if (appData.debugMode) {
3215 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3216 pattern, targetPxlSize, p);
3218 XFreeFontNames(fonts);
3224 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3225 // must be called before all non-first callse to CreateGCs()
3226 XtReleaseGC(shellWidget, highlineGC);
3227 XtReleaseGC(shellWidget, lightSquareGC);
3228 XtReleaseGC(shellWidget, darkSquareGC);
3229 XtReleaseGC(shellWidget, lineGC);
3230 if (appData.monoMode) {
3231 if (DefaultDepth(xDisplay, xScreen) == 1) {
3232 XtReleaseGC(shellWidget, wbPieceGC);
3234 XtReleaseGC(shellWidget, bwPieceGC);
3237 XtReleaseGC(shellWidget, prelineGC);
3238 XtReleaseGC(shellWidget, jailSquareGC);
3239 XtReleaseGC(shellWidget, wdPieceGC);
3240 XtReleaseGC(shellWidget, wlPieceGC);
3241 XtReleaseGC(shellWidget, wjPieceGC);
3242 XtReleaseGC(shellWidget, bdPieceGC);
3243 XtReleaseGC(shellWidget, blPieceGC);
3244 XtReleaseGC(shellWidget, bjPieceGC);
3248 void CreateGCs(int redo)
3250 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3251 | GCBackground | GCFunction | GCPlaneMask;
3252 XGCValues gc_values;
3255 gc_values.plane_mask = AllPlanes;
3256 gc_values.line_width = lineGap;
3257 gc_values.line_style = LineSolid;
3258 gc_values.function = GXcopy;
3261 DeleteGCs(); // called a second time; clean up old GCs first
3262 } else { // [HGM] grid and font GCs created on first call only
3263 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3264 gc_values.background = XWhitePixel(xDisplay, xScreen);
3265 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3266 XSetFont(xDisplay, coordGC, coordFontID);
3268 // [HGM] make font for holdings counts (white on black)
3269 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3270 gc_values.background = XBlackPixel(xDisplay, xScreen);
3271 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3272 XSetFont(xDisplay, countGC, countFontID);
3274 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3275 gc_values.background = XBlackPixel(xDisplay, xScreen);
3276 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3278 if (appData.monoMode) {
3279 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3280 gc_values.background = XWhitePixel(xDisplay, xScreen);
3281 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3283 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3284 gc_values.background = XBlackPixel(xDisplay, xScreen);
3285 lightSquareGC = wbPieceGC
3286 = XtGetGC(shellWidget, value_mask, &gc_values);
3288 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3289 gc_values.background = XWhitePixel(xDisplay, xScreen);
3290 darkSquareGC = bwPieceGC
3291 = XtGetGC(shellWidget, value_mask, &gc_values);
3293 if (DefaultDepth(xDisplay, xScreen) == 1) {
3294 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3295 gc_values.function = GXcopyInverted;
3296 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3297 gc_values.function = GXcopy;
3298 if (XBlackPixel(xDisplay, xScreen) == 1) {
3299 bwPieceGC = darkSquareGC;
3300 wbPieceGC = copyInvertedGC;
3302 bwPieceGC = copyInvertedGC;
3303 wbPieceGC = lightSquareGC;
3307 gc_values.foreground = highlightSquareColor;
3308 gc_values.background = highlightSquareColor;
3309 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3311 gc_values.foreground = premoveHighlightColor;
3312 gc_values.background = premoveHighlightColor;
3313 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3315 gc_values.foreground = lightSquareColor;
3316 gc_values.background = darkSquareColor;
3317 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3319 gc_values.foreground = darkSquareColor;
3320 gc_values.background = lightSquareColor;
3321 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3323 gc_values.foreground = jailSquareColor;
3324 gc_values.background = jailSquareColor;
3325 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3327 gc_values.foreground = whitePieceColor;
3328 gc_values.background = darkSquareColor;
3329 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3331 gc_values.foreground = whitePieceColor;
3332 gc_values.background = lightSquareColor;
3333 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3335 gc_values.foreground = whitePieceColor;
3336 gc_values.background = jailSquareColor;
3337 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3339 gc_values.foreground = blackPieceColor;
3340 gc_values.background = darkSquareColor;
3341 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3343 gc_values.foreground = blackPieceColor;
3344 gc_values.background = lightSquareColor;
3345 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3347 gc_values.foreground = blackPieceColor;
3348 gc_values.background = jailSquareColor;
3349 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3353 void loadXIM(xim, xmask, filename, dest, mask)
3366 fp = fopen(filename, "rb");
3368 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3375 for (y=0; y<h; ++y) {
3376 for (x=0; x<h; ++x) {
3381 XPutPixel(xim, x, y, blackPieceColor);
3383 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3386 XPutPixel(xim, x, y, darkSquareColor);
3388 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3391 XPutPixel(xim, x, y, whitePieceColor);
3393 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3396 XPutPixel(xim, x, y, lightSquareColor);
3398 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3406 /* create Pixmap of piece */
3407 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3409 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3412 /* create Pixmap of clipmask
3413 Note: We assume the white/black pieces have the same
3414 outline, so we make only 6 masks. This is okay
3415 since the XPM clipmask routines do the same. */
3417 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3419 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3422 /* now create the 1-bit version */
3423 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3426 values.foreground = 1;
3427 values.background = 0;
3429 /* Don't use XtGetGC, not read only */
3430 maskGC = XCreateGC(xDisplay, *mask,
3431 GCForeground | GCBackground, &values);
3432 XCopyPlane(xDisplay, temp, *mask, maskGC,
3433 0, 0, squareSize, squareSize, 0, 0, 1);
3434 XFreePixmap(xDisplay, temp);
3439 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3441 void CreateXIMPieces()
3446 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3451 /* The XSynchronize calls were copied from CreatePieces.
3452 Not sure if needed, but can't hurt */
3453 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3456 /* temp needed by loadXIM() */
3457 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3458 0, 0, ss, ss, AllPlanes, XYPixmap);
3460 if (strlen(appData.pixmapDirectory) == 0) {
3464 if (appData.monoMode) {
3465 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3469 fprintf(stderr, _("\nLoading XIMs...\n"));
3471 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3472 fprintf(stderr, "%d", piece+1);
3473 for (kind=0; kind<4; kind++) {
3474 fprintf(stderr, ".");
3475 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3476 ExpandPathName(appData.pixmapDirectory),
3477 piece <= (int) WhiteKing ? "" : "w",
3478 pieceBitmapNames[piece],
3480 ximPieceBitmap[kind][piece] =
3481 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3482 0, 0, ss, ss, AllPlanes, XYPixmap);
3483 if (appData.debugMode)
3484 fprintf(stderr, _("(File:%s:) "), buf);
3485 loadXIM(ximPieceBitmap[kind][piece],
3487 &(xpmPieceBitmap2[kind][piece]),
3488 &(ximMaskPm2[piece]));
3489 if(piece <= (int)WhiteKing)
3490 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3492 fprintf(stderr," ");
3494 /* Load light and dark squares */
3495 /* If the LSQ and DSQ pieces don't exist, we will
3496 draw them with solid squares. */
3497 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3498 if (access(buf, 0) != 0) {
3502 fprintf(stderr, _("light square "));
3504 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3505 0, 0, ss, ss, AllPlanes, XYPixmap);
3506 if (appData.debugMode)
3507 fprintf(stderr, _("(File:%s:) "), buf);
3509 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3510 fprintf(stderr, _("dark square "));
3511 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3512 ExpandPathName(appData.pixmapDirectory), ss);
3513 if (appData.debugMode)
3514 fprintf(stderr, _("(File:%s:) "), buf);
3516 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3517 0, 0, ss, ss, AllPlanes, XYPixmap);
3518 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3519 xpmJailSquare = xpmLightSquare;
3521 fprintf(stderr, _("Done.\n"));
3523 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3526 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3529 void CreateXPMBoard(char *s, int kind)
3533 if(s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3534 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3535 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3539 void FreeXPMPieces()
3540 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3541 // thisroutine has to be called t free the old piece pixmaps
3543 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3544 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3546 XFreePixmap(xDisplay, xpmLightSquare);
3547 XFreePixmap(xDisplay, xpmDarkSquare);
3551 void CreateXPMPieces()
3555 u_int ss = squareSize;
3557 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3558 XpmColorSymbol symbols[4];
3559 static int redo = False;
3561 if(redo) FreeXPMPieces(); else redo = 1;
3563 /* The XSynchronize calls were copied from CreatePieces.
3564 Not sure if needed, but can't hurt */
3565 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3567 /* Setup translations so piece colors match square colors */
3568 symbols[0].name = "light_piece";
3569 symbols[0].value = appData.whitePieceColor;
3570 symbols[1].name = "dark_piece";
3571 symbols[1].value = appData.blackPieceColor;
3572 symbols[2].name = "light_square";
3573 symbols[2].value = appData.lightSquareColor;
3574 symbols[3].name = "dark_square";
3575 symbols[3].value = appData.darkSquareColor;
3577 attr.valuemask = XpmColorSymbols;
3578 attr.colorsymbols = symbols;
3579 attr.numsymbols = 4;
3581 if (appData.monoMode) {
3582 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3586 if (strlen(appData.pixmapDirectory) == 0) {
3587 XpmPieces* pieces = builtInXpms;
3590 while (pieces->size != squareSize && pieces->size) pieces++;
3591 if (!pieces->size) {
3592 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3595 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3596 for (kind=0; kind<4; kind++) {
3598 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3599 pieces->xpm[piece][kind],
3600 &(xpmPieceBitmap2[kind][piece]),
3601 NULL, &attr)) != 0) {
3602 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3606 if(piece <= (int) WhiteKing)
3607 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3611 xpmJailSquare = xpmLightSquare;
3615 fprintf(stderr, _("\nLoading XPMs...\n"));
3618 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3619 fprintf(stderr, "%d ", piece+1);
3620 for (kind=0; kind<4; kind++) {
3621 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3622 ExpandPathName(appData.pixmapDirectory),
3623 piece > (int) WhiteKing ? "w" : "",
3624 pieceBitmapNames[piece],
3626 if (appData.debugMode) {
3627 fprintf(stderr, _("(File:%s:) "), buf);
3629 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3630 &(xpmPieceBitmap2[kind][piece]),
3631 NULL, &attr)) != 0) {
3632 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3633 // [HGM] missing: read of unorthodox piece failed; substitute King.
3634 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3635 ExpandPathName(appData.pixmapDirectory),
3637 if (appData.debugMode) {
3638 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3640 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3641 &(xpmPieceBitmap2[kind][piece]),
3645 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3650 if(piece <= (int) WhiteKing)
3651 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3654 /* Load light and dark squares */
3655 /* If the LSQ and DSQ pieces don't exist, we will
3656 draw them with solid squares. */
3657 fprintf(stderr, _("light square "));
3658 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3659 if (access(buf, 0) != 0) {
3663 if (appData.debugMode)
3664 fprintf(stderr, _("(File:%s:) "), buf);
3666 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3667 &xpmLightSquare, NULL, &attr)) != 0) {
3668 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3671 fprintf(stderr, _("dark square "));
3672 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3673 ExpandPathName(appData.pixmapDirectory), ss);
3674 if (appData.debugMode) {
3675 fprintf(stderr, _("(File:%s:) "), buf);
3677 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3678 &xpmDarkSquare, NULL, &attr)) != 0) {
3679 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3683 xpmJailSquare = xpmLightSquare;
3684 fprintf(stderr, _("Done.\n"));
3686 oldVariant = -1; // kludge to force re-makig of animation masks
3687 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3690 #endif /* HAVE_LIBXPM */
3693 /* No built-in bitmaps */
3698 u_int ss = squareSize;
3700 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3703 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3704 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3705 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3706 pieceBitmapNames[piece],
3707 ss, kind == SOLID ? 's' : 'o');
3708 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3709 if(piece <= (int)WhiteKing)
3710 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3714 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3718 /* With built-in bitmaps */
3721 BuiltInBits* bib = builtInBits;
3724 u_int ss = squareSize;
3726 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3729 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3731 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3732 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3733 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3734 pieceBitmapNames[piece],
3735 ss, kind == SOLID ? 's' : 'o');
3736 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3737 bib->bits[kind][piece], ss, ss);
3738 if(piece <= (int)WhiteKing)
3739 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3743 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3748 void ReadBitmap(pm, name, bits, wreq, hreq)
3751 unsigned char bits[];
3757 char msg[MSG_SIZ], fullname[MSG_SIZ];
3759 if (*appData.bitmapDirectory != NULLCHAR) {
3760 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3761 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3762 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3763 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3764 &w, &h, pm, &x_hot, &y_hot);
3765 fprintf(stderr, "load %s\n", name);
3766 if (errcode != BitmapSuccess) {
3768 case BitmapOpenFailed:
3769 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3771 case BitmapFileInvalid:
3772 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3774 case BitmapNoMemory:
3775 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3779 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3783 fprintf(stderr, _("%s: %s...using built-in\n"),
3785 } else if (w != wreq || h != hreq) {
3787 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3788 programName, fullname, w, h, wreq, hreq);
3794 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3803 if (lineGap == 0) return;
3805 /* [HR] Split this into 2 loops for non-square boards. */
3807 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3808 gridSegments[i].x1 = 0;
3809 gridSegments[i].x2 =
3810 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3811 gridSegments[i].y1 = gridSegments[i].y2
3812 = lineGap / 2 + (i * (squareSize + lineGap));
3815 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3816 gridSegments[j + i].y1 = 0;
3817 gridSegments[j + i].y2 =
3818 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3819 gridSegments[j + i].x1 = gridSegments[j + i].x2
3820 = lineGap / 2 + (j * (squareSize + lineGap));
3824 static void MenuBarSelect(w, addr, index)
3829 XtActionProc proc = (XtActionProc) addr;
3831 (proc)(NULL, NULL, NULL, NULL);
3834 void CreateMenuBarPopup(parent, name, mb)
3844 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3847 XtSetArg(args[j], XtNleftMargin, 20); j++;
3848 XtSetArg(args[j], XtNrightMargin, 20); j++;
3850 while (mi->string != NULL) {
3851 if (strcmp(mi->string, "----") == 0) {
3852 entry = XtCreateManagedWidget(_(mi->string), smeLineObjectClass,
3855 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3856 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3858 XtAddCallback(entry, XtNcallback,
3859 (XtCallbackProc) MenuBarSelect,
3860 (caddr_t) mi->proc);
3866 Widget CreateMenuBar(mb)
3870 Widget anchor, menuBar;
3872 char menuName[MSG_SIZ];
3875 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3876 XtSetArg(args[j], XtNvSpace, 0); j++;
3877 XtSetArg(args[j], XtNborderWidth, 0); j++;
3878 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3879 formWidget, args, j);
3881 while (mb->name != NULL) {
3882 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3883 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3885 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3888 shortName[0] = mb->name[0];
3889 shortName[1] = NULLCHAR;
3890 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3893 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3896 XtSetArg(args[j], XtNborderWidth, 0); j++;
3897 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3899 CreateMenuBarPopup(menuBar, menuName, mb);
3905 Widget CreateButtonBar(mi)
3909 Widget button, buttonBar;
3913 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3915 XtSetArg(args[j], XtNhSpace, 0); j++;
3917 XtSetArg(args[j], XtNborderWidth, 0); j++;
3918 XtSetArg(args[j], XtNvSpace, 0); j++;
3919 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3920 formWidget, args, j);
3922 while (mi->string != NULL) {
3925 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3926 XtSetArg(args[j], XtNborderWidth, 0); j++;
3928 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3929 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3930 buttonBar, args, j);
3931 XtAddCallback(button, XtNcallback,
3932 (XtCallbackProc) MenuBarSelect,
3933 (caddr_t) mi->proc);
3940 CreatePieceMenu(name, color)
3947 ChessSquare selection;
3949 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3950 boardWidget, args, 0);
3952 for (i = 0; i < PIECE_MENU_SIZE; i++) {
3953 String item = pieceMenuStrings[color][i];
3955 if (strcmp(item, "----") == 0) {
3956 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3959 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3960 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3962 selection = pieceMenuTranslation[color][i];
3963 XtAddCallback(entry, XtNcallback,
3964 (XtCallbackProc) PieceMenuSelect,
3965 (caddr_t) selection);
3966 if (selection == WhitePawn || selection == BlackPawn) {
3967 XtSetArg(args[0], XtNpopupOnEntry, entry);
3968 XtSetValues(menu, args, 1);
3981 ChessSquare selection;
3983 whitePieceMenu = CreatePieceMenu("menuW", 0);
3984 blackPieceMenu = CreatePieceMenu("menuB", 1);
3986 XtRegisterGrabAction(PieceMenuPopup, True,
3987 (unsigned)(ButtonPressMask|ButtonReleaseMask),
3988 GrabModeAsync, GrabModeAsync);
3990 XtSetArg(args[0], XtNlabel, _("Drop"));
3991 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3992 boardWidget, args, 1);
3993 for (i = 0; i < DROP_MENU_SIZE; i++) {
3994 String item = dropMenuStrings[i];
3996 if (strcmp(item, "----") == 0) {
3997 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4000 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4001 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4003 selection = dropMenuTranslation[i];
4004 XtAddCallback(entry, XtNcallback,
4005 (XtCallbackProc) DropMenuSelect,
4006 (caddr_t) selection);
4011 void SetupDropMenu()
4019 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
4020 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
4021 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
4022 dmEnables[i].piece);
4023 XtSetSensitive(entry, p != NULL || !appData.testLegality
4024 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
4025 && !appData.icsActive));
4027 while (p && *p++ == dmEnables[i].piece) count++;
4028 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
4030 XtSetArg(args[j], XtNlabel, label); j++;
4031 XtSetValues(entry, args, j);
4035 void PieceMenuPopup(w, event, params, num_params)
4039 Cardinal *num_params;
4041 String whichMenu; int menuNr = -2;
4042 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
4043 if (event->type == ButtonRelease)
4044 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4045 else if (event->type == ButtonPress)
4046 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4048 case 0: whichMenu = params[0]; break;
4049 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
4051 case -1: if (errorUp) ErrorPopDown();
4054 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
4057 static void PieceMenuSelect(w, piece, junk)
4062 if (pmFromX < 0 || pmFromY < 0) return;
4063 EditPositionMenuEvent(piece, pmFromX, pmFromY);
4066 static void DropMenuSelect(w, piece, junk)
4071 if (pmFromX < 0 || pmFromY < 0) return;
4072 DropMenuEvent(piece, pmFromX, pmFromY);
4075 void WhiteClock(w, event, prms, nprms)
4084 void BlackClock(w, event, prms, nprms)
4095 * If the user selects on a border boundary, return -1; if off the board,
4096 * return -2. Otherwise map the event coordinate to the square.
4098 int EventToSquare(x, limit)
4106 if ((x % (squareSize + lineGap)) >= squareSize)
4108 x /= (squareSize + lineGap);
4114 static void do_flash_delay(msec)
4120 static void drawHighlight(file, rank, gc)
4126 if (lineGap == 0) return;
4129 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4130 (squareSize + lineGap);
4131 y = lineGap/2 + rank * (squareSize + lineGap);
4133 x = lineGap/2 + file * (squareSize + lineGap);
4134 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4135 (squareSize + lineGap);
4138 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4139 squareSize+lineGap, squareSize+lineGap);
4142 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4143 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4146 SetHighlights(fromX, fromY, toX, toY)
4147 int fromX, fromY, toX, toY;
4149 if (hi1X != fromX || hi1Y != fromY) {
4150 if (hi1X >= 0 && hi1Y >= 0) {
4151 drawHighlight(hi1X, hi1Y, lineGC);
4153 } // [HGM] first erase both, then draw new!
4154 if (hi2X != toX || hi2Y != toY) {
4155 if (hi2X >= 0 && hi2Y >= 0) {
4156 drawHighlight(hi2X, hi2Y, lineGC);
4159 if (hi1X != fromX || hi1Y != fromY) {
4160 if (fromX >= 0 && fromY >= 0) {
4161 drawHighlight(fromX, fromY, highlineGC);
4164 if (hi2X != toX || hi2Y != toY) {
4165 if (toX >= 0 && toY >= 0) {
4166 drawHighlight(toX, toY, highlineGC);
4178 SetHighlights(-1, -1, -1, -1);
4183 SetPremoveHighlights(fromX, fromY, toX, toY)
4184 int fromX, fromY, toX, toY;
4186 if (pm1X != fromX || pm1Y != fromY) {
4187 if (pm1X >= 0 && pm1Y >= 0) {
4188 drawHighlight(pm1X, pm1Y, lineGC);
4190 if (fromX >= 0 && fromY >= 0) {
4191 drawHighlight(fromX, fromY, prelineGC);
4194 if (pm2X != toX || pm2Y != toY) {
4195 if (pm2X >= 0 && pm2Y >= 0) {
4196 drawHighlight(pm2X, pm2Y, lineGC);
4198 if (toX >= 0 && toY >= 0) {
4199 drawHighlight(toX, toY, prelineGC);
4209 ClearPremoveHighlights()
4211 SetPremoveHighlights(-1, -1, -1, -1);
4214 static int CutOutSquare(x, y, x0, y0, kind)
4215 int x, y, *x0, *y0, kind;
4217 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4218 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4220 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4221 if(textureW[kind] < W*squareSize)
4222 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4224 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4225 if(textureH[kind] < H*squareSize)
4226 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4228 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4232 static void BlankSquare(x, y, color, piece, dest, fac)
4233 int x, y, color, fac;
4236 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4238 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4239 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4240 squareSize, squareSize, x*fac, y*fac);
4242 if (useImages && useImageSqs) {
4246 pm = xpmLightSquare;
4251 case 2: /* neutral */
4256 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4257 squareSize, squareSize, x*fac, y*fac);
4267 case 2: /* neutral */
4272 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4277 I split out the routines to draw a piece so that I could
4278 make a generic flash routine.
4280 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4282 int square_color, x, y;
4285 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4286 switch (square_color) {
4288 case 2: /* neutral */
4290 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4291 ? *pieceToOutline(piece)
4292 : *pieceToSolid(piece),
4293 dest, bwPieceGC, 0, 0,
4294 squareSize, squareSize, x, y);
4297 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4298 ? *pieceToSolid(piece)
4299 : *pieceToOutline(piece),
4300 dest, wbPieceGC, 0, 0,
4301 squareSize, squareSize, x, y);
4306 static void monoDrawPiece(piece, square_color, x, y, dest)
4308 int square_color, x, y;
4311 switch (square_color) {
4313 case 2: /* neutral */
4315 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4316 ? *pieceToOutline(piece)
4317 : *pieceToSolid(piece),
4318 dest, bwPieceGC, 0, 0,
4319 squareSize, squareSize, x, y, 1);
4322 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4323 ? *pieceToSolid(piece)
4324 : *pieceToOutline(piece),
4325 dest, wbPieceGC, 0, 0,
4326 squareSize, squareSize, x, y, 1);
4331 static void colorDrawPiece(piece, square_color, x, y, dest)
4333 int square_color, x, y;
4336 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4337 switch (square_color) {
4339 XCopyPlane(xDisplay, *pieceToSolid(piece),
4340 dest, (int) piece < (int) BlackPawn
4341 ? wlPieceGC : blPieceGC, 0, 0,
4342 squareSize, squareSize, x, y, 1);
4345 XCopyPlane(xDisplay, *pieceToSolid(piece),
4346 dest, (int) piece < (int) BlackPawn
4347 ? wdPieceGC : bdPieceGC, 0, 0,
4348 squareSize, squareSize, x, y, 1);
4350 case 2: /* neutral */
4352 XCopyPlane(xDisplay, *pieceToSolid(piece),
4353 dest, (int) piece < (int) BlackPawn
4354 ? wjPieceGC : bjPieceGC, 0, 0,
4355 squareSize, squareSize, x, y, 1);
4360 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4362 int square_color, x, y;
4365 int kind, p = piece;
4367 switch (square_color) {
4369 case 2: /* neutral */
4371 if ((int)piece < (int) BlackPawn) {
4379 if ((int)piece < (int) BlackPawn) {
4387 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4388 if(useTexture & square_color+1) {
4389 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4390 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4391 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4392 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4393 XSetClipMask(xDisplay, wlPieceGC, None);
4394 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4396 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4397 dest, wlPieceGC, 0, 0,
4398 squareSize, squareSize, x, y);
4401 typedef void (*DrawFunc)();
4403 DrawFunc ChooseDrawFunc()
4405 if (appData.monoMode) {
4406 if (DefaultDepth(xDisplay, xScreen) == 1) {
4407 return monoDrawPiece_1bit;
4409 return monoDrawPiece;
4413 return colorDrawPieceImage;
4415 return colorDrawPiece;
4419 /* [HR] determine square color depending on chess variant. */
4420 static int SquareColor(row, column)
4425 if (gameInfo.variant == VariantXiangqi) {
4426 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4428 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4430 } else if (row <= 4) {
4436 square_color = ((column + row) % 2) == 1;
4439 /* [hgm] holdings: next line makes all holdings squares light */
4440 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4442 return square_color;
4445 void DrawSquare(row, column, piece, do_flash)
4446 int row, column, do_flash;
4449 int square_color, x, y, direction, font_ascent, font_descent;
4452 XCharStruct overall;
4456 /* Calculate delay in milliseconds (2-delays per complete flash) */
4457 flash_delay = 500 / appData.flashRate;
4460 x = lineGap + ((BOARD_WIDTH-1)-column) *
4461 (squareSize + lineGap);
4462 y = lineGap + row * (squareSize + lineGap);
4464 x = lineGap + column * (squareSize + lineGap);
4465 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4466 (squareSize + lineGap);
4469 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4471 square_color = SquareColor(row, column);
4473 if ( // [HGM] holdings: blank out area between board and holdings
4474 column == BOARD_LEFT-1 || column == BOARD_RGHT
4475 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4476 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4477 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4479 // [HGM] print piece counts next to holdings
4480 string[1] = NULLCHAR;
4481 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4482 string[0] = '0' + piece;
4483 XTextExtents(countFontStruct, string, 1, &direction,
4484 &font_ascent, &font_descent, &overall);
4485 if (appData.monoMode) {
4486 XDrawImageString(xDisplay, xBoardWindow, countGC,
4487 x + squareSize - overall.width - 2,
4488 y + font_ascent + 1, string, 1);
4490 XDrawString(xDisplay, xBoardWindow, countGC,
4491 x + squareSize - overall.width - 2,
4492 y + font_ascent + 1, string, 1);
4495 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4496 string[0] = '0' + piece;
4497 XTextExtents(countFontStruct, string, 1, &direction,
4498 &font_ascent, &font_descent, &overall);
4499 if (appData.monoMode) {
4500 XDrawImageString(xDisplay, xBoardWindow, countGC,
4501 x + 2, y + font_ascent + 1, string, 1);
4503 XDrawString(xDisplay, xBoardWindow, countGC,
4504 x + 2, y + font_ascent + 1, string, 1);
4508 if (piece == EmptySquare || appData.blindfold) {
4509 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4511 drawfunc = ChooseDrawFunc();
4513 if (do_flash && appData.flashCount > 0) {
4514 for (i=0; i<appData.flashCount; ++i) {
4515 drawfunc(piece, square_color, x, y, xBoardWindow);
4516 XSync(xDisplay, False);
4517 do_flash_delay(flash_delay);
4519 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4520 XSync(xDisplay, False);
4521 do_flash_delay(flash_delay);
4524 drawfunc(piece, square_color, x, y, xBoardWindow);
4528 string[1] = NULLCHAR;
4529 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4530 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4531 string[0] = 'a' + column - BOARD_LEFT;
4532 XTextExtents(coordFontStruct, string, 1, &direction,
4533 &font_ascent, &font_descent, &overall);
4534 if (appData.monoMode) {
4535 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4536 x + squareSize - overall.width - 2,
4537 y + squareSize - font_descent - 1, string, 1);
4539 XDrawString(xDisplay, xBoardWindow, coordGC,
4540 x + squareSize - overall.width - 2,
4541 y + squareSize - font_descent - 1, string, 1);
4544 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4545 string[0] = ONE + row;
4546 XTextExtents(coordFontStruct, string, 1, &direction,
4547 &font_ascent, &font_descent, &overall);
4548 if (appData.monoMode) {
4549 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4550 x + 2, y + font_ascent + 1, string, 1);
4552 XDrawString(xDisplay, xBoardWindow, coordGC,
4553 x + 2, y + font_ascent + 1, string, 1);
4556 if(!partnerUp && marker[row][column]) {
4557 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4558 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4563 /* Why is this needed on some versions of X? */
4564 void EventProc(widget, unused, event)
4569 if (!XtIsRealized(widget))
4572 switch (event->type) {
4574 if (event->xexpose.count > 0) return; /* no clipping is done */
4575 XDrawPosition(widget, True, NULL);
4576 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4577 flipView = !flipView; partnerUp = !partnerUp;
4578 XDrawPosition(widget, True, NULL);
4579 flipView = !flipView; partnerUp = !partnerUp;
4583 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4590 void DrawPosition(fullRedraw, board)
4591 /*Boolean*/int fullRedraw;
4594 XDrawPosition(boardWidget, fullRedraw, board);
4597 /* Returns 1 if there are "too many" differences between b1 and b2
4598 (i.e. more than 1 move was made) */
4599 static int too_many_diffs(b1, b2)
4605 for (i=0; i<BOARD_HEIGHT; ++i) {
4606 for (j=0; j<BOARD_WIDTH; ++j) {
4607 if (b1[i][j] != b2[i][j]) {
4608 if (++c > 4) /* Castling causes 4 diffs */
4616 /* Matrix describing castling maneuvers */
4617 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4618 static int castling_matrix[4][5] = {
4619 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4620 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4621 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4622 { 7, 7, 4, 5, 6 } /* 0-0, black */
4625 /* Checks whether castling occurred. If it did, *rrow and *rcol
4626 are set to the destination (row,col) of the rook that moved.
4628 Returns 1 if castling occurred, 0 if not.
4630 Note: Only handles a max of 1 castling move, so be sure
4631 to call too_many_diffs() first.
4633 static int check_castle_draw(newb, oldb, rrow, rcol)
4640 /* For each type of castling... */
4641 for (i=0; i<4; ++i) {
4642 r = castling_matrix[i];
4644 /* Check the 4 squares involved in the castling move */
4646 for (j=1; j<=4; ++j) {
4647 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4654 /* All 4 changed, so it must be a castling move */
4663 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4664 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4666 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4669 void DrawSeekBackground( int left, int top, int right, int bottom )
4671 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4674 void DrawSeekText(char *buf, int x, int y)
4676 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4679 void DrawSeekDot(int x, int y, int colorNr)
4681 int square = colorNr & 0x80;
4684 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4686 XFillRectangle(xDisplay, xBoardWindow, color,
4687 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4689 XFillArc(xDisplay, xBoardWindow, color,
4690 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4693 static int damage[2][BOARD_RANKS][BOARD_FILES];
4696 * event handler for redrawing the board
4698 void XDrawPosition(w, repaint, board)
4700 /*Boolean*/int repaint;
4704 static int lastFlipView = 0;
4705 static int lastBoardValid[2] = {0, 0};
4706 static Board lastBoard[2];
4709 int nr = twoBoards*partnerUp;
4711 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4713 if (board == NULL) {
4714 if (!lastBoardValid[nr]) return;
4715 board = lastBoard[nr];
4717 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4718 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4719 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4724 * It would be simpler to clear the window with XClearWindow()
4725 * but this causes a very distracting flicker.
4728 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4730 if ( lineGap && IsDrawArrowEnabled())
4731 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4732 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4734 /* If too much changes (begin observing new game, etc.), don't
4736 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4738 /* Special check for castling so we don't flash both the king
4739 and the rook (just flash the king). */
4741 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4742 /* Draw rook with NO flashing. King will be drawn flashing later */
4743 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4744 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4748 /* First pass -- Draw (newly) empty squares and repair damage.
4749 This prevents you from having a piece show up twice while it
4750 is flashing on its new square */
4751 for (i = 0; i < BOARD_HEIGHT; i++)
4752 for (j = 0; j < BOARD_WIDTH; j++)
4753 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4754 || damage[nr][i][j]) {
4755 DrawSquare(i, j, board[i][j], 0);
4756 damage[nr][i][j] = False;
4759 /* Second pass -- Draw piece(s) in new position and flash them */
4760 for (i = 0; i < BOARD_HEIGHT; i++)
4761 for (j = 0; j < BOARD_WIDTH; j++)
4762 if (board[i][j] != lastBoard[nr][i][j]) {
4763 DrawSquare(i, j, board[i][j], do_flash);
4767 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4768 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4769 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4771 for (i = 0; i < BOARD_HEIGHT; i++)
4772 for (j = 0; j < BOARD_WIDTH; j++) {
4773 DrawSquare(i, j, board[i][j], 0);
4774 damage[nr][i][j] = False;
4778 CopyBoard(lastBoard[nr], board);
4779 lastBoardValid[nr] = 1;
4780 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4781 lastFlipView = flipView;
4783 /* Draw highlights */
4784 if (pm1X >= 0 && pm1Y >= 0) {
4785 drawHighlight(pm1X, pm1Y, prelineGC);
4787 if (pm2X >= 0 && pm2Y >= 0) {
4788 drawHighlight(pm2X, pm2Y, prelineGC);
4790 if (hi1X >= 0 && hi1Y >= 0) {
4791 drawHighlight(hi1X, hi1Y, highlineGC);
4793 if (hi2X >= 0 && hi2Y >= 0) {
4794 drawHighlight(hi2X, hi2Y, highlineGC);
4796 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4798 /* If piece being dragged around board, must redraw that too */
4801 XSync(xDisplay, False);
4806 * event handler for redrawing the board
4808 void DrawPositionProc(w, event, prms, nprms)
4814 XDrawPosition(w, True, NULL);
4819 * event handler for parsing user moves
4821 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4822 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4823 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4824 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4825 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4826 // and at the end FinishMove() to perform the move after optional promotion popups.
4827 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4828 void HandleUserMove(w, event, prms, nprms)
4834 if (w != boardWidget || errorExitStatus != -1) return;
4835 if(nprms) shiftKey = !strcmp(prms[0], "1");
4838 if (event->type == ButtonPress) {
4839 XtPopdown(promotionShell);
4840 XtDestroyWidget(promotionShell);
4841 promotionUp = False;
4849 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4850 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4851 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4854 void AnimateUserMove (Widget w, XEvent * event,
4855 String * params, Cardinal * nParams)
4857 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4858 DragPieceMove(event->xmotion.x, event->xmotion.y);
4861 void HandlePV (Widget w, XEvent * event,
4862 String * params, Cardinal * nParams)
4863 { // [HGM] pv: walk PV
4864 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4867 static int savedIndex; /* gross that this is global */
4869 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4872 XawTextPosition index, dummy;
4875 XawTextGetSelectionPos(w, &index, &dummy);
4876 XtSetArg(arg, XtNstring, &val);
4877 XtGetValues(w, &arg, 1);
4878 ReplaceComment(savedIndex, val);
4879 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4880 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4883 void EditCommentPopUp(index, title, text)
4888 if (text == NULL) text = "";
4889 NewCommentPopup(title, text, index);
4892 void ICSInputBoxPopUp()
4897 extern Option boxOptions[];
4899 void ICSInputSendText()
4906 edit = boxOptions[0].handle;
4908 XtSetArg(args[j], XtNstring, &val); j++;
4909 XtGetValues(edit, args, j);
4911 SendMultiLineToICS(val);
4912 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4913 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4916 void ICSInputBoxPopDown()
4921 void CommentPopUp(title, text)
4924 savedIndex = currentMove; // [HGM] vari
4925 NewCommentPopup(title, text, currentMove);
4928 void CommentPopDown()
4933 void FileNamePopUp(label, def, filter, proc, openMode)
4940 fileProc = proc; /* I can't see a way not */
4941 fileOpenMode = openMode; /* to use globals here */
4942 { // [HGM] use file-selector dialog stolen from Ghostview
4944 int index; // this is not supported yet
4946 if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
4947 (def[0] ? def : NULL), filter, openMode, NULL, &name))
4948 (void) (*fileProc)(f, index=0, name);
4952 void FileNamePopDown()
4954 if (!filenameUp) return;
4955 XtPopdown(fileNameShell);
4956 XtDestroyWidget(fileNameShell);
4961 void FileNameCallback(w, client_data, call_data)
4963 XtPointer client_data, call_data;
4968 XtSetArg(args[0], XtNlabel, &name);
4969 XtGetValues(w, args, 1);
4971 if (strcmp(name, _("cancel")) == 0) {
4976 FileNameAction(w, NULL, NULL, NULL);
4979 void FileNameAction(w, event, prms, nprms)
4991 name = XawDialogGetValueString(w = XtParent(w));
4993 if ((name != NULL) && (*name != NULLCHAR)) {
4994 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
4995 XtPopdown(w = XtParent(XtParent(w)));
4999 p = strrchr(buf, ' ');
5006 fullname = ExpandPathName(buf);
5008 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5011 f = fopen(fullname, fileOpenMode);
5013 DisplayError(_("Failed to open file"), errno);
5015 (void) (*fileProc)(f, index, buf);
5022 XtPopdown(w = XtParent(XtParent(w)));
5028 void PromotionPopUp()
5031 Widget dialog, layout;
5033 Dimension bw_width, pw_width;
5037 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5038 XtGetValues(boardWidget, args, j);
5041 XtSetArg(args[j], XtNresizable, True); j++;
5042 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5044 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5045 shellWidget, args, j);
5047 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5048 layoutArgs, XtNumber(layoutArgs));
5051 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5052 XtSetArg(args[j], XtNborderWidth, 0); j++;
5053 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5056 if(gameInfo.variant != VariantShogi) {
5057 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5058 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback,
5059 (XtPointer) dialog);
5060 XawDialogAddButton(dialog, _("General"), PromotionCallback,
5061 (XtPointer) dialog);
5062 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback,
5063 (XtPointer) dialog);
5064 XawDialogAddButton(dialog, _("Captain"), PromotionCallback,
5065 (XtPointer) dialog);
5067 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
5068 (XtPointer) dialog);
5069 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
5070 (XtPointer) dialog);
5071 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
5072 (XtPointer) dialog);
5073 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
5074 (XtPointer) dialog);
5076 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5077 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
5078 gameInfo.variant == VariantGiveaway) {
5079 XawDialogAddButton(dialog, _("King"), PromotionCallback,
5080 (XtPointer) dialog);
5082 if(gameInfo.variant == VariantCapablanca ||
5083 gameInfo.variant == VariantGothic ||
5084 gameInfo.variant == VariantCapaRandom) {
5085 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5086 (XtPointer) dialog);
5087 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5088 (XtPointer) dialog);
5090 } else // [HGM] shogi
5092 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5093 (XtPointer) dialog);
5094 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5095 (XtPointer) dialog);
5097 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5098 (XtPointer) dialog);
5100 XtRealizeWidget(promotionShell);
5101 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5104 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5105 XtGetValues(promotionShell, args, j);
5107 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5108 lineGap + squareSize/3 +
5109 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5110 0 : 6*(squareSize + lineGap)), &x, &y);
5113 XtSetArg(args[j], XtNx, x); j++;
5114 XtSetArg(args[j], XtNy, y); j++;
5115 XtSetValues(promotionShell, args, j);
5117 XtPopup(promotionShell, XtGrabNone);
5122 void PromotionPopDown()
5124 if (!promotionUp) return;
5125 XtPopdown(promotionShell);
5126 XtDestroyWidget(promotionShell);
5127 promotionUp = False;
5130 void PromotionCallback(w, client_data, call_data)
5132 XtPointer client_data, call_data;
5138 XtSetArg(args[0], XtNlabel, &name);
5139 XtGetValues(w, args, 1);
5143 if (fromX == -1) return;
5145 if (strcmp(name, _("cancel")) == 0) {
5149 } else if (strcmp(name, _("Knight")) == 0) {
5151 } else if (strcmp(name, _("Promote")) == 0) {
5153 } else if (strcmp(name, _("Defer")) == 0) {
5156 promoChar = ToLower(name[0]);
5159 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5161 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5162 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5167 void ErrorCallback(w, client_data, call_data)
5169 XtPointer client_data, call_data;
5172 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5174 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5180 if (!errorUp) return;
5182 XtPopdown(errorShell);
5183 XtDestroyWidget(errorShell);
5184 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5187 void ErrorPopUp(title, label, modal)
5188 char *title, *label;
5192 Widget dialog, layout;
5196 Dimension bw_width, pw_width;
5197 Dimension pw_height;
5201 XtSetArg(args[i], XtNresizable, True); i++;
5202 XtSetArg(args[i], XtNtitle, title); i++;
5204 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5205 shellWidget, args, i);
5207 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5208 layoutArgs, XtNumber(layoutArgs));
5211 XtSetArg(args[i], XtNlabel, label); i++;
5212 XtSetArg(args[i], XtNborderWidth, 0); i++;
5213 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5216 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5218 XtRealizeWidget(errorShell);
5219 CatchDeleteWindow(errorShell, "ErrorPopDown");
5222 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5223 XtGetValues(boardWidget, args, i);
5225 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5226 XtSetArg(args[i], XtNheight, &pw_height); i++;
5227 XtGetValues(errorShell, args, i);
5230 /* This code seems to tickle an X bug if it is executed too soon
5231 after xboard starts up. The coordinates get transformed as if
5232 the main window was positioned at (0, 0).
5234 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5235 0 - pw_height + squareSize / 3, &x, &y);
5237 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5238 RootWindowOfScreen(XtScreen(boardWidget)),
5239 (bw_width - pw_width) / 2,
5240 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5244 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5247 XtSetArg(args[i], XtNx, x); i++;
5248 XtSetArg(args[i], XtNy, y); i++;
5249 XtSetValues(errorShell, args, i);
5252 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5255 /* Disable all user input other than deleting the window */
5256 static int frozen = 0;
5260 /* Grab by a widget that doesn't accept input */
5261 XtAddGrab(messageWidget, TRUE, FALSE);
5265 /* Undo a FreezeUI */
5268 if (!frozen) return;
5269 XtRemoveGrab(messageWidget);
5273 char *ModeToWidgetName(mode)
5277 case BeginningOfGame:
5278 if (appData.icsActive)
5279 return "menuMode.ICS Client";
5280 else if (appData.noChessProgram ||
5281 *appData.cmailGameName != NULLCHAR)
5282 return "menuMode.Edit Game";
5284 return "menuMode.Machine Black";
5285 case MachinePlaysBlack:
5286 return "menuMode.Machine Black";
5287 case MachinePlaysWhite:
5288 return "menuMode.Machine White";
5290 return "menuMode.Analysis Mode";
5292 return "menuMode.Analyze File";
5293 case TwoMachinesPlay:
5294 return "menuMode.Two Machines";
5296 return "menuMode.Edit Game";
5297 case PlayFromGameFile:
5298 return "menuFile.Load Game";
5300 return "menuMode.Edit Position";
5302 return "menuMode.Training";
5303 case IcsPlayingWhite:
5304 case IcsPlayingBlack:
5308 return "menuMode.ICS Client";
5315 void ModeHighlight()
5318 static int oldPausing = FALSE;
5319 static GameMode oldmode = (GameMode) -1;
5322 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5324 if (pausing != oldPausing) {
5325 oldPausing = pausing;
5327 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5329 XtSetArg(args[0], XtNleftBitmap, None);
5331 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5334 if (appData.showButtonBar) {
5335 /* Always toggle, don't set. Previous code messes up when
5336 invoked while the button is pressed, as releasing it
5337 toggles the state again. */
5340 XtSetArg(args[0], XtNbackground, &oldbg);
5341 XtSetArg(args[1], XtNforeground, &oldfg);
5342 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5344 XtSetArg(args[0], XtNbackground, oldfg);
5345 XtSetArg(args[1], XtNforeground, oldbg);
5347 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5351 wname = ModeToWidgetName(oldmode);
5352 if (wname != NULL) {
5353 XtSetArg(args[0], XtNleftBitmap, None);
5354 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5356 wname = ModeToWidgetName(gameMode);
5357 if (wname != NULL) {
5358 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5359 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5362 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5363 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5365 /* Maybe all the enables should be handled here, not just this one */
5366 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5367 gameMode == Training || gameMode == PlayFromGameFile);
5372 * Button/menu procedures
5374 void ResetProc(w, event, prms, nprms)
5383 int LoadGamePopUp(f, gameNumber, title)
5388 cmailMsgLoaded = FALSE;
5389 if (gameNumber == 0) {
5390 int error = GameListBuild(f);
5392 DisplayError(_("Cannot build game list"), error);
5393 } else if (!ListEmpty(&gameList) &&
5394 ((ListGame *) gameList.tailPred)->number > 1) {
5395 GameListPopUp(f, title);
5401 return LoadGame(f, gameNumber, title, FALSE);
5404 void LoadGameProc(w, event, prms, nprms)
5410 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5413 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5416 void LoadNextGameProc(w, event, prms, nprms)
5425 void LoadPrevGameProc(w, event, prms, nprms)
5434 void ReloadGameProc(w, event, prms, nprms)
5443 void LoadNextPositionProc(w, event, prms, nprms)
5452 void LoadPrevPositionProc(w, event, prms, nprms)
5461 void ReloadPositionProc(w, event, prms, nprms)
5470 void LoadPositionProc(w, event, prms, nprms)
5476 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5479 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5482 void SaveGameProc(w, event, prms, nprms)
5488 FileNamePopUp(_("Save game file name?"),
5489 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5490 appData.oldSaveStyle ? ".game" : ".pgn",
5494 void SavePositionProc(w, event, prms, nprms)
5500 FileNamePopUp(_("Save position file name?"),
5501 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5502 appData.oldSaveStyle ? ".pos" : ".fen",
5506 void ReloadCmailMsgProc(w, event, prms, nprms)
5512 ReloadCmailMsgEvent(FALSE);
5515 void MailMoveProc(w, event, prms, nprms)
5524 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5525 char *selected_fen_position=NULL;
5528 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5529 Atom *type_return, XtPointer *value_return,
5530 unsigned long *length_return, int *format_return)
5532 char *selection_tmp;
5534 if (!selected_fen_position) return False; /* should never happen */
5535 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5536 /* note: since no XtSelectionDoneProc was registered, Xt will
5537 * automatically call XtFree on the value returned. So have to
5538 * make a copy of it allocated with XtMalloc */
5539 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5540 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5542 *value_return=selection_tmp;
5543 *length_return=strlen(selection_tmp);
5544 *type_return=*target;
5545 *format_return = 8; /* bits per byte */
5547 } else if (*target == XA_TARGETS(xDisplay)) {
5548 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5549 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5550 targets_tmp[1] = XA_STRING;
5551 *value_return = targets_tmp;
5552 *type_return = XA_ATOM;
5554 *format_return = 8 * sizeof(Atom);
5555 if (*format_return > 32) {
5556 *length_return *= *format_return / 32;
5557 *format_return = 32;
5565 /* note: when called from menu all parameters are NULL, so no clue what the
5566 * Widget which was clicked on was, or what the click event was
5568 void CopyPositionProc(w, event, prms, nprms)
5575 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5576 * have a notion of a position that is selected but not copied.
5577 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5579 if(gameMode == EditPosition) EditPositionDone(TRUE);
5580 if (selected_fen_position) free(selected_fen_position);
5581 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5582 if (!selected_fen_position) return;
5583 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5585 SendPositionSelection,
5586 NULL/* lose_ownership_proc */ ,
5587 NULL/* transfer_done_proc */);
5588 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5590 SendPositionSelection,
5591 NULL/* lose_ownership_proc */ ,
5592 NULL/* transfer_done_proc */);
5595 /* function called when the data to Paste is ready */
5597 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5598 Atom *type, XtPointer value, unsigned long *len, int *format)
5601 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5602 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5603 EditPositionPasteFEN(fenstr);
5607 /* called when Paste Position button is pressed,
5608 * all parameters will be NULL */
5609 void PastePositionProc(w, event, prms, nprms)
5615 XtGetSelectionValue(menuBarWidget,
5616 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5617 /* (XtSelectionCallbackProc) */ PastePositionCB,
5618 NULL, /* client_data passed to PastePositionCB */
5620 /* better to use the time field from the event that triggered the
5621 * call to this function, but that isn't trivial to get
5629 SendGameSelection(Widget w, Atom *selection, Atom *target,
5630 Atom *type_return, XtPointer *value_return,
5631 unsigned long *length_return, int *format_return)
5633 char *selection_tmp;
5635 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5636 FILE* f = fopen(gameCopyFilename, "r");
5639 if (f == NULL) return False;
5643 selection_tmp = XtMalloc(len + 1);
5644 count = fread(selection_tmp, 1, len, f);
5647 XtFree(selection_tmp);
5650 selection_tmp[len] = NULLCHAR;
5651 *value_return = selection_tmp;
5652 *length_return = len;
5653 *type_return = *target;
5654 *format_return = 8; /* bits per byte */
5656 } else if (*target == XA_TARGETS(xDisplay)) {
5657 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5658 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5659 targets_tmp[1] = XA_STRING;
5660 *value_return = targets_tmp;
5661 *type_return = XA_ATOM;
5663 *format_return = 8 * sizeof(Atom);
5664 if (*format_return > 32) {
5665 *length_return *= *format_return / 32;
5666 *format_return = 32;
5674 void CopySomething()
5679 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5680 * have a notion of a game that is selected but not copied.
5681 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5683 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5686 NULL/* lose_ownership_proc */ ,
5687 NULL/* transfer_done_proc */);
5688 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5691 NULL/* lose_ownership_proc */ ,
5692 NULL/* transfer_done_proc */);
5695 /* note: when called from menu all parameters are NULL, so no clue what the
5696 * Widget which was clicked on was, or what the click event was
5698 void CopyGameProc(w, event, prms, nprms)
5706 ret = SaveGameToFile(gameCopyFilename, FALSE);
5712 void CopyGameListProc(w, event, prms, nprms)
5718 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5722 /* function called when the data to Paste is ready */
5724 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5725 Atom *type, XtPointer value, unsigned long *len, int *format)
5728 if (value == NULL || *len == 0) {
5729 return; /* nothing had been selected to copy */
5731 f = fopen(gamePasteFilename, "w");
5733 DisplayError(_("Can't open temp file"), errno);
5736 fwrite(value, 1, *len, f);
5739 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5742 /* called when Paste Game button is pressed,
5743 * all parameters will be NULL */
5744 void PasteGameProc(w, event, prms, nprms)
5750 XtGetSelectionValue(menuBarWidget,
5751 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5752 /* (XtSelectionCallbackProc) */ PasteGameCB,
5753 NULL, /* client_data passed to PasteGameCB */
5755 /* better to use the time field from the event that triggered the
5756 * call to this function, but that isn't trivial to get
5766 SaveGameProc(NULL, NULL, NULL, NULL);
5770 void QuitProc(w, event, prms, nprms)
5779 void PauseProc(w, event, prms, nprms)
5789 void MachineBlackProc(w, event, prms, nprms)
5795 MachineBlackEvent();
5798 void MachineWhiteProc(w, event, prms, nprms)
5804 MachineWhiteEvent();
5807 void AnalyzeModeProc(w, event, prms, nprms)
5815 if (!first.analysisSupport) {
5816 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5817 DisplayError(buf, 0);
5820 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5821 if (appData.icsActive) {
5822 if (gameMode != IcsObserving) {
5823 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5824 DisplayError(buf, 0);
5826 if (appData.icsEngineAnalyze) {
5827 if (appData.debugMode)
5828 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5834 /* if enable, use want disable icsEngineAnalyze */
5835 if (appData.icsEngineAnalyze) {
5840 appData.icsEngineAnalyze = TRUE;
5841 if (appData.debugMode)
5842 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5844 #ifndef OPTIONSDIALOG
5845 if (!appData.showThinking)
5846 ShowThinkingProc(w,event,prms,nprms);
5852 void AnalyzeFileProc(w, event, prms, nprms)
5858 if (!first.analysisSupport) {
5860 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5861 DisplayError(buf, 0);
5865 #ifndef OPTIONSDIALOG
5866 if (!appData.showThinking)
5867 ShowThinkingProc(w,event,prms,nprms);
5870 FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5871 AnalysisPeriodicEvent(1);
5874 void TwoMachinesProc(w, event, prms, nprms)
5883 void MatchProc(w, event, prms, nprms)
5892 void IcsClientProc(w, event, prms, nprms)
5901 void EditGameProc(w, event, prms, nprms)
5910 void EditPositionProc(w, event, prms, nprms)
5916 EditPositionEvent();
5919 void TrainingProc(w, event, prms, nprms)
5928 void EditCommentProc(w, event, prms, nprms)
5936 if (PopDown(1)) { // popdown succesful
5938 XtSetArg(args[j], XtNleftBitmap, None); j++;
5939 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
5940 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
5941 } else // was not up
5945 void IcsInputBoxProc(w, event, prms, nprms)
5951 if (!PopDown(4)) ICSInputBoxPopUp();
5954 void AcceptProc(w, event, prms, nprms)
5963 void DeclineProc(w, event, prms, nprms)
5972 void RematchProc(w, event, prms, nprms)
5981 void CallFlagProc(w, event, prms, nprms)
5990 void DrawProc(w, event, prms, nprms)
5999 void AbortProc(w, event, prms, nprms)
6008 void AdjournProc(w, event, prms, nprms)
6017 void ResignProc(w, event, prms, nprms)
6026 void AdjuWhiteProc(w, event, prms, nprms)
6032 UserAdjudicationEvent(+1);
6035 void AdjuBlackProc(w, event, prms, nprms)
6041 UserAdjudicationEvent(-1);
6044 void AdjuDrawProc(w, event, prms, nprms)
6050 UserAdjudicationEvent(0);
6053 void EnterKeyProc(w, event, prms, nprms)
6059 if (shellUp[4] == True)
6063 void UpKeyProc(w, event, prms, nprms)
6068 { // [HGM] input: let up-arrow recall previous line from history
6075 if (!shellUp[4]) return;
6076 edit = boxOptions[0].handle;
6078 XtSetArg(args[j], XtNstring, &val); j++;
6079 XtGetValues(edit, args, j);
6080 val = PrevInHistory(val);
6081 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6082 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6084 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6085 XawTextReplace(edit, 0, 0, &t);
6086 XawTextSetInsertionPoint(edit, 9999);
6090 void DownKeyProc(w, event, prms, nprms)
6095 { // [HGM] input: let down-arrow recall next line from history
6100 if (!shellUp[4]) return;
6101 edit = boxOptions[0].handle;
6102 val = NextInHistory();
6103 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6104 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6106 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6107 XawTextReplace(edit, 0, 0, &t);
6108 XawTextSetInsertionPoint(edit, 9999);
6112 void StopObservingProc(w, event, prms, nprms)
6118 StopObservingEvent();
6121 void StopExaminingProc(w, event, prms, nprms)
6127 StopExaminingEvent();
6130 void UploadProc(w, event, prms, nprms)
6140 void ForwardProc(w, event, prms, nprms)
6150 void BackwardProc(w, event, prms, nprms)
6159 void ToStartProc(w, event, prms, nprms)
6168 void ToEndProc(w, event, prms, nprms)
6177 void RevertProc(w, event, prms, nprms)
6186 void AnnotateProc(w, event, prms, nprms)
6195 void TruncateGameProc(w, event, prms, nprms)
6201 TruncateGameEvent();
6203 void RetractMoveProc(w, event, prms, nprms)
6212 void MoveNowProc(w, event, prms, nprms)
6221 void FlipViewProc(w, event, prms, nprms)
6227 flipView = !flipView;
6228 DrawPosition(True, NULL);
6231 void PonderNextMoveProc(w, event, prms, nprms)
6239 PonderNextMoveEvent(!appData.ponderNextMove);
6240 #ifndef OPTIONSDIALOG
6241 if (appData.ponderNextMove) {
6242 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6244 XtSetArg(args[0], XtNleftBitmap, None);
6246 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6251 #ifndef OPTIONSDIALOG
6252 void AlwaysQueenProc(w, event, prms, nprms)
6260 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6262 if (appData.alwaysPromoteToQueen) {
6263 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6265 XtSetArg(args[0], XtNleftBitmap, None);
6267 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6271 void AnimateDraggingProc(w, event, prms, nprms)
6279 appData.animateDragging = !appData.animateDragging;
6281 if (appData.animateDragging) {
6282 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6285 XtSetArg(args[0], XtNleftBitmap, None);
6287 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6291 void AnimateMovingProc(w, event, prms, nprms)
6299 appData.animate = !appData.animate;
6301 if (appData.animate) {
6302 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6305 XtSetArg(args[0], XtNleftBitmap, None);
6307 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6311 void AutoflagProc(w, event, prms, nprms)
6319 appData.autoCallFlag = !appData.autoCallFlag;
6321 if (appData.autoCallFlag) {
6322 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6324 XtSetArg(args[0], XtNleftBitmap, None);
6326 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6330 void AutoflipProc(w, event, prms, nprms)
6338 appData.autoFlipView = !appData.autoFlipView;
6340 if (appData.autoFlipView) {
6341 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6343 XtSetArg(args[0], XtNleftBitmap, None);
6345 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6349 void BlindfoldProc(w, event, prms, nprms)
6357 appData.blindfold = !appData.blindfold;
6359 if (appData.blindfold) {
6360 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6362 XtSetArg(args[0], XtNleftBitmap, None);
6364 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6367 DrawPosition(True, NULL);
6370 void TestLegalityProc(w, event, prms, nprms)
6378 appData.testLegality = !appData.testLegality;
6380 if (appData.testLegality) {
6381 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6383 XtSetArg(args[0], XtNleftBitmap, None);
6385 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6390 void FlashMovesProc(w, event, prms, nprms)
6398 if (appData.flashCount == 0) {
6399 appData.flashCount = 3;
6401 appData.flashCount = -appData.flashCount;
6404 if (appData.flashCount > 0) {
6405 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6407 XtSetArg(args[0], XtNleftBitmap, None);
6409 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6414 void HighlightDraggingProc(w, event, prms, nprms)
6422 appData.highlightDragging = !appData.highlightDragging;
6424 if (appData.highlightDragging) {
6425 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6427 XtSetArg(args[0], XtNleftBitmap, None);
6429 XtSetValues(XtNameToWidget(menuBarWidget,
6430 "menuOptions.Highlight Dragging"), args, 1);
6434 void HighlightLastMoveProc(w, event, prms, nprms)
6442 appData.highlightLastMove = !appData.highlightLastMove;
6444 if (appData.highlightLastMove) {
6445 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6447 XtSetArg(args[0], XtNleftBitmap, None);
6449 XtSetValues(XtNameToWidget(menuBarWidget,
6450 "menuOptions.Highlight Last Move"), args, 1);
6453 void HighlightArrowProc(w, event, prms, nprms)
6461 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6463 if (appData.highlightMoveWithArrow) {
6464 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6466 XtSetArg(args[0], XtNleftBitmap, None);
6468 XtSetValues(XtNameToWidget(menuBarWidget,
6469 "menuOptions.Arrow"), args, 1);
6473 void IcsAlarmProc(w, event, prms, nprms)
6481 appData.icsAlarm = !appData.icsAlarm;
6483 if (appData.icsAlarm) {
6484 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6486 XtSetArg(args[0], XtNleftBitmap, None);
6488 XtSetValues(XtNameToWidget(menuBarWidget,
6489 "menuOptions.ICS Alarm"), args, 1);
6493 void MoveSoundProc(w, event, prms, nprms)
6501 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6503 if (appData.ringBellAfterMoves) {
6504 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6506 XtSetArg(args[0], XtNleftBitmap, None);
6508 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6512 void OneClickProc(w, event, prms, nprms)
6520 appData.oneClick = !appData.oneClick;
6522 if (appData.oneClick) {
6523 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6525 XtSetArg(args[0], XtNleftBitmap, None);
6527 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6531 void PeriodicUpdatesProc(w, event, prms, nprms)
6539 PeriodicUpdatesEvent(!appData.periodicUpdates);
6541 if (appData.periodicUpdates) {
6542 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6544 XtSetArg(args[0], XtNleftBitmap, None);
6546 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6550 void PopupExitMessageProc(w, event, prms, nprms)
6558 appData.popupExitMessage = !appData.popupExitMessage;
6560 if (appData.popupExitMessage) {
6561 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6563 XtSetArg(args[0], XtNleftBitmap, None);
6565 XtSetValues(XtNameToWidget(menuBarWidget,
6566 "menuOptions.Popup Exit Message"), args, 1);
6569 void PopupMoveErrorsProc(w, event, prms, nprms)
6577 appData.popupMoveErrors = !appData.popupMoveErrors;
6579 if (appData.popupMoveErrors) {
6580 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6582 XtSetArg(args[0], XtNleftBitmap, None);
6584 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6589 void PremoveProc(w, event, prms, nprms)
6597 appData.premove = !appData.premove;
6599 if (appData.premove) {
6600 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6602 XtSetArg(args[0], XtNleftBitmap, None);
6604 XtSetValues(XtNameToWidget(menuBarWidget,
6605 "menuOptions.Premove"), args, 1);
6609 void ShowCoordsProc(w, event, prms, nprms)
6617 appData.showCoords = !appData.showCoords;
6619 if (appData.showCoords) {
6620 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6622 XtSetArg(args[0], XtNleftBitmap, None);
6624 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6627 DrawPosition(True, NULL);
6630 void ShowThinkingProc(w, event, prms, nprms)
6636 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6637 ShowThinkingEvent();
6640 void HideThinkingProc(w, event, prms, nprms)
6648 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6649 ShowThinkingEvent();
6651 if (appData.hideThinkingFromHuman) {
6652 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6654 XtSetArg(args[0], XtNleftBitmap, None);
6656 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6661 void SaveOnExitProc(w, event, prms, nprms)
6669 saveSettingsOnExit = !saveSettingsOnExit;
6671 if (saveSettingsOnExit) {
6672 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6674 XtSetArg(args[0], XtNleftBitmap, None);
6676 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6680 void SaveSettingsProc(w, event, prms, nprms)
6686 SaveSettings(settingsFileName);
6689 void InfoProc(w, event, prms, nprms)
6696 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6701 void ManProc(w, event, prms, nprms)
6709 if (nprms && *nprms > 0)
6713 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6717 void HintProc(w, event, prms, nprms)
6726 void BookProc(w, event, prms, nprms)
6735 void AboutProc(w, event, prms, nprms)
6743 char *zippy = " (with Zippy code)";
6747 snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
6748 programVersion, zippy,
6749 "Copyright 1991 Digital Equipment Corporation",
6750 "Enhancements Copyright 1992-2009 Free Software Foundation",
6751 "Enhancements Copyright 2005 Alessandro Scotti",
6752 PACKAGE, " is free software and carries NO WARRANTY;",
6753 "see the file COPYING for more information.");
6754 ErrorPopUp(_("About XBoard"), buf, FALSE);
6757 void DebugProc(w, event, prms, nprms)
6763 appData.debugMode = !appData.debugMode;
6766 void AboutGameProc(w, event, prms, nprms)
6775 void NothingProc(w, event, prms, nprms)
6784 void Iconify(w, event, prms, nprms)
6793 XtSetArg(args[0], XtNiconic, True);
6794 XtSetValues(shellWidget, args, 1);
6797 void DisplayMessage(message, extMessage)
6798 char *message, *extMessage;
6800 /* display a message in the message widget */
6809 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6814 message = extMessage;
6818 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6820 /* need to test if messageWidget already exists, since this function
6821 can also be called during the startup, if for example a Xresource
6822 is not set up correctly */
6825 XtSetArg(arg, XtNlabel, message);
6826 XtSetValues(messageWidget, &arg, 1);
6832 void DisplayTitle(text)
6837 char title[MSG_SIZ];
6840 if (text == NULL) text = "";
6842 if (appData.titleInWindow) {
6844 XtSetArg(args[i], XtNlabel, text); i++;
6845 XtSetValues(titleWidget, args, i);
6848 if (*text != NULLCHAR) {
6849 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6850 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6851 } else if (appData.icsActive) {
6852 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6853 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6854 } else if (appData.cmailGameName[0] != NULLCHAR) {
6855 snprintf(icon, sizeof(icon), "%s", "CMail");
6856 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6858 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6859 } else if (gameInfo.variant == VariantGothic) {
6860 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6861 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6864 } else if (gameInfo.variant == VariantFalcon) {
6865 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6866 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6868 } else if (appData.noChessProgram) {
6869 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6870 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6872 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6873 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6876 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6877 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6878 XtSetValues(shellWidget, args, i);
6883 DisplayError(message, error)
6890 if (appData.debugMode || appData.matchMode) {
6891 fprintf(stderr, "%s: %s\n", programName, message);
6894 if (appData.debugMode || appData.matchMode) {
6895 fprintf(stderr, "%s: %s: %s\n",
6896 programName, message, strerror(error));
6898 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6901 ErrorPopUp(_("Error"), message, FALSE);
6905 void DisplayMoveError(message)
6910 DrawPosition(FALSE, NULL);
6911 if (appData.debugMode || appData.matchMode) {
6912 fprintf(stderr, "%s: %s\n", programName, message);
6914 if (appData.popupMoveErrors) {
6915 ErrorPopUp(_("Error"), message, FALSE);
6917 DisplayMessage(message, "");
6922 void DisplayFatalError(message, error, status)
6928 errorExitStatus = status;
6930 fprintf(stderr, "%s: %s\n", programName, message);
6932 fprintf(stderr, "%s: %s: %s\n",
6933 programName, message, strerror(error));
6934 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6937 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6938 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6944 void DisplayInformation(message)
6948 ErrorPopUp(_("Information"), message, TRUE);
6951 void DisplayNote(message)
6955 ErrorPopUp(_("Note"), message, FALSE);
6959 NullXErrorCheck(dpy, error_event)
6961 XErrorEvent *error_event;
6966 void DisplayIcsInteractionTitle(message)
6969 if (oldICSInteractionTitle == NULL) {
6970 /* Magic to find the old window title, adapted from vim */
6971 char *wina = getenv("WINDOWID");
6973 Window win = (Window) atoi(wina);
6974 Window root, parent, *children;
6975 unsigned int nchildren;
6976 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6978 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6979 if (!XQueryTree(xDisplay, win, &root, &parent,
6980 &children, &nchildren)) break;
6981 if (children) XFree((void *)children);
6982 if (parent == root || parent == 0) break;
6985 XSetErrorHandler(oldHandler);
6987 if (oldICSInteractionTitle == NULL) {
6988 oldICSInteractionTitle = "xterm";
6991 printf("\033]0;%s\007", message);
6995 char pendingReplyPrefix[MSG_SIZ];
6996 ProcRef pendingReplyPR;
6998 void AskQuestionProc(w, event, prms, nprms)
7005 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7009 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7012 void AskQuestionPopDown()
7014 if (!askQuestionUp) return;
7015 XtPopdown(askQuestionShell);
7016 XtDestroyWidget(askQuestionShell);
7017 askQuestionUp = False;
7020 void AskQuestionReplyAction(w, event, prms, nprms)
7030 reply = XawDialogGetValueString(w = XtParent(w));
7031 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
7032 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
7033 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
7034 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
7035 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7036 AskQuestionPopDown();
7038 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7041 void AskQuestionCallback(w, client_data, call_data)
7043 XtPointer client_data, call_data;
7048 XtSetArg(args[0], XtNlabel, &name);
7049 XtGetValues(w, args, 1);
7051 if (strcmp(name, _("cancel")) == 0) {
7052 AskQuestionPopDown();
7054 AskQuestionReplyAction(w, NULL, NULL, NULL);
7058 void AskQuestion(title, question, replyPrefix, pr)
7059 char *title, *question, *replyPrefix;
7063 Widget popup, layout, dialog, edit;
7069 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7070 pendingReplyPR = pr;
7073 XtSetArg(args[i], XtNresizable, True); i++;
7074 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7075 askQuestionShell = popup =
7076 XtCreatePopupShell(title, transientShellWidgetClass,
7077 shellWidget, args, i);
7080 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7081 layoutArgs, XtNumber(layoutArgs));
7084 XtSetArg(args[i], XtNlabel, question); i++;
7085 XtSetArg(args[i], XtNvalue, ""); i++;
7086 XtSetArg(args[i], XtNborderWidth, 0); i++;
7087 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7090 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7091 (XtPointer) dialog);
7092 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7093 (XtPointer) dialog);
7095 XtRealizeWidget(popup);
7096 CatchDeleteWindow(popup, "AskQuestionPopDown");
7098 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7099 &x, &y, &win_x, &win_y, &mask);
7101 XtSetArg(args[0], XtNx, x - 10);
7102 XtSetArg(args[1], XtNy, y - 30);
7103 XtSetValues(popup, args, 2);
7105 XtPopup(popup, XtGrabExclusive);
7106 askQuestionUp = True;
7108 edit = XtNameToWidget(dialog, "*value");
7109 XtSetKeyboardFocus(popup, edit);
7117 if (*name == NULLCHAR) {
7119 } else if (strcmp(name, "$") == 0) {
7120 putc(BELLCHAR, stderr);
7123 char *prefix = "", *sep = "";
7124 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7125 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7133 PlaySound(appData.soundMove);
7139 PlaySound(appData.soundIcsWin);
7145 PlaySound(appData.soundIcsLoss);
7151 PlaySound(appData.soundIcsDraw);
7155 PlayIcsUnfinishedSound()
7157 PlaySound(appData.soundIcsUnfinished);
7163 PlaySound(appData.soundIcsAlarm);
7169 system("stty echo");
7175 system("stty -echo");
7179 Colorize(cc, continuation)
7184 int count, outCount, error;
7186 if (textColors[(int)cc].bg > 0) {
7187 if (textColors[(int)cc].fg > 0) {
7188 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7189 textColors[(int)cc].fg, textColors[(int)cc].bg);
7191 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7192 textColors[(int)cc].bg);
7195 if (textColors[(int)cc].fg > 0) {
7196 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7197 textColors[(int)cc].fg);
7199 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7202 count = strlen(buf);
7203 outCount = OutputToProcess(NoProc, buf, count, &error);
7204 if (outCount < count) {
7205 DisplayFatalError(_("Error writing to display"), error, 1);
7208 if (continuation) return;
7211 PlaySound(appData.soundShout);
7214 PlaySound(appData.soundSShout);
7217 PlaySound(appData.soundChannel1);
7220 PlaySound(appData.soundChannel);
7223 PlaySound(appData.soundKibitz);
7226 PlaySound(appData.soundTell);
7228 case ColorChallenge:
7229 PlaySound(appData.soundChallenge);
7232 PlaySound(appData.soundRequest);
7235 PlaySound(appData.soundSeek);
7246 return getpwuid(getuid())->pw_name;
7250 ExpandPathName(path)
7253 static char static_buf[4*MSG_SIZ];
7254 char *d, *s, buf[4*MSG_SIZ];
7260 while (*s && isspace(*s))
7269 if (*(s+1) == '/') {
7270 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7274 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7275 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7276 pwd = getpwnam(buf);
7279 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7283 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7284 strcat(d, strchr(s+1, '/'));
7288 safeStrCpy(d, s, 4*MSG_SIZ );
7295 static char host_name[MSG_SIZ];
7297 #if HAVE_GETHOSTNAME
7298 gethostname(host_name, MSG_SIZ);
7300 #else /* not HAVE_GETHOSTNAME */
7301 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7302 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7304 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7306 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7307 #endif /* not HAVE_GETHOSTNAME */
7310 XtIntervalId delayedEventTimerXID = 0;
7311 DelayedEventCallback delayedEventCallback = 0;
7316 delayedEventTimerXID = 0;
7317 delayedEventCallback();
7321 ScheduleDelayedEvent(cb, millisec)
7322 DelayedEventCallback cb; long millisec;
7324 if(delayedEventTimerXID && delayedEventCallback == cb)
7325 // [HGM] alive: replace, rather than add or flush identical event
7326 XtRemoveTimeOut(delayedEventTimerXID);
7327 delayedEventCallback = cb;
7328 delayedEventTimerXID =
7329 XtAppAddTimeOut(appContext, millisec,
7330 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7333 DelayedEventCallback
7336 if (delayedEventTimerXID) {
7337 return delayedEventCallback;
7344 CancelDelayedEvent()
7346 if (delayedEventTimerXID) {
7347 XtRemoveTimeOut(delayedEventTimerXID);
7348 delayedEventTimerXID = 0;
7352 XtIntervalId loadGameTimerXID = 0;
7354 int LoadGameTimerRunning()
7356 return loadGameTimerXID != 0;
7359 int StopLoadGameTimer()
7361 if (loadGameTimerXID != 0) {
7362 XtRemoveTimeOut(loadGameTimerXID);
7363 loadGameTimerXID = 0;
7371 LoadGameTimerCallback(arg, id)
7375 loadGameTimerXID = 0;
7380 StartLoadGameTimer(millisec)
7384 XtAppAddTimeOut(appContext, millisec,
7385 (XtTimerCallbackProc) LoadGameTimerCallback,
7389 XtIntervalId analysisClockXID = 0;
7392 AnalysisClockCallback(arg, id)
7396 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7397 || appData.icsEngineAnalyze) { // [DM]
7398 AnalysisPeriodicEvent(0);
7399 StartAnalysisClock();
7404 StartAnalysisClock()
7407 XtAppAddTimeOut(appContext, 2000,
7408 (XtTimerCallbackProc) AnalysisClockCallback,
7412 XtIntervalId clockTimerXID = 0;
7414 int ClockTimerRunning()
7416 return clockTimerXID != 0;
7419 int StopClockTimer()
7421 if (clockTimerXID != 0) {
7422 XtRemoveTimeOut(clockTimerXID);
7431 ClockTimerCallback(arg, id)
7440 StartClockTimer(millisec)
7444 XtAppAddTimeOut(appContext, millisec,
7445 (XtTimerCallbackProc) ClockTimerCallback,
7450 DisplayTimerLabel(w, color, timer, highlight)
7459 /* check for low time warning */
7460 Pixel foregroundOrWarningColor = timerForegroundPixel;
7463 appData.lowTimeWarning &&
7464 (timer / 1000) < appData.icsAlarmTime)
7465 foregroundOrWarningColor = lowTimeWarningColor;
7467 if (appData.clockMode) {
7468 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7469 XtSetArg(args[0], XtNlabel, buf);
7471 snprintf(buf, MSG_SIZ, "%s ", color);
7472 XtSetArg(args[0], XtNlabel, buf);
7477 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7478 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7480 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7481 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7484 XtSetValues(w, args, 3);
7488 DisplayWhiteClock(timeRemaining, highlight)
7494 if(appData.noGUI) return;
7495 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7496 if (highlight && iconPixmap == bIconPixmap) {
7497 iconPixmap = wIconPixmap;
7498 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7499 XtSetValues(shellWidget, args, 1);
7504 DisplayBlackClock(timeRemaining, highlight)
7510 if(appData.noGUI) return;
7511 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7512 if (highlight && iconPixmap == wIconPixmap) {
7513 iconPixmap = bIconPixmap;
7514 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7515 XtSetValues(shellWidget, args, 1);
7533 int StartChildProcess(cmdLine, dir, pr)
7540 int to_prog[2], from_prog[2];
7544 if (appData.debugMode) {
7545 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7548 /* We do NOT feed the cmdLine to the shell; we just
7549 parse it into blank-separated arguments in the
7550 most simple-minded way possible.
7553 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7556 while(*p == ' ') p++;
7558 if(*p == '"' || *p == '\'')
7559 p = strchr(++argv[i-1], *p);
7560 else p = strchr(p, ' ');
7561 if (p == NULL) break;
7566 SetUpChildIO(to_prog, from_prog);
7568 if ((pid = fork()) == 0) {
7570 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7571 close(to_prog[1]); // first close the unused pipe ends
7572 close(from_prog[0]);
7573 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7574 dup2(from_prog[1], 1);
7575 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7576 close(from_prog[1]); // and closing again loses one of the pipes!
7577 if(fileno(stderr) >= 2) // better safe than sorry...
7578 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7580 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7585 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7587 execvp(argv[0], argv);
7589 /* If we get here, exec failed */
7594 /* Parent process */
7596 close(from_prog[1]);
7598 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7601 cp->fdFrom = from_prog[0];
7602 cp->fdTo = to_prog[1];
7607 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7608 static RETSIGTYPE AlarmCallBack(int n)
7614 DestroyChildProcess(pr, signalType)
7618 ChildProc *cp = (ChildProc *) pr;
7620 if (cp->kind != CPReal) return;
7622 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7623 signal(SIGALRM, AlarmCallBack);
7625 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7626 kill(cp->pid, SIGKILL); // kill it forcefully
7627 wait((int *) 0); // and wait again
7631 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7633 /* Process is exiting either because of the kill or because of
7634 a quit command sent by the backend; either way, wait for it to die.
7643 InterruptChildProcess(pr)
7646 ChildProc *cp = (ChildProc *) pr;
7648 if (cp->kind != CPReal) return;
7649 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7652 int OpenTelnet(host, port, pr)
7657 char cmdLine[MSG_SIZ];
7659 if (port[0] == NULLCHAR) {
7660 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7662 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7664 return StartChildProcess(cmdLine, "", pr);
7667 int OpenTCP(host, port, pr)
7673 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7674 #else /* !OMIT_SOCKETS */
7675 struct addrinfo hints;
7676 struct addrinfo *ais, *ai;
7681 memset(&hints, 0, sizeof(hints));
7682 hints.ai_family = AF_UNSPEC;
7683 hints.ai_socktype = SOCK_STREAM;
7685 error = getaddrinfo(host, port, &hints, &ais);
7687 /* a getaddrinfo error is not an errno, so can't return it */
7688 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7689 host, port, gai_strerror(error));
7693 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7694 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7698 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7711 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7717 #endif /* !OMIT_SOCKETS */
7722 int OpenCommPort(name, pr)
7729 fd = open(name, 2, 0);
7730 if (fd < 0) return errno;
7732 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7742 int OpenLoopback(pr)
7748 SetUpChildIO(to, from);
7750 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7753 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7760 int OpenRcmd(host, user, cmd, pr)
7761 char *host, *user, *cmd;
7764 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7768 #define INPUT_SOURCE_BUF_SIZE 8192
7777 char buf[INPUT_SOURCE_BUF_SIZE];
7782 DoInputCallback(closure, source, xid)
7787 InputSource *is = (InputSource *) closure;
7792 if (is->lineByLine) {
7793 count = read(is->fd, is->unused,
7794 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7796 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7799 is->unused += count;
7801 while (p < is->unused) {
7802 q = memchr(p, '\n', is->unused - p);
7803 if (q == NULL) break;
7805 (is->func)(is, is->closure, p, q - p, 0);
7809 while (p < is->unused) {
7814 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7819 (is->func)(is, is->closure, is->buf, count, error);
7823 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7830 ChildProc *cp = (ChildProc *) pr;
7832 is = (InputSource *) calloc(1, sizeof(InputSource));
7833 is->lineByLine = lineByLine;
7837 is->fd = fileno(stdin);
7839 is->kind = cp->kind;
7840 is->fd = cp->fdFrom;
7843 is->unused = is->buf;
7846 is->xid = XtAppAddInput(appContext, is->fd,
7847 (XtPointer) (XtInputReadMask),
7848 (XtInputCallbackProc) DoInputCallback,
7850 is->closure = closure;
7851 return (InputSourceRef) is;
7855 RemoveInputSource(isr)
7858 InputSource *is = (InputSource *) isr;
7860 if (is->xid == 0) return;
7861 XtRemoveInput(is->xid);
7865 int OutputToProcess(pr, message, count, outError)
7871 static int line = 0;
7872 ChildProc *cp = (ChildProc *) pr;
7877 if (appData.noJoin || !appData.useInternalWrap)
7878 outCount = fwrite(message, 1, count, stdout);
7881 int width = get_term_width();
7882 int len = wrap(NULL, message, count, width, &line);
7883 char *msg = malloc(len);
7887 outCount = fwrite(message, 1, count, stdout);
7890 dbgchk = wrap(msg, message, count, width, &line);
7891 if (dbgchk != len && appData.debugMode)
7892 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7893 outCount = fwrite(msg, 1, dbgchk, stdout);
7899 outCount = write(cp->fdTo, message, count);
7909 /* Output message to process, with "ms" milliseconds of delay
7910 between each character. This is needed when sending the logon
7911 script to ICC, which for some reason doesn't like the
7912 instantaneous send. */
7913 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
7920 ChildProc *cp = (ChildProc *) pr;
7925 r = write(cp->fdTo, message++, 1);
7938 /**** Animation code by Hugh Fisher, DCS, ANU.
7940 Known problem: if a window overlapping the board is
7941 moved away while a piece is being animated underneath,
7942 the newly exposed area won't be updated properly.
7943 I can live with this.
7945 Known problem: if you look carefully at the animation
7946 of pieces in mono mode, they are being drawn as solid
7947 shapes without interior detail while moving. Fixing
7948 this would be a major complication for minimal return.
7951 /* Masks for XPM pieces. Black and white pieces can have
7952 different shapes, but in the interest of retaining my
7953 sanity pieces must have the same outline on both light
7954 and dark squares, and all pieces must use the same
7955 background square colors/images. */
7957 static int xpmDone = 0;
7960 CreateAnimMasks (pieceDepth)
7967 unsigned long plane;
7970 /* Need a bitmap just to get a GC with right depth */
7971 buf = XCreatePixmap(xDisplay, xBoardWindow,
7973 values.foreground = 1;
7974 values.background = 0;
7975 /* Don't use XtGetGC, not read only */
7976 maskGC = XCreateGC(xDisplay, buf,
7977 GCForeground | GCBackground, &values);
7978 XFreePixmap(xDisplay, buf);
7980 buf = XCreatePixmap(xDisplay, xBoardWindow,
7981 squareSize, squareSize, pieceDepth);
7982 values.foreground = XBlackPixel(xDisplay, xScreen);
7983 values.background = XWhitePixel(xDisplay, xScreen);
7984 bufGC = XCreateGC(xDisplay, buf,
7985 GCForeground | GCBackground, &values);
7987 for (piece = WhitePawn; piece <= BlackKing; piece++) {
7988 /* Begin with empty mask */
7989 if(!xpmDone) // [HGM] pieces: keep using existing
7990 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7991 squareSize, squareSize, 1);
7992 XSetFunction(xDisplay, maskGC, GXclear);
7993 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7994 0, 0, squareSize, squareSize);
7996 /* Take a copy of the piece */
8001 XSetFunction(xDisplay, bufGC, GXcopy);
8002 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8004 0, 0, squareSize, squareSize, 0, 0);
8006 /* XOR the background (light) over the piece */
8007 XSetFunction(xDisplay, bufGC, GXxor);
8009 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8010 0, 0, squareSize, squareSize, 0, 0);
8012 XSetForeground(xDisplay, bufGC, lightSquareColor);
8013 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8016 /* We now have an inverted piece image with the background
8017 erased. Construct mask by just selecting all the non-zero
8018 pixels - no need to reconstruct the original image. */
8019 XSetFunction(xDisplay, maskGC, GXor);
8021 /* Might be quicker to download an XImage and create bitmap
8022 data from it rather than this N copies per piece, but it
8023 only takes a fraction of a second and there is a much
8024 longer delay for loading the pieces. */
8025 for (n = 0; n < pieceDepth; n ++) {
8026 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8027 0, 0, squareSize, squareSize,
8033 XFreePixmap(xDisplay, buf);
8034 XFreeGC(xDisplay, bufGC);
8035 XFreeGC(xDisplay, maskGC);
8039 InitAnimState (anim, info)
8041 XWindowAttributes * info;
8046 /* Each buffer is square size, same depth as window */
8047 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8048 squareSize, squareSize, info->depth);
8049 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8050 squareSize, squareSize, info->depth);
8052 /* Create a plain GC for blitting */
8053 mask = GCForeground | GCBackground | GCFunction |
8054 GCPlaneMask | GCGraphicsExposures;
8055 values.foreground = XBlackPixel(xDisplay, xScreen);
8056 values.background = XWhitePixel(xDisplay, xScreen);
8057 values.function = GXcopy;
8058 values.plane_mask = AllPlanes;
8059 values.graphics_exposures = False;
8060 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8062 /* Piece will be copied from an existing context at
8063 the start of each new animation/drag. */
8064 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8066 /* Outline will be a read-only copy of an existing */
8067 anim->outlineGC = None;
8073 XWindowAttributes info;
8075 if (xpmDone && gameInfo.variant == oldVariant) return;
8076 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
8077 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8079 InitAnimState(&game, &info);
8080 InitAnimState(&player, &info);
8082 /* For XPM pieces, we need bitmaps to use as masks. */
8084 CreateAnimMasks(info.depth), xpmDone = 1;
8089 static Boolean frameWaiting;
8091 static RETSIGTYPE FrameAlarm (sig)
8094 frameWaiting = False;
8095 /* In case System-V style signals. Needed?? */
8096 signal(SIGALRM, FrameAlarm);
8103 struct itimerval delay;
8105 XSync(xDisplay, False);
8108 frameWaiting = True;
8109 signal(SIGALRM, FrameAlarm);
8110 delay.it_interval.tv_sec =
8111 delay.it_value.tv_sec = time / 1000;
8112 delay.it_interval.tv_usec =
8113 delay.it_value.tv_usec = (time % 1000) * 1000;
8114 setitimer(ITIMER_REAL, &delay, NULL);
8115 while (frameWaiting) pause();
8116 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8117 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8118 setitimer(ITIMER_REAL, &delay, NULL);
8128 XSync(xDisplay, False);
8130 usleep(time * 1000);
8135 /* Convert board position to corner of screen rect and color */
8138 ScreenSquare(column, row, pt, color)
8139 int column; int row; XPoint * pt; int * color;
8142 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8143 pt->y = lineGap + row * (squareSize + lineGap);
8145 pt->x = lineGap + column * (squareSize + lineGap);
8146 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8148 *color = SquareColor(row, column);
8151 /* Convert window coords to square */
8154 BoardSquare(x, y, column, row)
8155 int x; int y; int * column; int * row;
8157 *column = EventToSquare(x, BOARD_WIDTH);
8158 if (flipView && *column >= 0)
8159 *column = BOARD_WIDTH - 1 - *column;
8160 *row = EventToSquare(y, BOARD_HEIGHT);
8161 if (!flipView && *row >= 0)
8162 *row = BOARD_HEIGHT - 1 - *row;
8167 #undef Max /* just in case */
8169 #define Max(a, b) ((a) > (b) ? (a) : (b))
8170 #define Min(a, b) ((a) < (b) ? (a) : (b))
8173 SetRect(rect, x, y, width, height)
8174 XRectangle * rect; int x; int y; int width; int height;
8178 rect->width = width;
8179 rect->height = height;
8182 /* Test if two frames overlap. If they do, return
8183 intersection rect within old and location of
8184 that rect within new. */
8187 Intersect(old, new, size, area, pt)
8188 XPoint * old; XPoint * new;
8189 int size; XRectangle * area; XPoint * pt;
8191 if (old->x > new->x + size || new->x > old->x + size ||
8192 old->y > new->y + size || new->y > old->y + size) {
8195 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8196 size - abs(old->x - new->x), size - abs(old->y - new->y));
8197 pt->x = Max(old->x - new->x, 0);
8198 pt->y = Max(old->y - new->y, 0);
8203 /* For two overlapping frames, return the rect(s)
8204 in the old that do not intersect with the new. */
8207 CalcUpdateRects(old, new, size, update, nUpdates)
8208 XPoint * old; XPoint * new; int size;
8209 XRectangle update[]; int * nUpdates;
8213 /* If old = new (shouldn't happen) then nothing to draw */
8214 if (old->x == new->x && old->y == new->y) {
8218 /* Work out what bits overlap. Since we know the rects
8219 are the same size we don't need a full intersect calc. */
8221 /* Top or bottom edge? */
8222 if (new->y > old->y) {
8223 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8225 } else if (old->y > new->y) {
8226 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8227 size, old->y - new->y);
8230 /* Left or right edge - don't overlap any update calculated above. */
8231 if (new->x > old->x) {
8232 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8233 new->x - old->x, size - abs(new->y - old->y));
8235 } else if (old->x > new->x) {
8236 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8237 old->x - new->x, size - abs(new->y - old->y));
8244 /* Generate a series of frame coords from start->mid->finish.
8245 The movement rate doubles until the half way point is
8246 reached, then halves back down to the final destination,
8247 which gives a nice slow in/out effect. The algorithmn
8248 may seem to generate too many intermediates for short
8249 moves, but remember that the purpose is to attract the
8250 viewers attention to the piece about to be moved and
8251 then to where it ends up. Too few frames would be less
8255 Tween(start, mid, finish, factor, frames, nFrames)
8256 XPoint * start; XPoint * mid;
8257 XPoint * finish; int factor;
8258 XPoint frames[]; int * nFrames;
8260 int fraction, n, count;
8264 /* Slow in, stepping 1/16th, then 1/8th, ... */
8266 for (n = 0; n < factor; n++)
8268 for (n = 0; n < factor; n++) {
8269 frames[count].x = start->x + (mid->x - start->x) / fraction;
8270 frames[count].y = start->y + (mid->y - start->y) / fraction;
8272 fraction = fraction / 2;
8276 frames[count] = *mid;
8279 /* Slow out, stepping 1/2, then 1/4, ... */
8281 for (n = 0; n < factor; n++) {
8282 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8283 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8285 fraction = fraction * 2;
8290 /* Draw a piece on the screen without disturbing what's there */
8293 SelectGCMask(piece, clip, outline, mask)
8294 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8298 /* Bitmap for piece being moved. */
8299 if (appData.monoMode) {
8300 *mask = *pieceToSolid(piece);
8301 } else if (useImages) {
8303 *mask = xpmMask[piece];
8305 *mask = ximMaskPm[piece];
8308 *mask = *pieceToSolid(piece);
8311 /* GC for piece being moved. Square color doesn't matter, but
8312 since it gets modified we make a copy of the original. */
8314 if (appData.monoMode)
8319 if (appData.monoMode)
8324 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8326 /* Outline only used in mono mode and is not modified */
8328 *outline = bwPieceGC;
8330 *outline = wbPieceGC;
8334 OverlayPiece(piece, clip, outline, dest)
8335 ChessSquare piece; GC clip; GC outline; Drawable dest;
8340 /* Draw solid rectangle which will be clipped to shape of piece */
8341 XFillRectangle(xDisplay, dest, clip,
8342 0, 0, squareSize, squareSize);
8343 if (appData.monoMode)
8344 /* Also draw outline in contrasting color for black
8345 on black / white on white cases */
8346 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8347 0, 0, squareSize, squareSize, 0, 0, 1);
8349 /* Copy the piece */
8354 if(appData.upsideDown && flipView) kind ^= 2;
8355 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8357 0, 0, squareSize, squareSize,
8362 /* Animate the movement of a single piece */
8365 BeginAnimation(anim, piece, startColor, start)
8373 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8374 /* The old buffer is initialised with the start square (empty) */
8375 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8376 anim->prevFrame = *start;
8378 /* The piece will be drawn using its own bitmap as a matte */
8379 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8380 XSetClipMask(xDisplay, anim->pieceGC, mask);
8384 AnimationFrame(anim, frame, piece)
8389 XRectangle updates[4];
8394 /* Save what we are about to draw into the new buffer */
8395 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8396 frame->x, frame->y, squareSize, squareSize,
8399 /* Erase bits of the previous frame */
8400 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8401 /* Where the new frame overlapped the previous,
8402 the contents in newBuf are wrong. */
8403 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8404 overlap.x, overlap.y,
8405 overlap.width, overlap.height,
8407 /* Repaint the areas in the old that don't overlap new */
8408 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8409 for (i = 0; i < count; i++)
8410 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8411 updates[i].x - anim->prevFrame.x,
8412 updates[i].y - anim->prevFrame.y,
8413 updates[i].width, updates[i].height,
8414 updates[i].x, updates[i].y);
8416 /* Easy when no overlap */
8417 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8418 0, 0, squareSize, squareSize,
8419 anim->prevFrame.x, anim->prevFrame.y);
8422 /* Save this frame for next time round */
8423 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8424 0, 0, squareSize, squareSize,
8426 anim->prevFrame = *frame;
8428 /* Draw piece over original screen contents, not current,
8429 and copy entire rect. Wipes out overlapping piece images. */
8430 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8431 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8432 0, 0, squareSize, squareSize,
8433 frame->x, frame->y);
8437 EndAnimation (anim, finish)
8441 XRectangle updates[4];
8446 /* The main code will redraw the final square, so we
8447 only need to erase the bits that don't overlap. */
8448 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8449 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8450 for (i = 0; i < count; i++)
8451 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8452 updates[i].x - anim->prevFrame.x,
8453 updates[i].y - anim->prevFrame.y,
8454 updates[i].width, updates[i].height,
8455 updates[i].x, updates[i].y);
8457 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8458 0, 0, squareSize, squareSize,
8459 anim->prevFrame.x, anim->prevFrame.y);
8464 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8466 ChessSquare piece; int startColor;
8467 XPoint * start; XPoint * finish;
8468 XPoint frames[]; int nFrames;
8472 BeginAnimation(anim, piece, startColor, start);
8473 for (n = 0; n < nFrames; n++) {
8474 AnimationFrame(anim, &(frames[n]), piece);
8475 FrameDelay(appData.animSpeed);
8477 EndAnimation(anim, finish);
8481 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8484 ChessSquare piece = board[fromY][toY];
8485 board[fromY][toY] = EmptySquare;
8486 DrawPosition(FALSE, board);
8488 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8489 y = lineGap + toY * (squareSize + lineGap);
8491 x = lineGap + toX * (squareSize + lineGap);
8492 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8494 for(i=1; i<4*kFactor; i++) {
8495 int r = squareSize * 9 * i/(20*kFactor - 5);
8496 XFillArc(xDisplay, xBoardWindow, highlineGC,
8497 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8498 FrameDelay(appData.animSpeed);
8500 board[fromY][toY] = piece;
8503 /* Main control logic for deciding what to animate and how */
8506 AnimateMove(board, fromX, fromY, toX, toY)
8515 XPoint start, finish, mid;
8516 XPoint frames[kFactor * 2 + 1];
8517 int nFrames, startColor, endColor;
8519 /* Are we animating? */
8520 if (!appData.animate || appData.blindfold)
8523 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8524 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8525 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8527 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8528 piece = board[fromY][fromX];
8529 if (piece >= EmptySquare) return;
8534 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8537 if (appData.debugMode) {
8538 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8539 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8540 piece, fromX, fromY, toX, toY); }
8542 ScreenSquare(fromX, fromY, &start, &startColor);
8543 ScreenSquare(toX, toY, &finish, &endColor);
8546 /* Knight: make straight movement then diagonal */
8547 if (abs(toY - fromY) < abs(toX - fromX)) {
8548 mid.x = start.x + (finish.x - start.x) / 2;
8552 mid.y = start.y + (finish.y - start.y) / 2;
8555 mid.x = start.x + (finish.x - start.x) / 2;
8556 mid.y = start.y + (finish.y - start.y) / 2;
8559 /* Don't use as many frames for very short moves */
8560 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8561 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8563 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8564 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8565 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8567 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8568 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8571 /* Be sure end square is redrawn */
8572 damage[0][toY][toX] = True;
8576 DragPieceBegin(x, y)
8579 int boardX, boardY, color;
8582 /* Are we animating? */
8583 if (!appData.animateDragging || appData.blindfold)
8586 /* Figure out which square we start in and the
8587 mouse position relative to top left corner. */
8588 BoardSquare(x, y, &boardX, &boardY);
8589 player.startBoardX = boardX;
8590 player.startBoardY = boardY;
8591 ScreenSquare(boardX, boardY, &corner, &color);
8592 player.startSquare = corner;
8593 player.startColor = color;
8594 /* As soon as we start dragging, the piece will jump slightly to
8595 be centered over the mouse pointer. */
8596 player.mouseDelta.x = squareSize/2;
8597 player.mouseDelta.y = squareSize/2;
8598 /* Initialise animation */
8599 player.dragPiece = PieceForSquare(boardX, boardY);
8601 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8602 player.dragActive = True;
8603 BeginAnimation(&player, player.dragPiece, color, &corner);
8604 /* Mark this square as needing to be redrawn. Note that
8605 we don't remove the piece though, since logically (ie
8606 as seen by opponent) the move hasn't been made yet. */
8607 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8608 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8609 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8610 corner.x, corner.y, squareSize, squareSize,
8611 0, 0); // [HGM] zh: unstack in stead of grab
8612 if(gatingPiece != EmptySquare) {
8613 /* Kludge alert: When gating we want the introduced
8614 piece to appear on the from square. To generate an
8615 image of it, we draw it on the board, copy the image,
8616 and draw the original piece again. */
8617 ChessSquare piece = boards[currentMove][boardY][boardX];
8618 DrawSquare(boardY, boardX, gatingPiece, 0);
8619 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8620 corner.x, corner.y, squareSize, squareSize, 0, 0);
8621 DrawSquare(boardY, boardX, piece, 0);
8623 damage[0][boardY][boardX] = True;
8625 player.dragActive = False;
8630 ChangeDragPiece(ChessSquare piece)
8633 player.dragPiece = piece;
8634 /* The piece will be drawn using its own bitmap as a matte */
8635 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8636 XSetClipMask(xDisplay, player.pieceGC, mask);
8645 /* Are we animating? */
8646 if (!appData.animateDragging || appData.blindfold)
8650 if (! player.dragActive)
8652 /* Move piece, maintaining same relative position
8653 of mouse within square */
8654 corner.x = x - player.mouseDelta.x;
8655 corner.y = y - player.mouseDelta.y;
8656 AnimationFrame(&player, &corner, player.dragPiece);
8658 if (appData.highlightDragging) {
8660 BoardSquare(x, y, &boardX, &boardY);
8661 SetHighlights(fromX, fromY, boardX, boardY);
8670 int boardX, boardY, color;
8673 /* Are we animating? */
8674 if (!appData.animateDragging || appData.blindfold)
8678 if (! player.dragActive)
8680 /* Last frame in sequence is square piece is
8681 placed on, which may not match mouse exactly. */
8682 BoardSquare(x, y, &boardX, &boardY);
8683 ScreenSquare(boardX, boardY, &corner, &color);
8684 EndAnimation(&player, &corner);
8686 /* Be sure end square is redrawn */
8687 damage[0][boardY][boardX] = True;
8689 /* This prevents weird things happening with fast successive
8690 clicks which on my Sun at least can cause motion events
8691 without corresponding press/release. */
8692 player.dragActive = False;
8695 /* Handle expose event while piece being dragged */
8700 if (!player.dragActive || appData.blindfold)
8703 /* What we're doing: logically, the move hasn't been made yet,
8704 so the piece is still in it's original square. But visually
8705 it's being dragged around the board. So we erase the square
8706 that the piece is on and draw it at the last known drag point. */
8707 BlankSquare(player.startSquare.x, player.startSquare.y,
8708 player.startColor, EmptySquare, xBoardWindow, 1);
8709 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8710 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8713 #include <sys/ioctl.h>
8714 int get_term_width()
8716 int fd, default_width;
8719 default_width = 79; // this is FICS default anyway...
8721 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8723 if (!ioctl(fd, TIOCGSIZE, &win))
8724 default_width = win.ts_cols;
8725 #elif defined(TIOCGWINSZ)
8727 if (!ioctl(fd, TIOCGWINSZ, &win))
8728 default_width = win.ws_col;
8730 return default_width;
8736 static int old_width = 0;
8737 int new_width = get_term_width();
8739 if (old_width != new_width)
8740 ics_printf("set width %d\n", new_width);
8741 old_width = new_width;
8744 void NotifyFrontendLogin()
8749 /* [AS] Arrow highlighting support */
8751 static double A_WIDTH = 5; /* Width of arrow body */
8753 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8754 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8756 static double Sqr( double x )
8761 static int Round( double x )
8763 return (int) (x + 0.5);
8766 void SquareToPos(int rank, int file, int *x, int *y)
8769 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8770 *y = lineGap + rank * (squareSize + lineGap);
8772 *x = lineGap + file * (squareSize + lineGap);
8773 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8777 /* Draw an arrow between two points using current settings */
8778 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8781 double dx, dy, j, k, x, y;
8784 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8786 arrow[0].x = s_x + A_WIDTH + 0.5;
8789 arrow[1].x = s_x + A_WIDTH + 0.5;
8790 arrow[1].y = d_y - h;
8792 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8793 arrow[2].y = d_y - h;
8798 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8799 arrow[5].y = d_y - h;
8801 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8802 arrow[4].y = d_y - h;
8804 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8807 else if( d_y == s_y ) {
8808 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8811 arrow[0].y = s_y + A_WIDTH + 0.5;
8813 arrow[1].x = d_x - w;
8814 arrow[1].y = s_y + A_WIDTH + 0.5;
8816 arrow[2].x = d_x - w;
8817 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8822 arrow[5].x = d_x - w;
8823 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8825 arrow[4].x = d_x - w;
8826 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8829 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8832 /* [AS] Needed a lot of paper for this! :-) */
8833 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8834 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8836 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8838 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8843 arrow[0].x = Round(x - j);
8844 arrow[0].y = Round(y + j*dx);
8846 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8847 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8850 x = (double) d_x - k;
8851 y = (double) d_y - k*dy;
8854 x = (double) d_x + k;
8855 y = (double) d_y + k*dy;
8858 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8860 arrow[6].x = Round(x - j);
8861 arrow[6].y = Round(y + j*dx);
8863 arrow[2].x = Round(arrow[6].x + 2*j);
8864 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8866 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8867 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8872 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8873 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8876 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8877 // Polygon( hdc, arrow, 7 );
8880 /* [AS] Draw an arrow between two squares */
8881 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
8883 int s_x, s_y, d_x, d_y, hor, vert, i;
8885 if( s_col == d_col && s_row == d_row ) {
8889 /* Get source and destination points */
8890 SquareToPos( s_row, s_col, &s_x, &s_y);
8891 SquareToPos( d_row, d_col, &d_x, &d_y);
8894 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8896 else if( d_y < s_y ) {
8897 d_y += squareSize / 2 + squareSize / 4;
8900 d_y += squareSize / 2;
8904 d_x += squareSize / 2 - squareSize / 4;
8906 else if( d_x < s_x ) {
8907 d_x += squareSize / 2 + squareSize / 4;
8910 d_x += squareSize / 2;
8913 s_x += squareSize / 2;
8914 s_y += squareSize / 2;
8917 A_WIDTH = squareSize / 14.; //[HGM] make float
8919 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8921 hor = 64*s_col + 32; vert = 64*s_row + 32;
8922 for(i=0; i<= 64; i++) {
8923 damage[0][vert+6>>6][hor+6>>6] = True;
8924 damage[0][vert-6>>6][hor+6>>6] = True;
8925 damage[0][vert+6>>6][hor-6>>6] = True;
8926 damage[0][vert-6>>6][hor-6>>6] = True;
8927 hor += d_col - s_col; vert += d_row - s_row;
8931 Boolean IsDrawArrowEnabled()
8933 return appData.highlightMoveWithArrow && squareSize >= 32;
8936 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
8938 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8939 DrawArrowBetweenSquares(fromX, fromY, toX, toY);