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
149 #include <X11/Intrinsic.h>
150 #include <X11/StringDefs.h>
151 #include <X11/Shell.h>
152 #include <X11/cursorfont.h>
153 #include <X11/Xatom.h>
154 #include <X11/Xmu/Atoms.h>
156 #include <X11/Xaw3d/Dialog.h>
157 #include <X11/Xaw3d/Form.h>
158 #include <X11/Xaw3d/List.h>
159 #include <X11/Xaw3d/Label.h>
160 #include <X11/Xaw3d/SimpleMenu.h>
161 #include <X11/Xaw3d/SmeBSB.h>
162 #include <X11/Xaw3d/SmeLine.h>
163 #include <X11/Xaw3d/Box.h>
164 #include <X11/Xaw3d/MenuButton.h>
165 #include <X11/Xaw3d/Text.h>
166 #include <X11/Xaw3d/AsciiText.h>
168 #include <X11/Xaw/Dialog.h>
169 #include <X11/Xaw/Form.h>
170 #include <X11/Xaw/List.h>
171 #include <X11/Xaw/Label.h>
172 #include <X11/Xaw/SimpleMenu.h>
173 #include <X11/Xaw/SmeBSB.h>
174 #include <X11/Xaw/SmeLine.h>
175 #include <X11/Xaw/Box.h>
176 #include <X11/Xaw/MenuButton.h>
177 #include <X11/Xaw/Text.h>
178 #include <X11/Xaw/AsciiText.h>
181 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
186 #include "pixmaps/pixmaps.h"
187 #define IMAGE_EXT "xpm"
189 #define IMAGE_EXT "xim"
190 #include "bitmaps/bitmaps.h"
193 #include "bitmaps/icon_white.bm"
194 #include "bitmaps/icon_black.bm"
195 #include "bitmaps/checkmark.bm"
197 #include "frontend.h"
199 #include "backendz.h"
203 #include "xgamelist.h"
204 #include "xhistory.h"
205 #include "xedittags.h"
208 // must be moved to xengineoutput.h
210 void EngineOutputProc P((Widget w, XEvent *event,
211 String *prms, Cardinal *nprms));
212 void EvalGraphProc P((Widget w, XEvent *event,
213 String *prms, Cardinal *nprms));
220 #define usleep(t) _sleep2(((t)+500)/1000)
224 # define _(s) gettext (s)
225 # define N_(s) gettext_noop (s)
243 int main P((int argc, char **argv));
244 FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
245 char *init_path, char *filter, char *mode, int (*show_entry)(), char **name_return));
246 RETSIGTYPE CmailSigHandler P((int sig));
247 RETSIGTYPE IntSigHandler P((int sig));
248 RETSIGTYPE TermSizeSigHandler P((int sig));
249 void CreateGCs P((int redo));
250 void CreateAnyPieces P((void));
251 void CreateXIMPieces P((void));
252 void CreateXPMPieces P((void));
253 void CreateXPMBoard P((char *s, int n));
254 void CreatePieces P((void));
255 void CreatePieceMenus P((void));
256 Widget CreateMenuBar P((Menu *mb));
257 Widget CreateButtonBar P ((MenuItem *mi));
259 char *InsertPxlSize P((char *pattern, int targetPxlSize));
260 XFontSet CreateFontSet P((char *base_fnt_lst));
262 char *FindFont P((char *pattern, int targetPxlSize));
264 void PieceMenuPopup P((Widget w, XEvent *event,
265 String *params, Cardinal *num_params));
266 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
267 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
268 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
269 u_int wreq, u_int hreq));
270 void CreateGrid P((void));
271 int EventToSquare P((int x, int limit));
272 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
273 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
274 void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
275 void HandleUserMove P((Widget w, XEvent *event,
276 String *prms, Cardinal *nprms));
277 void AnimateUserMove P((Widget w, XEvent * event,
278 String * params, Cardinal * nParams));
279 void HandlePV P((Widget w, XEvent * event,
280 String * params, Cardinal * nParams));
281 void SelectPV P((Widget w, XEvent * event,
282 String * params, Cardinal * nParams));
283 void StopPV P((Widget w, XEvent * event,
284 String * params, Cardinal * nParams));
285 void WhiteClock P((Widget w, XEvent *event,
286 String *prms, Cardinal *nprms));
287 void BlackClock P((Widget w, XEvent *event,
288 String *prms, Cardinal *nprms));
289 void DrawPositionProc P((Widget w, XEvent *event,
290 String *prms, Cardinal *nprms));
291 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
293 void CommentClick P((Widget w, XEvent * event,
294 String * params, Cardinal * nParams));
295 void CommentPopUp P((char *title, char *label));
296 void CommentPopDown P((void));
297 void ICSInputBoxPopUp P((void));
298 void ICSInputBoxPopDown P((void));
299 void FileNamePopUp P((char *label, char *def, char *filter,
300 FileProc proc, char *openMode));
301 void FileNamePopDown P((void));
302 void FileNameCallback P((Widget w, XtPointer client_data,
303 XtPointer call_data));
304 void FileNameAction P((Widget w, XEvent *event,
305 String *prms, Cardinal *nprms));
306 void AskQuestionReplyAction P((Widget w, XEvent *event,
307 String *prms, Cardinal *nprms));
308 void AskQuestionProc P((Widget w, XEvent *event,
309 String *prms, Cardinal *nprms));
310 void AskQuestionPopDown P((void));
311 void PromotionPopDown P((void));
312 void PromotionCallback P((Widget w, XtPointer client_data,
313 XtPointer call_data));
314 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
315 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
316 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
317 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
319 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
321 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
323 void LoadPositionProc P((Widget w, XEvent *event,
324 String *prms, Cardinal *nprms));
325 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
327 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
329 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
331 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
333 void PastePositionProc P((Widget w, XEvent *event, String *prms,
335 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
336 void CopyGameListProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
337 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
338 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
339 void SavePositionProc P((Widget w, XEvent *event,
340 String *prms, Cardinal *nprms));
341 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
342 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
344 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
345 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
346 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
348 void MachineWhiteProc P((Widget w, XEvent *event,
349 String *prms, Cardinal *nprms));
350 void AnalyzeModeProc P((Widget w, XEvent *event,
351 String *prms, Cardinal *nprms));
352 void AnalyzeFileProc P((Widget w, XEvent *event,
353 String *prms, Cardinal *nprms));
354 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
356 void MatchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
357 void MatchOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
358 void IcsClientProc P((Widget w, XEvent *event, String *prms,
360 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
361 void EditPositionProc P((Widget w, XEvent *event,
362 String *prms, Cardinal *nprms));
363 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
364 void EditCommentProc P((Widget w, XEvent *event,
365 String *prms, Cardinal *nprms));
366 void IcsInputBoxProc P((Widget w, XEvent *event,
367 String *prms, Cardinal *nprms));
368 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
369 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
370 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
371 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
372 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
373 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
374 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
375 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
376 void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
377 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
378 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
379 void TypeInProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
380 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
381 void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
382 void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
383 void StopObservingProc P((Widget w, XEvent *event, String *prms,
385 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
387 void UploadProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
388 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
389 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
390 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
391 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
392 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
393 void AnnotateProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
394 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
396 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
398 void MoveNowProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
399 void AlwaysQueenProc P((Widget w, XEvent *event, String *prms,
401 void AnimateDraggingProc P((Widget w, XEvent *event, String *prms,
403 void AnimateMovingProc P((Widget w, XEvent *event, String *prms,
405 void AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
406 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
407 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
408 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
410 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
411 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
413 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
415 void HighlightArrowProc P((Widget w, XEvent *event, String *prms,
417 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
418 //void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
419 void OneClickProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
420 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
422 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
424 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
426 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
428 //void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
429 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
431 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
433 void HideThinkingProc P((Widget w, XEvent *event, String *prms,
435 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
437 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
438 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
439 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
440 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
441 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
442 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
443 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
444 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
445 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
446 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
447 void Iconify P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
448 void DisplayMove P((int moveNumber));
449 void DisplayTitle P((char *title));
450 void ICSInitScript P((void));
451 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
452 void ErrorPopUp P((char *title, char *text, int modal));
453 void ErrorPopDown P((void));
454 static char *ExpandPathName P((char *path));
455 static void CreateAnimVars P((void));
456 static void DragPieceMove P((int x, int y));
457 static void DrawDragPiece P((void));
458 char *ModeToWidgetName P((GameMode mode));
459 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
460 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
461 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
462 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
463 void OptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
464 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
465 void IcsTextProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
466 void LoadEngineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
467 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
468 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
469 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
470 void IcsOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
471 void SoundOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
472 void BoardOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
473 void LoadOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
474 void SaveOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
475 void EditBookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
476 void GameListOptionsPopDown P(());
477 void TimeControlPopDown P(());
478 void GenericPopDown P(());
479 void update_ics_width P(());
480 int get_term_width P(());
481 int CopyMemoProc P(());
482 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
483 Boolean IsDrawArrowEnabled P(());
486 * XBoard depends on Xt R4 or higher
488 int xtVersion = XtSpecificationRelease;
493 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
494 jailSquareColor, highlightSquareColor, premoveHighlightColor;
495 Pixel lowTimeWarningColor;
496 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
497 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
498 wjPieceGC, bjPieceGC, prelineGC, countGC;
499 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
500 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
501 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
502 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
503 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
504 ICSInputShell, fileNameShell, askQuestionShell;
505 Widget historyShell, evalGraphShell, gameListShell;
506 int hOffset; // [HGM] dual
507 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
508 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
509 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
511 XFontSet fontSet, clockFontSet;
514 XFontStruct *clockFontStruct;
516 Font coordFontID, countFontID;
517 XFontStruct *coordFontStruct, *countFontStruct;
518 XtAppContext appContext;
520 char *oldICSInteractionTitle;
524 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
526 Position commentX = -1, commentY = -1;
527 Dimension commentW, commentH;
528 typedef unsigned int BoardSize;
530 Boolean chessProgram;
532 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
533 int squareSize, smallLayout = 0, tinyLayout = 0,
534 marginW, marginH, // [HGM] for run-time resizing
535 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
536 ICSInputBoxUp = False, askQuestionUp = False,
537 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
538 errorUp = False, errorExitStatus = -1, lineGap, defaultLineGap;
539 Pixel timerForegroundPixel, timerBackgroundPixel;
540 Pixel buttonForegroundPixel, buttonBackgroundPixel;
541 char *chessDir, *programName, *programVersion,
542 *gameCopyFilename, *gamePasteFilename;
543 Boolean alwaysOnTop = False;
544 Boolean saveSettingsOnExit;
545 char *settingsFileName;
546 char *icsTextMenuString;
548 char *firstChessProgramNames;
549 char *secondChessProgramNames;
551 WindowPlacement wpMain;
552 WindowPlacement wpConsole;
553 WindowPlacement wpComment;
554 WindowPlacement wpMoveHistory;
555 WindowPlacement wpEvalGraph;
556 WindowPlacement wpEngineOutput;
557 WindowPlacement wpGameList;
558 WindowPlacement wpTags;
560 extern Widget shells[];
561 extern Boolean shellUp[];
565 Pixmap pieceBitmap[2][(int)BlackPawn];
566 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
567 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
568 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
569 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
570 Pixmap xpmBoardBitmap[2];
571 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
572 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
573 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
574 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
575 XImage *ximLightSquare, *ximDarkSquare;
578 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
579 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
581 #define White(piece) ((int)(piece) < (int)BlackPawn)
583 /* Variables for doing smooth animation. This whole thing
584 would be much easier if the board was double-buffered,
585 but that would require a fairly major rewrite. */
590 GC blitGC, pieceGC, outlineGC;
591 XPoint startSquare, prevFrame, mouseDelta;
595 int startBoardX, startBoardY;
598 /* There can be two pieces being animated at once: a player
599 can begin dragging a piece before the remote opponent has moved. */
601 static AnimState game, player;
603 /* Bitmaps for use as masks when drawing XPM pieces.
604 Need one for each black and white piece. */
605 static Pixmap xpmMask[BlackKing + 1];
607 /* This magic number is the number of intermediate frames used
608 in each half of the animation. For short moves it's reduced
609 by 1. The total number of frames will be factor * 2 + 1. */
612 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
614 MenuItem fileMenu[] = {
615 {N_("New Game Ctrl+N"), "New Game", ResetProc},
616 {N_("New Shuffle Game ..."), "New Shuffle Game", ShuffleMenuProc},
617 {N_("New Variant ... Alt+Shift+V"), "New Variant", NewVariantProc}, // [HGM] variant: not functional yet
618 {"----", NULL, NothingProc},
619 {N_("Load Game Ctrl+O"), "Load Game", LoadGameProc},
620 {N_("Load Position Ctrl+Shift+O"), "Load Position", LoadPositionProc},
621 // {N_("Load Next Game"), "Load Next Game", LoadNextGameProc},
622 // {N_("Load Previous Game"), "Load Previous Game", LoadPrevGameProc},
623 // {N_("Reload Same Game"), "Reload Same Game", ReloadGameProc},
624 {N_("Next Position Shift+PgDn"), "Load Next Position", LoadNextPositionProc},
625 {N_("Prev Position Shift+PgUp"), "Load Previous Position", LoadPrevPositionProc},
626 {"----", NULL, NothingProc},
627 // {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
628 {N_("Save Game Ctrl+S"), "Save Game", SaveGameProc},
629 {N_("Save Position Ctrl+Shift+S"), "Save Position", SavePositionProc},
630 {"----", NULL, NothingProc},
631 {N_("Mail Move"), "Mail Move", MailMoveProc},
632 {N_("Reload CMail Message"), "Reload CMail Message", ReloadCmailMsgProc},
633 {"----", NULL, NothingProc},
634 {N_("Quit Ctr+Q"), "Exit", QuitProc},
638 MenuItem editMenu[] = {
639 {N_("Copy Game Ctrl+C"), "Copy Game", CopyGameProc},
640 {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
641 {N_("Copy Game List"), "Copy Game List", CopyGameListProc},
642 {"----", NULL, NothingProc},
643 {N_("Paste Game Ctrl+V"), "Paste Game", PasteGameProc},
644 {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
645 {"----", NULL, NothingProc},
646 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
647 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
648 {N_("Edit Tags"), "Edit Tags", EditTagsProc},
649 {N_("Edit Comment"), "Edit Comment", EditCommentProc},
650 {N_("Edit Book"), "Edit Book", EditBookProc},
651 {"----", NULL, NothingProc},
652 {N_("Revert Home"), "Revert", RevertProc},
653 {N_("Annotate"), "Annotate", AnnotateProc},
654 {N_("Truncate Game End"), "Truncate Game", TruncateGameProc},
655 {"----", NULL, NothingProc},
656 {N_("Backward Alt+Left"), "Backward", BackwardProc},
657 {N_("Forward Alt+Right"), "Forward", ForwardProc},
658 {N_("Back to Start Alt+Home"), "Back to Start", ToStartProc},
659 {N_("Forward to End Alt+End"), "Forward to End", ToEndProc},
663 MenuItem viewMenu[] = {
664 {N_("Flip View F2"), "Flip View", FlipViewProc},
665 {"----", NULL, NothingProc},
666 {N_("Engine Output Alt+Shift+O"), "Show Engine Output", EngineOutputProc},
667 {N_("Move History Alt+Shift+H"), "Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
668 {N_("Evaluation Graph Alt+Shift+E"), "Show Evaluation Graph", EvalGraphProc},
669 {N_("Game List Alt+Shift+G"), "Show Game List", ShowGameListProc},
670 {N_("ICS text menu"), "ICStex", IcsTextProc},
671 {"----", NULL, NothingProc},
672 {N_("Tags"), "Show Tags", EditTagsProc},
673 {N_("Comments"), "Show Comments", EditCommentProc},
674 {N_("ICS Input Box"), "ICS Input Box", IcsInputBoxProc},
675 {"----", NULL, NothingProc},
676 {N_("Board..."), "Board Options", BoardOptionsProc},
677 {N_("Game List Tags..."), "Game List", GameListOptionsPopUp},
681 MenuItem modeMenu[] = {
682 {N_("Machine White Ctrl+W"), "Machine White", MachineWhiteProc},
683 {N_("Machine Black Ctrl+B"), "Machine Black", MachineBlackProc},
684 {N_("Two Machines Ctrl+T"), "Two Machines", TwoMachinesProc},
685 {N_("Analysis Mode Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
686 {N_("Analyze File Ctrl+F"), "Analyze File", AnalyzeFileProc },
687 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
688 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
689 {N_("Training"), "Training", TrainingProc},
690 {N_("ICS Client"), "ICS Client", IcsClientProc},
691 {"----", NULL, NothingProc},
692 {N_("Machine Match"), "Machine Match", MatchProc},
693 {N_("Pause Pause"), "Pause", PauseProc},
697 MenuItem actionMenu[] = {
698 {N_("Accept F3"), "Accept", AcceptProc},
699 {N_("Decline F4"), "Decline", DeclineProc},
700 {N_("Rematch F12"), "Rematch", RematchProc},
701 {"----", NULL, NothingProc},
702 {N_("Call Flag F5"), "Call Flag", CallFlagProc},
703 {N_("Draw F6"), "Draw", DrawProc},
704 {N_("Adjourn F7"), "Adjourn", AdjournProc},
705 {N_("Abort F8"),"Abort", AbortProc},
706 {N_("Resign F9"), "Resign", ResignProc},
707 {"----", NULL, NothingProc},
708 {N_("Stop Observing F10"), "Stop Observing", StopObservingProc},
709 {N_("Stop Examining F11"), "Stop Examining", StopExaminingProc},
710 {N_("Upload to Examine"), "Upload to Examine", UploadProc},
711 {"----", NULL, NothingProc},
712 {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
713 {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
714 {N_("Adjudicate Draw"), "Adjudicate Draw", AdjuDrawProc},
718 MenuItem engineMenu[] = {
719 {N_("Load New Engine ..."), "Load Engine", LoadEngineProc},
720 {"----", NULL, NothingProc},
721 {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
722 {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
723 {"----", NULL, NothingProc},
724 {N_("Hint"), "Hint", HintProc},
725 {N_("Book"), "Book", BookProc},
726 {"----", NULL, NothingProc},
727 {N_("Move Now Ctrl+M"), "Move Now", MoveNowProc},
728 {N_("Retract Move Ctrl+X"), "Retract Move", RetractMoveProc},
732 MenuItem optionsMenu[] = {
733 #define OPTIONSDIALOG
735 {N_("General ..."), "General", OptionsProc},
737 {N_("Time Control ... Alt+Shift+T"), "Time Control", TimeControlProc},
738 {N_("Common Engine ... Alt+Shift+U"), "Common Engine", UciMenuProc},
739 {N_("Adjudications ... Alt+Shift+J"), "Adjudications", EngineMenuProc},
740 {N_("ICS ..."), "ICS", IcsOptionsProc},
741 {N_("Match ..."), "Match", MatchOptionsProc},
742 {N_("Load Game ..."), "Load Game", LoadOptionsProc},
743 {N_("Save Game ..."), "Save Game", SaveOptionsProc},
744 // {N_(" ..."), "", OptionsProc},
745 {N_("Game List ..."), "Game List", GameListOptionsPopUp},
746 {N_("Sounds ..."), "Sounds", SoundOptionsProc},
747 {"----", NULL, NothingProc},
748 #ifndef OPTIONSDIALOG
749 {N_("Always Queen Ctrl+Shift+Q"), "Always Queen", AlwaysQueenProc},
750 {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
751 {N_("Animate Moving Ctrl+Shift+A"), "Animate Moving", AnimateMovingProc},
752 {N_("Auto Flag Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
753 {N_("Auto Flip View"), "Auto Flip View", AutoflipProc},
754 {N_("Blindfold"), "Blindfold", BlindfoldProc},
755 {N_("Flash Moves"), "Flash Moves", FlashMovesProc},
757 {N_("Highlight Dragging"), "Highlight Dragging", HighlightDraggingProc},
759 {N_("Highlight Last Move"), "Highlight Last Move", HighlightLastMoveProc},
760 {N_("Highlight With Arrow"), "Arrow", HighlightArrowProc},
761 {N_("Move Sound"), "Move Sound", MoveSoundProc},
762 // {N_("ICS Alarm"), "ICS Alarm", IcsAlarmProc},
763 {N_("One-Click Moving"), "OneClick", OneClickProc},
764 {N_("Periodic Updates"), "Periodic Updates", PeriodicUpdatesProc},
765 {N_("Ponder Next Move Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
766 {N_("Popup Exit Message"), "Popup Exit Message", PopupExitMessageProc},
767 {N_("Popup Move Errors"), "Popup Move Errors", PopupMoveErrorsProc},
768 // {N_("Premove"), "Premove", PremoveProc},
769 {N_("Show Coords"), "Show Coords", ShowCoordsProc},
770 {N_("Hide Thinking Ctrl+Shift+H"), "Hide Thinking", HideThinkingProc},
771 {N_("Test Legality Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
772 {"----", NULL, NothingProc},
774 {N_("Save Settings Now"), "Save Settings Now", SaveSettingsProc},
775 {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
779 MenuItem helpMenu[] = {
780 {N_("Info XBoard"), "Info XBoard", InfoProc},
781 {N_("Man XBoard F1"), "Man XBoard", ManProc},
782 {"----", NULL, NothingProc},
783 {N_("About XBoard"), "About XBoard", AboutProc},
788 {N_("File"), "File", fileMenu},
789 {N_("Edit"), "Edit", editMenu},
790 {N_("View"), "View", viewMenu},
791 {N_("Mode"), "Mode", modeMenu},
792 {N_("Action"), "Action", actionMenu},
793 {N_("Engine"), "Engine", engineMenu},
794 {N_("Options"), "Options", optionsMenu},
795 {N_("Help"), "Help", helpMenu},
799 #define PAUSE_BUTTON "P"
800 MenuItem buttonBar[] = {
801 {"<<", "<<", ToStartProc},
802 {"<", "<", BackwardProc},
803 {PAUSE_BUTTON, PAUSE_BUTTON, PauseProc},
804 {">", ">", ForwardProc},
805 {">>", ">>", ToEndProc},
809 #define PIECE_MENU_SIZE 18
810 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
811 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
812 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
813 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
814 N_("Empty square"), N_("Clear board") },
815 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
816 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
817 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
818 N_("Empty square"), N_("Clear board") }
820 /* must be in same order as pieceMenuStrings! */
821 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
822 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
823 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
824 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
825 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
826 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
827 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
828 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
829 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
832 #define DROP_MENU_SIZE 6
833 String dropMenuStrings[DROP_MENU_SIZE] = {
834 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
836 /* must be in same order as dropMenuStrings! */
837 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
838 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
839 WhiteRook, WhiteQueen
847 DropMenuEnables dmEnables[] = {
865 { XtNborderWidth, 0 },
866 { XtNdefaultDistance, 0 },
870 { XtNborderWidth, 0 },
871 { XtNresizable, (XtArgVal) True },
875 { XtNborderWidth, 0 },
881 { XtNjustify, (XtArgVal) XtJustifyRight },
882 { XtNlabel, (XtArgVal) "..." },
883 { XtNresizable, (XtArgVal) True },
884 { XtNresize, (XtArgVal) False }
887 Arg messageArgs[] = {
888 { XtNjustify, (XtArgVal) XtJustifyLeft },
889 { XtNlabel, (XtArgVal) "..." },
890 { XtNresizable, (XtArgVal) True },
891 { XtNresize, (XtArgVal) False }
895 { XtNborderWidth, 0 },
896 { XtNjustify, (XtArgVal) XtJustifyLeft }
899 XtResource clientResources[] = {
900 { "flashCount", "flashCount", XtRInt, sizeof(int),
901 XtOffset(AppDataPtr, flashCount), XtRImmediate,
902 (XtPointer) FLASH_COUNT },
905 XrmOptionDescRec shellOptions[] = {
906 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
907 { "-flash", "flashCount", XrmoptionNoArg, "3" },
908 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
911 XtActionsRec boardActions[] = {
912 { "DrawPosition", DrawPositionProc },
913 { "HandleUserMove", HandleUserMove },
914 { "AnimateUserMove", AnimateUserMove },
915 { "HandlePV", HandlePV },
916 { "SelectPV", SelectPV },
917 { "StopPV", StopPV },
918 { "FileNameAction", FileNameAction },
919 { "AskQuestionProc", AskQuestionProc },
920 { "AskQuestionReplyAction", AskQuestionReplyAction },
921 { "PieceMenuPopup", PieceMenuPopup },
922 { "WhiteClock", WhiteClock },
923 { "BlackClock", BlackClock },
924 { "Iconify", Iconify },
925 { "ResetProc", ResetProc },
926 { "NewVariantProc", NewVariantProc },
927 { "LoadGameProc", LoadGameProc },
928 { "LoadNextGameProc", LoadNextGameProc },
929 { "LoadPrevGameProc", LoadPrevGameProc },
930 { "LoadSelectedProc", LoadSelectedProc },
931 { "SetFilterProc", SetFilterProc },
932 { "ReloadGameProc", ReloadGameProc },
933 { "LoadPositionProc", LoadPositionProc },
934 { "LoadNextPositionProc", LoadNextPositionProc },
935 { "LoadPrevPositionProc", LoadPrevPositionProc },
936 { "ReloadPositionProc", ReloadPositionProc },
937 { "CopyPositionProc", CopyPositionProc },
938 { "PastePositionProc", PastePositionProc },
939 { "CopyGameProc", CopyGameProc },
940 { "CopyGameListProc", CopyGameListProc },
941 { "PasteGameProc", PasteGameProc },
942 { "SaveGameProc", SaveGameProc },
943 { "SavePositionProc", SavePositionProc },
944 { "MailMoveProc", MailMoveProc },
945 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
946 { "QuitProc", QuitProc },
947 { "MachineWhiteProc", MachineWhiteProc },
948 { "MachineBlackProc", MachineBlackProc },
949 { "AnalysisModeProc", AnalyzeModeProc },
950 { "AnalyzeFileProc", AnalyzeFileProc },
951 { "TwoMachinesProc", TwoMachinesProc },
952 { "IcsClientProc", IcsClientProc },
953 { "EditGameProc", EditGameProc },
954 { "EditPositionProc", EditPositionProc },
955 { "TrainingProc", EditPositionProc },
956 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
957 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
958 { "ShowGameListProc", ShowGameListProc },
959 { "ShowMoveListProc", HistoryShowProc},
960 { "EditTagsProc", EditCommentProc },
961 { "EditBookProc", EditBookProc },
962 { "EditCommentProc", EditCommentProc },
963 { "IcsInputBoxProc", IcsInputBoxProc },
964 { "PauseProc", PauseProc },
965 { "AcceptProc", AcceptProc },
966 { "DeclineProc", DeclineProc },
967 { "RematchProc", RematchProc },
968 { "CallFlagProc", CallFlagProc },
969 { "DrawProc", DrawProc },
970 { "AdjournProc", AdjournProc },
971 { "AbortProc", AbortProc },
972 { "ResignProc", ResignProc },
973 { "AdjuWhiteProc", AdjuWhiteProc },
974 { "AdjuBlackProc", AdjuBlackProc },
975 { "AdjuDrawProc", AdjuDrawProc },
976 { "TypeInProc", TypeInProc },
977 { "EnterKeyProc", EnterKeyProc },
978 { "UpKeyProc", UpKeyProc },
979 { "DownKeyProc", DownKeyProc },
980 { "StopObservingProc", StopObservingProc },
981 { "StopExaminingProc", StopExaminingProc },
982 { "UploadProc", UploadProc },
983 { "BackwardProc", BackwardProc },
984 { "ForwardProc", ForwardProc },
985 { "ToStartProc", ToStartProc },
986 { "ToEndProc", ToEndProc },
987 { "RevertProc", RevertProc },
988 { "AnnotateProc", AnnotateProc },
989 { "TruncateGameProc", TruncateGameProc },
990 { "MoveNowProc", MoveNowProc },
991 { "RetractMoveProc", RetractMoveProc },
992 { "EngineMenuProc", (XtActionProc) EngineMenuProc },
993 { "UciMenuProc", (XtActionProc) UciMenuProc },
994 { "TimeControlProc", (XtActionProc) TimeControlProc },
995 { "FlipViewProc", FlipViewProc },
996 { "PonderNextMoveProc", PonderNextMoveProc },
997 #ifndef OPTIONSDIALOG
998 { "AlwaysQueenProc", AlwaysQueenProc },
999 { "AnimateDraggingProc", AnimateDraggingProc },
1000 { "AnimateMovingProc", AnimateMovingProc },
1001 { "AutoflagProc", AutoflagProc },
1002 { "AutoflipProc", AutoflipProc },
1003 { "BlindfoldProc", BlindfoldProc },
1004 { "FlashMovesProc", FlashMovesProc },
1006 { "HighlightDraggingProc", HighlightDraggingProc },
1008 { "HighlightLastMoveProc", HighlightLastMoveProc },
1009 // { "IcsAlarmProc", IcsAlarmProc },
1010 { "MoveSoundProc", MoveSoundProc },
1011 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
1012 { "PopupExitMessageProc", PopupExitMessageProc },
1013 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
1014 // { "PremoveProc", PremoveProc },
1015 { "ShowCoordsProc", ShowCoordsProc },
1016 { "ShowThinkingProc", ShowThinkingProc },
1017 { "HideThinkingProc", HideThinkingProc },
1018 { "TestLegalityProc", TestLegalityProc },
1020 { "SaveSettingsProc", SaveSettingsProc },
1021 { "SaveOnExitProc", SaveOnExitProc },
1022 { "InfoProc", InfoProc },
1023 { "ManProc", ManProc },
1024 { "HintProc", HintProc },
1025 { "BookProc", BookProc },
1026 { "AboutGameProc", AboutGameProc },
1027 { "AboutProc", AboutProc },
1028 { "DebugProc", DebugProc },
1029 { "NothingProc", NothingProc },
1030 { "CommentClick", (XtActionProc) CommentClick },
1031 { "CommentPopDown", (XtActionProc) CommentPopDown },
1032 { "TagsPopDown", (XtActionProc) TagsPopDown },
1033 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1034 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1035 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1036 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1037 { "GameListPopDown", (XtActionProc) GameListPopDown },
1038 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1039 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1040 { "HistoryPopDown", (XtActionProc) HistoryPopDown },
1041 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1042 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1043 { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
1044 { "GenericPopDown", (XtActionProc) GenericPopDown },
1045 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1048 char globalTranslations[] =
1049 ":<Key>F9: ResignProc() \n \
1050 :Ctrl<Key>n: ResetProc() \n \
1051 :Meta<Key>V: NewVariantProc() \n \
1052 :Ctrl<Key>o: LoadGameProc() \n \
1053 :Meta<Key>Next: LoadNextGameProc() \n \
1054 :Meta<Key>Prior: LoadPrevGameProc() \n \
1055 :Ctrl<Key>s: SaveGameProc() \n \
1056 :Ctrl<Key>c: CopyGameProc() \n \
1057 :Ctrl<Key>v: PasteGameProc() \n \
1058 :Ctrl<Key>O: LoadPositionProc() \n \
1059 :Shift<Key>Next: LoadNextPositionProc() \n \
1060 :Shift<Key>Prior: LoadPrevPositionProc() \n \
1061 :Ctrl<Key>S: SavePositionProc() \n \
1062 :Ctrl<Key>C: CopyPositionProc() \n \
1063 :Ctrl<Key>V: PastePositionProc() \n \
1064 :Ctrl<Key>q: QuitProc() \n \
1065 :Ctrl<Key>w: MachineWhiteProc() \n \
1066 :Ctrl<Key>b: MachineBlackProc() \n \
1067 :Ctrl<Key>t: TwoMachinesProc() \n \
1068 :Ctrl<Key>a: AnalysisModeProc() \n \
1069 :Ctrl<Key>f: AnalyzeFileProc() \n \
1070 :Ctrl<Key>e: EditGameProc() \n \
1071 :Ctrl<Key>E: EditPositionProc() \n \
1072 :Meta<Key>O: EngineOutputProc() \n \
1073 :Meta<Key>E: EvalGraphProc() \n \
1074 :Meta<Key>G: ShowGameListProc() \n \
1075 :Meta<Key>H: ShowMoveListProc() \n \
1076 :<Key>Pause: PauseProc() \n \
1077 :<Key>F3: AcceptProc() \n \
1078 :<Key>F4: DeclineProc() \n \
1079 :<Key>F12: RematchProc() \n \
1080 :<Key>F5: CallFlagProc() \n \
1081 :<Key>F6: DrawProc() \n \
1082 :<Key>F7: AdjournProc() \n \
1083 :<Key>F8: AbortProc() \n \
1084 :<Key>F10: StopObservingProc() \n \
1085 :<Key>F11: StopExaminingProc() \n \
1086 :Meta Ctrl<Key>F12: DebugProc() \n \
1087 :Meta<Key>End: ToEndProc() \n \
1088 :Meta<Key>Right: ForwardProc() \n \
1089 :Meta<Key>Home: ToStartProc() \n \
1090 :Meta<Key>Left: BackwardProc() \n \
1091 :<Key>Home: RevertProc() \n \
1092 :<Key>End: TruncateGameProc() \n \
1093 :Ctrl<Key>m: MoveNowProc() \n \
1094 :Ctrl<Key>x: RetractMoveProc() \n \
1095 :Meta<Key>J: EngineMenuProc() \n \
1096 :Meta<Key>U: UciMenuProc() \n \
1097 :Meta<Key>T: TimeControlProc() \n \
1098 :Ctrl<Key>P: PonderNextMoveProc() \n "
1099 #ifndef OPTIONSDIALOG
1101 :Ctrl<Key>Q: AlwaysQueenProc() \n \
1102 :Ctrl<Key>F: AutoflagProc() \n \
1103 :Ctrl<Key>A: AnimateMovingProc() \n \
1104 :Ctrl<Key>L: TestLegalityProc() \n \
1105 :Ctrl<Key>H: HideThinkingProc() \n "
1108 :<Key>-: Iconify() \n \
1109 :<Key>F1: ManProc() \n \
1110 :<Key>F2: FlipViewProc() \n \
1111 <KeyDown>.: BackwardProc() \n \
1112 <KeyUp>.: ForwardProc() \n \
1113 Shift<Key>1: AskQuestionProc(\"Direct command\",\
1114 \"Send to chess program:\",,1) \n \
1115 Shift<Key>2: AskQuestionProc(\"Direct command\",\
1116 \"Send to second chess program:\",,2) \n";
1118 char boardTranslations[] =
1119 "<Btn1Down>: HandleUserMove(0) \n \
1120 Shift<Btn1Up>: HandleUserMove(1) \n \
1121 <Btn1Up>: HandleUserMove(0) \n \
1122 <Btn1Motion>: AnimateUserMove() \n \
1123 <Btn3Motion>: HandlePV() \n \
1124 <Btn3Up>: PieceMenuPopup(menuB) \n \
1125 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1126 PieceMenuPopup(menuB) \n \
1127 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1128 PieceMenuPopup(menuW) \n \
1129 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1130 PieceMenuPopup(menuW) \n \
1131 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1132 PieceMenuPopup(menuB) \n";
1134 char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
1135 char blackTranslations[] = "<BtnDown>: BlackClock()\n";
1137 char ICSInputTranslations[] =
1138 "<Key>Up: UpKeyProc() \n "
1139 "<Key>Down: DownKeyProc() \n "
1140 "<Key>Return: EnterKeyProc() \n";
1142 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1143 // as the widget is destroyed before the up-click can call extend-end
1144 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1146 String xboardResources[] = {
1147 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1148 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1149 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1154 /* Max possible square size */
1155 #define MAXSQSIZE 256
1157 static int xpm_avail[MAXSQSIZE];
1159 #ifdef HAVE_DIR_STRUCT
1161 /* Extract piece size from filename */
1163 xpm_getsize(name, len, ext)
1174 if ((p=strchr(name, '.')) == NULL ||
1175 StrCaseCmp(p+1, ext) != 0)
1181 while (*p && isdigit(*p))
1188 /* Setup xpm_avail */
1190 xpm_getavail(dirname, ext)
1198 for (i=0; i<MAXSQSIZE; ++i)
1201 if (appData.debugMode)
1202 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1204 dir = opendir(dirname);
1207 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1208 programName, dirname);
1212 while ((ent=readdir(dir)) != NULL) {
1213 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1214 if (i > 0 && i < MAXSQSIZE)
1224 xpm_print_avail(fp, ext)
1230 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1231 for (i=1; i<MAXSQSIZE; ++i) {
1237 /* Return XPM piecesize closest to size */
1239 xpm_closest_to(dirname, size, ext)
1245 int sm_diff = MAXSQSIZE;
1249 xpm_getavail(dirname, ext);
1251 if (appData.debugMode)
1252 xpm_print_avail(stderr, ext);
1254 for (i=1; i<MAXSQSIZE; ++i) {
1257 diff = (diff<0) ? -diff : diff;
1258 if (diff < sm_diff) {
1266 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1272 #else /* !HAVE_DIR_STRUCT */
1273 /* If we are on a system without a DIR struct, we can't
1274 read the directory, so we can't collect a list of
1275 filenames, etc., so we can't do any size-fitting. */
1277 xpm_closest_to(dirname, size, ext)
1282 fprintf(stderr, _("\
1283 Warning: No DIR structure found on this system --\n\
1284 Unable to autosize for XPM/XIM pieces.\n\
1285 Please report this error to %s.\n\
1286 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
1289 #endif /* HAVE_DIR_STRUCT */
1291 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1292 "magenta", "cyan", "white" };
1296 TextColors textColors[(int)NColorClasses];
1298 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1300 parse_color(str, which)
1304 char *p, buf[100], *d;
1307 if (strlen(str) > 99) /* watch bounds on buf */
1312 for (i=0; i<which; ++i) {
1319 /* Could be looking at something like:
1321 .. in which case we want to stop on a comma also */
1322 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1326 return -1; /* Use default for empty field */
1329 if (which == 2 || isdigit(*p))
1332 while (*p && isalpha(*p))
1337 for (i=0; i<8; ++i) {
1338 if (!StrCaseCmp(buf, cnames[i]))
1339 return which? (i+40) : (i+30);
1341 if (!StrCaseCmp(buf, "default")) return -1;
1343 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1348 parse_cpair(cc, str)
1352 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1353 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1358 /* bg and attr are optional */
1359 textColors[(int)cc].bg = parse_color(str, 1);
1360 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1361 textColors[(int)cc].attr = 0;
1367 /* Arrange to catch delete-window events */
1368 Atom wm_delete_window;
1370 CatchDeleteWindow(Widget w, String procname)
1373 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1374 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1375 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1382 XtSetArg(args[0], XtNiconic, False);
1383 XtSetValues(shellWidget, args, 1);
1385 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1388 //---------------------------------------------------------------------------------------------------------
1389 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1392 #define CW_USEDEFAULT (1<<31)
1393 #define ICS_TEXT_MENU_SIZE 90
1394 #define DEBUG_FILE "xboard.debug"
1395 #define SetCurrentDirectory chdir
1396 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1400 // these two must some day move to frontend.h, when they are implemented
1401 Boolean GameListIsUp();
1403 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1406 // front-end part of option handling
1408 // [HGM] This platform-dependent table provides the location for storing the color info
1409 extern char *crWhite, * crBlack;
1413 &appData.whitePieceColor,
1414 &appData.blackPieceColor,
1415 &appData.lightSquareColor,
1416 &appData.darkSquareColor,
1417 &appData.highlightSquareColor,
1418 &appData.premoveHighlightColor,
1419 &appData.lowTimeWarningColor,
1430 // [HGM] font: keep a font for each square size, even non-stndard ones
1431 #define NUM_SIZES 18
1432 #define MAX_SIZE 130
1433 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1434 char *fontTable[NUM_FONTS][MAX_SIZE];
1437 ParseFont(char *name, int number)
1438 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1440 if(sscanf(name, "size%d:", &size)) {
1441 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1442 // defer processing it until we know if it matches our board size
1443 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1444 fontTable[number][size] = strdup(strchr(name, ':')+1);
1445 fontValid[number][size] = True;
1450 case 0: // CLOCK_FONT
1451 appData.clockFont = strdup(name);
1453 case 1: // MESSAGE_FONT
1454 appData.font = strdup(name);
1456 case 2: // COORD_FONT
1457 appData.coordFont = strdup(name);
1462 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1467 { // only 2 fonts currently
1468 appData.clockFont = CLOCK_FONT_NAME;
1469 appData.coordFont = COORD_FONT_NAME;
1470 appData.font = DEFAULT_FONT_NAME;
1475 { // no-op, until we identify the code for this already in XBoard and move it here
1479 ParseColor(int n, char *name)
1480 { // in XBoard, just copy the color-name string
1481 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1485 ParseTextAttribs(ColorClass cc, char *s)
1487 (&appData.colorShout)[cc] = strdup(s);
1491 ParseBoardSize(void *addr, char *name)
1493 appData.boardSize = strdup(name);
1498 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1502 SetCommPortDefaults()
1503 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1506 // [HGM] args: these three cases taken out to stay in front-end
1508 SaveFontArg(FILE *f, ArgDescriptor *ad)
1511 int i, n = (int)(intptr_t)ad->argLoc;
1513 case 0: // CLOCK_FONT
1514 name = appData.clockFont;
1516 case 1: // MESSAGE_FONT
1517 name = appData.font;
1519 case 2: // COORD_FONT
1520 name = appData.coordFont;
1525 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1526 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1527 fontTable[n][squareSize] = strdup(name);
1528 fontValid[n][squareSize] = True;
1531 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1532 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1537 { // nothing to do, as the sounds are at all times represented by their text-string names already
1541 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1542 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1543 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1547 SaveColor(FILE *f, ArgDescriptor *ad)
1548 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1549 if(colorVariable[(int)(intptr_t)ad->argLoc])
1550 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1554 SaveBoardSize(FILE *f, char *name, void *addr)
1555 { // wrapper to shield back-end from BoardSize & sizeInfo
1556 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1560 ParseCommPortSettings(char *s)
1561 { // no such option in XBoard (yet)
1564 extern Widget engineOutputShell;
1567 GetActualPlacement(Widget wg, WindowPlacement *wp)
1577 XtSetArg(args[i], XtNx, &x); i++;
1578 XtSetArg(args[i], XtNy, &y); i++;
1579 XtSetArg(args[i], XtNwidth, &w); i++;
1580 XtSetArg(args[i], XtNheight, &h); i++;
1581 XtGetValues(wg, args, i);
1590 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1591 // In XBoard this will have to wait until awareness of window parameters is implemented
1592 GetActualPlacement(shellWidget, &wpMain);
1593 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1594 if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1595 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1596 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1597 if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1598 if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1602 PrintCommPortSettings(FILE *f, char *name)
1603 { // This option does not exist in XBoard
1607 MySearchPath(char *installDir, char *name, char *fullname)
1608 { // just append installDir and name. Perhaps ExpandPath should be used here?
1609 name = ExpandPathName(name);
1610 if(name && name[0] == '/')
1611 safeStrCpy(fullname, name, MSG_SIZ );
1613 sprintf(fullname, "%s%c%s", installDir, '/', name);
1619 MyGetFullPathName(char *name, char *fullname)
1620 { // should use ExpandPath?
1621 name = ExpandPathName(name);
1622 safeStrCpy(fullname, name, MSG_SIZ );
1627 EnsureOnScreen(int *x, int *y, int minX, int minY)
1634 { // [HGM] args: allows testing if main window is realized from back-end
1635 return xBoardWindow != 0;
1639 PopUpStartupDialog()
1640 { // start menu not implemented in XBoard
1644 ConvertToLine(int argc, char **argv)
1646 static char line[128*1024], buf[1024];
1650 for(i=1; i<argc; i++)
1652 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1653 && argv[i][0] != '{' )
1654 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1656 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1657 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1660 line[strlen(line)-1] = NULLCHAR;
1664 //--------------------------------------------------------------------------------------------
1666 extern Boolean twoBoards, partnerUp;
1669 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1671 #define BoardSize int
1672 void InitDrawingSizes(BoardSize boardSize, int flags)
1673 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1674 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1676 XtGeometryResult gres;
1679 if(!formWidget) return;
1682 * Enable shell resizing.
1684 shellArgs[0].value = (XtArgVal) &w;
1685 shellArgs[1].value = (XtArgVal) &h;
1686 XtGetValues(shellWidget, shellArgs, 2);
1688 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1689 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1690 XtSetValues(shellWidget, &shellArgs[2], 4);
1692 XtSetArg(args[0], XtNdefaultDistance, &sep);
1693 XtGetValues(formWidget, args, 1);
1695 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1696 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1697 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1699 hOffset = boardWidth + 10;
1700 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1701 secondSegments[i] = gridSegments[i];
1702 secondSegments[i].x1 += hOffset;
1703 secondSegments[i].x2 += hOffset;
1706 XtSetArg(args[0], XtNwidth, boardWidth);
1707 XtSetArg(args[1], XtNheight, boardHeight);
1708 XtSetValues(boardWidget, args, 2);
1710 timerWidth = (boardWidth - sep) / 2;
1711 XtSetArg(args[0], XtNwidth, timerWidth);
1712 XtSetValues(whiteTimerWidget, args, 1);
1713 XtSetValues(blackTimerWidget, args, 1);
1715 XawFormDoLayout(formWidget, False);
1717 if (appData.titleInWindow) {
1719 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1720 XtSetArg(args[i], XtNheight, &h); i++;
1721 XtGetValues(titleWidget, args, i);
1723 w = boardWidth - 2*bor;
1725 XtSetArg(args[0], XtNwidth, &w);
1726 XtGetValues(menuBarWidget, args, 1);
1727 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1730 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1731 if (gres != XtGeometryYes && appData.debugMode) {
1733 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1734 programName, gres, w, h, wr, hr);
1738 XawFormDoLayout(formWidget, True);
1741 * Inhibit shell resizing.
1743 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1744 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1745 shellArgs[4].value = shellArgs[2].value = w;
1746 shellArgs[5].value = shellArgs[3].value = h;
1747 XtSetValues(shellWidget, &shellArgs[0], 6);
1749 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1752 for(i=0; i<4; i++) {
1754 for(p=0; p<=(int)WhiteKing; p++)
1755 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1756 if(gameInfo.variant == VariantShogi) {
1757 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1758 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1759 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1760 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1761 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1764 if(gameInfo.variant == VariantGothic) {
1765 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1768 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1769 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1770 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1773 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1774 for(p=0; p<=(int)WhiteKing; p++)
1775 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1776 if(gameInfo.variant == VariantShogi) {
1777 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1778 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1779 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1780 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1781 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1784 if(gameInfo.variant == VariantGothic) {
1785 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1788 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1789 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1790 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1795 for(i=0; i<2; i++) {
1797 for(p=0; p<=(int)WhiteKing; p++)
1798 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1799 if(gameInfo.variant == VariantShogi) {
1800 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1801 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1802 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1803 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1804 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1807 if(gameInfo.variant == VariantGothic) {
1808 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1811 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1812 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1813 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1823 void ParseIcsTextColors()
1824 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1825 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1826 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1827 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1828 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1829 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1830 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1831 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1832 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1833 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1834 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1836 if (appData.colorize) {
1838 _("%s: can't parse color names; disabling colorization\n"),
1841 appData.colorize = FALSE;
1846 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1847 XrmValue vFrom, vTo;
1848 int forceMono = False;
1850 if (!appData.monoMode) {
1851 vFrom.addr = (caddr_t) appData.lightSquareColor;
1852 vFrom.size = strlen(appData.lightSquareColor);
1853 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1854 if (vTo.addr == NULL) {
1855 appData.monoMode = True;
1858 lightSquareColor = *(Pixel *) vTo.addr;
1861 if (!appData.monoMode) {
1862 vFrom.addr = (caddr_t) appData.darkSquareColor;
1863 vFrom.size = strlen(appData.darkSquareColor);
1864 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1865 if (vTo.addr == NULL) {
1866 appData.monoMode = True;
1869 darkSquareColor = *(Pixel *) vTo.addr;
1872 if (!appData.monoMode) {
1873 vFrom.addr = (caddr_t) appData.whitePieceColor;
1874 vFrom.size = strlen(appData.whitePieceColor);
1875 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1876 if (vTo.addr == NULL) {
1877 appData.monoMode = True;
1880 whitePieceColor = *(Pixel *) vTo.addr;
1883 if (!appData.monoMode) {
1884 vFrom.addr = (caddr_t) appData.blackPieceColor;
1885 vFrom.size = strlen(appData.blackPieceColor);
1886 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1887 if (vTo.addr == NULL) {
1888 appData.monoMode = True;
1891 blackPieceColor = *(Pixel *) vTo.addr;
1895 if (!appData.monoMode) {
1896 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1897 vFrom.size = strlen(appData.highlightSquareColor);
1898 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1899 if (vTo.addr == NULL) {
1900 appData.monoMode = True;
1903 highlightSquareColor = *(Pixel *) vTo.addr;
1907 if (!appData.monoMode) {
1908 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1909 vFrom.size = strlen(appData.premoveHighlightColor);
1910 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1911 if (vTo.addr == NULL) {
1912 appData.monoMode = True;
1915 premoveHighlightColor = *(Pixel *) vTo.addr;
1923 { // [HGM] taken out of main
1925 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1926 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1927 appData.bitmapDirectory = DEF_BITMAP_DIR;
1929 if (appData.bitmapDirectory[0] != NULLCHAR) {
1933 CreateXPMBoard(appData.liteBackTextureFile, 1);
1934 CreateXPMBoard(appData.darkBackTextureFile, 0);
1938 /* Create regular pieces */
1939 if (!useImages) CreatePieces();
1948 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1949 XSetWindowAttributes window_attributes;
1951 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1952 XrmValue vFrom, vTo;
1953 XtGeometryResult gres;
1956 int forceMono = False;
1958 srandom(time(0)); // [HGM] book: make random truly random
1960 setbuf(stdout, NULL);
1961 setbuf(stderr, NULL);
1964 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1965 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1969 programName = strrchr(argv[0], '/');
1970 if (programName == NULL)
1971 programName = argv[0];
1976 XtSetLanguageProc(NULL, NULL, NULL);
1977 bindtextdomain(PACKAGE, LOCALEDIR);
1978 textdomain(PACKAGE);
1982 XtAppInitialize(&appContext, "XBoard", shellOptions,
1983 XtNumber(shellOptions),
1984 &argc, argv, xboardResources, NULL, 0);
1985 appData.boardSize = "";
1986 InitAppData(ConvertToLine(argc, argv));
1988 if (p == NULL) p = "/tmp";
1989 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1990 gameCopyFilename = (char*) malloc(i);
1991 gamePasteFilename = (char*) malloc(i);
1992 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1993 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1995 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1996 clientResources, XtNumber(clientResources),
1999 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
2000 static char buf[MSG_SIZ];
2001 EscapeExpand(buf, appData.firstInitString);
2002 appData.firstInitString = strdup(buf);
2003 EscapeExpand(buf, appData.secondInitString);
2004 appData.secondInitString = strdup(buf);
2005 EscapeExpand(buf, appData.firstComputerString);
2006 appData.firstComputerString = strdup(buf);
2007 EscapeExpand(buf, appData.secondComputerString);
2008 appData.secondComputerString = strdup(buf);
2011 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
2014 if (chdir(chessDir) != 0) {
2015 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2021 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2022 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2023 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
2024 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2027 setbuf(debugFP, NULL);
2031 if (appData.debugMode) {
2032 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
2036 /* [HGM,HR] make sure board size is acceptable */
2037 if(appData.NrFiles > BOARD_FILES ||
2038 appData.NrRanks > BOARD_RANKS )
2039 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
2042 /* This feature does not work; animation needs a rewrite */
2043 appData.highlightDragging = FALSE;
2047 xDisplay = XtDisplay(shellWidget);
2048 xScreen = DefaultScreen(xDisplay);
2049 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2051 gameInfo.variant = StringToVariant(appData.variant);
2052 InitPosition(FALSE);
2055 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2057 if (isdigit(appData.boardSize[0])) {
2058 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2059 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2060 &fontPxlSize, &smallLayout, &tinyLayout);
2062 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2063 programName, appData.boardSize);
2067 /* Find some defaults; use the nearest known size */
2068 SizeDefaults *szd, *nearest;
2069 int distance = 99999;
2070 nearest = szd = sizeDefaults;
2071 while (szd->name != NULL) {
2072 if (abs(szd->squareSize - squareSize) < distance) {
2074 distance = abs(szd->squareSize - squareSize);
2075 if (distance == 0) break;
2079 if (i < 2) lineGap = nearest->lineGap;
2080 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2081 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2082 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2083 if (i < 6) smallLayout = nearest->smallLayout;
2084 if (i < 7) tinyLayout = nearest->tinyLayout;
2087 SizeDefaults *szd = sizeDefaults;
2088 if (*appData.boardSize == NULLCHAR) {
2089 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2090 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2093 if (szd->name == NULL) szd--;
2094 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2096 while (szd->name != NULL &&
2097 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2098 if (szd->name == NULL) {
2099 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2100 programName, appData.boardSize);
2104 squareSize = szd->squareSize;
2105 lineGap = szd->lineGap;
2106 clockFontPxlSize = szd->clockFontPxlSize;
2107 coordFontPxlSize = szd->coordFontPxlSize;
2108 fontPxlSize = szd->fontPxlSize;
2109 smallLayout = szd->smallLayout;
2110 tinyLayout = szd->tinyLayout;
2111 // [HGM] font: use defaults from settings file if available and not overruled
2113 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2114 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2115 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2116 appData.font = fontTable[MESSAGE_FONT][squareSize];
2117 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2118 appData.coordFont = fontTable[COORD_FONT][squareSize];
2120 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2121 if (strlen(appData.pixmapDirectory) > 0) {
2122 p = ExpandPathName(appData.pixmapDirectory);
2124 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2125 appData.pixmapDirectory);
2128 if (appData.debugMode) {
2129 fprintf(stderr, _("\
2130 XBoard square size (hint): %d\n\
2131 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2133 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2134 if (appData.debugMode) {
2135 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2138 defaultLineGap = lineGap;
2139 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2141 /* [HR] height treated separately (hacked) */
2142 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2143 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2144 if (appData.showJail == 1) {
2145 /* Jail on top and bottom */
2146 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2147 XtSetArg(boardArgs[2], XtNheight,
2148 boardHeight + 2*(lineGap + squareSize));
2149 } else if (appData.showJail == 2) {
2151 XtSetArg(boardArgs[1], XtNwidth,
2152 boardWidth + 2*(lineGap + squareSize));
2153 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2156 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2157 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2161 * Determine what fonts to use.
2164 appData.font = InsertPxlSize(appData.font, fontPxlSize);
2165 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
2166 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
2167 fontSet = CreateFontSet(appData.font);
2168 clockFontSet = CreateFontSet(appData.clockFont);
2170 /* For the coordFont, use the 0th font of the fontset. */
2171 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
2172 XFontStruct **font_struct_list;
2173 char **font_name_list;
2174 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
2175 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
2176 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2179 appData.font = FindFont(appData.font, fontPxlSize);
2180 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2181 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2182 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2183 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2184 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2185 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2187 countFontID = coordFontID; // [HGM] holdings
2188 countFontStruct = coordFontStruct;
2190 xdb = XtDatabase(xDisplay);
2192 XrmPutLineResource(&xdb, "*international: True");
2193 vTo.size = sizeof(XFontSet);
2194 vTo.addr = (XtPointer) &fontSet;
2195 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
2197 XrmPutStringResource(&xdb, "*font", appData.font);
2201 * Detect if there are not enough colors available and adapt.
2203 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2204 appData.monoMode = True;
2207 forceMono = MakeColors();
2210 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2212 appData.monoMode = True;
2215 if (appData.lowTimeWarning && !appData.monoMode) {
2216 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2217 vFrom.size = strlen(appData.lowTimeWarningColor);
2218 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2219 if (vTo.addr == NULL)
2220 appData.monoMode = True;
2222 lowTimeWarningColor = *(Pixel *) vTo.addr;
2225 if (appData.monoMode && appData.debugMode) {
2226 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2227 (unsigned long) XWhitePixel(xDisplay, xScreen),
2228 (unsigned long) XBlackPixel(xDisplay, xScreen));
2231 ParseIcsTextColors();
2232 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2233 textColors[ColorNone].attr = 0;
2235 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2241 layoutName = "tinyLayout";
2242 } else if (smallLayout) {
2243 layoutName = "smallLayout";
2245 layoutName = "normalLayout";
2247 /* Outer layoutWidget is there only to provide a name for use in
2248 resources that depend on the layout style */
2250 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2251 layoutArgs, XtNumber(layoutArgs));
2253 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2254 formArgs, XtNumber(formArgs));
2255 XtSetArg(args[0], XtNdefaultDistance, &sep);
2256 XtGetValues(formWidget, args, 1);
2259 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2260 XtSetArg(args[0], XtNtop, XtChainTop);
2261 XtSetArg(args[1], XtNbottom, XtChainTop);
2262 XtSetArg(args[2], XtNright, XtChainLeft);
2263 XtSetValues(menuBarWidget, args, 3);
2265 widgetList[j++] = whiteTimerWidget =
2266 XtCreateWidget("whiteTime", labelWidgetClass,
2267 formWidget, timerArgs, XtNumber(timerArgs));
2269 XtSetArg(args[0], XtNfontSet, clockFontSet);
2271 XtSetArg(args[0], XtNfont, clockFontStruct);
2273 XtSetArg(args[1], XtNtop, XtChainTop);
2274 XtSetArg(args[2], XtNbottom, XtChainTop);
2275 XtSetValues(whiteTimerWidget, args, 3);
2277 widgetList[j++] = blackTimerWidget =
2278 XtCreateWidget("blackTime", labelWidgetClass,
2279 formWidget, timerArgs, XtNumber(timerArgs));
2281 XtSetArg(args[0], XtNfontSet, clockFontSet);
2283 XtSetArg(args[0], XtNfont, clockFontStruct);
2285 XtSetArg(args[1], XtNtop, XtChainTop);
2286 XtSetArg(args[2], XtNbottom, XtChainTop);
2287 XtSetValues(blackTimerWidget, args, 3);
2289 if (appData.titleInWindow) {
2290 widgetList[j++] = titleWidget =
2291 XtCreateWidget("title", labelWidgetClass, formWidget,
2292 titleArgs, XtNumber(titleArgs));
2293 XtSetArg(args[0], XtNtop, XtChainTop);
2294 XtSetArg(args[1], XtNbottom, XtChainTop);
2295 XtSetValues(titleWidget, args, 2);
2298 if (appData.showButtonBar) {
2299 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2300 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2301 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2302 XtSetArg(args[2], XtNtop, XtChainTop);
2303 XtSetArg(args[3], XtNbottom, XtChainTop);
2304 XtSetValues(buttonBarWidget, args, 4);
2307 widgetList[j++] = messageWidget =
2308 XtCreateWidget("message", labelWidgetClass, formWidget,
2309 messageArgs, XtNumber(messageArgs));
2310 XtSetArg(args[0], XtNtop, XtChainTop);
2311 XtSetArg(args[1], XtNbottom, XtChainTop);
2312 XtSetValues(messageWidget, args, 2);
2314 widgetList[j++] = boardWidget =
2315 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2316 XtNumber(boardArgs));
2318 XtManageChildren(widgetList, j);
2320 timerWidth = (boardWidth - sep) / 2;
2321 XtSetArg(args[0], XtNwidth, timerWidth);
2322 XtSetValues(whiteTimerWidget, args, 1);
2323 XtSetValues(blackTimerWidget, args, 1);
2325 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2326 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2327 XtGetValues(whiteTimerWidget, args, 2);
2329 if (appData.showButtonBar) {
2330 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2331 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2332 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2336 * formWidget uses these constraints but they are stored
2340 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2341 XtSetValues(menuBarWidget, args, i);
2342 if (appData.titleInWindow) {
2345 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2346 XtSetValues(whiteTimerWidget, args, i);
2348 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2349 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2350 XtSetValues(blackTimerWidget, args, i);
2352 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2353 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2354 XtSetValues(titleWidget, args, i);
2356 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2357 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2358 XtSetValues(messageWidget, args, i);
2359 if (appData.showButtonBar) {
2361 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2362 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2363 XtSetValues(buttonBarWidget, args, i);
2367 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2368 XtSetValues(whiteTimerWidget, args, i);
2370 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2371 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2372 XtSetValues(blackTimerWidget, args, i);
2374 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2375 XtSetValues(titleWidget, args, i);
2377 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2378 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2379 XtSetValues(messageWidget, args, i);
2380 if (appData.showButtonBar) {
2382 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2383 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2384 XtSetValues(buttonBarWidget, args, i);
2389 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2390 XtSetValues(whiteTimerWidget, args, i);
2392 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2393 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2394 XtSetValues(blackTimerWidget, args, i);
2396 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2397 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2398 XtSetValues(messageWidget, args, i);
2399 if (appData.showButtonBar) {
2401 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2402 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2403 XtSetValues(buttonBarWidget, args, i);
2407 XtSetArg(args[0], XtNfromVert, messageWidget);
2408 XtSetArg(args[1], XtNtop, XtChainTop);
2409 XtSetArg(args[2], XtNbottom, XtChainBottom);
2410 XtSetArg(args[3], XtNleft, XtChainLeft);
2411 XtSetArg(args[4], XtNright, XtChainRight);
2412 XtSetValues(boardWidget, args, 5);
2414 XtRealizeWidget(shellWidget);
2417 XtSetArg(args[0], XtNx, wpMain.x);
2418 XtSetArg(args[1], XtNy, wpMain.y);
2419 XtSetValues(shellWidget, args, 2);
2423 * Correct the width of the message and title widgets.
2424 * It is not known why some systems need the extra fudge term.
2425 * The value "2" is probably larger than needed.
2427 XawFormDoLayout(formWidget, False);
2429 #define WIDTH_FUDGE 2
2431 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2432 XtSetArg(args[i], XtNheight, &h); i++;
2433 XtGetValues(messageWidget, args, i);
2434 if (appData.showButtonBar) {
2436 XtSetArg(args[i], XtNwidth, &w); i++;
2437 XtGetValues(buttonBarWidget, args, i);
2438 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2440 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2443 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2444 if (gres != XtGeometryYes && appData.debugMode) {
2445 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2446 programName, gres, w, h, wr, hr);
2449 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2450 /* The size used for the child widget in layout lags one resize behind
2451 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2453 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2454 if (gres != XtGeometryYes && appData.debugMode) {
2455 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2456 programName, gres, w, h, wr, hr);
2459 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2460 XtSetArg(args[1], XtNright, XtChainRight);
2461 XtSetValues(messageWidget, args, 2);
2463 if (appData.titleInWindow) {
2465 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2466 XtSetArg(args[i], XtNheight, &h); i++;
2467 XtGetValues(titleWidget, args, i);
2469 w = boardWidth - 2*bor;
2471 XtSetArg(args[0], XtNwidth, &w);
2472 XtGetValues(menuBarWidget, args, 1);
2473 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2476 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2477 if (gres != XtGeometryYes && appData.debugMode) {
2479 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2480 programName, gres, w, h, wr, hr);
2483 XawFormDoLayout(formWidget, True);
2485 xBoardWindow = XtWindow(boardWidget);
2487 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2488 // not need to go into InitDrawingSizes().
2492 * Create X checkmark bitmap and initialize option menu checks.
2494 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2495 checkmark_bits, checkmark_width, checkmark_height);
2496 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2497 #ifndef OPTIONSDIALOG
2498 if (appData.alwaysPromoteToQueen) {
2499 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2502 if (appData.animateDragging) {
2503 XtSetValues(XtNameToWidget(menuBarWidget,
2504 "menuOptions.Animate Dragging"),
2507 if (appData.animate) {
2508 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2511 if (appData.autoCallFlag) {
2512 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2515 if (appData.autoFlipView) {
2516 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2519 if (appData.blindfold) {
2520 XtSetValues(XtNameToWidget(menuBarWidget,
2521 "menuOptions.Blindfold"), args, 1);
2523 if (appData.flashCount > 0) {
2524 XtSetValues(XtNameToWidget(menuBarWidget,
2525 "menuOptions.Flash Moves"),
2529 if (appData.highlightDragging) {
2530 XtSetValues(XtNameToWidget(menuBarWidget,
2531 "menuOptions.Highlight Dragging"),
2535 if (appData.highlightLastMove) {
2536 XtSetValues(XtNameToWidget(menuBarWidget,
2537 "menuOptions.Highlight Last Move"),
2540 if (appData.highlightMoveWithArrow) {
2541 XtSetValues(XtNameToWidget(menuBarWidget,
2542 "menuOptions.Arrow"),
2545 // if (appData.icsAlarm) {
2546 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2549 if (appData.ringBellAfterMoves) {
2550 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2553 if (appData.oneClick) {
2554 XtSetValues(XtNameToWidget(menuBarWidget,
2555 "menuOptions.OneClick"), args, 1);
2557 if (appData.periodicUpdates) {
2558 XtSetValues(XtNameToWidget(menuBarWidget,
2559 "menuOptions.Periodic Updates"), args, 1);
2561 if (appData.ponderNextMove) {
2562 XtSetValues(XtNameToWidget(menuBarWidget,
2563 "menuOptions.Ponder Next Move"), args, 1);
2565 if (appData.popupExitMessage) {
2566 XtSetValues(XtNameToWidget(menuBarWidget,
2567 "menuOptions.Popup Exit Message"), args, 1);
2569 if (appData.popupMoveErrors) {
2570 XtSetValues(XtNameToWidget(menuBarWidget,
2571 "menuOptions.Popup Move Errors"), args, 1);
2573 // if (appData.premove) {
2574 // XtSetValues(XtNameToWidget(menuBarWidget,
2575 // "menuOptions.Premove"), args, 1);
2577 if (appData.showCoords) {
2578 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2581 if (appData.hideThinkingFromHuman) {
2582 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2585 if (appData.testLegality) {
2586 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2590 if (saveSettingsOnExit) {
2591 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2598 ReadBitmap(&wIconPixmap, "icon_white.bm",
2599 icon_white_bits, icon_white_width, icon_white_height);
2600 ReadBitmap(&bIconPixmap, "icon_black.bm",
2601 icon_black_bits, icon_black_width, icon_black_height);
2602 iconPixmap = wIconPixmap;
2604 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2605 XtSetValues(shellWidget, args, i);
2608 * Create a cursor for the board widget.
2610 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2611 XChangeWindowAttributes(xDisplay, xBoardWindow,
2612 CWCursor, &window_attributes);
2615 * Inhibit shell resizing.
2617 shellArgs[0].value = (XtArgVal) &w;
2618 shellArgs[1].value = (XtArgVal) &h;
2619 XtGetValues(shellWidget, shellArgs, 2);
2620 shellArgs[4].value = shellArgs[2].value = w;
2621 shellArgs[5].value = shellArgs[3].value = h;
2622 XtSetValues(shellWidget, &shellArgs[2], 4);
2623 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2624 marginH = h - boardHeight;
2626 CatchDeleteWindow(shellWidget, "QuitProc");
2634 if (appData.animate || appData.animateDragging)
2637 XtAugmentTranslations(formWidget,
2638 XtParseTranslationTable(globalTranslations));
2639 XtAugmentTranslations(boardWidget,
2640 XtParseTranslationTable(boardTranslations));
2641 XtAugmentTranslations(whiteTimerWidget,
2642 XtParseTranslationTable(whiteTranslations));
2643 XtAugmentTranslations(blackTimerWidget,
2644 XtParseTranslationTable(blackTranslations));
2646 /* Why is the following needed on some versions of X instead
2647 * of a translation? */
2648 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2649 (XtEventHandler) EventProc, NULL);
2651 XtAddEventHandler(formWidget, KeyPressMask, False,
2652 (XtEventHandler) MoveTypeInProc, NULL);
2654 /* [AS] Restore layout */
2655 if( wpMoveHistory.visible ) {
2659 if( wpEvalGraph.visible )
2664 if( wpEngineOutput.visible ) {
2665 EngineOutputPopUp();
2670 if (errorExitStatus == -1) {
2671 if (appData.icsActive) {
2672 /* We now wait until we see "login:" from the ICS before
2673 sending the logon script (problems with timestamp otherwise) */
2674 /*ICSInitScript();*/
2675 if (appData.icsInputBox) ICSInputBoxPopUp();
2679 signal(SIGWINCH, TermSizeSigHandler);
2681 signal(SIGINT, IntSigHandler);
2682 signal(SIGTERM, IntSigHandler);
2683 if (*appData.cmailGameName != NULLCHAR) {
2684 signal(SIGUSR1, CmailSigHandler);
2687 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2689 // XtSetKeyboardFocus(shellWidget, formWidget);
2690 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2692 XtAppMainLoop(appContext);
2693 if (appData.debugMode) fclose(debugFP); // [DM] debug
2700 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2701 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2703 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2704 unlink(gameCopyFilename);
2705 unlink(gamePasteFilename);
2708 RETSIGTYPE TermSizeSigHandler(int sig)
2721 CmailSigHandler(sig)
2727 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2729 /* Activate call-back function CmailSigHandlerCallBack() */
2730 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2732 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2736 CmailSigHandlerCallBack(isr, closure, message, count, error)
2744 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2746 /**** end signal code ****/
2752 /* try to open the icsLogon script, either in the location given
2753 * or in the users HOME directory
2760 f = fopen(appData.icsLogon, "r");
2763 homedir = getenv("HOME");
2764 if (homedir != NULL)
2766 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2767 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2768 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2769 f = fopen(buf, "r");
2774 ProcessICSInitScript(f);
2776 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2799 if (!menuBarWidget) return;
2800 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2802 DisplayError("menuEdit.Revert", 0);
2804 XtSetSensitive(w, !grey);
2806 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2808 DisplayError("menuEdit.Annotate", 0);
2810 XtSetSensitive(w, !grey);
2815 SetMenuEnables(enab)
2819 if (!menuBarWidget) return;
2820 while (enab->name != NULL) {
2821 w = XtNameToWidget(menuBarWidget, enab->name);
2823 DisplayError(enab->name, 0);
2825 XtSetSensitive(w, enab->value);
2831 Enables icsEnables[] = {
2832 { "menuFile.Mail Move", False },
2833 { "menuFile.Reload CMail Message", False },
2834 { "menuMode.Machine Black", False },
2835 { "menuMode.Machine White", False },
2836 { "menuMode.Analysis Mode", False },
2837 { "menuMode.Analyze File", False },
2838 { "menuMode.Two Machines", False },
2839 { "menuMode.Machine Match", False },
2841 { "menuEngine.Hint", False },
2842 { "menuEngine.Book", False },
2843 { "menuEngine.Move Now", False },
2844 #ifndef OPTIONSDIALOG
2845 { "menuOptions.Periodic Updates", False },
2846 { "menuOptions.Hide Thinking", False },
2847 { "menuOptions.Ponder Next Move", False },
2850 { "menuEngine.Engine #1 Settings", False },
2851 { "menuEngine.Engine #2 Settings", False },
2852 { "menuEngine.Load Engine", False },
2853 { "menuEdit.Annotate", False },
2854 { "menuOptions.Match", False },
2858 Enables ncpEnables[] = {
2859 { "menuFile.Mail Move", False },
2860 { "menuFile.Reload CMail Message", False },
2861 { "menuMode.Machine White", False },
2862 { "menuMode.Machine Black", False },
2863 { "menuMode.Analysis Mode", False },
2864 { "menuMode.Analyze File", False },
2865 { "menuMode.Two Machines", False },
2866 { "menuMode.Machine Match", False },
2867 { "menuMode.ICS Client", False },
2868 { "menuView.ICStex", False },
2869 { "menuView.ICS Input Box", False },
2870 { "Action", False },
2871 { "menuEdit.Revert", False },
2872 { "menuEdit.Annotate", False },
2873 { "menuEngine.Engine #1 Settings", False },
2874 { "menuEngine.Engine #2 Settings", False },
2875 { "menuEngine.Move Now", False },
2876 { "menuEngine.Retract Move", False },
2877 { "menuOptions.ICS", False },
2878 #ifndef OPTIONSDIALOG
2879 { "menuOptions.Auto Flag", False },
2880 { "menuOptions.Auto Flip View", False },
2881 // { "menuOptions.ICS Alarm", False },
2882 { "menuOptions.Move Sound", False },
2883 { "menuOptions.Hide Thinking", False },
2884 { "menuOptions.Periodic Updates", False },
2885 { "menuOptions.Ponder Next Move", False },
2887 { "menuEngine.Hint", False },
2888 { "menuEngine.Book", False },
2892 Enables gnuEnables[] = {
2893 { "menuMode.ICS Client", False },
2894 { "menuView.ICStex", False },
2895 { "menuView.ICS Input Box", False },
2896 { "menuAction.Accept", False },
2897 { "menuAction.Decline", False },
2898 { "menuAction.Rematch", False },
2899 { "menuAction.Adjourn", False },
2900 { "menuAction.Stop Examining", False },
2901 { "menuAction.Stop Observing", False },
2902 { "menuAction.Upload to Examine", False },
2903 { "menuEdit.Revert", False },
2904 { "menuEdit.Annotate", False },
2905 { "menuOptions.ICS", False },
2907 /* The next two options rely on SetCmailMode being called *after* */
2908 /* SetGNUMode so that when GNU is being used to give hints these */
2909 /* menu options are still available */
2911 { "menuFile.Mail Move", False },
2912 { "menuFile.Reload CMail Message", False },
2913 // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2914 { "menuMode.Machine White", True },
2915 { "menuMode.Machine Black", True },
2916 { "menuMode.Analysis Mode", True },
2917 { "menuMode.Analyze File", True },
2918 { "menuMode.Two Machines", True },
2919 { "menuMode.Machine Match", True },
2920 { "menuEngine.Engine #1 Settings", True },
2921 { "menuEngine.Engine #2 Settings", True },
2922 { "menuEngine.Hint", True },
2923 { "menuEngine.Book", True },
2924 { "menuEngine.Move Now", True },
2925 { "menuEngine.Retract Move", True },
2930 Enables cmailEnables[] = {
2932 { "menuAction.Call Flag", False },
2933 { "menuAction.Draw", True },
2934 { "menuAction.Adjourn", False },
2935 { "menuAction.Abort", False },
2936 { "menuAction.Stop Observing", False },
2937 { "menuAction.Stop Examining", False },
2938 { "menuFile.Mail Move", True },
2939 { "menuFile.Reload CMail Message", True },
2943 Enables trainingOnEnables[] = {
2944 { "menuMode.Edit Comment", False },
2945 { "menuMode.Pause", False },
2946 { "menuEdit.Forward", False },
2947 { "menuEdit.Backward", False },
2948 { "menuEdit.Forward to End", False },
2949 { "menuEdit.Back to Start", False },
2950 { "menuEngine.Move Now", False },
2951 { "menuEdit.Truncate Game", False },
2955 Enables trainingOffEnables[] = {
2956 { "menuMode.Edit Comment", True },
2957 { "menuMode.Pause", True },
2958 { "menuEdit.Forward", True },
2959 { "menuEdit.Backward", True },
2960 { "menuEdit.Forward to End", True },
2961 { "menuEdit.Back to Start", True },
2962 { "menuEngine.Move Now", True },
2963 { "menuEdit.Truncate Game", True },
2967 Enables machineThinkingEnables[] = {
2968 { "menuFile.Load Game", False },
2969 // { "menuFile.Load Next Game", False },
2970 // { "menuFile.Load Previous Game", False },
2971 // { "menuFile.Reload Same Game", False },
2972 { "menuEdit.Paste Game", False },
2973 { "menuFile.Load Position", False },
2974 // { "menuFile.Load Next Position", False },
2975 // { "menuFile.Load Previous Position", False },
2976 // { "menuFile.Reload Same Position", False },
2977 { "menuEdit.Paste Position", False },
2978 { "menuMode.Machine White", False },
2979 { "menuMode.Machine Black", False },
2980 { "menuMode.Two Machines", False },
2981 // { "menuMode.Machine Match", False },
2982 { "menuEngine.Retract Move", False },
2986 Enables userThinkingEnables[] = {
2987 { "menuFile.Load Game", True },
2988 // { "menuFile.Load Next Game", True },
2989 // { "menuFile.Load Previous Game", True },
2990 // { "menuFile.Reload Same Game", True },
2991 { "menuEdit.Paste Game", True },
2992 { "menuFile.Load Position", True },
2993 // { "menuFile.Load Next Position", True },
2994 // { "menuFile.Load Previous Position", True },
2995 // { "menuFile.Reload Same Position", True },
2996 { "menuEdit.Paste Position", True },
2997 { "menuMode.Machine White", True },
2998 { "menuMode.Machine Black", True },
2999 { "menuMode.Two Machines", True },
3000 // { "menuMode.Machine Match", True },
3001 { "menuEngine.Retract Move", True },
3007 SetMenuEnables(icsEnables);
3010 if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
3011 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
3012 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
3020 SetMenuEnables(ncpEnables);
3026 SetMenuEnables(gnuEnables);
3032 SetMenuEnables(cmailEnables);
3038 SetMenuEnables(trainingOnEnables);
3039 if (appData.showButtonBar) {
3040 XtSetSensitive(buttonBarWidget, False);
3046 SetTrainingModeOff()
3048 SetMenuEnables(trainingOffEnables);
3049 if (appData.showButtonBar) {
3050 XtSetSensitive(buttonBarWidget, True);
3055 SetUserThinkingEnables()
3057 if (appData.noChessProgram) return;
3058 SetMenuEnables(userThinkingEnables);
3062 SetMachineThinkingEnables()
3064 if (appData.noChessProgram) return;
3065 SetMenuEnables(machineThinkingEnables);
3067 case MachinePlaysBlack:
3068 case MachinePlaysWhite:
3069 case TwoMachinesPlay:
3070 XtSetSensitive(XtNameToWidget(menuBarWidget,
3071 ModeToWidgetName(gameMode)), True);
3078 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3079 #define HISTORY_SIZE 64
3080 static char *history[HISTORY_SIZE];
3081 int histIn = 0, histP = 0;
3084 SaveInHistory(char *cmd)
3086 if (history[histIn] != NULL) {
3087 free(history[histIn]);
3088 history[histIn] = NULL;
3090 if (*cmd == NULLCHAR) return;
3091 history[histIn] = StrSave(cmd);
3092 histIn = (histIn + 1) % HISTORY_SIZE;
3093 if (history[histIn] != NULL) {
3094 free(history[histIn]);
3095 history[histIn] = NULL;
3101 PrevInHistory(char *cmd)
3104 if (histP == histIn) {
3105 if (history[histIn] != NULL) free(history[histIn]);
3106 history[histIn] = StrSave(cmd);
3108 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3109 if (newhp == histIn || history[newhp] == NULL) return NULL;
3111 return history[histP];
3117 if (histP == histIn) return NULL;
3118 histP = (histP + 1) % HISTORY_SIZE;
3119 return history[histP];
3121 // end of borrowed code
3123 #define Abs(n) ((n)<0 ? -(n) : (n))
3127 InsertPxlSize(pattern, targetPxlSize)
3131 char *base_fnt_lst, strInt[12], *p, *q;
3132 int alternatives, i, len, strIntLen;
3135 * Replace the "*" (if present) in the pixel-size slot of each
3136 * alternative with the targetPxlSize.
3140 while ((p = strchr(p, ',')) != NULL) {
3144 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
3145 strIntLen = strlen(strInt);
3146 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
3150 while (alternatives--) {
3151 char *comma = strchr(p, ',');
3152 for (i=0; i<14; i++) {
3153 char *hyphen = strchr(p, '-');
3155 if (comma && hyphen > comma) break;
3156 len = hyphen + 1 - p;
3157 if (i == 7 && *p == '*' && len == 2) {
3159 memcpy(q, strInt, strIntLen);
3169 len = comma + 1 - p;
3176 return base_fnt_lst;
3180 CreateFontSet(base_fnt_lst)
3184 char **missing_list;
3188 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
3189 &missing_list, &missing_count, &def_string);
3190 if (appData.debugMode) {
3192 XFontStruct **font_struct_list;
3193 char **font_name_list;
3194 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
3196 fprintf(debugFP, " got list %s, locale %s\n",
3197 XBaseFontNameListOfFontSet(fntSet),
3198 XLocaleOfFontSet(fntSet));
3199 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
3200 for (i = 0; i < count; i++) {
3201 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
3204 for (i = 0; i < missing_count; i++) {
3205 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
3208 if (fntSet == NULL) {
3209 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
3214 #else // not ENABLE_NLS
3216 * Find a font that matches "pattern" that is as close as
3217 * possible to the targetPxlSize. Prefer fonts that are k
3218 * pixels smaller to fonts that are k pixels larger. The
3219 * pattern must be in the X Consortium standard format,
3220 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3221 * The return value should be freed with XtFree when no
3225 FindFont(pattern, targetPxlSize)
3229 char **fonts, *p, *best, *scalable, *scalableTail;
3230 int i, j, nfonts, minerr, err, pxlSize;
3232 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3234 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3235 programName, pattern);
3242 for (i=0; i<nfonts; i++) {
3245 if (*p != '-') continue;
3247 if (*p == NULLCHAR) break;
3248 if (*p++ == '-') j++;
3250 if (j < 7) continue;
3253 scalable = fonts[i];
3256 err = pxlSize - targetPxlSize;
3257 if (Abs(err) < Abs(minerr) ||
3258 (minerr > 0 && err < 0 && -err == minerr)) {
3264 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3265 /* If the error is too big and there is a scalable font,
3266 use the scalable font. */
3267 int headlen = scalableTail - scalable;
3268 p = (char *) XtMalloc(strlen(scalable) + 10);
3269 while (isdigit(*scalableTail)) scalableTail++;
3270 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3272 p = (char *) XtMalloc(strlen(best) + 2);
3273 safeStrCpy(p, best, strlen(best)+1 );
3275 if (appData.debugMode) {
3276 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3277 pattern, targetPxlSize, p);
3279 XFreeFontNames(fonts);
3285 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3286 // must be called before all non-first callse to CreateGCs()
3287 XtReleaseGC(shellWidget, highlineGC);
3288 XtReleaseGC(shellWidget, lightSquareGC);
3289 XtReleaseGC(shellWidget, darkSquareGC);
3290 XtReleaseGC(shellWidget, lineGC);
3291 if (appData.monoMode) {
3292 if (DefaultDepth(xDisplay, xScreen) == 1) {
3293 XtReleaseGC(shellWidget, wbPieceGC);
3295 XtReleaseGC(shellWidget, bwPieceGC);
3298 XtReleaseGC(shellWidget, prelineGC);
3299 XtReleaseGC(shellWidget, jailSquareGC);
3300 XtReleaseGC(shellWidget, wdPieceGC);
3301 XtReleaseGC(shellWidget, wlPieceGC);
3302 XtReleaseGC(shellWidget, wjPieceGC);
3303 XtReleaseGC(shellWidget, bdPieceGC);
3304 XtReleaseGC(shellWidget, blPieceGC);
3305 XtReleaseGC(shellWidget, bjPieceGC);
3309 void CreateGCs(int redo)
3311 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3312 | GCBackground | GCFunction | GCPlaneMask;
3313 XGCValues gc_values;
3316 gc_values.plane_mask = AllPlanes;
3317 gc_values.line_width = lineGap;
3318 gc_values.line_style = LineSolid;
3319 gc_values.function = GXcopy;
3322 DeleteGCs(); // called a second time; clean up old GCs first
3323 } else { // [HGM] grid and font GCs created on first call only
3324 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3325 gc_values.background = XWhitePixel(xDisplay, xScreen);
3326 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3327 XSetFont(xDisplay, coordGC, coordFontID);
3329 // [HGM] make font for holdings counts (white on black)
3330 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3331 gc_values.background = XBlackPixel(xDisplay, xScreen);
3332 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3333 XSetFont(xDisplay, countGC, countFontID);
3335 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3336 gc_values.background = XBlackPixel(xDisplay, xScreen);
3337 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3339 if (appData.monoMode) {
3340 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3341 gc_values.background = XWhitePixel(xDisplay, xScreen);
3342 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3344 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3345 gc_values.background = XBlackPixel(xDisplay, xScreen);
3346 lightSquareGC = wbPieceGC
3347 = XtGetGC(shellWidget, value_mask, &gc_values);
3349 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3350 gc_values.background = XWhitePixel(xDisplay, xScreen);
3351 darkSquareGC = bwPieceGC
3352 = XtGetGC(shellWidget, value_mask, &gc_values);
3354 if (DefaultDepth(xDisplay, xScreen) == 1) {
3355 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3356 gc_values.function = GXcopyInverted;
3357 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3358 gc_values.function = GXcopy;
3359 if (XBlackPixel(xDisplay, xScreen) == 1) {
3360 bwPieceGC = darkSquareGC;
3361 wbPieceGC = copyInvertedGC;
3363 bwPieceGC = copyInvertedGC;
3364 wbPieceGC = lightSquareGC;
3368 gc_values.foreground = highlightSquareColor;
3369 gc_values.background = highlightSquareColor;
3370 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3372 gc_values.foreground = premoveHighlightColor;
3373 gc_values.background = premoveHighlightColor;
3374 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3376 gc_values.foreground = lightSquareColor;
3377 gc_values.background = darkSquareColor;
3378 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3380 gc_values.foreground = darkSquareColor;
3381 gc_values.background = lightSquareColor;
3382 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3384 gc_values.foreground = jailSquareColor;
3385 gc_values.background = jailSquareColor;
3386 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3388 gc_values.foreground = whitePieceColor;
3389 gc_values.background = darkSquareColor;
3390 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3392 gc_values.foreground = whitePieceColor;
3393 gc_values.background = lightSquareColor;
3394 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3396 gc_values.foreground = whitePieceColor;
3397 gc_values.background = jailSquareColor;
3398 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3400 gc_values.foreground = blackPieceColor;
3401 gc_values.background = darkSquareColor;
3402 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3404 gc_values.foreground = blackPieceColor;
3405 gc_values.background = lightSquareColor;
3406 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3408 gc_values.foreground = blackPieceColor;
3409 gc_values.background = jailSquareColor;
3410 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3414 void loadXIM(xim, xmask, filename, dest, mask)
3427 fp = fopen(filename, "rb");
3429 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3436 for (y=0; y<h; ++y) {
3437 for (x=0; x<h; ++x) {
3442 XPutPixel(xim, x, y, blackPieceColor);
3444 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3447 XPutPixel(xim, x, y, darkSquareColor);
3449 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3452 XPutPixel(xim, x, y, whitePieceColor);
3454 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3457 XPutPixel(xim, x, y, lightSquareColor);
3459 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3467 /* create Pixmap of piece */
3468 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3470 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3473 /* create Pixmap of clipmask
3474 Note: We assume the white/black pieces have the same
3475 outline, so we make only 6 masks. This is okay
3476 since the XPM clipmask routines do the same. */
3478 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3480 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3483 /* now create the 1-bit version */
3484 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3487 values.foreground = 1;
3488 values.background = 0;
3490 /* Don't use XtGetGC, not read only */
3491 maskGC = XCreateGC(xDisplay, *mask,
3492 GCForeground | GCBackground, &values);
3493 XCopyPlane(xDisplay, temp, *mask, maskGC,
3494 0, 0, squareSize, squareSize, 0, 0, 1);
3495 XFreePixmap(xDisplay, temp);
3500 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3502 void CreateXIMPieces()
3507 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3512 /* The XSynchronize calls were copied from CreatePieces.
3513 Not sure if needed, but can't hurt */
3514 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3517 /* temp needed by loadXIM() */
3518 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3519 0, 0, ss, ss, AllPlanes, XYPixmap);
3521 if (strlen(appData.pixmapDirectory) == 0) {
3525 if (appData.monoMode) {
3526 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3530 fprintf(stderr, _("\nLoading XIMs...\n"));
3532 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3533 fprintf(stderr, "%d", piece+1);
3534 for (kind=0; kind<4; kind++) {
3535 fprintf(stderr, ".");
3536 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3537 ExpandPathName(appData.pixmapDirectory),
3538 piece <= (int) WhiteKing ? "" : "w",
3539 pieceBitmapNames[piece],
3541 ximPieceBitmap[kind][piece] =
3542 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3543 0, 0, ss, ss, AllPlanes, XYPixmap);
3544 if (appData.debugMode)
3545 fprintf(stderr, _("(File:%s:) "), buf);
3546 loadXIM(ximPieceBitmap[kind][piece],
3548 &(xpmPieceBitmap2[kind][piece]),
3549 &(ximMaskPm2[piece]));
3550 if(piece <= (int)WhiteKing)
3551 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3553 fprintf(stderr," ");
3555 /* Load light and dark squares */
3556 /* If the LSQ and DSQ pieces don't exist, we will
3557 draw them with solid squares. */
3558 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3559 if (access(buf, 0) != 0) {
3563 fprintf(stderr, _("light square "));
3565 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3566 0, 0, ss, ss, AllPlanes, XYPixmap);
3567 if (appData.debugMode)
3568 fprintf(stderr, _("(File:%s:) "), buf);
3570 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3571 fprintf(stderr, _("dark square "));
3572 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3573 ExpandPathName(appData.pixmapDirectory), ss);
3574 if (appData.debugMode)
3575 fprintf(stderr, _("(File:%s:) "), buf);
3577 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3578 0, 0, ss, ss, AllPlanes, XYPixmap);
3579 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3580 xpmJailSquare = xpmLightSquare;
3582 fprintf(stderr, _("Done.\n"));
3584 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3587 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3590 void CreateXPMBoard(char *s, int kind)
3594 if(s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3595 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3596 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3600 void FreeXPMPieces()
3601 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3602 // thisroutine has to be called t free the old piece pixmaps
3604 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3605 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3607 XFreePixmap(xDisplay, xpmLightSquare);
3608 XFreePixmap(xDisplay, xpmDarkSquare);
3612 void CreateXPMPieces()
3616 u_int ss = squareSize;
3618 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3619 XpmColorSymbol symbols[4];
3620 static int redo = False;
3622 if(redo) FreeXPMPieces(); else redo = 1;
3624 /* The XSynchronize calls were copied from CreatePieces.
3625 Not sure if needed, but can't hurt */
3626 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3628 /* Setup translations so piece colors match square colors */
3629 symbols[0].name = "light_piece";
3630 symbols[0].value = appData.whitePieceColor;
3631 symbols[1].name = "dark_piece";
3632 symbols[1].value = appData.blackPieceColor;
3633 symbols[2].name = "light_square";
3634 symbols[2].value = appData.lightSquareColor;
3635 symbols[3].name = "dark_square";
3636 symbols[3].value = appData.darkSquareColor;
3638 attr.valuemask = XpmColorSymbols;
3639 attr.colorsymbols = symbols;
3640 attr.numsymbols = 4;
3642 if (appData.monoMode) {
3643 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3647 if (strlen(appData.pixmapDirectory) == 0) {
3648 XpmPieces* pieces = builtInXpms;
3651 while (pieces->size != squareSize && pieces->size) pieces++;
3652 if (!pieces->size) {
3653 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3656 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3657 for (kind=0; kind<4; kind++) {
3659 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3660 pieces->xpm[piece][kind],
3661 &(xpmPieceBitmap2[kind][piece]),
3662 NULL, &attr)) != 0) {
3663 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3667 if(piece <= (int) WhiteKing)
3668 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3672 xpmJailSquare = xpmLightSquare;
3676 fprintf(stderr, _("\nLoading XPMs...\n"));
3679 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3680 fprintf(stderr, "%d ", piece+1);
3681 for (kind=0; kind<4; kind++) {
3682 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3683 ExpandPathName(appData.pixmapDirectory),
3684 piece > (int) WhiteKing ? "w" : "",
3685 pieceBitmapNames[piece],
3687 if (appData.debugMode) {
3688 fprintf(stderr, _("(File:%s:) "), buf);
3690 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3691 &(xpmPieceBitmap2[kind][piece]),
3692 NULL, &attr)) != 0) {
3693 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3694 // [HGM] missing: read of unorthodox piece failed; substitute King.
3695 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3696 ExpandPathName(appData.pixmapDirectory),
3698 if (appData.debugMode) {
3699 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3701 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3702 &(xpmPieceBitmap2[kind][piece]),
3706 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3711 if(piece <= (int) WhiteKing)
3712 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3715 /* Load light and dark squares */
3716 /* If the LSQ and DSQ pieces don't exist, we will
3717 draw them with solid squares. */
3718 fprintf(stderr, _("light square "));
3719 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3720 if (access(buf, 0) != 0) {
3724 if (appData.debugMode)
3725 fprintf(stderr, _("(File:%s:) "), buf);
3727 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3728 &xpmLightSquare, NULL, &attr)) != 0) {
3729 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3732 fprintf(stderr, _("dark square "));
3733 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3734 ExpandPathName(appData.pixmapDirectory), ss);
3735 if (appData.debugMode) {
3736 fprintf(stderr, _("(File:%s:) "), buf);
3738 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3739 &xpmDarkSquare, NULL, &attr)) != 0) {
3740 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3744 xpmJailSquare = xpmLightSquare;
3745 fprintf(stderr, _("Done.\n"));
3747 oldVariant = -1; // kludge to force re-makig of animation masks
3748 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3751 #endif /* HAVE_LIBXPM */
3754 /* No built-in bitmaps */
3759 u_int ss = squareSize;
3761 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3764 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3765 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3766 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3767 pieceBitmapNames[piece],
3768 ss, kind == SOLID ? 's' : 'o');
3769 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3770 if(piece <= (int)WhiteKing)
3771 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3775 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3779 /* With built-in bitmaps */
3782 BuiltInBits* bib = builtInBits;
3785 u_int ss = squareSize;
3787 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3790 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3792 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3793 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3794 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3795 pieceBitmapNames[piece],
3796 ss, kind == SOLID ? 's' : 'o');
3797 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3798 bib->bits[kind][piece], ss, ss);
3799 if(piece <= (int)WhiteKing)
3800 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3804 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3809 void ReadBitmap(pm, name, bits, wreq, hreq)
3812 unsigned char bits[];
3818 char msg[MSG_SIZ], fullname[MSG_SIZ];
3820 if (*appData.bitmapDirectory != NULLCHAR) {
3821 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3822 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3823 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3824 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3825 &w, &h, pm, &x_hot, &y_hot);
3826 fprintf(stderr, "load %s\n", name);
3827 if (errcode != BitmapSuccess) {
3829 case BitmapOpenFailed:
3830 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3832 case BitmapFileInvalid:
3833 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3835 case BitmapNoMemory:
3836 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3840 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3844 fprintf(stderr, _("%s: %s...using built-in\n"),
3846 } else if (w != wreq || h != hreq) {
3848 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3849 programName, fullname, w, h, wreq, hreq);
3855 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3864 if (lineGap == 0) return;
3866 /* [HR] Split this into 2 loops for non-square boards. */
3868 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3869 gridSegments[i].x1 = 0;
3870 gridSegments[i].x2 =
3871 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3872 gridSegments[i].y1 = gridSegments[i].y2
3873 = lineGap / 2 + (i * (squareSize + lineGap));
3876 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3877 gridSegments[j + i].y1 = 0;
3878 gridSegments[j + i].y2 =
3879 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3880 gridSegments[j + i].x1 = gridSegments[j + i].x2
3881 = lineGap / 2 + (j * (squareSize + lineGap));
3885 static void MenuBarSelect(w, addr, index)
3890 XtActionProc proc = (XtActionProc) addr;
3892 (proc)(NULL, NULL, NULL, NULL);
3895 void CreateMenuBarPopup(parent, name, mb)
3905 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3908 XtSetArg(args[j], XtNleftMargin, 20); j++;
3909 XtSetArg(args[j], XtNrightMargin, 20); j++;
3911 while (mi->string != NULL) {
3912 if (strcmp(mi->string, "----") == 0) {
3913 entry = XtCreateManagedWidget(_(mi->string), smeLineObjectClass,
3916 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3917 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3919 XtAddCallback(entry, XtNcallback,
3920 (XtCallbackProc) MenuBarSelect,
3921 (caddr_t) mi->proc);
3927 Widget CreateMenuBar(mb)
3931 Widget anchor, menuBar;
3933 char menuName[MSG_SIZ];
3936 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3937 XtSetArg(args[j], XtNvSpace, 0); j++;
3938 XtSetArg(args[j], XtNborderWidth, 0); j++;
3939 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3940 formWidget, args, j);
3942 while (mb->name != NULL) {
3943 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3944 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3946 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3949 shortName[0] = mb->name[0];
3950 shortName[1] = NULLCHAR;
3951 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3954 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3957 XtSetArg(args[j], XtNborderWidth, 0); j++;
3958 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3960 CreateMenuBarPopup(menuBar, menuName, mb);
3966 Widget CreateButtonBar(mi)
3970 Widget button, buttonBar;
3974 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3976 XtSetArg(args[j], XtNhSpace, 0); j++;
3978 XtSetArg(args[j], XtNborderWidth, 0); j++;
3979 XtSetArg(args[j], XtNvSpace, 0); j++;
3980 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3981 formWidget, args, j);
3983 while (mi->string != NULL) {
3986 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3987 XtSetArg(args[j], XtNborderWidth, 0); j++;
3989 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3990 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3991 buttonBar, args, j);
3992 XtAddCallback(button, XtNcallback,
3993 (XtCallbackProc) MenuBarSelect,
3994 (caddr_t) mi->proc);
4001 CreatePieceMenu(name, color)
4008 ChessSquare selection;
4010 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
4011 boardWidget, args, 0);
4013 for (i = 0; i < PIECE_MENU_SIZE; i++) {
4014 String item = pieceMenuStrings[color][i];
4016 if (strcmp(item, "----") == 0) {
4017 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4020 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4021 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4023 selection = pieceMenuTranslation[color][i];
4024 XtAddCallback(entry, XtNcallback,
4025 (XtCallbackProc) PieceMenuSelect,
4026 (caddr_t) selection);
4027 if (selection == WhitePawn || selection == BlackPawn) {
4028 XtSetArg(args[0], XtNpopupOnEntry, entry);
4029 XtSetValues(menu, args, 1);
4042 ChessSquare selection;
4044 whitePieceMenu = CreatePieceMenu("menuW", 0);
4045 blackPieceMenu = CreatePieceMenu("menuB", 1);
4047 XtRegisterGrabAction(PieceMenuPopup, True,
4048 (unsigned)(ButtonPressMask|ButtonReleaseMask),
4049 GrabModeAsync, GrabModeAsync);
4051 XtSetArg(args[0], XtNlabel, _("Drop"));
4052 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
4053 boardWidget, args, 1);
4054 for (i = 0; i < DROP_MENU_SIZE; i++) {
4055 String item = dropMenuStrings[i];
4057 if (strcmp(item, "----") == 0) {
4058 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4061 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4062 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4064 selection = dropMenuTranslation[i];
4065 XtAddCallback(entry, XtNcallback,
4066 (XtCallbackProc) DropMenuSelect,
4067 (caddr_t) selection);
4072 void SetupDropMenu()
4080 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
4081 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
4082 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
4083 dmEnables[i].piece);
4084 XtSetSensitive(entry, p != NULL || !appData.testLegality
4085 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
4086 && !appData.icsActive));
4088 while (p && *p++ == dmEnables[i].piece) count++;
4089 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
4091 XtSetArg(args[j], XtNlabel, label); j++;
4092 XtSetValues(entry, args, j);
4096 void PieceMenuPopup(w, event, params, num_params)
4100 Cardinal *num_params;
4102 String whichMenu; int menuNr = -2;
4103 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
4104 if (event->type == ButtonRelease)
4105 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4106 else if (event->type == ButtonPress)
4107 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4109 case 0: whichMenu = params[0]; break;
4110 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
4112 case -1: if (errorUp) ErrorPopDown();
4115 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
4118 static void PieceMenuSelect(w, piece, junk)
4123 if (pmFromX < 0 || pmFromY < 0) return;
4124 EditPositionMenuEvent(piece, pmFromX, pmFromY);
4127 static void DropMenuSelect(w, piece, junk)
4132 if (pmFromX < 0 || pmFromY < 0) return;
4133 DropMenuEvent(piece, pmFromX, pmFromY);
4136 void WhiteClock(w, event, prms, nprms)
4145 void BlackClock(w, event, prms, nprms)
4156 * If the user selects on a border boundary, return -1; if off the board,
4157 * return -2. Otherwise map the event coordinate to the square.
4159 int EventToSquare(x, limit)
4167 if ((x % (squareSize + lineGap)) >= squareSize)
4169 x /= (squareSize + lineGap);
4175 static void do_flash_delay(msec)
4181 static void drawHighlight(file, rank, gc)
4187 if (lineGap == 0) return;
4190 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4191 (squareSize + lineGap);
4192 y = lineGap/2 + rank * (squareSize + lineGap);
4194 x = lineGap/2 + file * (squareSize + lineGap);
4195 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4196 (squareSize + lineGap);
4199 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4200 squareSize+lineGap, squareSize+lineGap);
4203 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4204 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4207 SetHighlights(fromX, fromY, toX, toY)
4208 int fromX, fromY, toX, toY;
4210 if (hi1X != fromX || hi1Y != fromY) {
4211 if (hi1X >= 0 && hi1Y >= 0) {
4212 drawHighlight(hi1X, hi1Y, lineGC);
4214 } // [HGM] first erase both, then draw new!
4215 if (hi2X != toX || hi2Y != toY) {
4216 if (hi2X >= 0 && hi2Y >= 0) {
4217 drawHighlight(hi2X, hi2Y, lineGC);
4220 if (hi1X != fromX || hi1Y != fromY) {
4221 if (fromX >= 0 && fromY >= 0) {
4222 drawHighlight(fromX, fromY, highlineGC);
4225 if (hi2X != toX || hi2Y != toY) {
4226 if (toX >= 0 && toY >= 0) {
4227 drawHighlight(toX, toY, highlineGC);
4239 SetHighlights(-1, -1, -1, -1);
4244 SetPremoveHighlights(fromX, fromY, toX, toY)
4245 int fromX, fromY, toX, toY;
4247 if (pm1X != fromX || pm1Y != fromY) {
4248 if (pm1X >= 0 && pm1Y >= 0) {
4249 drawHighlight(pm1X, pm1Y, lineGC);
4251 if (fromX >= 0 && fromY >= 0) {
4252 drawHighlight(fromX, fromY, prelineGC);
4255 if (pm2X != toX || pm2Y != toY) {
4256 if (pm2X >= 0 && pm2Y >= 0) {
4257 drawHighlight(pm2X, pm2Y, lineGC);
4259 if (toX >= 0 && toY >= 0) {
4260 drawHighlight(toX, toY, prelineGC);
4270 ClearPremoveHighlights()
4272 SetPremoveHighlights(-1, -1, -1, -1);
4275 static int CutOutSquare(x, y, x0, y0, kind)
4276 int x, y, *x0, *y0, kind;
4278 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4279 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4281 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4282 if(textureW[kind] < W*squareSize)
4283 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4285 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4286 if(textureH[kind] < H*squareSize)
4287 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4289 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4293 static void BlankSquare(x, y, color, piece, dest, fac)
4294 int x, y, color, fac;
4297 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4299 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4300 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4301 squareSize, squareSize, x*fac, y*fac);
4303 if (useImages && useImageSqs) {
4307 pm = xpmLightSquare;
4312 case 2: /* neutral */
4317 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4318 squareSize, squareSize, x*fac, y*fac);
4328 case 2: /* neutral */
4333 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4338 I split out the routines to draw a piece so that I could
4339 make a generic flash routine.
4341 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4343 int square_color, x, y;
4346 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4347 switch (square_color) {
4349 case 2: /* neutral */
4351 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4352 ? *pieceToOutline(piece)
4353 : *pieceToSolid(piece),
4354 dest, bwPieceGC, 0, 0,
4355 squareSize, squareSize, x, y);
4358 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4359 ? *pieceToSolid(piece)
4360 : *pieceToOutline(piece),
4361 dest, wbPieceGC, 0, 0,
4362 squareSize, squareSize, x, y);
4367 static void monoDrawPiece(piece, square_color, x, y, dest)
4369 int square_color, x, y;
4372 switch (square_color) {
4374 case 2: /* neutral */
4376 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4377 ? *pieceToOutline(piece)
4378 : *pieceToSolid(piece),
4379 dest, bwPieceGC, 0, 0,
4380 squareSize, squareSize, x, y, 1);
4383 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4384 ? *pieceToSolid(piece)
4385 : *pieceToOutline(piece),
4386 dest, wbPieceGC, 0, 0,
4387 squareSize, squareSize, x, y, 1);
4392 static void colorDrawPiece(piece, square_color, x, y, dest)
4394 int square_color, x, y;
4397 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4398 switch (square_color) {
4400 XCopyPlane(xDisplay, *pieceToSolid(piece),
4401 dest, (int) piece < (int) BlackPawn
4402 ? wlPieceGC : blPieceGC, 0, 0,
4403 squareSize, squareSize, x, y, 1);
4406 XCopyPlane(xDisplay, *pieceToSolid(piece),
4407 dest, (int) piece < (int) BlackPawn
4408 ? wdPieceGC : bdPieceGC, 0, 0,
4409 squareSize, squareSize, x, y, 1);
4411 case 2: /* neutral */
4413 XCopyPlane(xDisplay, *pieceToSolid(piece),
4414 dest, (int) piece < (int) BlackPawn
4415 ? wjPieceGC : bjPieceGC, 0, 0,
4416 squareSize, squareSize, x, y, 1);
4421 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4423 int square_color, x, y;
4426 int kind, p = piece;
4428 switch (square_color) {
4430 case 2: /* neutral */
4432 if ((int)piece < (int) BlackPawn) {
4440 if ((int)piece < (int) BlackPawn) {
4448 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4449 if(useTexture & square_color+1) {
4450 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4451 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4452 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4453 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4454 XSetClipMask(xDisplay, wlPieceGC, None);
4455 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4457 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4458 dest, wlPieceGC, 0, 0,
4459 squareSize, squareSize, x, y);
4462 typedef void (*DrawFunc)();
4464 DrawFunc ChooseDrawFunc()
4466 if (appData.monoMode) {
4467 if (DefaultDepth(xDisplay, xScreen) == 1) {
4468 return monoDrawPiece_1bit;
4470 return monoDrawPiece;
4474 return colorDrawPieceImage;
4476 return colorDrawPiece;
4480 /* [HR] determine square color depending on chess variant. */
4481 static int SquareColor(row, column)
4486 if (gameInfo.variant == VariantXiangqi) {
4487 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4489 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4491 } else if (row <= 4) {
4497 square_color = ((column + row) % 2) == 1;
4500 /* [hgm] holdings: next line makes all holdings squares light */
4501 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4503 return square_color;
4506 void DrawSquare(row, column, piece, do_flash)
4507 int row, column, do_flash;
4510 int square_color, x, y, direction, font_ascent, font_descent;
4513 XCharStruct overall;
4517 /* Calculate delay in milliseconds (2-delays per complete flash) */
4518 flash_delay = 500 / appData.flashRate;
4521 x = lineGap + ((BOARD_WIDTH-1)-column) *
4522 (squareSize + lineGap);
4523 y = lineGap + row * (squareSize + lineGap);
4525 x = lineGap + column * (squareSize + lineGap);
4526 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4527 (squareSize + lineGap);
4530 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4532 square_color = SquareColor(row, column);
4534 if ( // [HGM] holdings: blank out area between board and holdings
4535 column == BOARD_LEFT-1 || column == BOARD_RGHT
4536 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4537 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4538 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4540 // [HGM] print piece counts next to holdings
4541 string[1] = NULLCHAR;
4542 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4543 string[0] = '0' + piece;
4544 XTextExtents(countFontStruct, string, 1, &direction,
4545 &font_ascent, &font_descent, &overall);
4546 if (appData.monoMode) {
4547 XDrawImageString(xDisplay, xBoardWindow, countGC,
4548 x + squareSize - overall.width - 2,
4549 y + font_ascent + 1, string, 1);
4551 XDrawString(xDisplay, xBoardWindow, countGC,
4552 x + squareSize - overall.width - 2,
4553 y + font_ascent + 1, string, 1);
4556 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4557 string[0] = '0' + piece;
4558 XTextExtents(countFontStruct, string, 1, &direction,
4559 &font_ascent, &font_descent, &overall);
4560 if (appData.monoMode) {
4561 XDrawImageString(xDisplay, xBoardWindow, countGC,
4562 x + 2, y + font_ascent + 1, string, 1);
4564 XDrawString(xDisplay, xBoardWindow, countGC,
4565 x + 2, y + font_ascent + 1, string, 1);
4569 if (piece == EmptySquare || appData.blindfold) {
4570 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4572 drawfunc = ChooseDrawFunc();
4574 if (do_flash && appData.flashCount > 0) {
4575 for (i=0; i<appData.flashCount; ++i) {
4576 drawfunc(piece, square_color, x, y, xBoardWindow);
4577 XSync(xDisplay, False);
4578 do_flash_delay(flash_delay);
4580 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4581 XSync(xDisplay, False);
4582 do_flash_delay(flash_delay);
4585 drawfunc(piece, square_color, x, y, xBoardWindow);
4589 string[1] = NULLCHAR;
4590 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4591 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4592 string[0] = 'a' + column - BOARD_LEFT;
4593 XTextExtents(coordFontStruct, string, 1, &direction,
4594 &font_ascent, &font_descent, &overall);
4595 if (appData.monoMode) {
4596 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4597 x + squareSize - overall.width - 2,
4598 y + squareSize - font_descent - 1, string, 1);
4600 XDrawString(xDisplay, xBoardWindow, coordGC,
4601 x + squareSize - overall.width - 2,
4602 y + squareSize - font_descent - 1, string, 1);
4605 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4606 string[0] = ONE + row;
4607 XTextExtents(coordFontStruct, string, 1, &direction,
4608 &font_ascent, &font_descent, &overall);
4609 if (appData.monoMode) {
4610 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4611 x + 2, y + font_ascent + 1, string, 1);
4613 XDrawString(xDisplay, xBoardWindow, coordGC,
4614 x + 2, y + font_ascent + 1, string, 1);
4617 if(!partnerUp && marker[row][column]) {
4618 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4619 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4624 /* Why is this needed on some versions of X? */
4625 void EventProc(widget, unused, event)
4630 if (!XtIsRealized(widget))
4633 switch (event->type) {
4635 if (event->xexpose.count > 0) return; /* no clipping is done */
4636 XDrawPosition(widget, True, NULL);
4637 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4638 flipView = !flipView; partnerUp = !partnerUp;
4639 XDrawPosition(widget, True, NULL);
4640 flipView = !flipView; partnerUp = !partnerUp;
4644 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4651 void DrawPosition(fullRedraw, board)
4652 /*Boolean*/int fullRedraw;
4655 XDrawPosition(boardWidget, fullRedraw, board);
4658 /* Returns 1 if there are "too many" differences between b1 and b2
4659 (i.e. more than 1 move was made) */
4660 static int too_many_diffs(b1, b2)
4666 for (i=0; i<BOARD_HEIGHT; ++i) {
4667 for (j=0; j<BOARD_WIDTH; ++j) {
4668 if (b1[i][j] != b2[i][j]) {
4669 if (++c > 4) /* Castling causes 4 diffs */
4677 /* Matrix describing castling maneuvers */
4678 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4679 static int castling_matrix[4][5] = {
4680 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4681 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4682 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4683 { 7, 7, 4, 5, 6 } /* 0-0, black */
4686 /* Checks whether castling occurred. If it did, *rrow and *rcol
4687 are set to the destination (row,col) of the rook that moved.
4689 Returns 1 if castling occurred, 0 if not.
4691 Note: Only handles a max of 1 castling move, so be sure
4692 to call too_many_diffs() first.
4694 static int check_castle_draw(newb, oldb, rrow, rcol)
4701 /* For each type of castling... */
4702 for (i=0; i<4; ++i) {
4703 r = castling_matrix[i];
4705 /* Check the 4 squares involved in the castling move */
4707 for (j=1; j<=4; ++j) {
4708 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4715 /* All 4 changed, so it must be a castling move */
4724 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4725 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4727 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4730 void DrawSeekBackground( int left, int top, int right, int bottom )
4732 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4735 void DrawSeekText(char *buf, int x, int y)
4737 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4740 void DrawSeekDot(int x, int y, int colorNr)
4742 int square = colorNr & 0x80;
4745 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4747 XFillRectangle(xDisplay, xBoardWindow, color,
4748 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4750 XFillArc(xDisplay, xBoardWindow, color,
4751 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4754 static int damage[2][BOARD_RANKS][BOARD_FILES];
4757 * event handler for redrawing the board
4759 void XDrawPosition(w, repaint, board)
4761 /*Boolean*/int repaint;
4765 static int lastFlipView = 0;
4766 static int lastBoardValid[2] = {0, 0};
4767 static Board lastBoard[2];
4770 int nr = twoBoards*partnerUp;
4772 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4774 if (board == NULL) {
4775 if (!lastBoardValid[nr]) return;
4776 board = lastBoard[nr];
4778 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4779 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4780 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4785 * It would be simpler to clear the window with XClearWindow()
4786 * but this causes a very distracting flicker.
4789 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4791 if ( lineGap && IsDrawArrowEnabled())
4792 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4793 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4795 /* If too much changes (begin observing new game, etc.), don't
4797 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4799 /* Special check for castling so we don't flash both the king
4800 and the rook (just flash the king). */
4802 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4803 /* Draw rook with NO flashing. King will be drawn flashing later */
4804 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4805 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4809 /* First pass -- Draw (newly) empty squares and repair damage.
4810 This prevents you from having a piece show up twice while it
4811 is flashing on its new square */
4812 for (i = 0; i < BOARD_HEIGHT; i++)
4813 for (j = 0; j < BOARD_WIDTH; j++)
4814 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4815 || damage[nr][i][j]) {
4816 DrawSquare(i, j, board[i][j], 0);
4817 damage[nr][i][j] = False;
4820 /* Second pass -- Draw piece(s) in new position and flash them */
4821 for (i = 0; i < BOARD_HEIGHT; i++)
4822 for (j = 0; j < BOARD_WIDTH; j++)
4823 if (board[i][j] != lastBoard[nr][i][j]) {
4824 DrawSquare(i, j, board[i][j], do_flash);
4828 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4829 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4830 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4832 for (i = 0; i < BOARD_HEIGHT; i++)
4833 for (j = 0; j < BOARD_WIDTH; j++) {
4834 DrawSquare(i, j, board[i][j], 0);
4835 damage[nr][i][j] = False;
4839 CopyBoard(lastBoard[nr], board);
4840 lastBoardValid[nr] = 1;
4841 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4842 lastFlipView = flipView;
4844 /* Draw highlights */
4845 if (pm1X >= 0 && pm1Y >= 0) {
4846 drawHighlight(pm1X, pm1Y, prelineGC);
4848 if (pm2X >= 0 && pm2Y >= 0) {
4849 drawHighlight(pm2X, pm2Y, prelineGC);
4851 if (hi1X >= 0 && hi1Y >= 0) {
4852 drawHighlight(hi1X, hi1Y, highlineGC);
4854 if (hi2X >= 0 && hi2Y >= 0) {
4855 drawHighlight(hi2X, hi2Y, highlineGC);
4857 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4859 /* If piece being dragged around board, must redraw that too */
4862 XSync(xDisplay, False);
4867 * event handler for redrawing the board
4869 void DrawPositionProc(w, event, prms, nprms)
4875 XDrawPosition(w, True, NULL);
4880 * event handler for parsing user moves
4882 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4883 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4884 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4885 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4886 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4887 // and at the end FinishMove() to perform the move after optional promotion popups.
4888 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4889 void HandleUserMove(w, event, prms, nprms)
4895 if (w != boardWidget || errorExitStatus != -1) return;
4896 if(nprms) shiftKey = !strcmp(prms[0], "1");
4899 if (event->type == ButtonPress) {
4900 XtPopdown(promotionShell);
4901 XtDestroyWidget(promotionShell);
4902 promotionUp = False;
4910 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4911 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4912 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4915 void AnimateUserMove (Widget w, XEvent * event,
4916 String * params, Cardinal * nParams)
4918 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4919 DragPieceMove(event->xmotion.x, event->xmotion.y);
4922 void HandlePV (Widget w, XEvent * event,
4923 String * params, Cardinal * nParams)
4924 { // [HGM] pv: walk PV
4925 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4928 static int savedIndex; /* gross that this is global */
4930 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4933 XawTextPosition index, dummy;
4936 XawTextGetSelectionPos(w, &index, &dummy);
4937 XtSetArg(arg, XtNstring, &val);
4938 XtGetValues(w, &arg, 1);
4939 ReplaceComment(savedIndex, val);
4940 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4941 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4944 void EditCommentPopUp(index, title, text)
4949 if (text == NULL) text = "";
4950 NewCommentPopup(title, text, index);
4953 void ICSInputBoxPopUp()
4958 extern Option boxOptions[];
4960 void ICSInputSendText()
4967 edit = boxOptions[0].handle;
4969 XtSetArg(args[j], XtNstring, &val); j++;
4970 XtGetValues(edit, args, j);
4972 SendMultiLineToICS(val);
4973 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4974 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4977 void ICSInputBoxPopDown()
4982 void CommentPopUp(title, text)
4985 savedIndex = currentMove; // [HGM] vari
4986 NewCommentPopup(title, text, currentMove);
4989 void CommentPopDown()
4994 void FileNamePopUp(label, def, filter, proc, openMode)
5001 fileProc = proc; /* I can't see a way not */
5002 fileOpenMode = openMode; /* to use globals here */
5003 { // [HGM] use file-selector dialog stolen from Ghostview
5005 int index; // this is not supported yet
5007 if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
5008 (def[0] ? def : NULL), filter, openMode, NULL, &name))
5009 (void) (*fileProc)(f, index=0, name);
5013 void FileNamePopDown()
5015 if (!filenameUp) return;
5016 XtPopdown(fileNameShell);
5017 XtDestroyWidget(fileNameShell);
5022 void FileNameCallback(w, client_data, call_data)
5024 XtPointer client_data, call_data;
5029 XtSetArg(args[0], XtNlabel, &name);
5030 XtGetValues(w, args, 1);
5032 if (strcmp(name, _("cancel")) == 0) {
5037 FileNameAction(w, NULL, NULL, NULL);
5040 void FileNameAction(w, event, prms, nprms)
5052 name = XawDialogGetValueString(w = XtParent(w));
5054 if ((name != NULL) && (*name != NULLCHAR)) {
5055 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5056 XtPopdown(w = XtParent(XtParent(w)));
5060 p = strrchr(buf, ' ');
5067 fullname = ExpandPathName(buf);
5069 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5072 f = fopen(fullname, fileOpenMode);
5074 DisplayError(_("Failed to open file"), errno);
5076 (void) (*fileProc)(f, index, buf);
5083 XtPopdown(w = XtParent(XtParent(w)));
5089 void PromotionPopUp()
5092 Widget dialog, layout;
5094 Dimension bw_width, pw_width;
5098 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5099 XtGetValues(boardWidget, args, j);
5102 XtSetArg(args[j], XtNresizable, True); j++;
5103 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5105 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5106 shellWidget, args, j);
5108 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5109 layoutArgs, XtNumber(layoutArgs));
5112 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5113 XtSetArg(args[j], XtNborderWidth, 0); j++;
5114 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5117 if(gameInfo.variant != VariantShogi) {
5118 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5119 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback,
5120 (XtPointer) dialog);
5121 XawDialogAddButton(dialog, _("General"), PromotionCallback,
5122 (XtPointer) dialog);
5123 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback,
5124 (XtPointer) dialog);
5125 XawDialogAddButton(dialog, _("Captain"), PromotionCallback,
5126 (XtPointer) dialog);
5128 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
5129 (XtPointer) dialog);
5130 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
5131 (XtPointer) dialog);
5132 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
5133 (XtPointer) dialog);
5134 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
5135 (XtPointer) dialog);
5137 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5138 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
5139 gameInfo.variant == VariantGiveaway) {
5140 XawDialogAddButton(dialog, _("King"), PromotionCallback,
5141 (XtPointer) dialog);
5143 if(gameInfo.variant == VariantCapablanca ||
5144 gameInfo.variant == VariantGothic ||
5145 gameInfo.variant == VariantCapaRandom) {
5146 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5147 (XtPointer) dialog);
5148 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5149 (XtPointer) dialog);
5151 } else // [HGM] shogi
5153 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5154 (XtPointer) dialog);
5155 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5156 (XtPointer) dialog);
5158 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5159 (XtPointer) dialog);
5161 XtRealizeWidget(promotionShell);
5162 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5165 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5166 XtGetValues(promotionShell, args, j);
5168 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5169 lineGap + squareSize/3 +
5170 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5171 0 : 6*(squareSize + lineGap)), &x, &y);
5174 XtSetArg(args[j], XtNx, x); j++;
5175 XtSetArg(args[j], XtNy, y); j++;
5176 XtSetValues(promotionShell, args, j);
5178 XtPopup(promotionShell, XtGrabNone);
5183 void PromotionPopDown()
5185 if (!promotionUp) return;
5186 XtPopdown(promotionShell);
5187 XtDestroyWidget(promotionShell);
5188 promotionUp = False;
5191 void PromotionCallback(w, client_data, call_data)
5193 XtPointer client_data, call_data;
5199 XtSetArg(args[0], XtNlabel, &name);
5200 XtGetValues(w, args, 1);
5204 if (fromX == -1) return;
5206 if (strcmp(name, _("cancel")) == 0) {
5210 } else if (strcmp(name, _("Knight")) == 0) {
5212 } else if (strcmp(name, _("Promote")) == 0) {
5214 } else if (strcmp(name, _("Defer")) == 0) {
5217 promoChar = ToLower(name[0]);
5220 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5222 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5223 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5228 void ErrorCallback(w, client_data, call_data)
5230 XtPointer client_data, call_data;
5233 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5235 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5241 if (!errorUp) return;
5243 XtPopdown(errorShell);
5244 XtDestroyWidget(errorShell);
5245 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5248 void ErrorPopUp(title, label, modal)
5249 char *title, *label;
5253 Widget dialog, layout;
5257 Dimension bw_width, pw_width;
5258 Dimension pw_height;
5262 XtSetArg(args[i], XtNresizable, True); i++;
5263 XtSetArg(args[i], XtNtitle, title); i++;
5265 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5266 shellWidget, args, i);
5268 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5269 layoutArgs, XtNumber(layoutArgs));
5272 XtSetArg(args[i], XtNlabel, label); i++;
5273 XtSetArg(args[i], XtNborderWidth, 0); i++;
5274 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5277 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5279 XtRealizeWidget(errorShell);
5280 CatchDeleteWindow(errorShell, "ErrorPopDown");
5283 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5284 XtGetValues(boardWidget, args, i);
5286 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5287 XtSetArg(args[i], XtNheight, &pw_height); i++;
5288 XtGetValues(errorShell, args, i);
5291 /* This code seems to tickle an X bug if it is executed too soon
5292 after xboard starts up. The coordinates get transformed as if
5293 the main window was positioned at (0, 0).
5295 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5296 0 - pw_height + squareSize / 3, &x, &y);
5298 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5299 RootWindowOfScreen(XtScreen(boardWidget)),
5300 (bw_width - pw_width) / 2,
5301 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5305 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5308 XtSetArg(args[i], XtNx, x); i++;
5309 XtSetArg(args[i], XtNy, y); i++;
5310 XtSetValues(errorShell, args, i);
5313 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5316 /* Disable all user input other than deleting the window */
5317 static int frozen = 0;
5321 /* Grab by a widget that doesn't accept input */
5322 XtAddGrab(messageWidget, TRUE, FALSE);
5326 /* Undo a FreezeUI */
5329 if (!frozen) return;
5330 XtRemoveGrab(messageWidget);
5334 char *ModeToWidgetName(mode)
5338 case BeginningOfGame:
5339 if (appData.icsActive)
5340 return "menuMode.ICS Client";
5341 else if (appData.noChessProgram ||
5342 *appData.cmailGameName != NULLCHAR)
5343 return "menuMode.Edit Game";
5345 return "menuMode.Machine Black";
5346 case MachinePlaysBlack:
5347 return "menuMode.Machine Black";
5348 case MachinePlaysWhite:
5349 return "menuMode.Machine White";
5351 return "menuMode.Analysis Mode";
5353 return "menuMode.Analyze File";
5354 case TwoMachinesPlay:
5355 return "menuMode.Two Machines";
5357 return "menuMode.Edit Game";
5358 case PlayFromGameFile:
5359 return "menuFile.Load Game";
5361 return "menuMode.Edit Position";
5363 return "menuMode.Training";
5364 case IcsPlayingWhite:
5365 case IcsPlayingBlack:
5369 return "menuMode.ICS Client";
5376 void ModeHighlight()
5379 static int oldPausing = FALSE;
5380 static GameMode oldmode = (GameMode) -1;
5383 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5385 if (pausing != oldPausing) {
5386 oldPausing = pausing;
5388 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5390 XtSetArg(args[0], XtNleftBitmap, None);
5392 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5395 if (appData.showButtonBar) {
5396 /* Always toggle, don't set. Previous code messes up when
5397 invoked while the button is pressed, as releasing it
5398 toggles the state again. */
5401 XtSetArg(args[0], XtNbackground, &oldbg);
5402 XtSetArg(args[1], XtNforeground, &oldfg);
5403 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5405 XtSetArg(args[0], XtNbackground, oldfg);
5406 XtSetArg(args[1], XtNforeground, oldbg);
5408 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5412 wname = ModeToWidgetName(oldmode);
5413 if (wname != NULL) {
5414 XtSetArg(args[0], XtNleftBitmap, None);
5415 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5417 wname = ModeToWidgetName(gameMode);
5418 if (wname != NULL) {
5419 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5420 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5423 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5424 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5426 /* Maybe all the enables should be handled here, not just this one */
5427 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5428 gameMode == Training || gameMode == PlayFromGameFile);
5433 * Button/menu procedures
5435 void ResetProc(w, event, prms, nprms)
5444 int LoadGamePopUp(f, gameNumber, title)
5449 cmailMsgLoaded = FALSE;
5450 if (gameNumber == 0) {
5451 int error = GameListBuild(f);
5453 DisplayError(_("Cannot build game list"), error);
5454 } else if (!ListEmpty(&gameList) &&
5455 ((ListGame *) gameList.tailPred)->number > 1) {
5456 GameListPopUp(f, title);
5462 return LoadGame(f, gameNumber, title, FALSE);
5465 void LoadGameProc(w, event, prms, nprms)
5471 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5474 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5477 void LoadNextGameProc(w, event, prms, nprms)
5486 void LoadPrevGameProc(w, event, prms, nprms)
5495 void ReloadGameProc(w, event, prms, nprms)
5504 void LoadNextPositionProc(w, event, prms, nprms)
5513 void LoadPrevPositionProc(w, event, prms, nprms)
5522 void ReloadPositionProc(w, event, prms, nprms)
5531 void LoadPositionProc(w, event, prms, nprms)
5537 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5540 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5543 void SaveGameProc(w, event, prms, nprms)
5549 FileNamePopUp(_("Save game file name?"),
5550 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5551 appData.oldSaveStyle ? ".game" : ".pgn",
5555 void SavePositionProc(w, event, prms, nprms)
5561 FileNamePopUp(_("Save position file name?"),
5562 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5563 appData.oldSaveStyle ? ".pos" : ".fen",
5567 void ReloadCmailMsgProc(w, event, prms, nprms)
5573 ReloadCmailMsgEvent(FALSE);
5576 void MailMoveProc(w, event, prms, nprms)
5585 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5586 char *selected_fen_position=NULL;
5589 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5590 Atom *type_return, XtPointer *value_return,
5591 unsigned long *length_return, int *format_return)
5593 char *selection_tmp;
5595 if (!selected_fen_position) return False; /* should never happen */
5596 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5597 /* note: since no XtSelectionDoneProc was registered, Xt will
5598 * automatically call XtFree on the value returned. So have to
5599 * make a copy of it allocated with XtMalloc */
5600 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5601 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5603 *value_return=selection_tmp;
5604 *length_return=strlen(selection_tmp);
5605 *type_return=*target;
5606 *format_return = 8; /* bits per byte */
5608 } else if (*target == XA_TARGETS(xDisplay)) {
5609 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5610 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5611 targets_tmp[1] = XA_STRING;
5612 *value_return = targets_tmp;
5613 *type_return = XA_ATOM;
5615 *format_return = 8 * sizeof(Atom);
5616 if (*format_return > 32) {
5617 *length_return *= *format_return / 32;
5618 *format_return = 32;
5626 /* note: when called from menu all parameters are NULL, so no clue what the
5627 * Widget which was clicked on was, or what the click event was
5629 void CopyPositionProc(w, event, prms, nprms)
5636 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5637 * have a notion of a position that is selected but not copied.
5638 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5640 if(gameMode == EditPosition) EditPositionDone(TRUE);
5641 if (selected_fen_position) free(selected_fen_position);
5642 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5643 if (!selected_fen_position) return;
5644 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5646 SendPositionSelection,
5647 NULL/* lose_ownership_proc */ ,
5648 NULL/* transfer_done_proc */);
5649 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5651 SendPositionSelection,
5652 NULL/* lose_ownership_proc */ ,
5653 NULL/* transfer_done_proc */);
5656 /* function called when the data to Paste is ready */
5658 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5659 Atom *type, XtPointer value, unsigned long *len, int *format)
5662 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5663 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5664 EditPositionPasteFEN(fenstr);
5668 /* called when Paste Position button is pressed,
5669 * all parameters will be NULL */
5670 void PastePositionProc(w, event, prms, nprms)
5676 XtGetSelectionValue(menuBarWidget,
5677 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5678 /* (XtSelectionCallbackProc) */ PastePositionCB,
5679 NULL, /* client_data passed to PastePositionCB */
5681 /* better to use the time field from the event that triggered the
5682 * call to this function, but that isn't trivial to get
5690 SendGameSelection(Widget w, Atom *selection, Atom *target,
5691 Atom *type_return, XtPointer *value_return,
5692 unsigned long *length_return, int *format_return)
5694 char *selection_tmp;
5696 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5697 FILE* f = fopen(gameCopyFilename, "r");
5700 if (f == NULL) return False;
5704 selection_tmp = XtMalloc(len + 1);
5705 count = fread(selection_tmp, 1, len, f);
5708 XtFree(selection_tmp);
5711 selection_tmp[len] = NULLCHAR;
5712 *value_return = selection_tmp;
5713 *length_return = len;
5714 *type_return = *target;
5715 *format_return = 8; /* bits per byte */
5717 } else if (*target == XA_TARGETS(xDisplay)) {
5718 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5719 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5720 targets_tmp[1] = XA_STRING;
5721 *value_return = targets_tmp;
5722 *type_return = XA_ATOM;
5724 *format_return = 8 * sizeof(Atom);
5725 if (*format_return > 32) {
5726 *length_return *= *format_return / 32;
5727 *format_return = 32;
5735 void CopySomething()
5740 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5741 * have a notion of a game that is selected but not copied.
5742 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5744 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5747 NULL/* lose_ownership_proc */ ,
5748 NULL/* transfer_done_proc */);
5749 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5752 NULL/* lose_ownership_proc */ ,
5753 NULL/* transfer_done_proc */);
5756 /* note: when called from menu all parameters are NULL, so no clue what the
5757 * Widget which was clicked on was, or what the click event was
5759 void CopyGameProc(w, event, prms, nprms)
5767 ret = SaveGameToFile(gameCopyFilename, FALSE);
5773 void CopyGameListProc(w, event, prms, nprms)
5779 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5783 /* function called when the data to Paste is ready */
5785 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5786 Atom *type, XtPointer value, unsigned long *len, int *format)
5789 if (value == NULL || *len == 0) {
5790 return; /* nothing had been selected to copy */
5792 f = fopen(gamePasteFilename, "w");
5794 DisplayError(_("Can't open temp file"), errno);
5797 fwrite(value, 1, *len, f);
5800 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5803 /* called when Paste Game button is pressed,
5804 * all parameters will be NULL */
5805 void PasteGameProc(w, event, prms, nprms)
5811 XtGetSelectionValue(menuBarWidget,
5812 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5813 /* (XtSelectionCallbackProc) */ PasteGameCB,
5814 NULL, /* client_data passed to PasteGameCB */
5816 /* better to use the time field from the event that triggered the
5817 * call to this function, but that isn't trivial to get
5827 SaveGameProc(NULL, NULL, NULL, NULL);
5831 void QuitProc(w, event, prms, nprms)
5840 void PauseProc(w, event, prms, nprms)
5850 void MachineBlackProc(w, event, prms, nprms)
5856 MachineBlackEvent();
5859 void MachineWhiteProc(w, event, prms, nprms)
5865 MachineWhiteEvent();
5868 void AnalyzeModeProc(w, event, prms, nprms)
5876 if (!first.analysisSupport) {
5877 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5878 DisplayError(buf, 0);
5881 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5882 if (appData.icsActive) {
5883 if (gameMode != IcsObserving) {
5884 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5885 DisplayError(buf, 0);
5887 if (appData.icsEngineAnalyze) {
5888 if (appData.debugMode)
5889 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5895 /* if enable, use want disable icsEngineAnalyze */
5896 if (appData.icsEngineAnalyze) {
5901 appData.icsEngineAnalyze = TRUE;
5902 if (appData.debugMode)
5903 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5905 #ifndef OPTIONSDIALOG
5906 if (!appData.showThinking)
5907 ShowThinkingProc(w,event,prms,nprms);
5913 void AnalyzeFileProc(w, event, prms, nprms)
5919 if (!first.analysisSupport) {
5921 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5922 DisplayError(buf, 0);
5926 #ifndef OPTIONSDIALOG
5927 if (!appData.showThinking)
5928 ShowThinkingProc(w,event,prms,nprms);
5931 FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5932 AnalysisPeriodicEvent(1);
5935 void TwoMachinesProc(w, event, prms, nprms)
5944 void MatchProc(w, event, prms, nprms)
5953 void IcsClientProc(w, event, prms, nprms)
5962 void EditGameProc(w, event, prms, nprms)
5971 void EditPositionProc(w, event, prms, nprms)
5977 EditPositionEvent();
5980 void TrainingProc(w, event, prms, nprms)
5989 void EditCommentProc(w, event, prms, nprms)
5997 if (PopDown(1)) { // popdown succesful
5999 XtSetArg(args[j], XtNleftBitmap, None); j++;
6000 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
6001 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
6002 } else // was not up
6006 void IcsInputBoxProc(w, event, prms, nprms)
6012 if (!PopDown(4)) ICSInputBoxPopUp();
6015 void AcceptProc(w, event, prms, nprms)
6024 void DeclineProc(w, event, prms, nprms)
6033 void RematchProc(w, event, prms, nprms)
6042 void CallFlagProc(w, event, prms, nprms)
6051 void DrawProc(w, event, prms, nprms)
6060 void AbortProc(w, event, prms, nprms)
6069 void AdjournProc(w, event, prms, nprms)
6078 void ResignProc(w, event, prms, nprms)
6087 void AdjuWhiteProc(w, event, prms, nprms)
6093 UserAdjudicationEvent(+1);
6096 void AdjuBlackProc(w, event, prms, nprms)
6102 UserAdjudicationEvent(-1);
6105 void AdjuDrawProc(w, event, prms, nprms)
6111 UserAdjudicationEvent(0);
6114 void EnterKeyProc(w, event, prms, nprms)
6120 if (shellUp[4] == True)
6124 void UpKeyProc(w, event, prms, nprms)
6129 { // [HGM] input: let up-arrow recall previous line from history
6136 if (!shellUp[4]) return;
6137 edit = boxOptions[0].handle;
6139 XtSetArg(args[j], XtNstring, &val); j++;
6140 XtGetValues(edit, args, j);
6141 val = PrevInHistory(val);
6142 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6143 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6145 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6146 XawTextReplace(edit, 0, 0, &t);
6147 XawTextSetInsertionPoint(edit, 9999);
6151 void DownKeyProc(w, event, prms, nprms)
6156 { // [HGM] input: let down-arrow recall next line from history
6161 if (!shellUp[4]) return;
6162 edit = boxOptions[0].handle;
6163 val = NextInHistory();
6164 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6165 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6167 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6168 XawTextReplace(edit, 0, 0, &t);
6169 XawTextSetInsertionPoint(edit, 9999);
6173 void StopObservingProc(w, event, prms, nprms)
6179 StopObservingEvent();
6182 void StopExaminingProc(w, event, prms, nprms)
6188 StopExaminingEvent();
6191 void UploadProc(w, event, prms, nprms)
6201 void ForwardProc(w, event, prms, nprms)
6211 void BackwardProc(w, event, prms, nprms)
6220 void ToStartProc(w, event, prms, nprms)
6229 void ToEndProc(w, event, prms, nprms)
6238 void RevertProc(w, event, prms, nprms)
6247 void AnnotateProc(w, event, prms, nprms)
6256 void TruncateGameProc(w, event, prms, nprms)
6262 TruncateGameEvent();
6264 void RetractMoveProc(w, event, prms, nprms)
6273 void MoveNowProc(w, event, prms, nprms)
6282 void FlipViewProc(w, event, prms, nprms)
6288 flipView = !flipView;
6289 DrawPosition(True, NULL);
6292 void PonderNextMoveProc(w, event, prms, nprms)
6300 PonderNextMoveEvent(!appData.ponderNextMove);
6301 #ifndef OPTIONSDIALOG
6302 if (appData.ponderNextMove) {
6303 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6305 XtSetArg(args[0], XtNleftBitmap, None);
6307 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6312 #ifndef OPTIONSDIALOG
6313 void AlwaysQueenProc(w, event, prms, nprms)
6321 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6323 if (appData.alwaysPromoteToQueen) {
6324 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6326 XtSetArg(args[0], XtNleftBitmap, None);
6328 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6332 void AnimateDraggingProc(w, event, prms, nprms)
6340 appData.animateDragging = !appData.animateDragging;
6342 if (appData.animateDragging) {
6343 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6346 XtSetArg(args[0], XtNleftBitmap, None);
6348 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6352 void AnimateMovingProc(w, event, prms, nprms)
6360 appData.animate = !appData.animate;
6362 if (appData.animate) {
6363 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6366 XtSetArg(args[0], XtNleftBitmap, None);
6368 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6372 void AutoflagProc(w, event, prms, nprms)
6380 appData.autoCallFlag = !appData.autoCallFlag;
6382 if (appData.autoCallFlag) {
6383 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6385 XtSetArg(args[0], XtNleftBitmap, None);
6387 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6391 void AutoflipProc(w, event, prms, nprms)
6399 appData.autoFlipView = !appData.autoFlipView;
6401 if (appData.autoFlipView) {
6402 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6404 XtSetArg(args[0], XtNleftBitmap, None);
6406 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6410 void BlindfoldProc(w, event, prms, nprms)
6418 appData.blindfold = !appData.blindfold;
6420 if (appData.blindfold) {
6421 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6423 XtSetArg(args[0], XtNleftBitmap, None);
6425 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6428 DrawPosition(True, NULL);
6431 void TestLegalityProc(w, event, prms, nprms)
6439 appData.testLegality = !appData.testLegality;
6441 if (appData.testLegality) {
6442 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6444 XtSetArg(args[0], XtNleftBitmap, None);
6446 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6451 void FlashMovesProc(w, event, prms, nprms)
6459 if (appData.flashCount == 0) {
6460 appData.flashCount = 3;
6462 appData.flashCount = -appData.flashCount;
6465 if (appData.flashCount > 0) {
6466 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6468 XtSetArg(args[0], XtNleftBitmap, None);
6470 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6475 void HighlightDraggingProc(w, event, prms, nprms)
6483 appData.highlightDragging = !appData.highlightDragging;
6485 if (appData.highlightDragging) {
6486 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6488 XtSetArg(args[0], XtNleftBitmap, None);
6490 XtSetValues(XtNameToWidget(menuBarWidget,
6491 "menuOptions.Highlight Dragging"), args, 1);
6495 void HighlightLastMoveProc(w, event, prms, nprms)
6503 appData.highlightLastMove = !appData.highlightLastMove;
6505 if (appData.highlightLastMove) {
6506 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6508 XtSetArg(args[0], XtNleftBitmap, None);
6510 XtSetValues(XtNameToWidget(menuBarWidget,
6511 "menuOptions.Highlight Last Move"), args, 1);
6514 void HighlightArrowProc(w, event, prms, nprms)
6522 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6524 if (appData.highlightMoveWithArrow) {
6525 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6527 XtSetArg(args[0], XtNleftBitmap, None);
6529 XtSetValues(XtNameToWidget(menuBarWidget,
6530 "menuOptions.Arrow"), args, 1);
6534 void IcsAlarmProc(w, event, prms, nprms)
6542 appData.icsAlarm = !appData.icsAlarm;
6544 if (appData.icsAlarm) {
6545 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6547 XtSetArg(args[0], XtNleftBitmap, None);
6549 XtSetValues(XtNameToWidget(menuBarWidget,
6550 "menuOptions.ICS Alarm"), args, 1);
6554 void MoveSoundProc(w, event, prms, nprms)
6562 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6564 if (appData.ringBellAfterMoves) {
6565 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6567 XtSetArg(args[0], XtNleftBitmap, None);
6569 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6573 void OneClickProc(w, event, prms, nprms)
6581 appData.oneClick = !appData.oneClick;
6583 if (appData.oneClick) {
6584 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6586 XtSetArg(args[0], XtNleftBitmap, None);
6588 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6592 void PeriodicUpdatesProc(w, event, prms, nprms)
6600 PeriodicUpdatesEvent(!appData.periodicUpdates);
6602 if (appData.periodicUpdates) {
6603 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6605 XtSetArg(args[0], XtNleftBitmap, None);
6607 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6611 void PopupExitMessageProc(w, event, prms, nprms)
6619 appData.popupExitMessage = !appData.popupExitMessage;
6621 if (appData.popupExitMessage) {
6622 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6624 XtSetArg(args[0], XtNleftBitmap, None);
6626 XtSetValues(XtNameToWidget(menuBarWidget,
6627 "menuOptions.Popup Exit Message"), args, 1);
6630 void PopupMoveErrorsProc(w, event, prms, nprms)
6638 appData.popupMoveErrors = !appData.popupMoveErrors;
6640 if (appData.popupMoveErrors) {
6641 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6643 XtSetArg(args[0], XtNleftBitmap, None);
6645 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6650 void PremoveProc(w, event, prms, nprms)
6658 appData.premove = !appData.premove;
6660 if (appData.premove) {
6661 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6663 XtSetArg(args[0], XtNleftBitmap, None);
6665 XtSetValues(XtNameToWidget(menuBarWidget,
6666 "menuOptions.Premove"), args, 1);
6670 void ShowCoordsProc(w, event, prms, nprms)
6678 appData.showCoords = !appData.showCoords;
6680 if (appData.showCoords) {
6681 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6683 XtSetArg(args[0], XtNleftBitmap, None);
6685 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6688 DrawPosition(True, NULL);
6691 void ShowThinkingProc(w, event, prms, nprms)
6697 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6698 ShowThinkingEvent();
6701 void HideThinkingProc(w, event, prms, nprms)
6709 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6710 ShowThinkingEvent();
6712 if (appData.hideThinkingFromHuman) {
6713 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6715 XtSetArg(args[0], XtNleftBitmap, None);
6717 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6722 void SaveOnExitProc(w, event, prms, nprms)
6730 saveSettingsOnExit = !saveSettingsOnExit;
6732 if (saveSettingsOnExit) {
6733 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6735 XtSetArg(args[0], XtNleftBitmap, None);
6737 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6741 void SaveSettingsProc(w, event, prms, nprms)
6747 SaveSettings(settingsFileName);
6750 void InfoProc(w, event, prms, nprms)
6757 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6762 void ManProc(w, event, prms, nprms)
6770 if (nprms && *nprms > 0)
6774 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6778 void HintProc(w, event, prms, nprms)
6787 void BookProc(w, event, prms, nprms)
6796 void AboutProc(w, event, prms, nprms)
6804 char *zippy = " (with Zippy code)";
6808 snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
6809 programVersion, zippy,
6810 "Copyright 1991 Digital Equipment Corporation",
6811 "Enhancements Copyright 1992-2009 Free Software Foundation",
6812 "Enhancements Copyright 2005 Alessandro Scotti",
6813 PACKAGE, " is free software and carries NO WARRANTY;",
6814 "see the file COPYING for more information.");
6815 ErrorPopUp(_("About XBoard"), buf, FALSE);
6818 void DebugProc(w, event, prms, nprms)
6824 appData.debugMode = !appData.debugMode;
6827 void AboutGameProc(w, event, prms, nprms)
6836 void NothingProc(w, event, prms, nprms)
6845 void Iconify(w, event, prms, nprms)
6854 XtSetArg(args[0], XtNiconic, True);
6855 XtSetValues(shellWidget, args, 1);
6858 void DisplayMessage(message, extMessage)
6859 char *message, *extMessage;
6861 /* display a message in the message widget */
6870 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6875 message = extMessage;
6879 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6881 /* need to test if messageWidget already exists, since this function
6882 can also be called during the startup, if for example a Xresource
6883 is not set up correctly */
6886 XtSetArg(arg, XtNlabel, message);
6887 XtSetValues(messageWidget, &arg, 1);
6893 void DisplayTitle(text)
6898 char title[MSG_SIZ];
6901 if (text == NULL) text = "";
6903 if (appData.titleInWindow) {
6905 XtSetArg(args[i], XtNlabel, text); i++;
6906 XtSetValues(titleWidget, args, i);
6909 if (*text != NULLCHAR) {
6910 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6911 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6912 } else if (appData.icsActive) {
6913 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6914 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6915 } else if (appData.cmailGameName[0] != NULLCHAR) {
6916 snprintf(icon, sizeof(icon), "%s", "CMail");
6917 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6919 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6920 } else if (gameInfo.variant == VariantGothic) {
6921 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6922 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6925 } else if (gameInfo.variant == VariantFalcon) {
6926 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6927 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6929 } else if (appData.noChessProgram) {
6930 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6931 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6933 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6934 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6937 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6938 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6939 XtSetValues(shellWidget, args, i);
6944 DisplayError(message, error)
6951 if (appData.debugMode || appData.matchMode) {
6952 fprintf(stderr, "%s: %s\n", programName, message);
6955 if (appData.debugMode || appData.matchMode) {
6956 fprintf(stderr, "%s: %s: %s\n",
6957 programName, message, strerror(error));
6959 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6962 ErrorPopUp(_("Error"), message, FALSE);
6966 void DisplayMoveError(message)
6971 DrawPosition(FALSE, NULL);
6972 if (appData.debugMode || appData.matchMode) {
6973 fprintf(stderr, "%s: %s\n", programName, message);
6975 if (appData.popupMoveErrors) {
6976 ErrorPopUp(_("Error"), message, FALSE);
6978 DisplayMessage(message, "");
6983 void DisplayFatalError(message, error, status)
6989 errorExitStatus = status;
6991 fprintf(stderr, "%s: %s\n", programName, message);
6993 fprintf(stderr, "%s: %s: %s\n",
6994 programName, message, strerror(error));
6995 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6998 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6999 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
7005 void DisplayInformation(message)
7009 ErrorPopUp(_("Information"), message, TRUE);
7012 void DisplayNote(message)
7016 ErrorPopUp(_("Note"), message, FALSE);
7020 NullXErrorCheck(dpy, error_event)
7022 XErrorEvent *error_event;
7027 void DisplayIcsInteractionTitle(message)
7030 if (oldICSInteractionTitle == NULL) {
7031 /* Magic to find the old window title, adapted from vim */
7032 char *wina = getenv("WINDOWID");
7034 Window win = (Window) atoi(wina);
7035 Window root, parent, *children;
7036 unsigned int nchildren;
7037 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
7039 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
7040 if (!XQueryTree(xDisplay, win, &root, &parent,
7041 &children, &nchildren)) break;
7042 if (children) XFree((void *)children);
7043 if (parent == root || parent == 0) break;
7046 XSetErrorHandler(oldHandler);
7048 if (oldICSInteractionTitle == NULL) {
7049 oldICSInteractionTitle = "xterm";
7052 printf("\033]0;%s\007", message);
7056 char pendingReplyPrefix[MSG_SIZ];
7057 ProcRef pendingReplyPR;
7059 void AskQuestionProc(w, event, prms, nprms)
7066 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7070 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7073 void AskQuestionPopDown()
7075 if (!askQuestionUp) return;
7076 XtPopdown(askQuestionShell);
7077 XtDestroyWidget(askQuestionShell);
7078 askQuestionUp = False;
7081 void AskQuestionReplyAction(w, event, prms, nprms)
7091 reply = XawDialogGetValueString(w = XtParent(w));
7092 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
7093 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
7094 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
7095 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
7096 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7097 AskQuestionPopDown();
7099 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7102 void AskQuestionCallback(w, client_data, call_data)
7104 XtPointer client_data, call_data;
7109 XtSetArg(args[0], XtNlabel, &name);
7110 XtGetValues(w, args, 1);
7112 if (strcmp(name, _("cancel")) == 0) {
7113 AskQuestionPopDown();
7115 AskQuestionReplyAction(w, NULL, NULL, NULL);
7119 void AskQuestion(title, question, replyPrefix, pr)
7120 char *title, *question, *replyPrefix;
7124 Widget popup, layout, dialog, edit;
7130 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7131 pendingReplyPR = pr;
7134 XtSetArg(args[i], XtNresizable, True); i++;
7135 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7136 askQuestionShell = popup =
7137 XtCreatePopupShell(title, transientShellWidgetClass,
7138 shellWidget, args, i);
7141 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7142 layoutArgs, XtNumber(layoutArgs));
7145 XtSetArg(args[i], XtNlabel, question); i++;
7146 XtSetArg(args[i], XtNvalue, ""); i++;
7147 XtSetArg(args[i], XtNborderWidth, 0); i++;
7148 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7151 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7152 (XtPointer) dialog);
7153 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7154 (XtPointer) dialog);
7156 XtRealizeWidget(popup);
7157 CatchDeleteWindow(popup, "AskQuestionPopDown");
7159 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7160 &x, &y, &win_x, &win_y, &mask);
7162 XtSetArg(args[0], XtNx, x - 10);
7163 XtSetArg(args[1], XtNy, y - 30);
7164 XtSetValues(popup, args, 2);
7166 XtPopup(popup, XtGrabExclusive);
7167 askQuestionUp = True;
7169 edit = XtNameToWidget(dialog, "*value");
7170 XtSetKeyboardFocus(popup, edit);
7178 if (*name == NULLCHAR) {
7180 } else if (strcmp(name, "$") == 0) {
7181 putc(BELLCHAR, stderr);
7184 char *prefix = "", *sep = "";
7185 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7186 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7194 PlaySound(appData.soundMove);
7200 PlaySound(appData.soundIcsWin);
7206 PlaySound(appData.soundIcsLoss);
7212 PlaySound(appData.soundIcsDraw);
7216 PlayIcsUnfinishedSound()
7218 PlaySound(appData.soundIcsUnfinished);
7224 PlaySound(appData.soundIcsAlarm);
7230 system("stty echo");
7236 system("stty -echo");
7240 Colorize(cc, continuation)
7245 int count, outCount, error;
7247 if (textColors[(int)cc].bg > 0) {
7248 if (textColors[(int)cc].fg > 0) {
7249 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7250 textColors[(int)cc].fg, textColors[(int)cc].bg);
7252 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7253 textColors[(int)cc].bg);
7256 if (textColors[(int)cc].fg > 0) {
7257 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7258 textColors[(int)cc].fg);
7260 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7263 count = strlen(buf);
7264 outCount = OutputToProcess(NoProc, buf, count, &error);
7265 if (outCount < count) {
7266 DisplayFatalError(_("Error writing to display"), error, 1);
7269 if (continuation) return;
7272 PlaySound(appData.soundShout);
7275 PlaySound(appData.soundSShout);
7278 PlaySound(appData.soundChannel1);
7281 PlaySound(appData.soundChannel);
7284 PlaySound(appData.soundKibitz);
7287 PlaySound(appData.soundTell);
7289 case ColorChallenge:
7290 PlaySound(appData.soundChallenge);
7293 PlaySound(appData.soundRequest);
7296 PlaySound(appData.soundSeek);
7307 return getpwuid(getuid())->pw_name;
7311 ExpandPathName(path)
7314 static char static_buf[4*MSG_SIZ];
7315 char *d, *s, buf[4*MSG_SIZ];
7321 while (*s && isspace(*s))
7330 if (*(s+1) == '/') {
7331 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7335 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7336 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7337 pwd = getpwnam(buf);
7340 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7344 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7345 strcat(d, strchr(s+1, '/'));
7349 safeStrCpy(d, s, 4*MSG_SIZ );
7356 static char host_name[MSG_SIZ];
7358 #if HAVE_GETHOSTNAME
7359 gethostname(host_name, MSG_SIZ);
7361 #else /* not HAVE_GETHOSTNAME */
7362 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7363 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7365 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7367 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7368 #endif /* not HAVE_GETHOSTNAME */
7371 XtIntervalId delayedEventTimerXID = 0;
7372 DelayedEventCallback delayedEventCallback = 0;
7377 delayedEventTimerXID = 0;
7378 delayedEventCallback();
7382 ScheduleDelayedEvent(cb, millisec)
7383 DelayedEventCallback cb; long millisec;
7385 if(delayedEventTimerXID && delayedEventCallback == cb)
7386 // [HGM] alive: replace, rather than add or flush identical event
7387 XtRemoveTimeOut(delayedEventTimerXID);
7388 delayedEventCallback = cb;
7389 delayedEventTimerXID =
7390 XtAppAddTimeOut(appContext, millisec,
7391 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7394 DelayedEventCallback
7397 if (delayedEventTimerXID) {
7398 return delayedEventCallback;
7405 CancelDelayedEvent()
7407 if (delayedEventTimerXID) {
7408 XtRemoveTimeOut(delayedEventTimerXID);
7409 delayedEventTimerXID = 0;
7413 XtIntervalId loadGameTimerXID = 0;
7415 int LoadGameTimerRunning()
7417 return loadGameTimerXID != 0;
7420 int StopLoadGameTimer()
7422 if (loadGameTimerXID != 0) {
7423 XtRemoveTimeOut(loadGameTimerXID);
7424 loadGameTimerXID = 0;
7432 LoadGameTimerCallback(arg, id)
7436 loadGameTimerXID = 0;
7441 StartLoadGameTimer(millisec)
7445 XtAppAddTimeOut(appContext, millisec,
7446 (XtTimerCallbackProc) LoadGameTimerCallback,
7450 XtIntervalId analysisClockXID = 0;
7453 AnalysisClockCallback(arg, id)
7457 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7458 || appData.icsEngineAnalyze) { // [DM]
7459 AnalysisPeriodicEvent(0);
7460 StartAnalysisClock();
7465 StartAnalysisClock()
7468 XtAppAddTimeOut(appContext, 2000,
7469 (XtTimerCallbackProc) AnalysisClockCallback,
7473 XtIntervalId clockTimerXID = 0;
7475 int ClockTimerRunning()
7477 return clockTimerXID != 0;
7480 int StopClockTimer()
7482 if (clockTimerXID != 0) {
7483 XtRemoveTimeOut(clockTimerXID);
7492 ClockTimerCallback(arg, id)
7501 StartClockTimer(millisec)
7505 XtAppAddTimeOut(appContext, millisec,
7506 (XtTimerCallbackProc) ClockTimerCallback,
7511 DisplayTimerLabel(w, color, timer, highlight)
7520 /* check for low time warning */
7521 Pixel foregroundOrWarningColor = timerForegroundPixel;
7524 appData.lowTimeWarning &&
7525 (timer / 1000) < appData.icsAlarmTime)
7526 foregroundOrWarningColor = lowTimeWarningColor;
7528 if (appData.clockMode) {
7529 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7530 XtSetArg(args[0], XtNlabel, buf);
7532 snprintf(buf, MSG_SIZ, "%s ", color);
7533 XtSetArg(args[0], XtNlabel, buf);
7538 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7539 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7541 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7542 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7545 XtSetValues(w, args, 3);
7549 DisplayWhiteClock(timeRemaining, highlight)
7555 if(appData.noGUI) return;
7556 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7557 if (highlight && iconPixmap == bIconPixmap) {
7558 iconPixmap = wIconPixmap;
7559 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7560 XtSetValues(shellWidget, args, 1);
7565 DisplayBlackClock(timeRemaining, highlight)
7571 if(appData.noGUI) return;
7572 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7573 if (highlight && iconPixmap == wIconPixmap) {
7574 iconPixmap = bIconPixmap;
7575 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7576 XtSetValues(shellWidget, args, 1);
7594 int StartChildProcess(cmdLine, dir, pr)
7601 int to_prog[2], from_prog[2];
7605 if (appData.debugMode) {
7606 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7609 /* We do NOT feed the cmdLine to the shell; we just
7610 parse it into blank-separated arguments in the
7611 most simple-minded way possible.
7614 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7617 while(*p == ' ') p++;
7619 if(*p == '"' || *p == '\'')
7620 p = strchr(++argv[i-1], *p);
7621 else p = strchr(p, ' ');
7622 if (p == NULL) break;
7627 SetUpChildIO(to_prog, from_prog);
7629 if ((pid = fork()) == 0) {
7631 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7632 close(to_prog[1]); // first close the unused pipe ends
7633 close(from_prog[0]);
7634 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7635 dup2(from_prog[1], 1);
7636 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7637 close(from_prog[1]); // and closing again loses one of the pipes!
7638 if(fileno(stderr) >= 2) // better safe than sorry...
7639 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7641 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7646 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7648 execvp(argv[0], argv);
7650 /* If we get here, exec failed */
7655 /* Parent process */
7657 close(from_prog[1]);
7659 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7662 cp->fdFrom = from_prog[0];
7663 cp->fdTo = to_prog[1];
7668 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7669 static RETSIGTYPE AlarmCallBack(int n)
7675 DestroyChildProcess(pr, signalType)
7679 ChildProc *cp = (ChildProc *) pr;
7681 if (cp->kind != CPReal) return;
7683 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7684 signal(SIGALRM, AlarmCallBack);
7686 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7687 kill(cp->pid, SIGKILL); // kill it forcefully
7688 wait((int *) 0); // and wait again
7692 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7694 /* Process is exiting either because of the kill or because of
7695 a quit command sent by the backend; either way, wait for it to die.
7704 InterruptChildProcess(pr)
7707 ChildProc *cp = (ChildProc *) pr;
7709 if (cp->kind != CPReal) return;
7710 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7713 int OpenTelnet(host, port, pr)
7718 char cmdLine[MSG_SIZ];
7720 if (port[0] == NULLCHAR) {
7721 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7723 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7725 return StartChildProcess(cmdLine, "", pr);
7728 int OpenTCP(host, port, pr)
7734 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7735 #else /* !OMIT_SOCKETS */
7736 struct addrinfo hints;
7737 struct addrinfo *ais, *ai;
7742 memset(&hints, 0, sizeof(hints));
7743 hints.ai_family = AF_UNSPEC;
7744 hints.ai_socktype = SOCK_STREAM;
7746 error = getaddrinfo(host, port, &hints, &ais);
7748 /* a getaddrinfo error is not an errno, so can't return it */
7749 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7750 host, port, gai_strerror(error));
7754 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7755 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7759 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7772 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7778 #endif /* !OMIT_SOCKETS */
7783 int OpenCommPort(name, pr)
7790 fd = open(name, 2, 0);
7791 if (fd < 0) return errno;
7793 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7803 int OpenLoopback(pr)
7809 SetUpChildIO(to, from);
7811 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7814 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7821 int OpenRcmd(host, user, cmd, pr)
7822 char *host, *user, *cmd;
7825 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7829 #define INPUT_SOURCE_BUF_SIZE 8192
7838 char buf[INPUT_SOURCE_BUF_SIZE];
7843 DoInputCallback(closure, source, xid)
7848 InputSource *is = (InputSource *) closure;
7853 if (is->lineByLine) {
7854 count = read(is->fd, is->unused,
7855 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7857 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7860 is->unused += count;
7862 while (p < is->unused) {
7863 q = memchr(p, '\n', is->unused - p);
7864 if (q == NULL) break;
7866 (is->func)(is, is->closure, p, q - p, 0);
7870 while (p < is->unused) {
7875 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7880 (is->func)(is, is->closure, is->buf, count, error);
7884 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7891 ChildProc *cp = (ChildProc *) pr;
7893 is = (InputSource *) calloc(1, sizeof(InputSource));
7894 is->lineByLine = lineByLine;
7898 is->fd = fileno(stdin);
7900 is->kind = cp->kind;
7901 is->fd = cp->fdFrom;
7904 is->unused = is->buf;
7907 is->xid = XtAppAddInput(appContext, is->fd,
7908 (XtPointer) (XtInputReadMask),
7909 (XtInputCallbackProc) DoInputCallback,
7911 is->closure = closure;
7912 return (InputSourceRef) is;
7916 RemoveInputSource(isr)
7919 InputSource *is = (InputSource *) isr;
7921 if (is->xid == 0) return;
7922 XtRemoveInput(is->xid);
7926 int OutputToProcess(pr, message, count, outError)
7932 static int line = 0;
7933 ChildProc *cp = (ChildProc *) pr;
7938 if (appData.noJoin || !appData.useInternalWrap)
7939 outCount = fwrite(message, 1, count, stdout);
7942 int width = get_term_width();
7943 int len = wrap(NULL, message, count, width, &line);
7944 char *msg = malloc(len);
7948 outCount = fwrite(message, 1, count, stdout);
7951 dbgchk = wrap(msg, message, count, width, &line);
7952 if (dbgchk != len && appData.debugMode)
7953 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7954 outCount = fwrite(msg, 1, dbgchk, stdout);
7960 outCount = write(cp->fdTo, message, count);
7970 /* Output message to process, with "ms" milliseconds of delay
7971 between each character. This is needed when sending the logon
7972 script to ICC, which for some reason doesn't like the
7973 instantaneous send. */
7974 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
7981 ChildProc *cp = (ChildProc *) pr;
7986 r = write(cp->fdTo, message++, 1);
7999 /**** Animation code by Hugh Fisher, DCS, ANU.
8001 Known problem: if a window overlapping the board is
8002 moved away while a piece is being animated underneath,
8003 the newly exposed area won't be updated properly.
8004 I can live with this.
8006 Known problem: if you look carefully at the animation
8007 of pieces in mono mode, they are being drawn as solid
8008 shapes without interior detail while moving. Fixing
8009 this would be a major complication for minimal return.
8012 /* Masks for XPM pieces. Black and white pieces can have
8013 different shapes, but in the interest of retaining my
8014 sanity pieces must have the same outline on both light
8015 and dark squares, and all pieces must use the same
8016 background square colors/images. */
8018 static int xpmDone = 0;
8021 CreateAnimMasks (pieceDepth)
8028 unsigned long plane;
8031 /* Need a bitmap just to get a GC with right depth */
8032 buf = XCreatePixmap(xDisplay, xBoardWindow,
8034 values.foreground = 1;
8035 values.background = 0;
8036 /* Don't use XtGetGC, not read only */
8037 maskGC = XCreateGC(xDisplay, buf,
8038 GCForeground | GCBackground, &values);
8039 XFreePixmap(xDisplay, buf);
8041 buf = XCreatePixmap(xDisplay, xBoardWindow,
8042 squareSize, squareSize, pieceDepth);
8043 values.foreground = XBlackPixel(xDisplay, xScreen);
8044 values.background = XWhitePixel(xDisplay, xScreen);
8045 bufGC = XCreateGC(xDisplay, buf,
8046 GCForeground | GCBackground, &values);
8048 for (piece = WhitePawn; piece <= BlackKing; piece++) {
8049 /* Begin with empty mask */
8050 if(!xpmDone) // [HGM] pieces: keep using existing
8051 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8052 squareSize, squareSize, 1);
8053 XSetFunction(xDisplay, maskGC, GXclear);
8054 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8055 0, 0, squareSize, squareSize);
8057 /* Take a copy of the piece */
8062 XSetFunction(xDisplay, bufGC, GXcopy);
8063 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8065 0, 0, squareSize, squareSize, 0, 0);
8067 /* XOR the background (light) over the piece */
8068 XSetFunction(xDisplay, bufGC, GXxor);
8070 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8071 0, 0, squareSize, squareSize, 0, 0);
8073 XSetForeground(xDisplay, bufGC, lightSquareColor);
8074 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8077 /* We now have an inverted piece image with the background
8078 erased. Construct mask by just selecting all the non-zero
8079 pixels - no need to reconstruct the original image. */
8080 XSetFunction(xDisplay, maskGC, GXor);
8082 /* Might be quicker to download an XImage and create bitmap
8083 data from it rather than this N copies per piece, but it
8084 only takes a fraction of a second and there is a much
8085 longer delay for loading the pieces. */
8086 for (n = 0; n < pieceDepth; n ++) {
8087 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8088 0, 0, squareSize, squareSize,
8094 XFreePixmap(xDisplay, buf);
8095 XFreeGC(xDisplay, bufGC);
8096 XFreeGC(xDisplay, maskGC);
8100 InitAnimState (anim, info)
8102 XWindowAttributes * info;
8107 /* Each buffer is square size, same depth as window */
8108 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8109 squareSize, squareSize, info->depth);
8110 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8111 squareSize, squareSize, info->depth);
8113 /* Create a plain GC for blitting */
8114 mask = GCForeground | GCBackground | GCFunction |
8115 GCPlaneMask | GCGraphicsExposures;
8116 values.foreground = XBlackPixel(xDisplay, xScreen);
8117 values.background = XWhitePixel(xDisplay, xScreen);
8118 values.function = GXcopy;
8119 values.plane_mask = AllPlanes;
8120 values.graphics_exposures = False;
8121 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8123 /* Piece will be copied from an existing context at
8124 the start of each new animation/drag. */
8125 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8127 /* Outline will be a read-only copy of an existing */
8128 anim->outlineGC = None;
8134 XWindowAttributes info;
8136 if (xpmDone && gameInfo.variant == oldVariant) return;
8137 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
8138 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8140 InitAnimState(&game, &info);
8141 InitAnimState(&player, &info);
8143 /* For XPM pieces, we need bitmaps to use as masks. */
8145 CreateAnimMasks(info.depth), xpmDone = 1;
8150 static Boolean frameWaiting;
8152 static RETSIGTYPE FrameAlarm (sig)
8155 frameWaiting = False;
8156 /* In case System-V style signals. Needed?? */
8157 signal(SIGALRM, FrameAlarm);
8164 struct itimerval delay;
8166 XSync(xDisplay, False);
8169 frameWaiting = True;
8170 signal(SIGALRM, FrameAlarm);
8171 delay.it_interval.tv_sec =
8172 delay.it_value.tv_sec = time / 1000;
8173 delay.it_interval.tv_usec =
8174 delay.it_value.tv_usec = (time % 1000) * 1000;
8175 setitimer(ITIMER_REAL, &delay, NULL);
8176 while (frameWaiting) pause();
8177 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8178 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8179 setitimer(ITIMER_REAL, &delay, NULL);
8189 XSync(xDisplay, False);
8191 usleep(time * 1000);
8196 /* Convert board position to corner of screen rect and color */
8199 ScreenSquare(column, row, pt, color)
8200 int column; int row; XPoint * pt; int * color;
8203 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8204 pt->y = lineGap + row * (squareSize + lineGap);
8206 pt->x = lineGap + column * (squareSize + lineGap);
8207 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8209 *color = SquareColor(row, column);
8212 /* Convert window coords to square */
8215 BoardSquare(x, y, column, row)
8216 int x; int y; int * column; int * row;
8218 *column = EventToSquare(x, BOARD_WIDTH);
8219 if (flipView && *column >= 0)
8220 *column = BOARD_WIDTH - 1 - *column;
8221 *row = EventToSquare(y, BOARD_HEIGHT);
8222 if (!flipView && *row >= 0)
8223 *row = BOARD_HEIGHT - 1 - *row;
8228 #undef Max /* just in case */
8230 #define Max(a, b) ((a) > (b) ? (a) : (b))
8231 #define Min(a, b) ((a) < (b) ? (a) : (b))
8234 SetRect(rect, x, y, width, height)
8235 XRectangle * rect; int x; int y; int width; int height;
8239 rect->width = width;
8240 rect->height = height;
8243 /* Test if two frames overlap. If they do, return
8244 intersection rect within old and location of
8245 that rect within new. */
8248 Intersect(old, new, size, area, pt)
8249 XPoint * old; XPoint * new;
8250 int size; XRectangle * area; XPoint * pt;
8252 if (old->x > new->x + size || new->x > old->x + size ||
8253 old->y > new->y + size || new->y > old->y + size) {
8256 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8257 size - abs(old->x - new->x), size - abs(old->y - new->y));
8258 pt->x = Max(old->x - new->x, 0);
8259 pt->y = Max(old->y - new->y, 0);
8264 /* For two overlapping frames, return the rect(s)
8265 in the old that do not intersect with the new. */
8268 CalcUpdateRects(old, new, size, update, nUpdates)
8269 XPoint * old; XPoint * new; int size;
8270 XRectangle update[]; int * nUpdates;
8274 /* If old = new (shouldn't happen) then nothing to draw */
8275 if (old->x == new->x && old->y == new->y) {
8279 /* Work out what bits overlap. Since we know the rects
8280 are the same size we don't need a full intersect calc. */
8282 /* Top or bottom edge? */
8283 if (new->y > old->y) {
8284 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8286 } else if (old->y > new->y) {
8287 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8288 size, old->y - new->y);
8291 /* Left or right edge - don't overlap any update calculated above. */
8292 if (new->x > old->x) {
8293 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8294 new->x - old->x, size - abs(new->y - old->y));
8296 } else if (old->x > new->x) {
8297 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8298 old->x - new->x, size - abs(new->y - old->y));
8305 /* Generate a series of frame coords from start->mid->finish.
8306 The movement rate doubles until the half way point is
8307 reached, then halves back down to the final destination,
8308 which gives a nice slow in/out effect. The algorithmn
8309 may seem to generate too many intermediates for short
8310 moves, but remember that the purpose is to attract the
8311 viewers attention to the piece about to be moved and
8312 then to where it ends up. Too few frames would be less
8316 Tween(start, mid, finish, factor, frames, nFrames)
8317 XPoint * start; XPoint * mid;
8318 XPoint * finish; int factor;
8319 XPoint frames[]; int * nFrames;
8321 int fraction, n, count;
8325 /* Slow in, stepping 1/16th, then 1/8th, ... */
8327 for (n = 0; n < factor; n++)
8329 for (n = 0; n < factor; n++) {
8330 frames[count].x = start->x + (mid->x - start->x) / fraction;
8331 frames[count].y = start->y + (mid->y - start->y) / fraction;
8333 fraction = fraction / 2;
8337 frames[count] = *mid;
8340 /* Slow out, stepping 1/2, then 1/4, ... */
8342 for (n = 0; n < factor; n++) {
8343 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8344 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8346 fraction = fraction * 2;
8351 /* Draw a piece on the screen without disturbing what's there */
8354 SelectGCMask(piece, clip, outline, mask)
8355 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8359 /* Bitmap for piece being moved. */
8360 if (appData.monoMode) {
8361 *mask = *pieceToSolid(piece);
8362 } else if (useImages) {
8364 *mask = xpmMask[piece];
8366 *mask = ximMaskPm[piece];
8369 *mask = *pieceToSolid(piece);
8372 /* GC for piece being moved. Square color doesn't matter, but
8373 since it gets modified we make a copy of the original. */
8375 if (appData.monoMode)
8380 if (appData.monoMode)
8385 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8387 /* Outline only used in mono mode and is not modified */
8389 *outline = bwPieceGC;
8391 *outline = wbPieceGC;
8395 OverlayPiece(piece, clip, outline, dest)
8396 ChessSquare piece; GC clip; GC outline; Drawable dest;
8401 /* Draw solid rectangle which will be clipped to shape of piece */
8402 XFillRectangle(xDisplay, dest, clip,
8403 0, 0, squareSize, squareSize);
8404 if (appData.monoMode)
8405 /* Also draw outline in contrasting color for black
8406 on black / white on white cases */
8407 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8408 0, 0, squareSize, squareSize, 0, 0, 1);
8410 /* Copy the piece */
8415 if(appData.upsideDown && flipView) kind ^= 2;
8416 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8418 0, 0, squareSize, squareSize,
8423 /* Animate the movement of a single piece */
8426 BeginAnimation(anim, piece, startColor, start)
8434 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8435 /* The old buffer is initialised with the start square (empty) */
8436 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8437 anim->prevFrame = *start;
8439 /* The piece will be drawn using its own bitmap as a matte */
8440 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8441 XSetClipMask(xDisplay, anim->pieceGC, mask);
8445 AnimationFrame(anim, frame, piece)
8450 XRectangle updates[4];
8455 /* Save what we are about to draw into the new buffer */
8456 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8457 frame->x, frame->y, squareSize, squareSize,
8460 /* Erase bits of the previous frame */
8461 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8462 /* Where the new frame overlapped the previous,
8463 the contents in newBuf are wrong. */
8464 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8465 overlap.x, overlap.y,
8466 overlap.width, overlap.height,
8468 /* Repaint the areas in the old that don't overlap new */
8469 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8470 for (i = 0; i < count; i++)
8471 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8472 updates[i].x - anim->prevFrame.x,
8473 updates[i].y - anim->prevFrame.y,
8474 updates[i].width, updates[i].height,
8475 updates[i].x, updates[i].y);
8477 /* Easy when no overlap */
8478 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8479 0, 0, squareSize, squareSize,
8480 anim->prevFrame.x, anim->prevFrame.y);
8483 /* Save this frame for next time round */
8484 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8485 0, 0, squareSize, squareSize,
8487 anim->prevFrame = *frame;
8489 /* Draw piece over original screen contents, not current,
8490 and copy entire rect. Wipes out overlapping piece images. */
8491 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8492 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8493 0, 0, squareSize, squareSize,
8494 frame->x, frame->y);
8498 EndAnimation (anim, finish)
8502 XRectangle updates[4];
8507 /* The main code will redraw the final square, so we
8508 only need to erase the bits that don't overlap. */
8509 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8510 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8511 for (i = 0; i < count; i++)
8512 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8513 updates[i].x - anim->prevFrame.x,
8514 updates[i].y - anim->prevFrame.y,
8515 updates[i].width, updates[i].height,
8516 updates[i].x, updates[i].y);
8518 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8519 0, 0, squareSize, squareSize,
8520 anim->prevFrame.x, anim->prevFrame.y);
8525 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8527 ChessSquare piece; int startColor;
8528 XPoint * start; XPoint * finish;
8529 XPoint frames[]; int nFrames;
8533 BeginAnimation(anim, piece, startColor, start);
8534 for (n = 0; n < nFrames; n++) {
8535 AnimationFrame(anim, &(frames[n]), piece);
8536 FrameDelay(appData.animSpeed);
8538 EndAnimation(anim, finish);
8542 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8545 ChessSquare piece = board[fromY][toY];
8546 board[fromY][toY] = EmptySquare;
8547 DrawPosition(FALSE, board);
8549 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8550 y = lineGap + toY * (squareSize + lineGap);
8552 x = lineGap + toX * (squareSize + lineGap);
8553 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8555 for(i=1; i<4*kFactor; i++) {
8556 int r = squareSize * 9 * i/(20*kFactor - 5);
8557 XFillArc(xDisplay, xBoardWindow, highlineGC,
8558 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8559 FrameDelay(appData.animSpeed);
8561 board[fromY][toY] = piece;
8564 /* Main control logic for deciding what to animate and how */
8567 AnimateMove(board, fromX, fromY, toX, toY)
8576 XPoint start, finish, mid;
8577 XPoint frames[kFactor * 2 + 1];
8578 int nFrames, startColor, endColor;
8580 /* Are we animating? */
8581 if (!appData.animate || appData.blindfold)
8584 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8585 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8586 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8588 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8589 piece = board[fromY][fromX];
8590 if (piece >= EmptySquare) return;
8595 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8598 if (appData.debugMode) {
8599 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8600 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8601 piece, fromX, fromY, toX, toY); }
8603 ScreenSquare(fromX, fromY, &start, &startColor);
8604 ScreenSquare(toX, toY, &finish, &endColor);
8607 /* Knight: make straight movement then diagonal */
8608 if (abs(toY - fromY) < abs(toX - fromX)) {
8609 mid.x = start.x + (finish.x - start.x) / 2;
8613 mid.y = start.y + (finish.y - start.y) / 2;
8616 mid.x = start.x + (finish.x - start.x) / 2;
8617 mid.y = start.y + (finish.y - start.y) / 2;
8620 /* Don't use as many frames for very short moves */
8621 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8622 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8624 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8625 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8626 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8628 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8629 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8632 /* Be sure end square is redrawn */
8633 damage[0][toY][toX] = True;
8637 DragPieceBegin(x, y)
8640 int boardX, boardY, color;
8643 /* Are we animating? */
8644 if (!appData.animateDragging || appData.blindfold)
8647 /* Figure out which square we start in and the
8648 mouse position relative to top left corner. */
8649 BoardSquare(x, y, &boardX, &boardY);
8650 player.startBoardX = boardX;
8651 player.startBoardY = boardY;
8652 ScreenSquare(boardX, boardY, &corner, &color);
8653 player.startSquare = corner;
8654 player.startColor = color;
8655 /* As soon as we start dragging, the piece will jump slightly to
8656 be centered over the mouse pointer. */
8657 player.mouseDelta.x = squareSize/2;
8658 player.mouseDelta.y = squareSize/2;
8659 /* Initialise animation */
8660 player.dragPiece = PieceForSquare(boardX, boardY);
8662 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8663 player.dragActive = True;
8664 BeginAnimation(&player, player.dragPiece, color, &corner);
8665 /* Mark this square as needing to be redrawn. Note that
8666 we don't remove the piece though, since logically (ie
8667 as seen by opponent) the move hasn't been made yet. */
8668 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8669 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8670 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8671 corner.x, corner.y, squareSize, squareSize,
8672 0, 0); // [HGM] zh: unstack in stead of grab
8673 if(gatingPiece != EmptySquare) {
8674 /* Kludge alert: When gating we want the introduced
8675 piece to appear on the from square. To generate an
8676 image of it, we draw it on the board, copy the image,
8677 and draw the original piece again. */
8678 ChessSquare piece = boards[currentMove][boardY][boardX];
8679 DrawSquare(boardY, boardX, gatingPiece, 0);
8680 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8681 corner.x, corner.y, squareSize, squareSize, 0, 0);
8682 DrawSquare(boardY, boardX, piece, 0);
8684 damage[0][boardY][boardX] = True;
8686 player.dragActive = False;
8691 ChangeDragPiece(ChessSquare piece)
8694 player.dragPiece = piece;
8695 /* The piece will be drawn using its own bitmap as a matte */
8696 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8697 XSetClipMask(xDisplay, player.pieceGC, mask);
8706 /* Are we animating? */
8707 if (!appData.animateDragging || appData.blindfold)
8711 if (! player.dragActive)
8713 /* Move piece, maintaining same relative position
8714 of mouse within square */
8715 corner.x = x - player.mouseDelta.x;
8716 corner.y = y - player.mouseDelta.y;
8717 AnimationFrame(&player, &corner, player.dragPiece);
8719 if (appData.highlightDragging) {
8721 BoardSquare(x, y, &boardX, &boardY);
8722 SetHighlights(fromX, fromY, boardX, boardY);
8731 int boardX, boardY, color;
8734 /* Are we animating? */
8735 if (!appData.animateDragging || appData.blindfold)
8739 if (! player.dragActive)
8741 /* Last frame in sequence is square piece is
8742 placed on, which may not match mouse exactly. */
8743 BoardSquare(x, y, &boardX, &boardY);
8744 ScreenSquare(boardX, boardY, &corner, &color);
8745 EndAnimation(&player, &corner);
8747 /* Be sure end square is redrawn */
8748 damage[0][boardY][boardX] = True;
8750 /* This prevents weird things happening with fast successive
8751 clicks which on my Sun at least can cause motion events
8752 without corresponding press/release. */
8753 player.dragActive = False;
8756 /* Handle expose event while piece being dragged */
8761 if (!player.dragActive || appData.blindfold)
8764 /* What we're doing: logically, the move hasn't been made yet,
8765 so the piece is still in it's original square. But visually
8766 it's being dragged around the board. So we erase the square
8767 that the piece is on and draw it at the last known drag point. */
8768 BlankSquare(player.startSquare.x, player.startSquare.y,
8769 player.startColor, EmptySquare, xBoardWindow, 1);
8770 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8771 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8774 #include <sys/ioctl.h>
8775 int get_term_width()
8777 int fd, default_width;
8780 default_width = 79; // this is FICS default anyway...
8782 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8784 if (!ioctl(fd, TIOCGSIZE, &win))
8785 default_width = win.ts_cols;
8786 #elif defined(TIOCGWINSZ)
8788 if (!ioctl(fd, TIOCGWINSZ, &win))
8789 default_width = win.ws_col;
8791 return default_width;
8797 static int old_width = 0;
8798 int new_width = get_term_width();
8800 if (old_width != new_width)
8801 ics_printf("set width %d\n", new_width);
8802 old_width = new_width;
8805 void NotifyFrontendLogin()
8810 /* [AS] Arrow highlighting support */
8812 static double A_WIDTH = 5; /* Width of arrow body */
8814 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8815 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8817 static double Sqr( double x )
8822 static int Round( double x )
8824 return (int) (x + 0.5);
8827 void SquareToPos(int rank, int file, int *x, int *y)
8830 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8831 *y = lineGap + rank * (squareSize + lineGap);
8833 *x = lineGap + file * (squareSize + lineGap);
8834 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8838 /* Draw an arrow between two points using current settings */
8839 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8842 double dx, dy, j, k, x, y;
8845 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8847 arrow[0].x = s_x + A_WIDTH + 0.5;
8850 arrow[1].x = s_x + A_WIDTH + 0.5;
8851 arrow[1].y = d_y - h;
8853 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8854 arrow[2].y = d_y - h;
8859 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8860 arrow[5].y = d_y - h;
8862 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8863 arrow[4].y = d_y - h;
8865 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8868 else if( d_y == s_y ) {
8869 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8872 arrow[0].y = s_y + A_WIDTH + 0.5;
8874 arrow[1].x = d_x - w;
8875 arrow[1].y = s_y + A_WIDTH + 0.5;
8877 arrow[2].x = d_x - w;
8878 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8883 arrow[5].x = d_x - w;
8884 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8886 arrow[4].x = d_x - w;
8887 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8890 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8893 /* [AS] Needed a lot of paper for this! :-) */
8894 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8895 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8897 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8899 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8904 arrow[0].x = Round(x - j);
8905 arrow[0].y = Round(y + j*dx);
8907 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8908 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8911 x = (double) d_x - k;
8912 y = (double) d_y - k*dy;
8915 x = (double) d_x + k;
8916 y = (double) d_y + k*dy;
8919 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8921 arrow[6].x = Round(x - j);
8922 arrow[6].y = Round(y + j*dx);
8924 arrow[2].x = Round(arrow[6].x + 2*j);
8925 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8927 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8928 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8933 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8934 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8937 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8938 // Polygon( hdc, arrow, 7 );
8941 /* [AS] Draw an arrow between two squares */
8942 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
8944 int s_x, s_y, d_x, d_y, hor, vert, i;
8946 if( s_col == d_col && s_row == d_row ) {
8950 /* Get source and destination points */
8951 SquareToPos( s_row, s_col, &s_x, &s_y);
8952 SquareToPos( d_row, d_col, &d_x, &d_y);
8955 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8957 else if( d_y < s_y ) {
8958 d_y += squareSize / 2 + squareSize / 4;
8961 d_y += squareSize / 2;
8965 d_x += squareSize / 2 - squareSize / 4;
8967 else if( d_x < s_x ) {
8968 d_x += squareSize / 2 + squareSize / 4;
8971 d_x += squareSize / 2;
8974 s_x += squareSize / 2;
8975 s_y += squareSize / 2;
8978 A_WIDTH = squareSize / 14.; //[HGM] make float
8980 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8982 hor = 64*s_col + 32; vert = 64*s_row + 32;
8983 for(i=0; i<= 64; i++) {
8984 damage[0][vert+6>>6][hor+6>>6] = True;
8985 damage[0][vert-6>>6][hor+6>>6] = True;
8986 damage[0][vert+6>>6][hor-6>>6] = True;
8987 damage[0][vert-6>>6][hor-6>>6] = True;
8988 hor += d_col - s_col; vert += d_row - s_row;
8992 Boolean IsDrawArrowEnabled()
8994 return appData.highlightMoveWithArrow && squareSize >= 32;
8997 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
8999 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
9000 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
9003 void UpdateLogos(int displ)
9005 return; // no logos in XBoard yet