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 SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
477 void GameListOptionsPopDown 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 Game Ctrl+G"), "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 {N_(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 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1041 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1042 { "GenericPopDown", (XtActionProc) GenericPopDown },
1043 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1044 { "SelectMove", (XtActionProc) SelectMove },
1047 char globalTranslations[] =
1048 ":<Key>F9: ResignProc() \n \
1049 :Ctrl<Key>n: ResetProc() \n \
1050 :Meta<Key>V: NewVariantProc() \n \
1051 :Ctrl<Key>o: LoadGameProc() \n \
1052 :Meta<Key>Next: LoadNextGameProc() \n \
1053 :Meta<Key>Prior: LoadPrevGameProc() \n \
1054 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
1055 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
1056 :Ctrl<Key>s: SaveGameProc() \n \
1057 :Ctrl<Key>c: CopyGameProc() \n \
1058 :Ctrl<Key>v: PasteGameProc() \n \
1059 :Ctrl<Key>O: LoadPositionProc() \n \
1060 :Shift<Key>Next: LoadNextPositionProc() \n \
1061 :Shift<Key>Prior: LoadPrevPositionProc() \n \
1062 :Ctrl<Key>S: SavePositionProc() \n \
1063 :Ctrl<Key>C: CopyPositionProc() \n \
1064 :Ctrl<Key>V: PastePositionProc() \n \
1065 :Ctrl<Key>q: QuitProc() \n \
1066 :Ctrl<Key>w: MachineWhiteProc() \n \
1067 :Ctrl<Key>b: MachineBlackProc() \n \
1068 :Ctrl<Key>t: TwoMachinesProc() \n \
1069 :Ctrl<Key>a: AnalysisModeProc() \n \
1070 :Ctrl<Key>g: AnalyzeFileProc() \n \
1071 :Ctrl<Key>e: EditGameProc() \n \
1072 :Ctrl<Key>E: EditPositionProc() \n \
1073 :Meta<Key>O: EngineOutputProc() \n \
1074 :Meta<Key>E: EvalGraphProc() \n \
1075 :Meta<Key>G: ShowGameListProc() \n \
1076 :Meta<Key>H: ShowMoveListProc() \n \
1077 :<Key>Pause: PauseProc() \n \
1078 :<Key>F3: AcceptProc() \n \
1079 :<Key>F4: DeclineProc() \n \
1080 :<Key>F12: RematchProc() \n \
1081 :<Key>F5: CallFlagProc() \n \
1082 :<Key>F6: DrawProc() \n \
1083 :<Key>F7: AdjournProc() \n \
1084 :<Key>F8: AbortProc() \n \
1085 :<Key>F10: StopObservingProc() \n \
1086 :<Key>F11: StopExaminingProc() \n \
1087 :Meta Ctrl<Key>F12: DebugProc() \n \
1088 :Meta<Key>End: ToEndProc() \n \
1089 :Meta<Key>Right: ForwardProc() \n \
1090 :Meta<Key>Home: ToStartProc() \n \
1091 :Meta<Key>Left: BackwardProc() \n \
1092 :<Key>Left: BackwardProc() \n \
1093 :<Key>Right: ForwardProc() \n \
1094 :<Key>Home: RevertProc() \n \
1095 :<Key>End: TruncateGameProc() \n \
1096 :Ctrl<Key>m: MoveNowProc() \n \
1097 :Ctrl<Key>x: RetractMoveProc() \n \
1098 :Meta<Key>J: EngineMenuProc() \n \
1099 :Meta<Key>U: UciMenuProc() \n \
1100 :Meta<Key>T: TimeControlProc() \n \
1101 :Ctrl<Key>P: PonderNextMoveProc() \n "
1102 #ifndef OPTIONSDIALOG
1104 :Ctrl<Key>Q: AlwaysQueenProc() \n \
1105 :Ctrl<Key>F: AutoflagProc() \n \
1106 :Ctrl<Key>A: AnimateMovingProc() \n \
1107 :Ctrl<Key>L: TestLegalityProc() \n \
1108 :Ctrl<Key>H: HideThinkingProc() \n "
1111 :<Key>-: Iconify() \n \
1112 :<Key>F1: ManProc() \n \
1113 :<Key>F2: FlipViewProc() \n \
1114 <KeyDown>.: BackwardProc() \n \
1115 <KeyUp>.: ForwardProc() \n \
1116 Shift<Key>1: AskQuestionProc(\"Direct command\",\
1117 \"Send to chess program:\",,1) \n \
1118 Shift<Key>2: AskQuestionProc(\"Direct command\",\
1119 \"Send to second chess program:\",,2) \n";
1121 char boardTranslations[] =
1122 "<Btn1Down>: HandleUserMove(0) \n \
1123 Shift<Btn1Up>: HandleUserMove(1) \n \
1124 <Btn1Up>: HandleUserMove(0) \n \
1125 <Btn1Motion>: AnimateUserMove() \n \
1126 <Btn3Motion>: HandlePV() \n \
1127 <Btn3Up>: PieceMenuPopup(menuB) \n \
1128 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1129 PieceMenuPopup(menuB) \n \
1130 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1131 PieceMenuPopup(menuW) \n \
1132 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1133 PieceMenuPopup(menuW) \n \
1134 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1135 PieceMenuPopup(menuB) \n";
1137 char whiteTranslations[] =
1138 "Shift<BtnDown>: WhiteClock(1)\n \
1139 <BtnDown>: WhiteClock(0)\n";
1140 char blackTranslations[] =
1141 "Shift<BtnDown>: BlackClock(1)\n \
1142 <BtnDown>: BlackClock(0)\n";
1144 char ICSInputTranslations[] =
1145 "<Key>Up: UpKeyProc() \n "
1146 "<Key>Down: DownKeyProc() \n "
1147 "<Key>Return: EnterKeyProc() \n";
1149 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1150 // as the widget is destroyed before the up-click can call extend-end
1151 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1153 String xboardResources[] = {
1154 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1155 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1156 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1161 /* Max possible square size */
1162 #define MAXSQSIZE 256
1164 static int xpm_avail[MAXSQSIZE];
1166 #ifdef HAVE_DIR_STRUCT
1168 /* Extract piece size from filename */
1170 xpm_getsize(name, len, ext)
1181 if ((p=strchr(name, '.')) == NULL ||
1182 StrCaseCmp(p+1, ext) != 0)
1188 while (*p && isdigit(*p))
1195 /* Setup xpm_avail */
1197 xpm_getavail(dirname, ext)
1205 for (i=0; i<MAXSQSIZE; ++i)
1208 if (appData.debugMode)
1209 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1211 dir = opendir(dirname);
1214 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1215 programName, dirname);
1219 while ((ent=readdir(dir)) != NULL) {
1220 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1221 if (i > 0 && i < MAXSQSIZE)
1231 xpm_print_avail(fp, ext)
1237 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1238 for (i=1; i<MAXSQSIZE; ++i) {
1244 /* Return XPM piecesize closest to size */
1246 xpm_closest_to(dirname, size, ext)
1252 int sm_diff = MAXSQSIZE;
1256 xpm_getavail(dirname, ext);
1258 if (appData.debugMode)
1259 xpm_print_avail(stderr, ext);
1261 for (i=1; i<MAXSQSIZE; ++i) {
1264 diff = (diff<0) ? -diff : diff;
1265 if (diff < sm_diff) {
1273 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1279 #else /* !HAVE_DIR_STRUCT */
1280 /* If we are on a system without a DIR struct, we can't
1281 read the directory, so we can't collect a list of
1282 filenames, etc., so we can't do any size-fitting. */
1284 xpm_closest_to(dirname, size, ext)
1289 fprintf(stderr, _("\
1290 Warning: No DIR structure found on this system --\n\
1291 Unable to autosize for XPM/XIM pieces.\n\
1292 Please report this error to %s.\n\
1293 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
1296 #endif /* HAVE_DIR_STRUCT */
1298 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1299 "magenta", "cyan", "white" };
1303 TextColors textColors[(int)NColorClasses];
1305 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1307 parse_color(str, which)
1311 char *p, buf[100], *d;
1314 if (strlen(str) > 99) /* watch bounds on buf */
1319 for (i=0; i<which; ++i) {
1326 /* Could be looking at something like:
1328 .. in which case we want to stop on a comma also */
1329 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1333 return -1; /* Use default for empty field */
1336 if (which == 2 || isdigit(*p))
1339 while (*p && isalpha(*p))
1344 for (i=0; i<8; ++i) {
1345 if (!StrCaseCmp(buf, cnames[i]))
1346 return which? (i+40) : (i+30);
1348 if (!StrCaseCmp(buf, "default")) return -1;
1350 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1355 parse_cpair(cc, str)
1359 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1360 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1365 /* bg and attr are optional */
1366 textColors[(int)cc].bg = parse_color(str, 1);
1367 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1368 textColors[(int)cc].attr = 0;
1374 /* Arrange to catch delete-window events */
1375 Atom wm_delete_window;
1377 CatchDeleteWindow(Widget w, String procname)
1380 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1381 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1382 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1389 XtSetArg(args[0], XtNiconic, False);
1390 XtSetValues(shellWidget, args, 1);
1392 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1395 //---------------------------------------------------------------------------------------------------------
1396 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1399 #define CW_USEDEFAULT (1<<31)
1400 #define ICS_TEXT_MENU_SIZE 90
1401 #define DEBUG_FILE "xboard.debug"
1402 #define SetCurrentDirectory chdir
1403 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1407 // these two must some day move to frontend.h, when they are implemented
1408 Boolean GameListIsUp();
1410 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1413 // front-end part of option handling
1415 // [HGM] This platform-dependent table provides the location for storing the color info
1416 extern char *crWhite, * crBlack;
1420 &appData.whitePieceColor,
1421 &appData.blackPieceColor,
1422 &appData.lightSquareColor,
1423 &appData.darkSquareColor,
1424 &appData.highlightSquareColor,
1425 &appData.premoveHighlightColor,
1426 &appData.lowTimeWarningColor,
1437 // [HGM] font: keep a font for each square size, even non-stndard ones
1438 #define NUM_SIZES 18
1439 #define MAX_SIZE 130
1440 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1441 char *fontTable[NUM_FONTS][MAX_SIZE];
1444 ParseFont(char *name, int number)
1445 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1447 if(sscanf(name, "size%d:", &size)) {
1448 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1449 // defer processing it until we know if it matches our board size
1450 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1451 fontTable[number][size] = strdup(strchr(name, ':')+1);
1452 fontValid[number][size] = True;
1457 case 0: // CLOCK_FONT
1458 appData.clockFont = strdup(name);
1460 case 1: // MESSAGE_FONT
1461 appData.font = strdup(name);
1463 case 2: // COORD_FONT
1464 appData.coordFont = strdup(name);
1469 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1474 { // only 2 fonts currently
1475 appData.clockFont = CLOCK_FONT_NAME;
1476 appData.coordFont = COORD_FONT_NAME;
1477 appData.font = DEFAULT_FONT_NAME;
1482 { // no-op, until we identify the code for this already in XBoard and move it here
1486 ParseColor(int n, char *name)
1487 { // in XBoard, just copy the color-name string
1488 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1492 ParseTextAttribs(ColorClass cc, char *s)
1494 (&appData.colorShout)[cc] = strdup(s);
1498 ParseBoardSize(void *addr, char *name)
1500 appData.boardSize = strdup(name);
1505 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1509 SetCommPortDefaults()
1510 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1513 // [HGM] args: these three cases taken out to stay in front-end
1515 SaveFontArg(FILE *f, ArgDescriptor *ad)
1518 int i, n = (int)(intptr_t)ad->argLoc;
1520 case 0: // CLOCK_FONT
1521 name = appData.clockFont;
1523 case 1: // MESSAGE_FONT
1524 name = appData.font;
1526 case 2: // COORD_FONT
1527 name = appData.coordFont;
1532 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1533 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1534 fontTable[n][squareSize] = strdup(name);
1535 fontValid[n][squareSize] = True;
1538 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1539 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1544 { // nothing to do, as the sounds are at all times represented by their text-string names already
1548 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1549 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1550 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1554 SaveColor(FILE *f, ArgDescriptor *ad)
1555 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1556 if(colorVariable[(int)(intptr_t)ad->argLoc])
1557 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1561 SaveBoardSize(FILE *f, char *name, void *addr)
1562 { // wrapper to shield back-end from BoardSize & sizeInfo
1563 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1567 ParseCommPortSettings(char *s)
1568 { // no such option in XBoard (yet)
1571 extern Widget engineOutputShell;
1574 GetActualPlacement(Widget wg, WindowPlacement *wp)
1584 XtSetArg(args[i], XtNx, &x); i++;
1585 XtSetArg(args[i], XtNy, &y); i++;
1586 XtSetArg(args[i], XtNwidth, &w); i++;
1587 XtSetArg(args[i], XtNheight, &h); i++;
1588 XtGetValues(wg, args, i);
1597 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1598 // In XBoard this will have to wait until awareness of window parameters is implemented
1599 GetActualPlacement(shellWidget, &wpMain);
1600 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1601 if(MoveHistoryIsUp()) GetActualPlacement(shells[7], &wpMoveHistory);
1602 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1603 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1604 if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1605 if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1609 PrintCommPortSettings(FILE *f, char *name)
1610 { // This option does not exist in XBoard
1614 MySearchPath(char *installDir, char *name, char *fullname)
1615 { // just append installDir and name. Perhaps ExpandPath should be used here?
1616 name = ExpandPathName(name);
1617 if(name && name[0] == '/')
1618 safeStrCpy(fullname, name, MSG_SIZ );
1620 sprintf(fullname, "%s%c%s", installDir, '/', name);
1626 MyGetFullPathName(char *name, char *fullname)
1627 { // should use ExpandPath?
1628 name = ExpandPathName(name);
1629 safeStrCpy(fullname, name, MSG_SIZ );
1634 EnsureOnScreen(int *x, int *y, int minX, int minY)
1641 { // [HGM] args: allows testing if main window is realized from back-end
1642 return xBoardWindow != 0;
1646 PopUpStartupDialog()
1647 { // start menu not implemented in XBoard
1651 ConvertToLine(int argc, char **argv)
1653 static char line[128*1024], buf[1024];
1657 for(i=1; i<argc; i++)
1659 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1660 && argv[i][0] != '{' )
1661 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1663 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1664 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1667 line[strlen(line)-1] = NULLCHAR;
1671 //--------------------------------------------------------------------------------------------
1673 extern Boolean twoBoards, partnerUp;
1676 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1678 #define BoardSize int
1679 void InitDrawingSizes(BoardSize boardSize, int flags)
1680 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1681 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1683 XtGeometryResult gres;
1685 static Dimension oldWidth, oldHeight;
1686 static VariantClass oldVariant;
1687 static int oldDual = -1;
1689 if(!formWidget) return;
1691 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1692 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1693 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1695 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1697 * Enable shell resizing.
1699 shellArgs[0].value = (XtArgVal) &w;
1700 shellArgs[1].value = (XtArgVal) &h;
1701 XtGetValues(shellWidget, shellArgs, 2);
1703 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1704 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1705 XtSetValues(shellWidget, &shellArgs[2], 4);
1707 XtSetArg(args[0], XtNdefaultDistance, &sep);
1708 XtGetValues(formWidget, args, 1);
1710 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1712 hOffset = boardWidth + 10;
1713 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1714 secondSegments[i] = gridSegments[i];
1715 secondSegments[i].x1 += hOffset;
1716 secondSegments[i].x2 += hOffset;
1719 XtSetArg(args[0], XtNwidth, boardWidth);
1720 XtSetArg(args[1], XtNheight, boardHeight);
1721 XtSetValues(boardWidget, args, 2);
1723 timerWidth = (boardWidth - sep) / 2;
1724 XtSetArg(args[0], XtNwidth, timerWidth);
1725 XtSetValues(whiteTimerWidget, args, 1);
1726 XtSetValues(blackTimerWidget, args, 1);
1728 XawFormDoLayout(formWidget, False);
1730 if (appData.titleInWindow) {
1732 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1733 XtSetArg(args[i], XtNheight, &h); i++;
1734 XtGetValues(titleWidget, args, i);
1736 w = boardWidth - 2*bor;
1738 XtSetArg(args[0], XtNwidth, &w);
1739 XtGetValues(menuBarWidget, args, 1);
1740 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1743 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1744 if (gres != XtGeometryYes && appData.debugMode) {
1746 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1747 programName, gres, w, h, wr, hr);
1751 XawFormDoLayout(formWidget, True);
1754 * Inhibit shell resizing.
1756 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1757 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1758 shellArgs[4].value = shellArgs[2].value = w;
1759 shellArgs[5].value = shellArgs[3].value = h;
1760 XtSetValues(shellWidget, &shellArgs[0], 6);
1763 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1766 if(gameInfo.variant == oldVariant) return; // and only if variant changed
1769 for(i=0; i<4; i++) {
1771 for(p=0; p<=(int)WhiteKing; p++)
1772 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1773 if(gameInfo.variant == VariantShogi) {
1774 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1775 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1776 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1777 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1778 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1781 if(gameInfo.variant == VariantGothic) {
1782 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1785 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1786 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1787 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1790 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1791 for(p=0; p<=(int)WhiteKing; p++)
1792 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1793 if(gameInfo.variant == VariantShogi) {
1794 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1795 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1796 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1797 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1798 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1801 if(gameInfo.variant == VariantGothic) {
1802 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1805 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1806 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1807 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1812 for(i=0; i<2; i++) {
1814 for(p=0; p<=(int)WhiteKing; p++)
1815 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1816 if(gameInfo.variant == VariantShogi) {
1817 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1818 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1819 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1820 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1821 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1824 if(gameInfo.variant == VariantGothic) {
1825 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1828 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1829 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1830 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1840 void ParseIcsTextColors()
1841 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1842 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1843 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1844 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1845 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1846 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1847 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1848 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1849 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1850 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1851 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1853 if (appData.colorize) {
1855 _("%s: can't parse color names; disabling colorization\n"),
1858 appData.colorize = FALSE;
1863 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1864 XrmValue vFrom, vTo;
1865 int forceMono = False;
1867 if (!appData.monoMode) {
1868 vFrom.addr = (caddr_t) appData.lightSquareColor;
1869 vFrom.size = strlen(appData.lightSquareColor);
1870 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1871 if (vTo.addr == NULL) {
1872 appData.monoMode = True;
1875 lightSquareColor = *(Pixel *) vTo.addr;
1878 if (!appData.monoMode) {
1879 vFrom.addr = (caddr_t) appData.darkSquareColor;
1880 vFrom.size = strlen(appData.darkSquareColor);
1881 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1882 if (vTo.addr == NULL) {
1883 appData.monoMode = True;
1886 darkSquareColor = *(Pixel *) vTo.addr;
1889 if (!appData.monoMode) {
1890 vFrom.addr = (caddr_t) appData.whitePieceColor;
1891 vFrom.size = strlen(appData.whitePieceColor);
1892 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1893 if (vTo.addr == NULL) {
1894 appData.monoMode = True;
1897 whitePieceColor = *(Pixel *) vTo.addr;
1900 if (!appData.monoMode) {
1901 vFrom.addr = (caddr_t) appData.blackPieceColor;
1902 vFrom.size = strlen(appData.blackPieceColor);
1903 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1904 if (vTo.addr == NULL) {
1905 appData.monoMode = True;
1908 blackPieceColor = *(Pixel *) vTo.addr;
1912 if (!appData.monoMode) {
1913 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1914 vFrom.size = strlen(appData.highlightSquareColor);
1915 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1916 if (vTo.addr == NULL) {
1917 appData.monoMode = True;
1920 highlightSquareColor = *(Pixel *) vTo.addr;
1924 if (!appData.monoMode) {
1925 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1926 vFrom.size = strlen(appData.premoveHighlightColor);
1927 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1928 if (vTo.addr == NULL) {
1929 appData.monoMode = True;
1932 premoveHighlightColor = *(Pixel *) vTo.addr;
1940 { // [HGM] taken out of main
1942 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1943 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1944 appData.bitmapDirectory = DEF_BITMAP_DIR;
1946 if (appData.bitmapDirectory[0] != NULLCHAR) {
1950 CreateXPMBoard(appData.liteBackTextureFile, 1);
1951 CreateXPMBoard(appData.darkBackTextureFile, 0);
1955 /* Create regular pieces */
1956 if (!useImages) CreatePieces();
1965 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1966 XSetWindowAttributes window_attributes;
1968 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1969 XrmValue vFrom, vTo;
1970 XtGeometryResult gres;
1973 int forceMono = False;
1975 srandom(time(0)); // [HGM] book: make random truly random
1977 setbuf(stdout, NULL);
1978 setbuf(stderr, NULL);
1981 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1982 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1986 programName = strrchr(argv[0], '/');
1987 if (programName == NULL)
1988 programName = argv[0];
1993 XtSetLanguageProc(NULL, NULL, NULL);
1994 bindtextdomain(PACKAGE, LOCALEDIR);
1995 textdomain(PACKAGE);
1999 XtAppInitialize(&appContext, "XBoard", shellOptions,
2000 XtNumber(shellOptions),
2001 &argc, argv, xboardResources, NULL, 0);
2002 appData.boardSize = "";
2003 InitAppData(ConvertToLine(argc, argv));
2005 if (p == NULL) p = "/tmp";
2006 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
2007 gameCopyFilename = (char*) malloc(i);
2008 gamePasteFilename = (char*) malloc(i);
2009 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
2010 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
2012 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
2013 clientResources, XtNumber(clientResources),
2016 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
2017 static char buf[MSG_SIZ];
2018 EscapeExpand(buf, appData.firstInitString);
2019 appData.firstInitString = strdup(buf);
2020 EscapeExpand(buf, appData.secondInitString);
2021 appData.secondInitString = strdup(buf);
2022 EscapeExpand(buf, appData.firstComputerString);
2023 appData.firstComputerString = strdup(buf);
2024 EscapeExpand(buf, appData.secondComputerString);
2025 appData.secondComputerString = strdup(buf);
2028 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
2031 if (chdir(chessDir) != 0) {
2032 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2038 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2039 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2040 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
2041 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2044 setbuf(debugFP, NULL);
2048 if (appData.debugMode) {
2049 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
2053 /* [HGM,HR] make sure board size is acceptable */
2054 if(appData.NrFiles > BOARD_FILES ||
2055 appData.NrRanks > BOARD_RANKS )
2056 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
2059 /* This feature does not work; animation needs a rewrite */
2060 appData.highlightDragging = FALSE;
2064 xDisplay = XtDisplay(shellWidget);
2065 xScreen = DefaultScreen(xDisplay);
2066 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2068 gameInfo.variant = StringToVariant(appData.variant);
2069 InitPosition(FALSE);
2072 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2074 if (isdigit(appData.boardSize[0])) {
2075 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2076 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2077 &fontPxlSize, &smallLayout, &tinyLayout);
2079 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2080 programName, appData.boardSize);
2084 /* Find some defaults; use the nearest known size */
2085 SizeDefaults *szd, *nearest;
2086 int distance = 99999;
2087 nearest = szd = sizeDefaults;
2088 while (szd->name != NULL) {
2089 if (abs(szd->squareSize - squareSize) < distance) {
2091 distance = abs(szd->squareSize - squareSize);
2092 if (distance == 0) break;
2096 if (i < 2) lineGap = nearest->lineGap;
2097 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2098 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2099 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2100 if (i < 6) smallLayout = nearest->smallLayout;
2101 if (i < 7) tinyLayout = nearest->tinyLayout;
2104 SizeDefaults *szd = sizeDefaults;
2105 if (*appData.boardSize == NULLCHAR) {
2106 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2107 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2110 if (szd->name == NULL) szd--;
2111 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2113 while (szd->name != NULL &&
2114 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2115 if (szd->name == NULL) {
2116 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2117 programName, appData.boardSize);
2121 squareSize = szd->squareSize;
2122 lineGap = szd->lineGap;
2123 clockFontPxlSize = szd->clockFontPxlSize;
2124 coordFontPxlSize = szd->coordFontPxlSize;
2125 fontPxlSize = szd->fontPxlSize;
2126 smallLayout = szd->smallLayout;
2127 tinyLayout = szd->tinyLayout;
2128 // [HGM] font: use defaults from settings file if available and not overruled
2130 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2131 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2132 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2133 appData.font = fontTable[MESSAGE_FONT][squareSize];
2134 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2135 appData.coordFont = fontTable[COORD_FONT][squareSize];
2137 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2138 if (strlen(appData.pixmapDirectory) > 0) {
2139 p = ExpandPathName(appData.pixmapDirectory);
2141 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2142 appData.pixmapDirectory);
2145 if (appData.debugMode) {
2146 fprintf(stderr, _("\
2147 XBoard square size (hint): %d\n\
2148 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2150 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2151 if (appData.debugMode) {
2152 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2155 defaultLineGap = lineGap;
2156 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2158 /* [HR] height treated separately (hacked) */
2159 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2160 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2161 if (appData.showJail == 1) {
2162 /* Jail on top and bottom */
2163 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2164 XtSetArg(boardArgs[2], XtNheight,
2165 boardHeight + 2*(lineGap + squareSize));
2166 } else if (appData.showJail == 2) {
2168 XtSetArg(boardArgs[1], XtNwidth,
2169 boardWidth + 2*(lineGap + squareSize));
2170 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2173 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2174 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2178 * Determine what fonts to use.
2181 appData.font = InsertPxlSize(appData.font, fontPxlSize);
2182 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
2183 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
2184 fontSet = CreateFontSet(appData.font);
2185 clockFontSet = CreateFontSet(appData.clockFont);
2187 /* For the coordFont, use the 0th font of the fontset. */
2188 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
2189 XFontStruct **font_struct_list;
2190 char **font_name_list;
2191 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
2192 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
2193 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2196 appData.font = FindFont(appData.font, fontPxlSize);
2197 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2198 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2199 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2200 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2201 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2202 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2204 countFontID = coordFontID; // [HGM] holdings
2205 countFontStruct = coordFontStruct;
2207 xdb = XtDatabase(xDisplay);
2209 XrmPutLineResource(&xdb, "*international: True");
2210 vTo.size = sizeof(XFontSet);
2211 vTo.addr = (XtPointer) &fontSet;
2212 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
2214 XrmPutStringResource(&xdb, "*font", appData.font);
2218 * Detect if there are not enough colors available and adapt.
2220 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2221 appData.monoMode = True;
2224 forceMono = MakeColors();
2227 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2229 appData.monoMode = True;
2232 if (appData.lowTimeWarning && !appData.monoMode) {
2233 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2234 vFrom.size = strlen(appData.lowTimeWarningColor);
2235 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2236 if (vTo.addr == NULL)
2237 appData.monoMode = True;
2239 lowTimeWarningColor = *(Pixel *) vTo.addr;
2242 if (appData.monoMode && appData.debugMode) {
2243 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2244 (unsigned long) XWhitePixel(xDisplay, xScreen),
2245 (unsigned long) XBlackPixel(xDisplay, xScreen));
2248 ParseIcsTextColors();
2249 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2250 textColors[ColorNone].attr = 0;
2252 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2258 layoutName = "tinyLayout";
2259 } else if (smallLayout) {
2260 layoutName = "smallLayout";
2262 layoutName = "normalLayout";
2264 /* Outer layoutWidget is there only to provide a name for use in
2265 resources that depend on the layout style */
2267 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2268 layoutArgs, XtNumber(layoutArgs));
2270 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2271 formArgs, XtNumber(formArgs));
2272 XtSetArg(args[0], XtNdefaultDistance, &sep);
2273 XtGetValues(formWidget, args, 1);
2276 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2277 XtSetArg(args[0], XtNtop, XtChainTop);
2278 XtSetArg(args[1], XtNbottom, XtChainTop);
2279 XtSetArg(args[2], XtNright, XtChainLeft);
2280 XtSetValues(menuBarWidget, args, 3);
2282 widgetList[j++] = whiteTimerWidget =
2283 XtCreateWidget("whiteTime", labelWidgetClass,
2284 formWidget, timerArgs, XtNumber(timerArgs));
2286 XtSetArg(args[0], XtNfontSet, clockFontSet);
2288 XtSetArg(args[0], XtNfont, clockFontStruct);
2290 XtSetArg(args[1], XtNtop, XtChainTop);
2291 XtSetArg(args[2], XtNbottom, XtChainTop);
2292 XtSetValues(whiteTimerWidget, args, 3);
2294 widgetList[j++] = blackTimerWidget =
2295 XtCreateWidget("blackTime", labelWidgetClass,
2296 formWidget, timerArgs, XtNumber(timerArgs));
2298 XtSetArg(args[0], XtNfontSet, clockFontSet);
2300 XtSetArg(args[0], XtNfont, clockFontStruct);
2302 XtSetArg(args[1], XtNtop, XtChainTop);
2303 XtSetArg(args[2], XtNbottom, XtChainTop);
2304 XtSetValues(blackTimerWidget, args, 3);
2306 if (appData.titleInWindow) {
2307 widgetList[j++] = titleWidget =
2308 XtCreateWidget("title", labelWidgetClass, formWidget,
2309 titleArgs, XtNumber(titleArgs));
2310 XtSetArg(args[0], XtNtop, XtChainTop);
2311 XtSetArg(args[1], XtNbottom, XtChainTop);
2312 XtSetValues(titleWidget, args, 2);
2315 if (appData.showButtonBar) {
2316 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2317 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2318 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2319 XtSetArg(args[2], XtNtop, XtChainTop);
2320 XtSetArg(args[3], XtNbottom, XtChainTop);
2321 XtSetValues(buttonBarWidget, args, 4);
2324 widgetList[j++] = messageWidget =
2325 XtCreateWidget("message", labelWidgetClass, formWidget,
2326 messageArgs, XtNumber(messageArgs));
2327 XtSetArg(args[0], XtNtop, XtChainTop);
2328 XtSetArg(args[1], XtNbottom, XtChainTop);
2329 XtSetValues(messageWidget, args, 2);
2331 widgetList[j++] = boardWidget =
2332 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2333 XtNumber(boardArgs));
2335 XtManageChildren(widgetList, j);
2337 timerWidth = (boardWidth - sep) / 2;
2338 XtSetArg(args[0], XtNwidth, timerWidth);
2339 XtSetValues(whiteTimerWidget, args, 1);
2340 XtSetValues(blackTimerWidget, args, 1);
2342 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2343 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2344 XtGetValues(whiteTimerWidget, args, 2);
2346 if (appData.showButtonBar) {
2347 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2348 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2349 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2353 * formWidget uses these constraints but they are stored
2357 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2358 XtSetValues(menuBarWidget, args, i);
2359 if (appData.titleInWindow) {
2362 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2363 XtSetValues(whiteTimerWidget, args, i);
2365 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2366 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2367 XtSetValues(blackTimerWidget, args, i);
2369 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2370 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2371 XtSetValues(titleWidget, args, i);
2373 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2374 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2375 XtSetValues(messageWidget, args, i);
2376 if (appData.showButtonBar) {
2378 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2379 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2380 XtSetValues(buttonBarWidget, args, i);
2384 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2385 XtSetValues(whiteTimerWidget, args, i);
2387 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2388 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2389 XtSetValues(blackTimerWidget, args, i);
2391 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2392 XtSetValues(titleWidget, args, i);
2394 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2395 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2396 XtSetValues(messageWidget, args, i);
2397 if (appData.showButtonBar) {
2399 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2400 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2401 XtSetValues(buttonBarWidget, args, i);
2406 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2407 XtSetValues(whiteTimerWidget, args, i);
2409 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2410 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2411 XtSetValues(blackTimerWidget, args, i);
2413 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2414 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2415 XtSetValues(messageWidget, args, i);
2416 if (appData.showButtonBar) {
2418 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2419 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2420 XtSetValues(buttonBarWidget, args, i);
2424 XtSetArg(args[0], XtNfromVert, messageWidget);
2425 XtSetArg(args[1], XtNtop, XtChainTop);
2426 XtSetArg(args[2], XtNbottom, XtChainBottom);
2427 XtSetArg(args[3], XtNleft, XtChainLeft);
2428 XtSetArg(args[4], XtNright, XtChainRight);
2429 XtSetValues(boardWidget, args, 5);
2431 XtRealizeWidget(shellWidget);
2434 XtSetArg(args[0], XtNx, wpMain.x);
2435 XtSetArg(args[1], XtNy, wpMain.y);
2436 XtSetValues(shellWidget, args, 2);
2440 * Correct the width of the message and title widgets.
2441 * It is not known why some systems need the extra fudge term.
2442 * The value "2" is probably larger than needed.
2444 XawFormDoLayout(formWidget, False);
2446 #define WIDTH_FUDGE 2
2448 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2449 XtSetArg(args[i], XtNheight, &h); i++;
2450 XtGetValues(messageWidget, args, i);
2451 if (appData.showButtonBar) {
2453 XtSetArg(args[i], XtNwidth, &w); i++;
2454 XtGetValues(buttonBarWidget, args, i);
2455 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2457 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2460 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2461 if (gres != XtGeometryYes && appData.debugMode) {
2462 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2463 programName, gres, w, h, wr, hr);
2466 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2467 /* The size used for the child widget in layout lags one resize behind
2468 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2470 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2471 if (gres != XtGeometryYes && appData.debugMode) {
2472 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2473 programName, gres, w, h, wr, hr);
2476 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2477 XtSetArg(args[1], XtNright, XtChainRight);
2478 XtSetValues(messageWidget, args, 2);
2480 if (appData.titleInWindow) {
2482 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2483 XtSetArg(args[i], XtNheight, &h); i++;
2484 XtGetValues(titleWidget, args, i);
2486 w = boardWidth - 2*bor;
2488 XtSetArg(args[0], XtNwidth, &w);
2489 XtGetValues(menuBarWidget, args, 1);
2490 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2493 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2494 if (gres != XtGeometryYes && appData.debugMode) {
2496 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2497 programName, gres, w, h, wr, hr);
2500 XawFormDoLayout(formWidget, True);
2502 xBoardWindow = XtWindow(boardWidget);
2504 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2505 // not need to go into InitDrawingSizes().
2509 * Create X checkmark bitmap and initialize option menu checks.
2511 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2512 checkmark_bits, checkmark_width, checkmark_height);
2513 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2514 #ifndef OPTIONSDIALOG
2515 if (appData.alwaysPromoteToQueen) {
2516 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2519 if (appData.animateDragging) {
2520 XtSetValues(XtNameToWidget(menuBarWidget,
2521 "menuOptions.Animate Dragging"),
2524 if (appData.animate) {
2525 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2528 if (appData.autoCallFlag) {
2529 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2532 if (appData.autoFlipView) {
2533 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2536 if (appData.blindfold) {
2537 XtSetValues(XtNameToWidget(menuBarWidget,
2538 "menuOptions.Blindfold"), args, 1);
2540 if (appData.flashCount > 0) {
2541 XtSetValues(XtNameToWidget(menuBarWidget,
2542 "menuOptions.Flash Moves"),
2546 if (appData.highlightDragging) {
2547 XtSetValues(XtNameToWidget(menuBarWidget,
2548 "menuOptions.Highlight Dragging"),
2552 if (appData.highlightLastMove) {
2553 XtSetValues(XtNameToWidget(menuBarWidget,
2554 "menuOptions.Highlight Last Move"),
2557 if (appData.highlightMoveWithArrow) {
2558 XtSetValues(XtNameToWidget(menuBarWidget,
2559 "menuOptions.Arrow"),
2562 // if (appData.icsAlarm) {
2563 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2566 if (appData.ringBellAfterMoves) {
2567 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2570 if (appData.oneClick) {
2571 XtSetValues(XtNameToWidget(menuBarWidget,
2572 "menuOptions.OneClick"), args, 1);
2574 if (appData.periodicUpdates) {
2575 XtSetValues(XtNameToWidget(menuBarWidget,
2576 "menuOptions.Periodic Updates"), args, 1);
2578 if (appData.ponderNextMove) {
2579 XtSetValues(XtNameToWidget(menuBarWidget,
2580 "menuOptions.Ponder Next Move"), args, 1);
2582 if (appData.popupExitMessage) {
2583 XtSetValues(XtNameToWidget(menuBarWidget,
2584 "menuOptions.Popup Exit Message"), args, 1);
2586 if (appData.popupMoveErrors) {
2587 XtSetValues(XtNameToWidget(menuBarWidget,
2588 "menuOptions.Popup Move Errors"), args, 1);
2590 // if (appData.premove) {
2591 // XtSetValues(XtNameToWidget(menuBarWidget,
2592 // "menuOptions.Premove"), args, 1);
2594 if (appData.showCoords) {
2595 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2598 if (appData.hideThinkingFromHuman) {
2599 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2602 if (appData.testLegality) {
2603 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2607 if (saveSettingsOnExit) {
2608 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2615 ReadBitmap(&wIconPixmap, "icon_white.bm",
2616 icon_white_bits, icon_white_width, icon_white_height);
2617 ReadBitmap(&bIconPixmap, "icon_black.bm",
2618 icon_black_bits, icon_black_width, icon_black_height);
2619 iconPixmap = wIconPixmap;
2621 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2622 XtSetValues(shellWidget, args, i);
2625 * Create a cursor for the board widget.
2627 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2628 XChangeWindowAttributes(xDisplay, xBoardWindow,
2629 CWCursor, &window_attributes);
2632 * Inhibit shell resizing.
2634 shellArgs[0].value = (XtArgVal) &w;
2635 shellArgs[1].value = (XtArgVal) &h;
2636 XtGetValues(shellWidget, shellArgs, 2);
2637 shellArgs[4].value = shellArgs[2].value = w;
2638 shellArgs[5].value = shellArgs[3].value = h;
2639 XtSetValues(shellWidget, &shellArgs[2], 4);
2640 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2641 marginH = h - boardHeight;
2643 CatchDeleteWindow(shellWidget, "QuitProc");
2651 if (appData.animate || appData.animateDragging)
2654 XtAugmentTranslations(formWidget,
2655 XtParseTranslationTable(globalTranslations));
2656 XtAugmentTranslations(boardWidget,
2657 XtParseTranslationTable(boardTranslations));
2658 XtAugmentTranslations(whiteTimerWidget,
2659 XtParseTranslationTable(whiteTranslations));
2660 XtAugmentTranslations(blackTimerWidget,
2661 XtParseTranslationTable(blackTranslations));
2663 /* Why is the following needed on some versions of X instead
2664 * of a translation? */
2665 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2666 (XtEventHandler) EventProc, NULL);
2668 XtAddEventHandler(formWidget, KeyPressMask, False,
2669 (XtEventHandler) MoveTypeInProc, NULL);
2671 /* [AS] Restore layout */
2672 if( wpMoveHistory.visible ) {
2676 if( wpEvalGraph.visible )
2681 if( wpEngineOutput.visible ) {
2682 EngineOutputPopUp();
2687 if (errorExitStatus == -1) {
2688 if (appData.icsActive) {
2689 /* We now wait until we see "login:" from the ICS before
2690 sending the logon script (problems with timestamp otherwise) */
2691 /*ICSInitScript();*/
2692 if (appData.icsInputBox) ICSInputBoxPopUp();
2696 signal(SIGWINCH, TermSizeSigHandler);
2698 signal(SIGINT, IntSigHandler);
2699 signal(SIGTERM, IntSigHandler);
2700 if (*appData.cmailGameName != NULLCHAR) {
2701 signal(SIGUSR1, CmailSigHandler);
2704 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2706 // XtSetKeyboardFocus(shellWidget, formWidget);
2707 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2709 XtAppMainLoop(appContext);
2710 if (appData.debugMode) fclose(debugFP); // [DM] debug
2714 static Boolean noEcho;
2719 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2720 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2722 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2723 unlink(gameCopyFilename);
2724 unlink(gamePasteFilename);
2725 if(noEcho) EchoOn();
2728 RETSIGTYPE TermSizeSigHandler(int sig)
2741 CmailSigHandler(sig)
2747 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2749 /* Activate call-back function CmailSigHandlerCallBack() */
2750 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2752 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2756 CmailSigHandlerCallBack(isr, closure, message, count, error)
2764 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2766 /**** end signal code ****/
2772 /* try to open the icsLogon script, either in the location given
2773 * or in the users HOME directory
2780 f = fopen(appData.icsLogon, "r");
2783 homedir = getenv("HOME");
2784 if (homedir != NULL)
2786 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2787 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2788 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2789 f = fopen(buf, "r");
2794 ProcessICSInitScript(f);
2796 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2819 if (!menuBarWidget) return;
2820 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2822 DisplayError("menuEdit.Revert", 0);
2824 XtSetSensitive(w, !grey);
2826 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2828 DisplayError("menuEdit.Annotate", 0);
2830 XtSetSensitive(w, !grey);
2835 SetMenuEnables(enab)
2839 if (!menuBarWidget) return;
2840 while (enab->name != NULL) {
2841 w = XtNameToWidget(menuBarWidget, enab->name);
2843 DisplayError(enab->name, 0);
2845 XtSetSensitive(w, enab->value);
2851 Enables icsEnables[] = {
2852 { "menuFile.Mail Move", False },
2853 { "menuFile.Reload CMail Message", False },
2854 { "menuMode.Machine Black", False },
2855 { "menuMode.Machine White", False },
2856 { "menuMode.Analysis Mode", False },
2857 { "menuMode.Analyze File", False },
2858 { "menuMode.Two Machines", False },
2859 { "menuMode.Machine Match", False },
2861 { "menuEngine.Hint", False },
2862 { "menuEngine.Book", False },
2863 { "menuEngine.Move Now", False },
2864 #ifndef OPTIONSDIALOG
2865 { "menuOptions.Periodic Updates", False },
2866 { "menuOptions.Hide Thinking", False },
2867 { "menuOptions.Ponder Next Move", False },
2870 { "menuEngine.Engine #1 Settings", False },
2871 { "menuEngine.Engine #2 Settings", False },
2872 { "menuEngine.Load Engine", False },
2873 { "menuEdit.Annotate", False },
2874 { "menuOptions.Match", False },
2878 Enables ncpEnables[] = {
2879 { "menuFile.Mail Move", False },
2880 { "menuFile.Reload CMail Message", False },
2881 { "menuMode.Machine White", False },
2882 { "menuMode.Machine Black", False },
2883 { "menuMode.Analysis Mode", False },
2884 { "menuMode.Analyze File", False },
2885 { "menuMode.Two Machines", False },
2886 { "menuMode.Machine Match", False },
2887 { "menuMode.ICS Client", False },
2888 { "menuView.ICStex", False },
2889 { "menuView.ICS Input Box", False },
2890 { "Action", False },
2891 { "menuEdit.Revert", False },
2892 { "menuEdit.Annotate", False },
2893 { "menuEngine.Engine #1 Settings", False },
2894 { "menuEngine.Engine #2 Settings", False },
2895 { "menuEngine.Move Now", False },
2896 { "menuEngine.Retract Move", False },
2897 { "menuOptions.ICS", False },
2898 #ifndef OPTIONSDIALOG
2899 { "menuOptions.Auto Flag", False },
2900 { "menuOptions.Auto Flip View", False },
2901 // { "menuOptions.ICS Alarm", False },
2902 { "menuOptions.Move Sound", False },
2903 { "menuOptions.Hide Thinking", False },
2904 { "menuOptions.Periodic Updates", False },
2905 { "menuOptions.Ponder Next Move", False },
2907 { "menuEngine.Hint", False },
2908 { "menuEngine.Book", False },
2912 Enables gnuEnables[] = {
2913 { "menuMode.ICS Client", False },
2914 { "menuView.ICStex", False },
2915 { "menuView.ICS Input Box", False },
2916 { "menuAction.Accept", False },
2917 { "menuAction.Decline", False },
2918 { "menuAction.Rematch", False },
2919 { "menuAction.Adjourn", False },
2920 { "menuAction.Stop Examining", False },
2921 { "menuAction.Stop Observing", False },
2922 { "menuAction.Upload to Examine", False },
2923 { "menuEdit.Revert", False },
2924 { "menuEdit.Annotate", False },
2925 { "menuOptions.ICS", False },
2927 /* The next two options rely on SetCmailMode being called *after* */
2928 /* SetGNUMode so that when GNU is being used to give hints these */
2929 /* menu options are still available */
2931 { "menuFile.Mail Move", False },
2932 { "menuFile.Reload CMail Message", False },
2933 // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2934 { "menuMode.Machine White", True },
2935 { "menuMode.Machine Black", True },
2936 { "menuMode.Analysis Mode", True },
2937 { "menuMode.Analyze File", True },
2938 { "menuMode.Two Machines", True },
2939 { "menuMode.Machine Match", True },
2940 { "menuEngine.Engine #1 Settings", True },
2941 { "menuEngine.Engine #2 Settings", True },
2942 { "menuEngine.Hint", True },
2943 { "menuEngine.Book", True },
2944 { "menuEngine.Move Now", True },
2945 { "menuEngine.Retract Move", True },
2950 Enables cmailEnables[] = {
2952 { "menuAction.Call Flag", False },
2953 { "menuAction.Draw", True },
2954 { "menuAction.Adjourn", False },
2955 { "menuAction.Abort", False },
2956 { "menuAction.Stop Observing", False },
2957 { "menuAction.Stop Examining", False },
2958 { "menuFile.Mail Move", True },
2959 { "menuFile.Reload CMail Message", True },
2963 Enables trainingOnEnables[] = {
2964 { "menuMode.Edit Comment", False },
2965 { "menuMode.Pause", False },
2966 { "menuEdit.Forward", False },
2967 { "menuEdit.Backward", False },
2968 { "menuEdit.Forward to End", False },
2969 { "menuEdit.Back to Start", False },
2970 { "menuEngine.Move Now", False },
2971 { "menuEdit.Truncate Game", False },
2975 Enables trainingOffEnables[] = {
2976 { "menuMode.Edit Comment", True },
2977 { "menuMode.Pause", True },
2978 { "menuEdit.Forward", True },
2979 { "menuEdit.Backward", True },
2980 { "menuEdit.Forward to End", True },
2981 { "menuEdit.Back to Start", True },
2982 { "menuEngine.Move Now", True },
2983 { "menuEdit.Truncate Game", True },
2987 Enables machineThinkingEnables[] = {
2988 { "menuFile.Load Game", False },
2989 // { "menuFile.Load Next Game", False },
2990 // { "menuFile.Load Previous Game", False },
2991 // { "menuFile.Reload Same Game", False },
2992 { "menuEdit.Paste Game", False },
2993 { "menuFile.Load Position", False },
2994 // { "menuFile.Load Next Position", False },
2995 // { "menuFile.Load Previous Position", False },
2996 // { "menuFile.Reload Same Position", False },
2997 { "menuEdit.Paste Position", False },
2998 { "menuMode.Machine White", False },
2999 { "menuMode.Machine Black", False },
3000 { "menuMode.Two Machines", False },
3001 // { "menuMode.Machine Match", False },
3002 { "menuEngine.Retract Move", False },
3006 Enables userThinkingEnables[] = {
3007 { "menuFile.Load Game", True },
3008 // { "menuFile.Load Next Game", True },
3009 // { "menuFile.Load Previous Game", True },
3010 // { "menuFile.Reload Same Game", True },
3011 { "menuEdit.Paste Game", True },
3012 { "menuFile.Load Position", True },
3013 // { "menuFile.Load Next Position", True },
3014 // { "menuFile.Load Previous Position", True },
3015 // { "menuFile.Reload Same Position", True },
3016 { "menuEdit.Paste Position", True },
3017 { "menuMode.Machine White", True },
3018 { "menuMode.Machine Black", True },
3019 { "menuMode.Two Machines", True },
3020 // { "menuMode.Machine Match", True },
3021 { "menuEngine.Retract Move", True },
3027 SetMenuEnables(icsEnables);
3030 if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
3031 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
3032 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
3040 SetMenuEnables(ncpEnables);
3046 SetMenuEnables(gnuEnables);
3052 SetMenuEnables(cmailEnables);
3058 SetMenuEnables(trainingOnEnables);
3059 if (appData.showButtonBar) {
3060 XtSetSensitive(buttonBarWidget, False);
3066 SetTrainingModeOff()
3068 SetMenuEnables(trainingOffEnables);
3069 if (appData.showButtonBar) {
3070 XtSetSensitive(buttonBarWidget, True);
3075 SetUserThinkingEnables()
3077 if (appData.noChessProgram) return;
3078 SetMenuEnables(userThinkingEnables);
3082 SetMachineThinkingEnables()
3084 if (appData.noChessProgram) return;
3085 SetMenuEnables(machineThinkingEnables);
3087 case MachinePlaysBlack:
3088 case MachinePlaysWhite:
3089 case TwoMachinesPlay:
3090 XtSetSensitive(XtNameToWidget(menuBarWidget,
3091 ModeToWidgetName(gameMode)), True);
3098 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3099 #define HISTORY_SIZE 64
3100 static char *history[HISTORY_SIZE];
3101 int histIn = 0, histP = 0;
3104 SaveInHistory(char *cmd)
3106 if (history[histIn] != NULL) {
3107 free(history[histIn]);
3108 history[histIn] = NULL;
3110 if (*cmd == NULLCHAR) return;
3111 history[histIn] = StrSave(cmd);
3112 histIn = (histIn + 1) % HISTORY_SIZE;
3113 if (history[histIn] != NULL) {
3114 free(history[histIn]);
3115 history[histIn] = NULL;
3121 PrevInHistory(char *cmd)
3124 if (histP == histIn) {
3125 if (history[histIn] != NULL) free(history[histIn]);
3126 history[histIn] = StrSave(cmd);
3128 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3129 if (newhp == histIn || history[newhp] == NULL) return NULL;
3131 return history[histP];
3137 if (histP == histIn) return NULL;
3138 histP = (histP + 1) % HISTORY_SIZE;
3139 return history[histP];
3141 // end of borrowed code
3143 #define Abs(n) ((n)<0 ? -(n) : (n))
3147 InsertPxlSize(pattern, targetPxlSize)
3151 char *base_fnt_lst, strInt[12], *p, *q;
3152 int alternatives, i, len, strIntLen;
3155 * Replace the "*" (if present) in the pixel-size slot of each
3156 * alternative with the targetPxlSize.
3160 while ((p = strchr(p, ',')) != NULL) {
3164 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
3165 strIntLen = strlen(strInt);
3166 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
3170 while (alternatives--) {
3171 char *comma = strchr(p, ',');
3172 for (i=0; i<14; i++) {
3173 char *hyphen = strchr(p, '-');
3175 if (comma && hyphen > comma) break;
3176 len = hyphen + 1 - p;
3177 if (i == 7 && *p == '*' && len == 2) {
3179 memcpy(q, strInt, strIntLen);
3189 len = comma + 1 - p;
3196 return base_fnt_lst;
3200 CreateFontSet(base_fnt_lst)
3204 char **missing_list;
3208 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
3209 &missing_list, &missing_count, &def_string);
3210 if (appData.debugMode) {
3212 XFontStruct **font_struct_list;
3213 char **font_name_list;
3214 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
3216 fprintf(debugFP, " got list %s, locale %s\n",
3217 XBaseFontNameListOfFontSet(fntSet),
3218 XLocaleOfFontSet(fntSet));
3219 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
3220 for (i = 0; i < count; i++) {
3221 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
3224 for (i = 0; i < missing_count; i++) {
3225 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
3228 if (fntSet == NULL) {
3229 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
3234 #else // not ENABLE_NLS
3236 * Find a font that matches "pattern" that is as close as
3237 * possible to the targetPxlSize. Prefer fonts that are k
3238 * pixels smaller to fonts that are k pixels larger. The
3239 * pattern must be in the X Consortium standard format,
3240 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3241 * The return value should be freed with XtFree when no
3245 FindFont(pattern, targetPxlSize)
3249 char **fonts, *p, *best, *scalable, *scalableTail;
3250 int i, j, nfonts, minerr, err, pxlSize;
3252 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3254 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3255 programName, pattern);
3262 for (i=0; i<nfonts; i++) {
3265 if (*p != '-') continue;
3267 if (*p == NULLCHAR) break;
3268 if (*p++ == '-') j++;
3270 if (j < 7) continue;
3273 scalable = fonts[i];
3276 err = pxlSize - targetPxlSize;
3277 if (Abs(err) < Abs(minerr) ||
3278 (minerr > 0 && err < 0 && -err == minerr)) {
3284 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3285 /* If the error is too big and there is a scalable font,
3286 use the scalable font. */
3287 int headlen = scalableTail - scalable;
3288 p = (char *) XtMalloc(strlen(scalable) + 10);
3289 while (isdigit(*scalableTail)) scalableTail++;
3290 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3292 p = (char *) XtMalloc(strlen(best) + 2);
3293 safeStrCpy(p, best, strlen(best)+1 );
3295 if (appData.debugMode) {
3296 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3297 pattern, targetPxlSize, p);
3299 XFreeFontNames(fonts);
3305 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3306 // must be called before all non-first callse to CreateGCs()
3307 XtReleaseGC(shellWidget, highlineGC);
3308 XtReleaseGC(shellWidget, lightSquareGC);
3309 XtReleaseGC(shellWidget, darkSquareGC);
3310 XtReleaseGC(shellWidget, lineGC);
3311 if (appData.monoMode) {
3312 if (DefaultDepth(xDisplay, xScreen) == 1) {
3313 XtReleaseGC(shellWidget, wbPieceGC);
3315 XtReleaseGC(shellWidget, bwPieceGC);
3318 XtReleaseGC(shellWidget, prelineGC);
3319 XtReleaseGC(shellWidget, jailSquareGC);
3320 XtReleaseGC(shellWidget, wdPieceGC);
3321 XtReleaseGC(shellWidget, wlPieceGC);
3322 XtReleaseGC(shellWidget, wjPieceGC);
3323 XtReleaseGC(shellWidget, bdPieceGC);
3324 XtReleaseGC(shellWidget, blPieceGC);
3325 XtReleaseGC(shellWidget, bjPieceGC);
3329 void CreateGCs(int redo)
3331 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3332 | GCBackground | GCFunction | GCPlaneMask;
3333 XGCValues gc_values;
3336 gc_values.plane_mask = AllPlanes;
3337 gc_values.line_width = lineGap;
3338 gc_values.line_style = LineSolid;
3339 gc_values.function = GXcopy;
3342 DeleteGCs(); // called a second time; clean up old GCs first
3343 } else { // [HGM] grid and font GCs created on first call only
3344 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3345 gc_values.background = XWhitePixel(xDisplay, xScreen);
3346 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3347 XSetFont(xDisplay, coordGC, coordFontID);
3349 // [HGM] make font for holdings counts (white on black)
3350 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3351 gc_values.background = XBlackPixel(xDisplay, xScreen);
3352 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3353 XSetFont(xDisplay, countGC, countFontID);
3355 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3356 gc_values.background = XBlackPixel(xDisplay, xScreen);
3357 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3359 if (appData.monoMode) {
3360 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3361 gc_values.background = XWhitePixel(xDisplay, xScreen);
3362 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3364 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3365 gc_values.background = XBlackPixel(xDisplay, xScreen);
3366 lightSquareGC = wbPieceGC
3367 = XtGetGC(shellWidget, value_mask, &gc_values);
3369 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3370 gc_values.background = XWhitePixel(xDisplay, xScreen);
3371 darkSquareGC = bwPieceGC
3372 = XtGetGC(shellWidget, value_mask, &gc_values);
3374 if (DefaultDepth(xDisplay, xScreen) == 1) {
3375 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3376 gc_values.function = GXcopyInverted;
3377 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3378 gc_values.function = GXcopy;
3379 if (XBlackPixel(xDisplay, xScreen) == 1) {
3380 bwPieceGC = darkSquareGC;
3381 wbPieceGC = copyInvertedGC;
3383 bwPieceGC = copyInvertedGC;
3384 wbPieceGC = lightSquareGC;
3388 gc_values.foreground = highlightSquareColor;
3389 gc_values.background = highlightSquareColor;
3390 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3392 gc_values.foreground = premoveHighlightColor;
3393 gc_values.background = premoveHighlightColor;
3394 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3396 gc_values.foreground = lightSquareColor;
3397 gc_values.background = darkSquareColor;
3398 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3400 gc_values.foreground = darkSquareColor;
3401 gc_values.background = lightSquareColor;
3402 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3404 gc_values.foreground = jailSquareColor;
3405 gc_values.background = jailSquareColor;
3406 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3408 gc_values.foreground = whitePieceColor;
3409 gc_values.background = darkSquareColor;
3410 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3412 gc_values.foreground = whitePieceColor;
3413 gc_values.background = lightSquareColor;
3414 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3416 gc_values.foreground = whitePieceColor;
3417 gc_values.background = jailSquareColor;
3418 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3420 gc_values.foreground = blackPieceColor;
3421 gc_values.background = darkSquareColor;
3422 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3424 gc_values.foreground = blackPieceColor;
3425 gc_values.background = lightSquareColor;
3426 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3428 gc_values.foreground = blackPieceColor;
3429 gc_values.background = jailSquareColor;
3430 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3434 void loadXIM(xim, xmask, filename, dest, mask)
3447 fp = fopen(filename, "rb");
3449 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3456 for (y=0; y<h; ++y) {
3457 for (x=0; x<h; ++x) {
3462 XPutPixel(xim, x, y, blackPieceColor);
3464 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3467 XPutPixel(xim, x, y, darkSquareColor);
3469 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3472 XPutPixel(xim, x, y, whitePieceColor);
3474 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3477 XPutPixel(xim, x, y, lightSquareColor);
3479 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3487 /* create Pixmap of piece */
3488 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3490 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3493 /* create Pixmap of clipmask
3494 Note: We assume the white/black pieces have the same
3495 outline, so we make only 6 masks. This is okay
3496 since the XPM clipmask routines do the same. */
3498 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3500 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3503 /* now create the 1-bit version */
3504 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3507 values.foreground = 1;
3508 values.background = 0;
3510 /* Don't use XtGetGC, not read only */
3511 maskGC = XCreateGC(xDisplay, *mask,
3512 GCForeground | GCBackground, &values);
3513 XCopyPlane(xDisplay, temp, *mask, maskGC,
3514 0, 0, squareSize, squareSize, 0, 0, 1);
3515 XFreePixmap(xDisplay, temp);
3520 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3522 void CreateXIMPieces()
3527 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3532 /* The XSynchronize calls were copied from CreatePieces.
3533 Not sure if needed, but can't hurt */
3534 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3537 /* temp needed by loadXIM() */
3538 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3539 0, 0, ss, ss, AllPlanes, XYPixmap);
3541 if (strlen(appData.pixmapDirectory) == 0) {
3545 if (appData.monoMode) {
3546 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3550 fprintf(stderr, _("\nLoading XIMs...\n"));
3552 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3553 fprintf(stderr, "%d", piece+1);
3554 for (kind=0; kind<4; kind++) {
3555 fprintf(stderr, ".");
3556 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3557 ExpandPathName(appData.pixmapDirectory),
3558 piece <= (int) WhiteKing ? "" : "w",
3559 pieceBitmapNames[piece],
3561 ximPieceBitmap[kind][piece] =
3562 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3563 0, 0, ss, ss, AllPlanes, XYPixmap);
3564 if (appData.debugMode)
3565 fprintf(stderr, _("(File:%s:) "), buf);
3566 loadXIM(ximPieceBitmap[kind][piece],
3568 &(xpmPieceBitmap2[kind][piece]),
3569 &(ximMaskPm2[piece]));
3570 if(piece <= (int)WhiteKing)
3571 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3573 fprintf(stderr," ");
3575 /* Load light and dark squares */
3576 /* If the LSQ and DSQ pieces don't exist, we will
3577 draw them with solid squares. */
3578 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3579 if (access(buf, 0) != 0) {
3583 fprintf(stderr, _("light square "));
3585 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3586 0, 0, ss, ss, AllPlanes, XYPixmap);
3587 if (appData.debugMode)
3588 fprintf(stderr, _("(File:%s:) "), buf);
3590 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3591 fprintf(stderr, _("dark square "));
3592 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3593 ExpandPathName(appData.pixmapDirectory), ss);
3594 if (appData.debugMode)
3595 fprintf(stderr, _("(File:%s:) "), buf);
3597 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3598 0, 0, ss, ss, AllPlanes, XYPixmap);
3599 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3600 xpmJailSquare = xpmLightSquare;
3602 fprintf(stderr, _("Done.\n"));
3604 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3607 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3610 void CreateXPMBoard(char *s, int kind)
3614 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3615 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3616 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3620 void FreeXPMPieces()
3621 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3622 // thisroutine has to be called t free the old piece pixmaps
3624 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3625 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3627 XFreePixmap(xDisplay, xpmLightSquare);
3628 XFreePixmap(xDisplay, xpmDarkSquare);
3632 void CreateXPMPieces()
3636 u_int ss = squareSize;
3638 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3639 XpmColorSymbol symbols[4];
3640 static int redo = False;
3642 if(redo) FreeXPMPieces(); else redo = 1;
3644 /* The XSynchronize calls were copied from CreatePieces.
3645 Not sure if needed, but can't hurt */
3646 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3648 /* Setup translations so piece colors match square colors */
3649 symbols[0].name = "light_piece";
3650 symbols[0].value = appData.whitePieceColor;
3651 symbols[1].name = "dark_piece";
3652 symbols[1].value = appData.blackPieceColor;
3653 symbols[2].name = "light_square";
3654 symbols[2].value = appData.lightSquareColor;
3655 symbols[3].name = "dark_square";
3656 symbols[3].value = appData.darkSquareColor;
3658 attr.valuemask = XpmColorSymbols;
3659 attr.colorsymbols = symbols;
3660 attr.numsymbols = 4;
3662 if (appData.monoMode) {
3663 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3667 if (strlen(appData.pixmapDirectory) == 0) {
3668 XpmPieces* pieces = builtInXpms;
3671 while (pieces->size != squareSize && pieces->size) pieces++;
3672 if (!pieces->size) {
3673 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3676 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3677 for (kind=0; kind<4; kind++) {
3679 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3680 pieces->xpm[piece][kind],
3681 &(xpmPieceBitmap2[kind][piece]),
3682 NULL, &attr)) != 0) {
3683 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3687 if(piece <= (int) WhiteKing)
3688 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3692 xpmJailSquare = xpmLightSquare;
3696 fprintf(stderr, _("\nLoading XPMs...\n"));
3699 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3700 fprintf(stderr, "%d ", piece+1);
3701 for (kind=0; kind<4; kind++) {
3702 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3703 ExpandPathName(appData.pixmapDirectory),
3704 piece > (int) WhiteKing ? "w" : "",
3705 pieceBitmapNames[piece],
3707 if (appData.debugMode) {
3708 fprintf(stderr, _("(File:%s:) "), buf);
3710 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3711 &(xpmPieceBitmap2[kind][piece]),
3712 NULL, &attr)) != 0) {
3713 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3714 // [HGM] missing: read of unorthodox piece failed; substitute King.
3715 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3716 ExpandPathName(appData.pixmapDirectory),
3718 if (appData.debugMode) {
3719 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3721 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3722 &(xpmPieceBitmap2[kind][piece]),
3726 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3731 if(piece <= (int) WhiteKing)
3732 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3735 /* Load light and dark squares */
3736 /* If the LSQ and DSQ pieces don't exist, we will
3737 draw them with solid squares. */
3738 fprintf(stderr, _("light square "));
3739 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3740 if (access(buf, 0) != 0) {
3744 if (appData.debugMode)
3745 fprintf(stderr, _("(File:%s:) "), buf);
3747 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3748 &xpmLightSquare, NULL, &attr)) != 0) {
3749 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3752 fprintf(stderr, _("dark square "));
3753 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3754 ExpandPathName(appData.pixmapDirectory), ss);
3755 if (appData.debugMode) {
3756 fprintf(stderr, _("(File:%s:) "), buf);
3758 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3759 &xpmDarkSquare, NULL, &attr)) != 0) {
3760 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3764 xpmJailSquare = xpmLightSquare;
3765 fprintf(stderr, _("Done.\n"));
3767 oldVariant = -1; // kludge to force re-makig of animation masks
3768 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3771 #endif /* HAVE_LIBXPM */
3774 /* No built-in bitmaps */
3779 u_int ss = squareSize;
3781 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3784 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3785 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3786 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3787 pieceBitmapNames[piece],
3788 ss, kind == SOLID ? 's' : 'o');
3789 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3790 if(piece <= (int)WhiteKing)
3791 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3795 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3799 /* With built-in bitmaps */
3802 BuiltInBits* bib = builtInBits;
3805 u_int ss = squareSize;
3807 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3810 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3812 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3813 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3814 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3815 pieceBitmapNames[piece],
3816 ss, kind == SOLID ? 's' : 'o');
3817 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3818 bib->bits[kind][piece], ss, ss);
3819 if(piece <= (int)WhiteKing)
3820 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3824 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3829 void ReadBitmap(pm, name, bits, wreq, hreq)
3832 unsigned char bits[];
3838 char msg[MSG_SIZ], fullname[MSG_SIZ];
3840 if (*appData.bitmapDirectory != NULLCHAR) {
3841 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3842 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3843 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3844 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3845 &w, &h, pm, &x_hot, &y_hot);
3846 fprintf(stderr, "load %s\n", name);
3847 if (errcode != BitmapSuccess) {
3849 case BitmapOpenFailed:
3850 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3852 case BitmapFileInvalid:
3853 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3855 case BitmapNoMemory:
3856 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3860 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3864 fprintf(stderr, _("%s: %s...using built-in\n"),
3866 } else if (w != wreq || h != hreq) {
3868 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3869 programName, fullname, w, h, wreq, hreq);
3875 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3884 if (lineGap == 0) return;
3886 /* [HR] Split this into 2 loops for non-square boards. */
3888 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3889 gridSegments[i].x1 = 0;
3890 gridSegments[i].x2 =
3891 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3892 gridSegments[i].y1 = gridSegments[i].y2
3893 = lineGap / 2 + (i * (squareSize + lineGap));
3896 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3897 gridSegments[j + i].y1 = 0;
3898 gridSegments[j + i].y2 =
3899 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3900 gridSegments[j + i].x1 = gridSegments[j + i].x2
3901 = lineGap / 2 + (j * (squareSize + lineGap));
3905 static void MenuBarSelect(w, addr, index)
3910 XtActionProc proc = (XtActionProc) addr;
3912 (proc)(NULL, NULL, NULL, NULL);
3915 void CreateMenuBarPopup(parent, name, mb)
3925 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3928 XtSetArg(args[j], XtNleftMargin, 20); j++;
3929 XtSetArg(args[j], XtNrightMargin, 20); j++;
3931 while (mi->string != NULL) {
3932 if (strcmp(mi->string, "----") == 0) {
3933 entry = XtCreateManagedWidget(_(mi->string), smeLineObjectClass,
3936 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3937 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3939 XtAddCallback(entry, XtNcallback,
3940 (XtCallbackProc) MenuBarSelect,
3941 (caddr_t) mi->proc);
3947 Widget CreateMenuBar(mb)
3951 Widget anchor, menuBar;
3953 char menuName[MSG_SIZ];
3956 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3957 XtSetArg(args[j], XtNvSpace, 0); j++;
3958 XtSetArg(args[j], XtNborderWidth, 0); j++;
3959 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3960 formWidget, args, j);
3962 while (mb->name != NULL) {
3963 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3964 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3966 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3969 shortName[0] = mb->name[0];
3970 shortName[1] = NULLCHAR;
3971 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3974 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3977 XtSetArg(args[j], XtNborderWidth, 0); j++;
3978 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3980 CreateMenuBarPopup(menuBar, menuName, mb);
3986 Widget CreateButtonBar(mi)
3990 Widget button, buttonBar;
3994 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3996 XtSetArg(args[j], XtNhSpace, 0); j++;
3998 XtSetArg(args[j], XtNborderWidth, 0); j++;
3999 XtSetArg(args[j], XtNvSpace, 0); j++;
4000 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
4001 formWidget, args, j);
4003 while (mi->string != NULL) {
4006 XtSetArg(args[j], XtNinternalWidth, 2); j++;
4007 XtSetArg(args[j], XtNborderWidth, 0); j++;
4009 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
4010 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
4011 buttonBar, args, j);
4012 XtAddCallback(button, XtNcallback,
4013 (XtCallbackProc) MenuBarSelect,
4014 (caddr_t) mi->proc);
4021 CreatePieceMenu(name, color)
4028 ChessSquare selection;
4030 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
4031 boardWidget, args, 0);
4033 for (i = 0; i < PIECE_MENU_SIZE; i++) {
4034 String item = pieceMenuStrings[color][i];
4036 if (strcmp(item, "----") == 0) {
4037 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4040 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4041 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4043 selection = pieceMenuTranslation[color][i];
4044 XtAddCallback(entry, XtNcallback,
4045 (XtCallbackProc) PieceMenuSelect,
4046 (caddr_t) selection);
4047 if (selection == WhitePawn || selection == BlackPawn) {
4048 XtSetArg(args[0], XtNpopupOnEntry, entry);
4049 XtSetValues(menu, args, 1);
4062 ChessSquare selection;
4064 whitePieceMenu = CreatePieceMenu("menuW", 0);
4065 blackPieceMenu = CreatePieceMenu("menuB", 1);
4067 XtRegisterGrabAction(PieceMenuPopup, True,
4068 (unsigned)(ButtonPressMask|ButtonReleaseMask),
4069 GrabModeAsync, GrabModeAsync);
4071 XtSetArg(args[0], XtNlabel, _("Drop"));
4072 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
4073 boardWidget, args, 1);
4074 for (i = 0; i < DROP_MENU_SIZE; i++) {
4075 String item = dropMenuStrings[i];
4077 if (strcmp(item, "----") == 0) {
4078 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4081 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4082 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4084 selection = dropMenuTranslation[i];
4085 XtAddCallback(entry, XtNcallback,
4086 (XtCallbackProc) DropMenuSelect,
4087 (caddr_t) selection);
4092 void SetupDropMenu()
4100 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
4101 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
4102 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
4103 dmEnables[i].piece);
4104 XtSetSensitive(entry, p != NULL || !appData.testLegality
4105 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
4106 && !appData.icsActive));
4108 while (p && *p++ == dmEnables[i].piece) count++;
4109 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
4111 XtSetArg(args[j], XtNlabel, label); j++;
4112 XtSetValues(entry, args, j);
4116 void PieceMenuPopup(w, event, params, num_params)
4120 Cardinal *num_params;
4122 String whichMenu; int menuNr = -2;
4123 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
4124 if (event->type == ButtonRelease)
4125 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4126 else if (event->type == ButtonPress)
4127 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4129 case 0: whichMenu = params[0]; break;
4130 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
4132 case -1: if (errorUp) ErrorPopDown();
4135 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
4138 static void PieceMenuSelect(w, piece, junk)
4143 if (pmFromX < 0 || pmFromY < 0) return;
4144 EditPositionMenuEvent(piece, pmFromX, pmFromY);
4147 static void DropMenuSelect(w, piece, junk)
4152 if (pmFromX < 0 || pmFromY < 0) return;
4153 DropMenuEvent(piece, pmFromX, pmFromY);
4156 void WhiteClock(w, event, prms, nprms)
4162 shiftKey = prms[0][0] & 1;
4166 void BlackClock(w, event, prms, nprms)
4172 shiftKey = prms[0][0] & 1;
4178 * If the user selects on a border boundary, return -1; if off the board,
4179 * return -2. Otherwise map the event coordinate to the square.
4181 int EventToSquare(x, limit)
4189 if ((x % (squareSize + lineGap)) >= squareSize)
4191 x /= (squareSize + lineGap);
4197 static void do_flash_delay(msec)
4203 static void drawHighlight(file, rank, gc)
4209 if (lineGap == 0) return;
4212 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4213 (squareSize + lineGap);
4214 y = lineGap/2 + rank * (squareSize + lineGap);
4216 x = lineGap/2 + file * (squareSize + lineGap);
4217 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4218 (squareSize + lineGap);
4221 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4222 squareSize+lineGap, squareSize+lineGap);
4225 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4226 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4229 SetHighlights(fromX, fromY, toX, toY)
4230 int fromX, fromY, toX, toY;
4232 if (hi1X != fromX || hi1Y != fromY) {
4233 if (hi1X >= 0 && hi1Y >= 0) {
4234 drawHighlight(hi1X, hi1Y, lineGC);
4236 } // [HGM] first erase both, then draw new!
4237 if (hi2X != toX || hi2Y != toY) {
4238 if (hi2X >= 0 && hi2Y >= 0) {
4239 drawHighlight(hi2X, hi2Y, lineGC);
4242 if (hi1X != fromX || hi1Y != fromY) {
4243 if (fromX >= 0 && fromY >= 0) {
4244 drawHighlight(fromX, fromY, highlineGC);
4247 if (hi2X != toX || hi2Y != toY) {
4248 if (toX >= 0 && toY >= 0) {
4249 drawHighlight(toX, toY, highlineGC);
4261 SetHighlights(-1, -1, -1, -1);
4266 SetPremoveHighlights(fromX, fromY, toX, toY)
4267 int fromX, fromY, toX, toY;
4269 if (pm1X != fromX || pm1Y != fromY) {
4270 if (pm1X >= 0 && pm1Y >= 0) {
4271 drawHighlight(pm1X, pm1Y, lineGC);
4273 if (fromX >= 0 && fromY >= 0) {
4274 drawHighlight(fromX, fromY, prelineGC);
4277 if (pm2X != toX || pm2Y != toY) {
4278 if (pm2X >= 0 && pm2Y >= 0) {
4279 drawHighlight(pm2X, pm2Y, lineGC);
4281 if (toX >= 0 && toY >= 0) {
4282 drawHighlight(toX, toY, prelineGC);
4292 ClearPremoveHighlights()
4294 SetPremoveHighlights(-1, -1, -1, -1);
4297 static int CutOutSquare(x, y, x0, y0, kind)
4298 int x, y, *x0, *y0, kind;
4300 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4301 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4303 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4304 if(textureW[kind] < W*squareSize)
4305 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4307 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4308 if(textureH[kind] < H*squareSize)
4309 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4311 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4315 static void BlankSquare(x, y, color, piece, dest, fac)
4316 int x, y, color, fac;
4319 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4321 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4322 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4323 squareSize, squareSize, x*fac, y*fac);
4325 if (useImages && useImageSqs) {
4329 pm = xpmLightSquare;
4334 case 2: /* neutral */
4339 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4340 squareSize, squareSize, x*fac, y*fac);
4350 case 2: /* neutral */
4355 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4360 I split out the routines to draw a piece so that I could
4361 make a generic flash routine.
4363 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4365 int square_color, x, y;
4368 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4369 switch (square_color) {
4371 case 2: /* neutral */
4373 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4374 ? *pieceToOutline(piece)
4375 : *pieceToSolid(piece),
4376 dest, bwPieceGC, 0, 0,
4377 squareSize, squareSize, x, y);
4380 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4381 ? *pieceToSolid(piece)
4382 : *pieceToOutline(piece),
4383 dest, wbPieceGC, 0, 0,
4384 squareSize, squareSize, x, y);
4389 static void monoDrawPiece(piece, square_color, x, y, dest)
4391 int square_color, x, y;
4394 switch (square_color) {
4396 case 2: /* neutral */
4398 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4399 ? *pieceToOutline(piece)
4400 : *pieceToSolid(piece),
4401 dest, bwPieceGC, 0, 0,
4402 squareSize, squareSize, x, y, 1);
4405 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4406 ? *pieceToSolid(piece)
4407 : *pieceToOutline(piece),
4408 dest, wbPieceGC, 0, 0,
4409 squareSize, squareSize, x, y, 1);
4414 static void colorDrawPiece(piece, square_color, x, y, dest)
4416 int square_color, x, y;
4419 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4420 switch (square_color) {
4422 XCopyPlane(xDisplay, *pieceToSolid(piece),
4423 dest, (int) piece < (int) BlackPawn
4424 ? wlPieceGC : blPieceGC, 0, 0,
4425 squareSize, squareSize, x, y, 1);
4428 XCopyPlane(xDisplay, *pieceToSolid(piece),
4429 dest, (int) piece < (int) BlackPawn
4430 ? wdPieceGC : bdPieceGC, 0, 0,
4431 squareSize, squareSize, x, y, 1);
4433 case 2: /* neutral */
4435 XCopyPlane(xDisplay, *pieceToSolid(piece),
4436 dest, (int) piece < (int) BlackPawn
4437 ? wjPieceGC : bjPieceGC, 0, 0,
4438 squareSize, squareSize, x, y, 1);
4443 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4445 int square_color, x, y;
4448 int kind, p = piece;
4450 switch (square_color) {
4452 case 2: /* neutral */
4454 if ((int)piece < (int) BlackPawn) {
4462 if ((int)piece < (int) BlackPawn) {
4470 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4471 if(useTexture & square_color+1) {
4472 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4473 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4474 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4475 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4476 XSetClipMask(xDisplay, wlPieceGC, None);
4477 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4479 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4480 dest, wlPieceGC, 0, 0,
4481 squareSize, squareSize, x, y);
4484 typedef void (*DrawFunc)();
4486 DrawFunc ChooseDrawFunc()
4488 if (appData.monoMode) {
4489 if (DefaultDepth(xDisplay, xScreen) == 1) {
4490 return monoDrawPiece_1bit;
4492 return monoDrawPiece;
4496 return colorDrawPieceImage;
4498 return colorDrawPiece;
4502 /* [HR] determine square color depending on chess variant. */
4503 static int SquareColor(row, column)
4508 if (gameInfo.variant == VariantXiangqi) {
4509 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4511 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4513 } else if (row <= 4) {
4519 square_color = ((column + row) % 2) == 1;
4522 /* [hgm] holdings: next line makes all holdings squares light */
4523 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4525 return square_color;
4528 void DrawSquare(row, column, piece, do_flash)
4529 int row, column, do_flash;
4532 int square_color, x, y, direction, font_ascent, font_descent;
4535 XCharStruct overall;
4539 /* Calculate delay in milliseconds (2-delays per complete flash) */
4540 flash_delay = 500 / appData.flashRate;
4543 x = lineGap + ((BOARD_WIDTH-1)-column) *
4544 (squareSize + lineGap);
4545 y = lineGap + row * (squareSize + lineGap);
4547 x = lineGap + column * (squareSize + lineGap);
4548 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4549 (squareSize + lineGap);
4552 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4554 square_color = SquareColor(row, column);
4556 if ( // [HGM] holdings: blank out area between board and holdings
4557 column == BOARD_LEFT-1 || column == BOARD_RGHT
4558 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4559 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4560 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4562 // [HGM] print piece counts next to holdings
4563 string[1] = NULLCHAR;
4564 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4565 string[0] = '0' + piece;
4566 XTextExtents(countFontStruct, string, 1, &direction,
4567 &font_ascent, &font_descent, &overall);
4568 if (appData.monoMode) {
4569 XDrawImageString(xDisplay, xBoardWindow, countGC,
4570 x + squareSize - overall.width - 2,
4571 y + font_ascent + 1, string, 1);
4573 XDrawString(xDisplay, xBoardWindow, countGC,
4574 x + squareSize - overall.width - 2,
4575 y + font_ascent + 1, string, 1);
4578 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4579 string[0] = '0' + piece;
4580 XTextExtents(countFontStruct, string, 1, &direction,
4581 &font_ascent, &font_descent, &overall);
4582 if (appData.monoMode) {
4583 XDrawImageString(xDisplay, xBoardWindow, countGC,
4584 x + 2, y + font_ascent + 1, string, 1);
4586 XDrawString(xDisplay, xBoardWindow, countGC,
4587 x + 2, y + font_ascent + 1, string, 1);
4591 if (piece == EmptySquare || appData.blindfold) {
4592 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4594 drawfunc = ChooseDrawFunc();
4596 if (do_flash && appData.flashCount > 0) {
4597 for (i=0; i<appData.flashCount; ++i) {
4598 drawfunc(piece, square_color, x, y, xBoardWindow);
4599 XSync(xDisplay, False);
4600 do_flash_delay(flash_delay);
4602 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4603 XSync(xDisplay, False);
4604 do_flash_delay(flash_delay);
4607 drawfunc(piece, square_color, x, y, xBoardWindow);
4611 string[1] = NULLCHAR;
4612 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4613 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4614 string[0] = 'a' + column - BOARD_LEFT;
4615 XTextExtents(coordFontStruct, string, 1, &direction,
4616 &font_ascent, &font_descent, &overall);
4617 if (appData.monoMode) {
4618 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4619 x + squareSize - overall.width - 2,
4620 y + squareSize - font_descent - 1, string, 1);
4622 XDrawString(xDisplay, xBoardWindow, coordGC,
4623 x + squareSize - overall.width - 2,
4624 y + squareSize - font_descent - 1, string, 1);
4627 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4628 string[0] = ONE + row;
4629 XTextExtents(coordFontStruct, string, 1, &direction,
4630 &font_ascent, &font_descent, &overall);
4631 if (appData.monoMode) {
4632 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4633 x + 2, y + font_ascent + 1, string, 1);
4635 XDrawString(xDisplay, xBoardWindow, coordGC,
4636 x + 2, y + font_ascent + 1, string, 1);
4639 if(!partnerUp && marker[row][column]) {
4640 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4641 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4646 /* Why is this needed on some versions of X? */
4647 void EventProc(widget, unused, event)
4652 if (!XtIsRealized(widget))
4655 switch (event->type) {
4657 if (event->xexpose.count > 0) return; /* no clipping is done */
4658 XDrawPosition(widget, True, NULL);
4659 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4660 flipView = !flipView; partnerUp = !partnerUp;
4661 XDrawPosition(widget, True, NULL);
4662 flipView = !flipView; partnerUp = !partnerUp;
4666 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4673 void DrawPosition(fullRedraw, board)
4674 /*Boolean*/int fullRedraw;
4677 XDrawPosition(boardWidget, fullRedraw, board);
4680 /* Returns 1 if there are "too many" differences between b1 and b2
4681 (i.e. more than 1 move was made) */
4682 static int too_many_diffs(b1, b2)
4688 for (i=0; i<BOARD_HEIGHT; ++i) {
4689 for (j=0; j<BOARD_WIDTH; ++j) {
4690 if (b1[i][j] != b2[i][j]) {
4691 if (++c > 4) /* Castling causes 4 diffs */
4699 /* Matrix describing castling maneuvers */
4700 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4701 static int castling_matrix[4][5] = {
4702 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4703 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4704 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4705 { 7, 7, 4, 5, 6 } /* 0-0, black */
4708 /* Checks whether castling occurred. If it did, *rrow and *rcol
4709 are set to the destination (row,col) of the rook that moved.
4711 Returns 1 if castling occurred, 0 if not.
4713 Note: Only handles a max of 1 castling move, so be sure
4714 to call too_many_diffs() first.
4716 static int check_castle_draw(newb, oldb, rrow, rcol)
4723 /* For each type of castling... */
4724 for (i=0; i<4; ++i) {
4725 r = castling_matrix[i];
4727 /* Check the 4 squares involved in the castling move */
4729 for (j=1; j<=4; ++j) {
4730 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4737 /* All 4 changed, so it must be a castling move */
4746 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4747 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4749 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4752 void DrawSeekBackground( int left, int top, int right, int bottom )
4754 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4757 void DrawSeekText(char *buf, int x, int y)
4759 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4762 void DrawSeekDot(int x, int y, int colorNr)
4764 int square = colorNr & 0x80;
4767 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4769 XFillRectangle(xDisplay, xBoardWindow, color,
4770 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4772 XFillArc(xDisplay, xBoardWindow, color,
4773 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4776 static int damage[2][BOARD_RANKS][BOARD_FILES];
4779 * event handler for redrawing the board
4781 void XDrawPosition(w, repaint, board)
4783 /*Boolean*/int repaint;
4787 static int lastFlipView = 0;
4788 static int lastBoardValid[2] = {0, 0};
4789 static Board lastBoard[2];
4792 int nr = twoBoards*partnerUp;
4794 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4796 if (board == NULL) {
4797 if (!lastBoardValid[nr]) return;
4798 board = lastBoard[nr];
4800 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4801 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4802 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4807 * It would be simpler to clear the window with XClearWindow()
4808 * but this causes a very distracting flicker.
4811 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4813 if ( lineGap && IsDrawArrowEnabled())
4814 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4815 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4817 /* If too much changes (begin observing new game, etc.), don't
4819 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4821 /* Special check for castling so we don't flash both the king
4822 and the rook (just flash the king). */
4824 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4825 /* Draw rook with NO flashing. King will be drawn flashing later */
4826 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4827 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4831 /* First pass -- Draw (newly) empty squares and repair damage.
4832 This prevents you from having a piece show up twice while it
4833 is flashing on its new square */
4834 for (i = 0; i < BOARD_HEIGHT; i++)
4835 for (j = 0; j < BOARD_WIDTH; j++)
4836 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4837 || damage[nr][i][j]) {
4838 DrawSquare(i, j, board[i][j], 0);
4839 damage[nr][i][j] = False;
4842 /* Second pass -- Draw piece(s) in new position and flash them */
4843 for (i = 0; i < BOARD_HEIGHT; i++)
4844 for (j = 0; j < BOARD_WIDTH; j++)
4845 if (board[i][j] != lastBoard[nr][i][j]) {
4846 DrawSquare(i, j, board[i][j], do_flash);
4850 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4851 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4852 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4854 for (i = 0; i < BOARD_HEIGHT; i++)
4855 for (j = 0; j < BOARD_WIDTH; j++) {
4856 DrawSquare(i, j, board[i][j], 0);
4857 damage[nr][i][j] = False;
4861 CopyBoard(lastBoard[nr], board);
4862 lastBoardValid[nr] = 1;
4863 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4864 lastFlipView = flipView;
4866 /* Draw highlights */
4867 if (pm1X >= 0 && pm1Y >= 0) {
4868 drawHighlight(pm1X, pm1Y, prelineGC);
4870 if (pm2X >= 0 && pm2Y >= 0) {
4871 drawHighlight(pm2X, pm2Y, prelineGC);
4873 if (hi1X >= 0 && hi1Y >= 0) {
4874 drawHighlight(hi1X, hi1Y, highlineGC);
4876 if (hi2X >= 0 && hi2Y >= 0) {
4877 drawHighlight(hi2X, hi2Y, highlineGC);
4879 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4881 /* If piece being dragged around board, must redraw that too */
4884 XSync(xDisplay, False);
4889 * event handler for redrawing the board
4891 void DrawPositionProc(w, event, prms, nprms)
4897 XDrawPosition(w, True, NULL);
4902 * event handler for parsing user moves
4904 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4905 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4906 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4907 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4908 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4909 // and at the end FinishMove() to perform the move after optional promotion popups.
4910 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4911 void HandleUserMove(w, event, prms, nprms)
4917 if (w != boardWidget || errorExitStatus != -1) return;
4918 if(nprms) shiftKey = !strcmp(prms[0], "1");
4921 if (event->type == ButtonPress) {
4922 XtPopdown(promotionShell);
4923 XtDestroyWidget(promotionShell);
4924 promotionUp = False;
4932 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4933 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4934 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4937 void AnimateUserMove (Widget w, XEvent * event,
4938 String * params, Cardinal * nParams)
4940 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4941 DragPieceMove(event->xmotion.x, event->xmotion.y);
4944 void HandlePV (Widget w, XEvent * event,
4945 String * params, Cardinal * nParams)
4946 { // [HGM] pv: walk PV
4947 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4950 static int savedIndex; /* gross that this is global */
4952 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4955 XawTextPosition index, dummy;
4958 XawTextGetSelectionPos(w, &index, &dummy);
4959 XtSetArg(arg, XtNstring, &val);
4960 XtGetValues(w, &arg, 1);
4961 ReplaceComment(savedIndex, val);
4962 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4963 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4966 void EditCommentPopUp(index, title, text)
4971 if (text == NULL) text = "";
4972 NewCommentPopup(title, text, index);
4975 void ICSInputBoxPopUp()
4980 extern Option boxOptions[];
4982 void ICSInputSendText()
4989 edit = boxOptions[0].handle;
4991 XtSetArg(args[j], XtNstring, &val); j++;
4992 XtGetValues(edit, args, j);
4994 SendMultiLineToICS(val);
4995 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4996 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4999 void ICSInputBoxPopDown()
5004 void CommentPopUp(title, text)
5007 savedIndex = currentMove; // [HGM] vari
5008 NewCommentPopup(title, text, currentMove);
5011 void CommentPopDown()
5016 static char *openName;
5021 (void) (*fileProc)(openFP, 0, openName);
5024 void FileNamePopUp(label, def, filter, proc, openMode)
5031 fileProc = proc; /* I can't see a way not */
5032 fileOpenMode = openMode; /* to use globals here */
5033 { // [HGM] use file-selector dialog stolen from Ghostview
5034 int index; // this is not supported yet
5035 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
5036 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
5037 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
5038 ScheduleDelayedEvent(&DelayedLoad, 50);
5042 void FileNamePopDown()
5044 if (!filenameUp) return;
5045 XtPopdown(fileNameShell);
5046 XtDestroyWidget(fileNameShell);
5051 void FileNameCallback(w, client_data, call_data)
5053 XtPointer client_data, call_data;
5058 XtSetArg(args[0], XtNlabel, &name);
5059 XtGetValues(w, args, 1);
5061 if (strcmp(name, _("cancel")) == 0) {
5066 FileNameAction(w, NULL, NULL, NULL);
5069 void FileNameAction(w, event, prms, nprms)
5081 name = XawDialogGetValueString(w = XtParent(w));
5083 if ((name != NULL) && (*name != NULLCHAR)) {
5084 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5085 XtPopdown(w = XtParent(XtParent(w)));
5089 p = strrchr(buf, ' ');
5096 fullname = ExpandPathName(buf);
5098 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5101 f = fopen(fullname, fileOpenMode);
5103 DisplayError(_("Failed to open file"), errno);
5105 (void) (*fileProc)(f, index, buf);
5112 XtPopdown(w = XtParent(XtParent(w)));
5118 void PromotionPopUp()
5121 Widget dialog, layout;
5123 Dimension bw_width, pw_width;
5127 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5128 XtGetValues(boardWidget, args, j);
5131 XtSetArg(args[j], XtNresizable, True); j++;
5132 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5134 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5135 shellWidget, args, j);
5137 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5138 layoutArgs, XtNumber(layoutArgs));
5141 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5142 XtSetArg(args[j], XtNborderWidth, 0); j++;
5143 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5146 if(gameInfo.variant != VariantShogi) {
5147 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5148 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback,
5149 (XtPointer) dialog);
5150 XawDialogAddButton(dialog, _("General"), PromotionCallback,
5151 (XtPointer) dialog);
5152 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback,
5153 (XtPointer) dialog);
5154 XawDialogAddButton(dialog, _("Captain"), PromotionCallback,
5155 (XtPointer) dialog);
5157 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
5158 (XtPointer) dialog);
5159 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
5160 (XtPointer) dialog);
5161 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
5162 (XtPointer) dialog);
5163 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
5164 (XtPointer) dialog);
5166 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5167 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
5168 gameInfo.variant == VariantGiveaway) {
5169 XawDialogAddButton(dialog, _("King"), PromotionCallback,
5170 (XtPointer) dialog);
5172 if(gameInfo.variant == VariantCapablanca ||
5173 gameInfo.variant == VariantGothic ||
5174 gameInfo.variant == VariantCapaRandom) {
5175 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5176 (XtPointer) dialog);
5177 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5178 (XtPointer) dialog);
5180 } else // [HGM] shogi
5182 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5183 (XtPointer) dialog);
5184 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5185 (XtPointer) dialog);
5187 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5188 (XtPointer) dialog);
5190 XtRealizeWidget(promotionShell);
5191 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5194 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5195 XtGetValues(promotionShell, args, j);
5197 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5198 lineGap + squareSize/3 +
5199 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5200 0 : 6*(squareSize + lineGap)), &x, &y);
5203 XtSetArg(args[j], XtNx, x); j++;
5204 XtSetArg(args[j], XtNy, y); j++;
5205 XtSetValues(promotionShell, args, j);
5207 XtPopup(promotionShell, XtGrabNone);
5212 void PromotionPopDown()
5214 if (!promotionUp) return;
5215 XtPopdown(promotionShell);
5216 XtDestroyWidget(promotionShell);
5217 promotionUp = False;
5220 void PromotionCallback(w, client_data, call_data)
5222 XtPointer client_data, call_data;
5228 XtSetArg(args[0], XtNlabel, &name);
5229 XtGetValues(w, args, 1);
5233 if (fromX == -1) return;
5235 if (strcmp(name, _("cancel")) == 0) {
5239 } else if (strcmp(name, _("Knight")) == 0) {
5241 } else if (strcmp(name, _("Promote")) == 0) {
5243 } else if (strcmp(name, _("Defer")) == 0) {
5246 promoChar = ToLower(name[0]);
5249 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5251 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5252 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5257 void ErrorCallback(w, client_data, call_data)
5259 XtPointer client_data, call_data;
5262 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5264 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5270 if (!errorUp) return;
5272 XtPopdown(errorShell);
5273 XtDestroyWidget(errorShell);
5274 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5277 void ErrorPopUp(title, label, modal)
5278 char *title, *label;
5282 Widget dialog, layout;
5286 Dimension bw_width, pw_width;
5287 Dimension pw_height;
5291 XtSetArg(args[i], XtNresizable, True); i++;
5292 XtSetArg(args[i], XtNtitle, title); i++;
5294 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5295 shellWidget, args, i);
5297 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5298 layoutArgs, XtNumber(layoutArgs));
5301 XtSetArg(args[i], XtNlabel, label); i++;
5302 XtSetArg(args[i], XtNborderWidth, 0); i++;
5303 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5306 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5308 XtRealizeWidget(errorShell);
5309 CatchDeleteWindow(errorShell, "ErrorPopDown");
5312 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5313 XtGetValues(boardWidget, args, i);
5315 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5316 XtSetArg(args[i], XtNheight, &pw_height); i++;
5317 XtGetValues(errorShell, args, i);
5320 /* This code seems to tickle an X bug if it is executed too soon
5321 after xboard starts up. The coordinates get transformed as if
5322 the main window was positioned at (0, 0).
5324 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5325 0 - pw_height + squareSize / 3, &x, &y);
5327 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5328 RootWindowOfScreen(XtScreen(boardWidget)),
5329 (bw_width - pw_width) / 2,
5330 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5334 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5337 XtSetArg(args[i], XtNx, x); i++;
5338 XtSetArg(args[i], XtNy, y); i++;
5339 XtSetValues(errorShell, args, i);
5342 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5345 /* Disable all user input other than deleting the window */
5346 static int frozen = 0;
5350 /* Grab by a widget that doesn't accept input */
5351 XtAddGrab(messageWidget, TRUE, FALSE);
5355 /* Undo a FreezeUI */
5358 if (!frozen) return;
5359 XtRemoveGrab(messageWidget);
5363 char *ModeToWidgetName(mode)
5367 case BeginningOfGame:
5368 if (appData.icsActive)
5369 return "menuMode.ICS Client";
5370 else if (appData.noChessProgram ||
5371 *appData.cmailGameName != NULLCHAR)
5372 return "menuMode.Edit Game";
5374 return "menuMode.Machine Black";
5375 case MachinePlaysBlack:
5376 return "menuMode.Machine Black";
5377 case MachinePlaysWhite:
5378 return "menuMode.Machine White";
5380 return "menuMode.Analysis Mode";
5382 return "menuMode.Analyze File";
5383 case TwoMachinesPlay:
5384 return "menuMode.Two Machines";
5386 return "menuMode.Edit Game";
5387 case PlayFromGameFile:
5388 return "menuFile.Load Game";
5390 return "menuMode.Edit Position";
5392 return "menuMode.Training";
5393 case IcsPlayingWhite:
5394 case IcsPlayingBlack:
5398 return "menuMode.ICS Client";
5405 void ModeHighlight()
5408 static int oldPausing = FALSE;
5409 static GameMode oldmode = (GameMode) -1;
5412 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5414 if (pausing != oldPausing) {
5415 oldPausing = pausing;
5417 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5419 XtSetArg(args[0], XtNleftBitmap, None);
5421 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5424 if (appData.showButtonBar) {
5425 /* Always toggle, don't set. Previous code messes up when
5426 invoked while the button is pressed, as releasing it
5427 toggles the state again. */
5430 XtSetArg(args[0], XtNbackground, &oldbg);
5431 XtSetArg(args[1], XtNforeground, &oldfg);
5432 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5434 XtSetArg(args[0], XtNbackground, oldfg);
5435 XtSetArg(args[1], XtNforeground, oldbg);
5437 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5441 wname = ModeToWidgetName(oldmode);
5442 if (wname != NULL) {
5443 XtSetArg(args[0], XtNleftBitmap, None);
5444 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5446 wname = ModeToWidgetName(gameMode);
5447 if (wname != NULL) {
5448 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5449 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5452 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5453 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5455 /* Maybe all the enables should be handled here, not just this one */
5456 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5457 gameMode == Training || gameMode == PlayFromGameFile);
5462 * Button/menu procedures
5464 void ResetProc(w, event, prms, nprms)
5473 int LoadGamePopUp(f, gameNumber, title)
5478 cmailMsgLoaded = FALSE;
5479 if (gameNumber == 0) {
5480 int error = GameListBuild(f);
5482 DisplayError(_("Cannot build game list"), error);
5483 } else if (!ListEmpty(&gameList) &&
5484 ((ListGame *) gameList.tailPred)->number > 1) {
5485 GameListPopUp(f, title);
5491 return LoadGame(f, gameNumber, title, FALSE);
5494 void LoadGameProc(w, event, prms, nprms)
5500 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5503 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5506 void LoadNextGameProc(w, event, prms, nprms)
5515 void LoadPrevGameProc(w, event, prms, nprms)
5524 void ReloadGameProc(w, event, prms, nprms)
5533 void LoadNextPositionProc(w, event, prms, nprms)
5542 void LoadPrevPositionProc(w, event, prms, nprms)
5551 void ReloadPositionProc(w, event, prms, nprms)
5560 void LoadPositionProc(w, event, prms, nprms)
5566 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5569 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5572 void SaveGameProc(w, event, prms, nprms)
5578 FileNamePopUp(_("Save game file name?"),
5579 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5580 appData.oldSaveStyle ? ".game" : ".pgn",
5584 void SavePositionProc(w, event, prms, nprms)
5590 FileNamePopUp(_("Save position file name?"),
5591 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5592 appData.oldSaveStyle ? ".pos" : ".fen",
5596 void ReloadCmailMsgProc(w, event, prms, nprms)
5602 ReloadCmailMsgEvent(FALSE);
5605 void MailMoveProc(w, event, prms, nprms)
5614 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5615 char *selected_fen_position=NULL;
5618 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5619 Atom *type_return, XtPointer *value_return,
5620 unsigned long *length_return, int *format_return)
5622 char *selection_tmp;
5624 if (!selected_fen_position) return False; /* should never happen */
5625 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5626 /* note: since no XtSelectionDoneProc was registered, Xt will
5627 * automatically call XtFree on the value returned. So have to
5628 * make a copy of it allocated with XtMalloc */
5629 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5630 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5632 *value_return=selection_tmp;
5633 *length_return=strlen(selection_tmp);
5634 *type_return=*target;
5635 *format_return = 8; /* bits per byte */
5637 } else if (*target == XA_TARGETS(xDisplay)) {
5638 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5639 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5640 targets_tmp[1] = XA_STRING;
5641 *value_return = targets_tmp;
5642 *type_return = XA_ATOM;
5644 *format_return = 8 * sizeof(Atom);
5645 if (*format_return > 32) {
5646 *length_return *= *format_return / 32;
5647 *format_return = 32;
5655 /* note: when called from menu all parameters are NULL, so no clue what the
5656 * Widget which was clicked on was, or what the click event was
5658 void CopyPositionProc(w, event, prms, nprms)
5665 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5666 * have a notion of a position that is selected but not copied.
5667 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5669 if(gameMode == EditPosition) EditPositionDone(TRUE);
5670 if (selected_fen_position) free(selected_fen_position);
5671 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5672 if (!selected_fen_position) return;
5673 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5675 SendPositionSelection,
5676 NULL/* lose_ownership_proc */ ,
5677 NULL/* transfer_done_proc */);
5678 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5680 SendPositionSelection,
5681 NULL/* lose_ownership_proc */ ,
5682 NULL/* transfer_done_proc */);
5685 /* function called when the data to Paste is ready */
5687 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5688 Atom *type, XtPointer value, unsigned long *len, int *format)
5691 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5692 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5693 EditPositionPasteFEN(fenstr);
5697 /* called when Paste Position button is pressed,
5698 * all parameters will be NULL */
5699 void PastePositionProc(w, event, prms, nprms)
5705 XtGetSelectionValue(menuBarWidget,
5706 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5707 /* (XtSelectionCallbackProc) */ PastePositionCB,
5708 NULL, /* client_data passed to PastePositionCB */
5710 /* better to use the time field from the event that triggered the
5711 * call to this function, but that isn't trivial to get
5719 SendGameSelection(Widget w, Atom *selection, Atom *target,
5720 Atom *type_return, XtPointer *value_return,
5721 unsigned long *length_return, int *format_return)
5723 char *selection_tmp;
5725 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5726 FILE* f = fopen(gameCopyFilename, "r");
5729 if (f == NULL) return False;
5733 selection_tmp = XtMalloc(len + 1);
5734 count = fread(selection_tmp, 1, len, f);
5737 XtFree(selection_tmp);
5740 selection_tmp[len] = NULLCHAR;
5741 *value_return = selection_tmp;
5742 *length_return = len;
5743 *type_return = *target;
5744 *format_return = 8; /* bits per byte */
5746 } else if (*target == XA_TARGETS(xDisplay)) {
5747 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5748 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5749 targets_tmp[1] = XA_STRING;
5750 *value_return = targets_tmp;
5751 *type_return = XA_ATOM;
5753 *format_return = 8 * sizeof(Atom);
5754 if (*format_return > 32) {
5755 *length_return *= *format_return / 32;
5756 *format_return = 32;
5764 void CopySomething()
5767 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5768 * have a notion of a game that is selected but not copied.
5769 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5771 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5774 NULL/* lose_ownership_proc */ ,
5775 NULL/* transfer_done_proc */);
5776 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5779 NULL/* lose_ownership_proc */ ,
5780 NULL/* transfer_done_proc */);
5783 /* note: when called from menu all parameters are NULL, so no clue what the
5784 * Widget which was clicked on was, or what the click event was
5786 void CopyGameProc(w, event, prms, nprms)
5794 ret = SaveGameToFile(gameCopyFilename, FALSE);
5800 void CopyGameListProc(w, event, prms, nprms)
5806 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5810 /* function called when the data to Paste is ready */
5812 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5813 Atom *type, XtPointer value, unsigned long *len, int *format)
5816 if (value == NULL || *len == 0) {
5817 return; /* nothing had been selected to copy */
5819 f = fopen(gamePasteFilename, "w");
5821 DisplayError(_("Can't open temp file"), errno);
5824 fwrite(value, 1, *len, f);
5827 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5830 /* called when Paste Game button is pressed,
5831 * all parameters will be NULL */
5832 void PasteGameProc(w, event, prms, nprms)
5838 XtGetSelectionValue(menuBarWidget,
5839 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5840 /* (XtSelectionCallbackProc) */ PasteGameCB,
5841 NULL, /* client_data passed to PasteGameCB */
5843 /* better to use the time field from the event that triggered the
5844 * call to this function, but that isn't trivial to get
5854 SaveGameProc(NULL, NULL, NULL, NULL);
5858 void QuitProc(w, event, prms, nprms)
5867 void PauseProc(w, event, prms, nprms)
5877 void MachineBlackProc(w, event, prms, nprms)
5883 MachineBlackEvent();
5886 void MachineWhiteProc(w, event, prms, nprms)
5892 MachineWhiteEvent();
5895 void AnalyzeModeProc(w, event, prms, nprms)
5903 if (!first.analysisSupport) {
5904 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5905 DisplayError(buf, 0);
5908 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5909 if (appData.icsActive) {
5910 if (gameMode != IcsObserving) {
5911 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5912 DisplayError(buf, 0);
5914 if (appData.icsEngineAnalyze) {
5915 if (appData.debugMode)
5916 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5922 /* if enable, use want disable icsEngineAnalyze */
5923 if (appData.icsEngineAnalyze) {
5928 appData.icsEngineAnalyze = TRUE;
5929 if (appData.debugMode)
5930 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5932 #ifndef OPTIONSDIALOG
5933 if (!appData.showThinking)
5934 ShowThinkingProc(w,event,prms,nprms);
5940 void AnalyzeFileProc(w, event, prms, nprms)
5946 if (!first.analysisSupport) {
5948 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5949 DisplayError(buf, 0);
5952 // Reset(FALSE, TRUE);
5953 #ifndef OPTIONSDIALOG
5954 if (!appData.showThinking)
5955 ShowThinkingProc(w,event,prms,nprms);
5958 // FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5959 AnalysisPeriodicEvent(1);
5962 void TwoMachinesProc(w, event, prms, nprms)
5971 void MatchProc(w, event, prms, nprms)
5980 void IcsClientProc(w, event, prms, nprms)
5989 void EditGameProc(w, event, prms, nprms)
5998 void EditPositionProc(w, event, prms, nprms)
6004 EditPositionEvent();
6007 void TrainingProc(w, event, prms, nprms)
6016 void EditCommentProc(w, event, prms, nprms)
6024 if (PopDown(1)) { // popdown succesful
6026 XtSetArg(args[j], XtNleftBitmap, None); j++;
6027 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
6028 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
6029 } else // was not up
6033 void IcsInputBoxProc(w, event, prms, nprms)
6039 if (!PopDown(4)) ICSInputBoxPopUp();
6042 void AcceptProc(w, event, prms, nprms)
6051 void DeclineProc(w, event, prms, nprms)
6060 void RematchProc(w, event, prms, nprms)
6069 void CallFlagProc(w, event, prms, nprms)
6078 void DrawProc(w, event, prms, nprms)
6087 void AbortProc(w, event, prms, nprms)
6096 void AdjournProc(w, event, prms, nprms)
6105 void ResignProc(w, event, prms, nprms)
6114 void AdjuWhiteProc(w, event, prms, nprms)
6120 UserAdjudicationEvent(+1);
6123 void AdjuBlackProc(w, event, prms, nprms)
6129 UserAdjudicationEvent(-1);
6132 void AdjuDrawProc(w, event, prms, nprms)
6138 UserAdjudicationEvent(0);
6141 void EnterKeyProc(w, event, prms, nprms)
6147 if (shellUp[4] == True)
6151 void UpKeyProc(w, event, prms, nprms)
6156 { // [HGM] input: let up-arrow recall previous line from history
6163 if (!shellUp[4]) return;
6164 edit = boxOptions[0].handle;
6166 XtSetArg(args[j], XtNstring, &val); j++;
6167 XtGetValues(edit, args, j);
6168 val = PrevInHistory(val);
6169 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6170 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6172 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6173 XawTextReplace(edit, 0, 0, &t);
6174 XawTextSetInsertionPoint(edit, 9999);
6178 void DownKeyProc(w, event, prms, nprms)
6183 { // [HGM] input: let down-arrow recall next line from history
6188 if (!shellUp[4]) return;
6189 edit = boxOptions[0].handle;
6190 val = NextInHistory();
6191 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6192 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6194 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6195 XawTextReplace(edit, 0, 0, &t);
6196 XawTextSetInsertionPoint(edit, 9999);
6200 void StopObservingProc(w, event, prms, nprms)
6206 StopObservingEvent();
6209 void StopExaminingProc(w, event, prms, nprms)
6215 StopExaminingEvent();
6218 void UploadProc(w, event, prms, nprms)
6228 void ForwardProc(w, event, prms, nprms)
6238 void BackwardProc(w, event, prms, nprms)
6247 void ToStartProc(w, event, prms, nprms)
6256 void ToEndProc(w, event, prms, nprms)
6265 void RevertProc(w, event, prms, nprms)
6274 void AnnotateProc(w, event, prms, nprms)
6283 void TruncateGameProc(w, event, prms, nprms)
6289 TruncateGameEvent();
6291 void RetractMoveProc(w, event, prms, nprms)
6300 void MoveNowProc(w, event, prms, nprms)
6309 void FlipViewProc(w, event, prms, nprms)
6315 flipView = !flipView;
6316 DrawPosition(True, NULL);
6319 void PonderNextMoveProc(w, event, prms, nprms)
6327 PonderNextMoveEvent(!appData.ponderNextMove);
6328 #ifndef OPTIONSDIALOG
6329 if (appData.ponderNextMove) {
6330 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6332 XtSetArg(args[0], XtNleftBitmap, None);
6334 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6339 #ifndef OPTIONSDIALOG
6340 void AlwaysQueenProc(w, event, prms, nprms)
6348 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6350 if (appData.alwaysPromoteToQueen) {
6351 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6353 XtSetArg(args[0], XtNleftBitmap, None);
6355 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6359 void AnimateDraggingProc(w, event, prms, nprms)
6367 appData.animateDragging = !appData.animateDragging;
6369 if (appData.animateDragging) {
6370 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6373 XtSetArg(args[0], XtNleftBitmap, None);
6375 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6379 void AnimateMovingProc(w, event, prms, nprms)
6387 appData.animate = !appData.animate;
6389 if (appData.animate) {
6390 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6393 XtSetArg(args[0], XtNleftBitmap, None);
6395 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6399 void AutoflagProc(w, event, prms, nprms)
6407 appData.autoCallFlag = !appData.autoCallFlag;
6409 if (appData.autoCallFlag) {
6410 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6412 XtSetArg(args[0], XtNleftBitmap, None);
6414 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6418 void AutoflipProc(w, event, prms, nprms)
6426 appData.autoFlipView = !appData.autoFlipView;
6428 if (appData.autoFlipView) {
6429 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6431 XtSetArg(args[0], XtNleftBitmap, None);
6433 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6437 void BlindfoldProc(w, event, prms, nprms)
6445 appData.blindfold = !appData.blindfold;
6447 if (appData.blindfold) {
6448 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6450 XtSetArg(args[0], XtNleftBitmap, None);
6452 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6455 DrawPosition(True, NULL);
6458 void TestLegalityProc(w, event, prms, nprms)
6466 appData.testLegality = !appData.testLegality;
6468 if (appData.testLegality) {
6469 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6471 XtSetArg(args[0], XtNleftBitmap, None);
6473 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6478 void FlashMovesProc(w, event, prms, nprms)
6486 if (appData.flashCount == 0) {
6487 appData.flashCount = 3;
6489 appData.flashCount = -appData.flashCount;
6492 if (appData.flashCount > 0) {
6493 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6495 XtSetArg(args[0], XtNleftBitmap, None);
6497 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6502 void HighlightDraggingProc(w, event, prms, nprms)
6510 appData.highlightDragging = !appData.highlightDragging;
6512 if (appData.highlightDragging) {
6513 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6515 XtSetArg(args[0], XtNleftBitmap, None);
6517 XtSetValues(XtNameToWidget(menuBarWidget,
6518 "menuOptions.Highlight Dragging"), args, 1);
6522 void HighlightLastMoveProc(w, event, prms, nprms)
6530 appData.highlightLastMove = !appData.highlightLastMove;
6532 if (appData.highlightLastMove) {
6533 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6535 XtSetArg(args[0], XtNleftBitmap, None);
6537 XtSetValues(XtNameToWidget(menuBarWidget,
6538 "menuOptions.Highlight Last Move"), args, 1);
6541 void HighlightArrowProc(w, event, prms, nprms)
6549 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6551 if (appData.highlightMoveWithArrow) {
6552 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6554 XtSetArg(args[0], XtNleftBitmap, None);
6556 XtSetValues(XtNameToWidget(menuBarWidget,
6557 "menuOptions.Arrow"), args, 1);
6561 void IcsAlarmProc(w, event, prms, nprms)
6569 appData.icsAlarm = !appData.icsAlarm;
6571 if (appData.icsAlarm) {
6572 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6574 XtSetArg(args[0], XtNleftBitmap, None);
6576 XtSetValues(XtNameToWidget(menuBarWidget,
6577 "menuOptions.ICS Alarm"), args, 1);
6581 void MoveSoundProc(w, event, prms, nprms)
6589 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6591 if (appData.ringBellAfterMoves) {
6592 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6594 XtSetArg(args[0], XtNleftBitmap, None);
6596 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6600 void OneClickProc(w, event, prms, nprms)
6608 appData.oneClick = !appData.oneClick;
6610 if (appData.oneClick) {
6611 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6613 XtSetArg(args[0], XtNleftBitmap, None);
6615 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6619 void PeriodicUpdatesProc(w, event, prms, nprms)
6627 PeriodicUpdatesEvent(!appData.periodicUpdates);
6629 if (appData.periodicUpdates) {
6630 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6632 XtSetArg(args[0], XtNleftBitmap, None);
6634 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6638 void PopupExitMessageProc(w, event, prms, nprms)
6646 appData.popupExitMessage = !appData.popupExitMessage;
6648 if (appData.popupExitMessage) {
6649 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6651 XtSetArg(args[0], XtNleftBitmap, None);
6653 XtSetValues(XtNameToWidget(menuBarWidget,
6654 "menuOptions.Popup Exit Message"), args, 1);
6657 void PopupMoveErrorsProc(w, event, prms, nprms)
6665 appData.popupMoveErrors = !appData.popupMoveErrors;
6667 if (appData.popupMoveErrors) {
6668 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6670 XtSetArg(args[0], XtNleftBitmap, None);
6672 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6677 void PremoveProc(w, event, prms, nprms)
6685 appData.premove = !appData.premove;
6687 if (appData.premove) {
6688 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6690 XtSetArg(args[0], XtNleftBitmap, None);
6692 XtSetValues(XtNameToWidget(menuBarWidget,
6693 "menuOptions.Premove"), args, 1);
6697 void ShowCoordsProc(w, event, prms, nprms)
6705 appData.showCoords = !appData.showCoords;
6707 if (appData.showCoords) {
6708 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6710 XtSetArg(args[0], XtNleftBitmap, None);
6712 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6715 DrawPosition(True, NULL);
6718 void ShowThinkingProc(w, event, prms, nprms)
6724 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6725 ShowThinkingEvent();
6728 void HideThinkingProc(w, event, prms, nprms)
6736 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6737 ShowThinkingEvent();
6739 if (appData.hideThinkingFromHuman) {
6740 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6742 XtSetArg(args[0], XtNleftBitmap, None);
6744 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6749 void SaveOnExitProc(w, event, prms, nprms)
6757 saveSettingsOnExit = !saveSettingsOnExit;
6759 if (saveSettingsOnExit) {
6760 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6762 XtSetArg(args[0], XtNleftBitmap, None);
6764 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6768 void SaveSettingsProc(w, event, prms, nprms)
6774 SaveSettings(settingsFileName);
6777 void InfoProc(w, event, prms, nprms)
6784 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6789 void ManProc(w, event, prms, nprms)
6797 if (nprms && *nprms > 0)
6801 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6805 void HintProc(w, event, prms, nprms)
6814 void BookProc(w, event, prms, nprms)
6823 void AboutProc(w, event, prms, nprms)
6831 char *zippy = _(" (with Zippy code)");
6835 snprintf(buf, sizeof(buf),
6837 "Copyright 1991 Digital Equipment Corporation\n"
6838 "Enhancements Copyright 1992-2009 Free Software Foundation\n"
6839 "Enhancements Copyright 2005 Alessandro Scotti\n\n"
6840 "%s is free software and carries NO WARRANTY;"
6841 "see the file COPYING for more information."),
6842 programVersion, zippy, PACKAGE);
6843 ErrorPopUp(_("About XBoard"), buf, FALSE);
6846 void DebugProc(w, event, prms, nprms)
6852 appData.debugMode = !appData.debugMode;
6855 void AboutGameProc(w, event, prms, nprms)
6864 void NothingProc(w, event, prms, nprms)
6873 void Iconify(w, event, prms, nprms)
6882 XtSetArg(args[0], XtNiconic, True);
6883 XtSetValues(shellWidget, args, 1);
6886 void DisplayMessage(message, extMessage)
6887 char *message, *extMessage;
6889 /* display a message in the message widget */
6898 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6903 message = extMessage;
6907 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6909 /* need to test if messageWidget already exists, since this function
6910 can also be called during the startup, if for example a Xresource
6911 is not set up correctly */
6914 XtSetArg(arg, XtNlabel, message);
6915 XtSetValues(messageWidget, &arg, 1);
6921 void DisplayTitle(text)
6926 char title[MSG_SIZ];
6929 if (text == NULL) text = "";
6931 if (appData.titleInWindow) {
6933 XtSetArg(args[i], XtNlabel, text); i++;
6934 XtSetValues(titleWidget, args, i);
6937 if (*text != NULLCHAR) {
6938 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6939 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6940 } else if (appData.icsActive) {
6941 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6942 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6943 } else if (appData.cmailGameName[0] != NULLCHAR) {
6944 snprintf(icon, sizeof(icon), "%s", "CMail");
6945 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6947 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6948 } else if (gameInfo.variant == VariantGothic) {
6949 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6950 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6953 } else if (gameInfo.variant == VariantFalcon) {
6954 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6955 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6957 } else if (appData.noChessProgram) {
6958 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6959 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6961 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6962 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6965 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6966 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6967 XtSetValues(shellWidget, args, i);
6968 XSync(xDisplay, False);
6973 DisplayError(message, error)
6980 if (appData.debugMode || appData.matchMode) {
6981 fprintf(stderr, "%s: %s\n", programName, message);
6984 if (appData.debugMode || appData.matchMode) {
6985 fprintf(stderr, "%s: %s: %s\n",
6986 programName, message, strerror(error));
6988 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6991 ErrorPopUp(_("Error"), message, FALSE);
6995 void DisplayMoveError(message)
7000 DrawPosition(FALSE, NULL);
7001 if (appData.debugMode || appData.matchMode) {
7002 fprintf(stderr, "%s: %s\n", programName, message);
7004 if (appData.popupMoveErrors) {
7005 ErrorPopUp(_("Error"), message, FALSE);
7007 DisplayMessage(message, "");
7012 void DisplayFatalError(message, error, status)
7018 errorExitStatus = status;
7020 fprintf(stderr, "%s: %s\n", programName, message);
7022 fprintf(stderr, "%s: %s: %s\n",
7023 programName, message, strerror(error));
7024 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7027 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
7028 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
7034 void DisplayInformation(message)
7038 ErrorPopUp(_("Information"), message, TRUE);
7041 void DisplayNote(message)
7045 ErrorPopUp(_("Note"), message, FALSE);
7049 NullXErrorCheck(dpy, error_event)
7051 XErrorEvent *error_event;
7056 void DisplayIcsInteractionTitle(message)
7059 if (oldICSInteractionTitle == NULL) {
7060 /* Magic to find the old window title, adapted from vim */
7061 char *wina = getenv("WINDOWID");
7063 Window win = (Window) atoi(wina);
7064 Window root, parent, *children;
7065 unsigned int nchildren;
7066 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
7068 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
7069 if (!XQueryTree(xDisplay, win, &root, &parent,
7070 &children, &nchildren)) break;
7071 if (children) XFree((void *)children);
7072 if (parent == root || parent == 0) break;
7075 XSetErrorHandler(oldHandler);
7077 if (oldICSInteractionTitle == NULL) {
7078 oldICSInteractionTitle = "xterm";
7081 printf("\033]0;%s\007", message);
7085 char pendingReplyPrefix[MSG_SIZ];
7086 ProcRef pendingReplyPR;
7088 void AskQuestionProc(w, event, prms, nprms)
7095 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7099 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7102 void AskQuestionPopDown()
7104 if (!askQuestionUp) return;
7105 XtPopdown(askQuestionShell);
7106 XtDestroyWidget(askQuestionShell);
7107 askQuestionUp = False;
7110 void AskQuestionReplyAction(w, event, prms, nprms)
7120 reply = XawDialogGetValueString(w = XtParent(w));
7121 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
7122 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
7123 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
7124 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
7125 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7126 AskQuestionPopDown();
7128 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7131 void AskQuestionCallback(w, client_data, call_data)
7133 XtPointer client_data, call_data;
7138 XtSetArg(args[0], XtNlabel, &name);
7139 XtGetValues(w, args, 1);
7141 if (strcmp(name, _("cancel")) == 0) {
7142 AskQuestionPopDown();
7144 AskQuestionReplyAction(w, NULL, NULL, NULL);
7148 void AskQuestion(title, question, replyPrefix, pr)
7149 char *title, *question, *replyPrefix;
7153 Widget popup, layout, dialog, edit;
7159 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7160 pendingReplyPR = pr;
7163 XtSetArg(args[i], XtNresizable, True); i++;
7164 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7165 askQuestionShell = popup =
7166 XtCreatePopupShell(title, transientShellWidgetClass,
7167 shellWidget, args, i);
7170 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7171 layoutArgs, XtNumber(layoutArgs));
7174 XtSetArg(args[i], XtNlabel, question); i++;
7175 XtSetArg(args[i], XtNvalue, ""); i++;
7176 XtSetArg(args[i], XtNborderWidth, 0); i++;
7177 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7180 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7181 (XtPointer) dialog);
7182 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7183 (XtPointer) dialog);
7185 XtRealizeWidget(popup);
7186 CatchDeleteWindow(popup, "AskQuestionPopDown");
7188 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7189 &x, &y, &win_x, &win_y, &mask);
7191 XtSetArg(args[0], XtNx, x - 10);
7192 XtSetArg(args[1], XtNy, y - 30);
7193 XtSetValues(popup, args, 2);
7195 XtPopup(popup, XtGrabExclusive);
7196 askQuestionUp = True;
7198 edit = XtNameToWidget(dialog, "*value");
7199 XtSetKeyboardFocus(popup, edit);
7207 if (*name == NULLCHAR) {
7209 } else if (strcmp(name, "$") == 0) {
7210 putc(BELLCHAR, stderr);
7213 char *prefix = "", *sep = "";
7214 if(appData.soundProgram[0] == NULLCHAR) return;
7215 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7216 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7224 PlaySound(appData.soundMove);
7230 PlaySound(appData.soundIcsWin);
7236 PlaySound(appData.soundIcsLoss);
7242 PlaySound(appData.soundIcsDraw);
7246 PlayIcsUnfinishedSound()
7248 PlaySound(appData.soundIcsUnfinished);
7254 PlaySound(appData.soundIcsAlarm);
7260 PlaySound(appData.soundTell);
7266 system("stty echo");
7273 system("stty -echo");
7278 RunCommand(char *buf)
7284 Colorize(cc, continuation)
7289 int count, outCount, error;
7291 if (textColors[(int)cc].bg > 0) {
7292 if (textColors[(int)cc].fg > 0) {
7293 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7294 textColors[(int)cc].fg, textColors[(int)cc].bg);
7296 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7297 textColors[(int)cc].bg);
7300 if (textColors[(int)cc].fg > 0) {
7301 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7302 textColors[(int)cc].fg);
7304 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7307 count = strlen(buf);
7308 outCount = OutputToProcess(NoProc, buf, count, &error);
7309 if (outCount < count) {
7310 DisplayFatalError(_("Error writing to display"), error, 1);
7313 if (continuation) return;
7316 PlaySound(appData.soundShout);
7319 PlaySound(appData.soundSShout);
7322 PlaySound(appData.soundChannel1);
7325 PlaySound(appData.soundChannel);
7328 PlaySound(appData.soundKibitz);
7331 PlaySound(appData.soundTell);
7333 case ColorChallenge:
7334 PlaySound(appData.soundChallenge);
7337 PlaySound(appData.soundRequest);
7340 PlaySound(appData.soundSeek);
7351 return getpwuid(getuid())->pw_name;
7355 ExpandPathName(path)
7358 static char static_buf[4*MSG_SIZ];
7359 char *d, *s, buf[4*MSG_SIZ];
7365 while (*s && isspace(*s))
7374 if (*(s+1) == '/') {
7375 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7379 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7380 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7381 pwd = getpwnam(buf);
7384 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7388 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7389 strcat(d, strchr(s+1, '/'));
7393 safeStrCpy(d, s, 4*MSG_SIZ );
7400 static char host_name[MSG_SIZ];
7402 #if HAVE_GETHOSTNAME
7403 gethostname(host_name, MSG_SIZ);
7405 #else /* not HAVE_GETHOSTNAME */
7406 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7407 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7409 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7411 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7412 #endif /* not HAVE_GETHOSTNAME */
7415 XtIntervalId delayedEventTimerXID = 0;
7416 DelayedEventCallback delayedEventCallback = 0;
7421 delayedEventTimerXID = 0;
7422 delayedEventCallback();
7426 ScheduleDelayedEvent(cb, millisec)
7427 DelayedEventCallback cb; long millisec;
7429 if(delayedEventTimerXID && delayedEventCallback == cb)
7430 // [HGM] alive: replace, rather than add or flush identical event
7431 XtRemoveTimeOut(delayedEventTimerXID);
7432 delayedEventCallback = cb;
7433 delayedEventTimerXID =
7434 XtAppAddTimeOut(appContext, millisec,
7435 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7438 DelayedEventCallback
7441 if (delayedEventTimerXID) {
7442 return delayedEventCallback;
7449 CancelDelayedEvent()
7451 if (delayedEventTimerXID) {
7452 XtRemoveTimeOut(delayedEventTimerXID);
7453 delayedEventTimerXID = 0;
7457 XtIntervalId loadGameTimerXID = 0;
7459 int LoadGameTimerRunning()
7461 return loadGameTimerXID != 0;
7464 int StopLoadGameTimer()
7466 if (loadGameTimerXID != 0) {
7467 XtRemoveTimeOut(loadGameTimerXID);
7468 loadGameTimerXID = 0;
7476 LoadGameTimerCallback(arg, id)
7480 loadGameTimerXID = 0;
7485 StartLoadGameTimer(millisec)
7489 XtAppAddTimeOut(appContext, millisec,
7490 (XtTimerCallbackProc) LoadGameTimerCallback,
7494 XtIntervalId analysisClockXID = 0;
7497 AnalysisClockCallback(arg, id)
7501 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7502 || appData.icsEngineAnalyze) { // [DM]
7503 AnalysisPeriodicEvent(0);
7504 StartAnalysisClock();
7509 StartAnalysisClock()
7512 XtAppAddTimeOut(appContext, 2000,
7513 (XtTimerCallbackProc) AnalysisClockCallback,
7517 XtIntervalId clockTimerXID = 0;
7519 int ClockTimerRunning()
7521 return clockTimerXID != 0;
7524 int StopClockTimer()
7526 if (clockTimerXID != 0) {
7527 XtRemoveTimeOut(clockTimerXID);
7536 ClockTimerCallback(arg, id)
7545 StartClockTimer(millisec)
7549 XtAppAddTimeOut(appContext, millisec,
7550 (XtTimerCallbackProc) ClockTimerCallback,
7555 DisplayTimerLabel(w, color, timer, highlight)
7564 /* check for low time warning */
7565 Pixel foregroundOrWarningColor = timerForegroundPixel;
7568 appData.lowTimeWarning &&
7569 (timer / 1000) < appData.icsAlarmTime)
7570 foregroundOrWarningColor = lowTimeWarningColor;
7572 if (appData.clockMode) {
7573 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7574 XtSetArg(args[0], XtNlabel, buf);
7576 snprintf(buf, MSG_SIZ, "%s ", color);
7577 XtSetArg(args[0], XtNlabel, buf);
7582 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7583 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7585 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7586 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7589 XtSetValues(w, args, 3);
7593 DisplayWhiteClock(timeRemaining, highlight)
7599 if(appData.noGUI) return;
7600 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7601 if (highlight && iconPixmap == bIconPixmap) {
7602 iconPixmap = wIconPixmap;
7603 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7604 XtSetValues(shellWidget, args, 1);
7609 DisplayBlackClock(timeRemaining, highlight)
7615 if(appData.noGUI) return;
7616 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7617 if (highlight && iconPixmap == wIconPixmap) {
7618 iconPixmap = bIconPixmap;
7619 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7620 XtSetValues(shellWidget, args, 1);
7638 int StartChildProcess(cmdLine, dir, pr)
7645 int to_prog[2], from_prog[2];
7649 if (appData.debugMode) {
7650 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7653 /* We do NOT feed the cmdLine to the shell; we just
7654 parse it into blank-separated arguments in the
7655 most simple-minded way possible.
7658 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7661 while(*p == ' ') p++;
7663 if(*p == '"' || *p == '\'')
7664 p = strchr(++argv[i-1], *p);
7665 else p = strchr(p, ' ');
7666 if (p == NULL) break;
7671 SetUpChildIO(to_prog, from_prog);
7673 if ((pid = fork()) == 0) {
7675 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7676 close(to_prog[1]); // first close the unused pipe ends
7677 close(from_prog[0]);
7678 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7679 dup2(from_prog[1], 1);
7680 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7681 close(from_prog[1]); // and closing again loses one of the pipes!
7682 if(fileno(stderr) >= 2) // better safe than sorry...
7683 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7685 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7690 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7692 execvp(argv[0], argv);
7694 /* If we get here, exec failed */
7699 /* Parent process */
7701 close(from_prog[1]);
7703 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7706 cp->fdFrom = from_prog[0];
7707 cp->fdTo = to_prog[1];
7712 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7713 static RETSIGTYPE AlarmCallBack(int n)
7719 DestroyChildProcess(pr, signalType)
7723 ChildProc *cp = (ChildProc *) pr;
7725 if (cp->kind != CPReal) return;
7727 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7728 signal(SIGALRM, AlarmCallBack);
7730 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7731 kill(cp->pid, SIGKILL); // kill it forcefully
7732 wait((int *) 0); // and wait again
7736 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7738 /* Process is exiting either because of the kill or because of
7739 a quit command sent by the backend; either way, wait for it to die.
7748 InterruptChildProcess(pr)
7751 ChildProc *cp = (ChildProc *) pr;
7753 if (cp->kind != CPReal) return;
7754 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7757 int OpenTelnet(host, port, pr)
7762 char cmdLine[MSG_SIZ];
7764 if (port[0] == NULLCHAR) {
7765 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7767 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7769 return StartChildProcess(cmdLine, "", pr);
7772 int OpenTCP(host, port, pr)
7778 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7779 #else /* !OMIT_SOCKETS */
7780 struct addrinfo hints;
7781 struct addrinfo *ais, *ai;
7786 memset(&hints, 0, sizeof(hints));
7787 hints.ai_family = AF_UNSPEC;
7788 hints.ai_socktype = SOCK_STREAM;
7790 error = getaddrinfo(host, port, &hints, &ais);
7792 /* a getaddrinfo error is not an errno, so can't return it */
7793 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7794 host, port, gai_strerror(error));
7798 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7799 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7803 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7816 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7822 #endif /* !OMIT_SOCKETS */
7827 int OpenCommPort(name, pr)
7834 fd = open(name, 2, 0);
7835 if (fd < 0) return errno;
7837 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7847 int OpenLoopback(pr)
7853 SetUpChildIO(to, from);
7855 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7858 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7865 int OpenRcmd(host, user, cmd, pr)
7866 char *host, *user, *cmd;
7869 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7873 #define INPUT_SOURCE_BUF_SIZE 8192
7882 char buf[INPUT_SOURCE_BUF_SIZE];
7887 DoInputCallback(closure, source, xid)
7892 InputSource *is = (InputSource *) closure;
7897 if (is->lineByLine) {
7898 count = read(is->fd, is->unused,
7899 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7901 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7904 is->unused += count;
7906 while (p < is->unused) {
7907 q = memchr(p, '\n', is->unused - p);
7908 if (q == NULL) break;
7910 (is->func)(is, is->closure, p, q - p, 0);
7914 while (p < is->unused) {
7919 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7924 (is->func)(is, is->closure, is->buf, count, error);
7928 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7935 ChildProc *cp = (ChildProc *) pr;
7937 is = (InputSource *) calloc(1, sizeof(InputSource));
7938 is->lineByLine = lineByLine;
7942 is->fd = fileno(stdin);
7944 is->kind = cp->kind;
7945 is->fd = cp->fdFrom;
7948 is->unused = is->buf;
7951 is->xid = XtAppAddInput(appContext, is->fd,
7952 (XtPointer) (XtInputReadMask),
7953 (XtInputCallbackProc) DoInputCallback,
7955 is->closure = closure;
7956 return (InputSourceRef) is;
7960 RemoveInputSource(isr)
7963 InputSource *is = (InputSource *) isr;
7965 if (is->xid == 0) return;
7966 XtRemoveInput(is->xid);
7970 int OutputToProcess(pr, message, count, outError)
7976 static int line = 0;
7977 ChildProc *cp = (ChildProc *) pr;
7982 if (appData.noJoin || !appData.useInternalWrap)
7983 outCount = fwrite(message, 1, count, stdout);
7986 int width = get_term_width();
7987 int len = wrap(NULL, message, count, width, &line);
7988 char *msg = malloc(len);
7992 outCount = fwrite(message, 1, count, stdout);
7995 dbgchk = wrap(msg, message, count, width, &line);
7996 if (dbgchk != len && appData.debugMode)
7997 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7998 outCount = fwrite(msg, 1, dbgchk, stdout);
8004 outCount = write(cp->fdTo, message, count);
8014 /* Output message to process, with "ms" milliseconds of delay
8015 between each character. This is needed when sending the logon
8016 script to ICC, which for some reason doesn't like the
8017 instantaneous send. */
8018 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
8025 ChildProc *cp = (ChildProc *) pr;
8030 r = write(cp->fdTo, message++, 1);
8043 /**** Animation code by Hugh Fisher, DCS, ANU.
8045 Known problem: if a window overlapping the board is
8046 moved away while a piece is being animated underneath,
8047 the newly exposed area won't be updated properly.
8048 I can live with this.
8050 Known problem: if you look carefully at the animation
8051 of pieces in mono mode, they are being drawn as solid
8052 shapes without interior detail while moving. Fixing
8053 this would be a major complication for minimal return.
8056 /* Masks for XPM pieces. Black and white pieces can have
8057 different shapes, but in the interest of retaining my
8058 sanity pieces must have the same outline on both light
8059 and dark squares, and all pieces must use the same
8060 background square colors/images. */
8062 static int xpmDone = 0;
8065 CreateAnimMasks (pieceDepth)
8072 unsigned long plane;
8075 /* Need a bitmap just to get a GC with right depth */
8076 buf = XCreatePixmap(xDisplay, xBoardWindow,
8078 values.foreground = 1;
8079 values.background = 0;
8080 /* Don't use XtGetGC, not read only */
8081 maskGC = XCreateGC(xDisplay, buf,
8082 GCForeground | GCBackground, &values);
8083 XFreePixmap(xDisplay, buf);
8085 buf = XCreatePixmap(xDisplay, xBoardWindow,
8086 squareSize, squareSize, pieceDepth);
8087 values.foreground = XBlackPixel(xDisplay, xScreen);
8088 values.background = XWhitePixel(xDisplay, xScreen);
8089 bufGC = XCreateGC(xDisplay, buf,
8090 GCForeground | GCBackground, &values);
8092 for (piece = WhitePawn; piece <= BlackKing; piece++) {
8093 /* Begin with empty mask */
8094 if(!xpmDone) // [HGM] pieces: keep using existing
8095 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8096 squareSize, squareSize, 1);
8097 XSetFunction(xDisplay, maskGC, GXclear);
8098 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8099 0, 0, squareSize, squareSize);
8101 /* Take a copy of the piece */
8106 XSetFunction(xDisplay, bufGC, GXcopy);
8107 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8109 0, 0, squareSize, squareSize, 0, 0);
8111 /* XOR the background (light) over the piece */
8112 XSetFunction(xDisplay, bufGC, GXxor);
8114 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8115 0, 0, squareSize, squareSize, 0, 0);
8117 XSetForeground(xDisplay, bufGC, lightSquareColor);
8118 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8121 /* We now have an inverted piece image with the background
8122 erased. Construct mask by just selecting all the non-zero
8123 pixels - no need to reconstruct the original image. */
8124 XSetFunction(xDisplay, maskGC, GXor);
8126 /* Might be quicker to download an XImage and create bitmap
8127 data from it rather than this N copies per piece, but it
8128 only takes a fraction of a second and there is a much
8129 longer delay for loading the pieces. */
8130 for (n = 0; n < pieceDepth; n ++) {
8131 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8132 0, 0, squareSize, squareSize,
8138 XFreePixmap(xDisplay, buf);
8139 XFreeGC(xDisplay, bufGC);
8140 XFreeGC(xDisplay, maskGC);
8144 InitAnimState (anim, info)
8146 XWindowAttributes * info;
8151 /* Each buffer is square size, same depth as window */
8152 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8153 squareSize, squareSize, info->depth);
8154 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8155 squareSize, squareSize, info->depth);
8157 /* Create a plain GC for blitting */
8158 mask = GCForeground | GCBackground | GCFunction |
8159 GCPlaneMask | GCGraphicsExposures;
8160 values.foreground = XBlackPixel(xDisplay, xScreen);
8161 values.background = XWhitePixel(xDisplay, xScreen);
8162 values.function = GXcopy;
8163 values.plane_mask = AllPlanes;
8164 values.graphics_exposures = False;
8165 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8167 /* Piece will be copied from an existing context at
8168 the start of each new animation/drag. */
8169 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8171 /* Outline will be a read-only copy of an existing */
8172 anim->outlineGC = None;
8178 XWindowAttributes info;
8180 if (xpmDone && gameInfo.variant == oldVariant) return;
8181 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
8182 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8184 InitAnimState(&game, &info);
8185 InitAnimState(&player, &info);
8187 /* For XPM pieces, we need bitmaps to use as masks. */
8189 CreateAnimMasks(info.depth), xpmDone = 1;
8194 static Boolean frameWaiting;
8196 static RETSIGTYPE FrameAlarm (sig)
8199 frameWaiting = False;
8200 /* In case System-V style signals. Needed?? */
8201 signal(SIGALRM, FrameAlarm);
8208 struct itimerval delay;
8210 XSync(xDisplay, False);
8213 frameWaiting = True;
8214 signal(SIGALRM, FrameAlarm);
8215 delay.it_interval.tv_sec =
8216 delay.it_value.tv_sec = time / 1000;
8217 delay.it_interval.tv_usec =
8218 delay.it_value.tv_usec = (time % 1000) * 1000;
8219 setitimer(ITIMER_REAL, &delay, NULL);
8220 while (frameWaiting) pause();
8221 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8222 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8223 setitimer(ITIMER_REAL, &delay, NULL);
8233 XSync(xDisplay, False);
8235 usleep(time * 1000);
8246 /* Convert board position to corner of screen rect and color */
8249 ScreenSquare(column, row, pt, color)
8250 int column; int row; XPoint * pt; int * color;
8253 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8254 pt->y = lineGap + row * (squareSize + lineGap);
8256 pt->x = lineGap + column * (squareSize + lineGap);
8257 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8259 *color = SquareColor(row, column);
8262 /* Convert window coords to square */
8265 BoardSquare(x, y, column, row)
8266 int x; int y; int * column; int * row;
8268 *column = EventToSquare(x, BOARD_WIDTH);
8269 if (flipView && *column >= 0)
8270 *column = BOARD_WIDTH - 1 - *column;
8271 *row = EventToSquare(y, BOARD_HEIGHT);
8272 if (!flipView && *row >= 0)
8273 *row = BOARD_HEIGHT - 1 - *row;
8278 #undef Max /* just in case */
8280 #define Max(a, b) ((a) > (b) ? (a) : (b))
8281 #define Min(a, b) ((a) < (b) ? (a) : (b))
8284 SetRect(rect, x, y, width, height)
8285 XRectangle * rect; int x; int y; int width; int height;
8289 rect->width = width;
8290 rect->height = height;
8293 /* Test if two frames overlap. If they do, return
8294 intersection rect within old and location of
8295 that rect within new. */
8298 Intersect(old, new, size, area, pt)
8299 XPoint * old; XPoint * new;
8300 int size; XRectangle * area; XPoint * pt;
8302 if (old->x > new->x + size || new->x > old->x + size ||
8303 old->y > new->y + size || new->y > old->y + size) {
8306 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8307 size - abs(old->x - new->x), size - abs(old->y - new->y));
8308 pt->x = Max(old->x - new->x, 0);
8309 pt->y = Max(old->y - new->y, 0);
8314 /* For two overlapping frames, return the rect(s)
8315 in the old that do not intersect with the new. */
8318 CalcUpdateRects(old, new, size, update, nUpdates)
8319 XPoint * old; XPoint * new; int size;
8320 XRectangle update[]; int * nUpdates;
8324 /* If old = new (shouldn't happen) then nothing to draw */
8325 if (old->x == new->x && old->y == new->y) {
8329 /* Work out what bits overlap. Since we know the rects
8330 are the same size we don't need a full intersect calc. */
8332 /* Top or bottom edge? */
8333 if (new->y > old->y) {
8334 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8336 } else if (old->y > new->y) {
8337 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8338 size, old->y - new->y);
8341 /* Left or right edge - don't overlap any update calculated above. */
8342 if (new->x > old->x) {
8343 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8344 new->x - old->x, size - abs(new->y - old->y));
8346 } else if (old->x > new->x) {
8347 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8348 old->x - new->x, size - abs(new->y - old->y));
8355 /* Generate a series of frame coords from start->mid->finish.
8356 The movement rate doubles until the half way point is
8357 reached, then halves back down to the final destination,
8358 which gives a nice slow in/out effect. The algorithmn
8359 may seem to generate too many intermediates for short
8360 moves, but remember that the purpose is to attract the
8361 viewers attention to the piece about to be moved and
8362 then to where it ends up. Too few frames would be less
8366 Tween(start, mid, finish, factor, frames, nFrames)
8367 XPoint * start; XPoint * mid;
8368 XPoint * finish; int factor;
8369 XPoint frames[]; int * nFrames;
8371 int fraction, n, count;
8375 /* Slow in, stepping 1/16th, then 1/8th, ... */
8377 for (n = 0; n < factor; n++)
8379 for (n = 0; n < factor; n++) {
8380 frames[count].x = start->x + (mid->x - start->x) / fraction;
8381 frames[count].y = start->y + (mid->y - start->y) / fraction;
8383 fraction = fraction / 2;
8387 frames[count] = *mid;
8390 /* Slow out, stepping 1/2, then 1/4, ... */
8392 for (n = 0; n < factor; n++) {
8393 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8394 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8396 fraction = fraction * 2;
8401 /* Draw a piece on the screen without disturbing what's there */
8404 SelectGCMask(piece, clip, outline, mask)
8405 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8409 /* Bitmap for piece being moved. */
8410 if (appData.monoMode) {
8411 *mask = *pieceToSolid(piece);
8412 } else if (useImages) {
8414 *mask = xpmMask[piece];
8416 *mask = ximMaskPm[piece];
8419 *mask = *pieceToSolid(piece);
8422 /* GC for piece being moved. Square color doesn't matter, but
8423 since it gets modified we make a copy of the original. */
8425 if (appData.monoMode)
8430 if (appData.monoMode)
8435 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8437 /* Outline only used in mono mode and is not modified */
8439 *outline = bwPieceGC;
8441 *outline = wbPieceGC;
8445 OverlayPiece(piece, clip, outline, dest)
8446 ChessSquare piece; GC clip; GC outline; Drawable dest;
8451 /* Draw solid rectangle which will be clipped to shape of piece */
8452 XFillRectangle(xDisplay, dest, clip,
8453 0, 0, squareSize, squareSize);
8454 if (appData.monoMode)
8455 /* Also draw outline in contrasting color for black
8456 on black / white on white cases */
8457 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8458 0, 0, squareSize, squareSize, 0, 0, 1);
8460 /* Copy the piece */
8465 if(appData.upsideDown && flipView) kind ^= 2;
8466 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8468 0, 0, squareSize, squareSize,
8473 /* Animate the movement of a single piece */
8476 BeginAnimation(anim, piece, startColor, start)
8484 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8485 /* The old buffer is initialised with the start square (empty) */
8486 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8487 anim->prevFrame = *start;
8489 /* The piece will be drawn using its own bitmap as a matte */
8490 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8491 XSetClipMask(xDisplay, anim->pieceGC, mask);
8495 AnimationFrame(anim, frame, piece)
8500 XRectangle updates[4];
8505 /* Save what we are about to draw into the new buffer */
8506 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8507 frame->x, frame->y, squareSize, squareSize,
8510 /* Erase bits of the previous frame */
8511 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8512 /* Where the new frame overlapped the previous,
8513 the contents in newBuf are wrong. */
8514 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8515 overlap.x, overlap.y,
8516 overlap.width, overlap.height,
8518 /* Repaint the areas in the old that don't overlap new */
8519 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8520 for (i = 0; i < count; i++)
8521 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8522 updates[i].x - anim->prevFrame.x,
8523 updates[i].y - anim->prevFrame.y,
8524 updates[i].width, updates[i].height,
8525 updates[i].x, updates[i].y);
8527 /* Easy when no overlap */
8528 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8529 0, 0, squareSize, squareSize,
8530 anim->prevFrame.x, anim->prevFrame.y);
8533 /* Save this frame for next time round */
8534 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8535 0, 0, squareSize, squareSize,
8537 anim->prevFrame = *frame;
8539 /* Draw piece over original screen contents, not current,
8540 and copy entire rect. Wipes out overlapping piece images. */
8541 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8542 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8543 0, 0, squareSize, squareSize,
8544 frame->x, frame->y);
8548 EndAnimation (anim, finish)
8552 XRectangle updates[4];
8557 /* The main code will redraw the final square, so we
8558 only need to erase the bits that don't overlap. */
8559 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8560 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8561 for (i = 0; i < count; i++)
8562 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8563 updates[i].x - anim->prevFrame.x,
8564 updates[i].y - anim->prevFrame.y,
8565 updates[i].width, updates[i].height,
8566 updates[i].x, updates[i].y);
8568 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8569 0, 0, squareSize, squareSize,
8570 anim->prevFrame.x, anim->prevFrame.y);
8575 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8577 ChessSquare piece; int startColor;
8578 XPoint * start; XPoint * finish;
8579 XPoint frames[]; int nFrames;
8583 BeginAnimation(anim, piece, startColor, start);
8584 for (n = 0; n < nFrames; n++) {
8585 AnimationFrame(anim, &(frames[n]), piece);
8586 FrameDelay(appData.animSpeed);
8588 EndAnimation(anim, finish);
8592 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8595 ChessSquare piece = board[fromY][toY];
8596 board[fromY][toY] = EmptySquare;
8597 DrawPosition(FALSE, board);
8599 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8600 y = lineGap + toY * (squareSize + lineGap);
8602 x = lineGap + toX * (squareSize + lineGap);
8603 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8605 for(i=1; i<4*kFactor; i++) {
8606 int r = squareSize * 9 * i/(20*kFactor - 5);
8607 XFillArc(xDisplay, xBoardWindow, highlineGC,
8608 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8609 FrameDelay(appData.animSpeed);
8611 board[fromY][toY] = piece;
8614 /* Main control logic for deciding what to animate and how */
8617 AnimateMove(board, fromX, fromY, toX, toY)
8626 XPoint start, finish, mid;
8627 XPoint frames[kFactor * 2 + 1];
8628 int nFrames, startColor, endColor;
8630 /* Are we animating? */
8631 if (!appData.animate || appData.blindfold)
8634 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8635 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8636 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8638 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8639 piece = board[fromY][fromX];
8640 if (piece >= EmptySquare) return;
8645 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8648 if (appData.debugMode) {
8649 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8650 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8651 piece, fromX, fromY, toX, toY); }
8653 ScreenSquare(fromX, fromY, &start, &startColor);
8654 ScreenSquare(toX, toY, &finish, &endColor);
8657 /* Knight: make straight movement then diagonal */
8658 if (abs(toY - fromY) < abs(toX - fromX)) {
8659 mid.x = start.x + (finish.x - start.x) / 2;
8663 mid.y = start.y + (finish.y - start.y) / 2;
8666 mid.x = start.x + (finish.x - start.x) / 2;
8667 mid.y = start.y + (finish.y - start.y) / 2;
8670 /* Don't use as many frames for very short moves */
8671 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8672 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8674 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8675 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8676 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8678 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8679 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8682 /* Be sure end square is redrawn */
8683 damage[0][toY][toX] = True;
8687 DragPieceBegin(x, y, instantly)
8688 int x; int y; Boolean instantly;
8690 int boardX, boardY, color;
8693 /* Are we animating? */
8694 if (!appData.animateDragging || appData.blindfold)
8697 /* Figure out which square we start in and the
8698 mouse position relative to top left corner. */
8699 BoardSquare(x, y, &boardX, &boardY);
8700 player.startBoardX = boardX;
8701 player.startBoardY = boardY;
8702 ScreenSquare(boardX, boardY, &corner, &color);
8703 player.startSquare = corner;
8704 player.startColor = color;
8705 /* As soon as we start dragging, the piece will jump slightly to
8706 be centered over the mouse pointer. */
8707 player.mouseDelta.x = squareSize/2;
8708 player.mouseDelta.y = squareSize/2;
8709 /* Initialise animation */
8710 player.dragPiece = PieceForSquare(boardX, boardY);
8712 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8713 player.dragActive = True;
8714 BeginAnimation(&player, player.dragPiece, color, &corner);
8715 /* Mark this square as needing to be redrawn. Note that
8716 we don't remove the piece though, since logically (ie
8717 as seen by opponent) the move hasn't been made yet. */
8718 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8719 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8720 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8721 corner.x, corner.y, squareSize, squareSize,
8722 0, 0); // [HGM] zh: unstack in stead of grab
8723 if(gatingPiece != EmptySquare) {
8724 /* Kludge alert: When gating we want the introduced
8725 piece to appear on the from square. To generate an
8726 image of it, we draw it on the board, copy the image,
8727 and draw the original piece again. */
8728 ChessSquare piece = boards[currentMove][boardY][boardX];
8729 DrawSquare(boardY, boardX, gatingPiece, 0);
8730 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8731 corner.x, corner.y, squareSize, squareSize, 0, 0);
8732 DrawSquare(boardY, boardX, piece, 0);
8734 damage[0][boardY][boardX] = True;
8736 player.dragActive = False;
8741 ChangeDragPiece(ChessSquare piece)
8744 player.dragPiece = piece;
8745 /* The piece will be drawn using its own bitmap as a matte */
8746 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8747 XSetClipMask(xDisplay, player.pieceGC, mask);
8756 /* Are we animating? */
8757 if (!appData.animateDragging || appData.blindfold)
8761 if (! player.dragActive)
8763 /* Move piece, maintaining same relative position
8764 of mouse within square */
8765 corner.x = x - player.mouseDelta.x;
8766 corner.y = y - player.mouseDelta.y;
8767 AnimationFrame(&player, &corner, player.dragPiece);
8769 if (appData.highlightDragging) {
8771 BoardSquare(x, y, &boardX, &boardY);
8772 SetHighlights(fromX, fromY, boardX, boardY);
8781 int boardX, boardY, color;
8784 /* Are we animating? */
8785 if (!appData.animateDragging || appData.blindfold)
8789 if (! player.dragActive)
8791 /* Last frame in sequence is square piece is
8792 placed on, which may not match mouse exactly. */
8793 BoardSquare(x, y, &boardX, &boardY);
8794 ScreenSquare(boardX, boardY, &corner, &color);
8795 EndAnimation(&player, &corner);
8797 /* Be sure end square is redrawn */
8798 damage[0][boardY][boardX] = True;
8800 /* This prevents weird things happening with fast successive
8801 clicks which on my Sun at least can cause motion events
8802 without corresponding press/release. */
8803 player.dragActive = False;
8806 /* Handle expose event while piece being dragged */
8811 if (!player.dragActive || appData.blindfold)
8814 /* What we're doing: logically, the move hasn't been made yet,
8815 so the piece is still in it's original square. But visually
8816 it's being dragged around the board. So we erase the square
8817 that the piece is on and draw it at the last known drag point. */
8818 BlankSquare(player.startSquare.x, player.startSquare.y,
8819 player.startColor, EmptySquare, xBoardWindow, 1);
8820 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8821 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8824 #include <sys/ioctl.h>
8825 int get_term_width()
8827 int fd, default_width;
8830 default_width = 79; // this is FICS default anyway...
8832 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8834 if (!ioctl(fd, TIOCGSIZE, &win))
8835 default_width = win.ts_cols;
8836 #elif defined(TIOCGWINSZ)
8838 if (!ioctl(fd, TIOCGWINSZ, &win))
8839 default_width = win.ws_col;
8841 return default_width;
8847 static int old_width = 0;
8848 int new_width = get_term_width();
8850 if (old_width != new_width)
8851 ics_printf("set width %d\n", new_width);
8852 old_width = new_width;
8855 void NotifyFrontendLogin()
8860 /* [AS] Arrow highlighting support */
8862 static double A_WIDTH = 5; /* Width of arrow body */
8864 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8865 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8867 static double Sqr( double x )
8872 static int Round( double x )
8874 return (int) (x + 0.5);
8877 void SquareToPos(int rank, int file, int *x, int *y)
8880 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8881 *y = lineGap + rank * (squareSize + lineGap);
8883 *x = lineGap + file * (squareSize + lineGap);
8884 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8888 /* Draw an arrow between two points using current settings */
8889 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8892 double dx, dy, j, k, x, y;
8895 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8897 arrow[0].x = s_x + A_WIDTH + 0.5;
8900 arrow[1].x = s_x + A_WIDTH + 0.5;
8901 arrow[1].y = d_y - h;
8903 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8904 arrow[2].y = d_y - h;
8909 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8910 arrow[5].y = d_y - h;
8912 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8913 arrow[4].y = d_y - h;
8915 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8918 else if( d_y == s_y ) {
8919 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8922 arrow[0].y = s_y + A_WIDTH + 0.5;
8924 arrow[1].x = d_x - w;
8925 arrow[1].y = s_y + A_WIDTH + 0.5;
8927 arrow[2].x = d_x - w;
8928 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8933 arrow[5].x = d_x - w;
8934 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8936 arrow[4].x = d_x - w;
8937 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8940 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8943 /* [AS] Needed a lot of paper for this! :-) */
8944 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8945 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8947 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8949 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8954 arrow[0].x = Round(x - j);
8955 arrow[0].y = Round(y + j*dx);
8957 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8958 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8961 x = (double) d_x - k;
8962 y = (double) d_y - k*dy;
8965 x = (double) d_x + k;
8966 y = (double) d_y + k*dy;
8969 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8971 arrow[6].x = Round(x - j);
8972 arrow[6].y = Round(y + j*dx);
8974 arrow[2].x = Round(arrow[6].x + 2*j);
8975 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8977 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8978 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8983 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8984 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8987 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8988 // Polygon( hdc, arrow, 7 );
8991 /* [AS] Draw an arrow between two squares */
8992 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
8994 int s_x, s_y, d_x, d_y, hor, vert, i;
8996 if( s_col == d_col && s_row == d_row ) {
9000 /* Get source and destination points */
9001 SquareToPos( s_row, s_col, &s_x, &s_y);
9002 SquareToPos( d_row, d_col, &d_x, &d_y);
9005 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
9007 else if( d_y < s_y ) {
9008 d_y += squareSize / 2 + squareSize / 4;
9011 d_y += squareSize / 2;
9015 d_x += squareSize / 2 - squareSize / 4;
9017 else if( d_x < s_x ) {
9018 d_x += squareSize / 2 + squareSize / 4;
9021 d_x += squareSize / 2;
9024 s_x += squareSize / 2;
9025 s_y += squareSize / 2;
9028 A_WIDTH = squareSize / 14.; //[HGM] make float
9030 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
9032 hor = 64*s_col + 32; vert = 64*s_row + 32;
9033 for(i=0; i<= 64; i++) {
9034 damage[0][vert+6>>6][hor+6>>6] = True;
9035 damage[0][vert-6>>6][hor+6>>6] = True;
9036 damage[0][vert+6>>6][hor-6>>6] = True;
9037 damage[0][vert-6>>6][hor-6>>6] = True;
9038 hor += d_col - s_col; vert += d_row - s_row;
9042 Boolean IsDrawArrowEnabled()
9044 return appData.highlightMoveWithArrow && squareSize >= 32;
9047 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
9049 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
9050 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
9053 void UpdateLogos(int displ)
9055 return; // no logos in XBoard yet