2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
66 # if HAVE_SYS_SOCKET_H
67 # include <sys/socket.h>
68 # include <netinet/in.h>
70 # else /* not HAVE_SYS_SOCKET_H */
71 # if HAVE_LAN_SOCKET_H
72 # include <lan/socket.h>
74 # include <lan/netdb.h>
75 # else /* not HAVE_LAN_SOCKET_H */
76 # define OMIT_SOCKETS 1
77 # endif /* not HAVE_LAN_SOCKET_H */
78 # endif /* not HAVE_SYS_SOCKET_H */
79 #endif /* !OMIT_SOCKETS */
84 #else /* not STDC_HEADERS */
85 extern char *getenv();
88 # else /* not HAVE_STRING_H */
90 # endif /* not HAVE_STRING_H */
91 #endif /* not STDC_HEADERS */
94 # include <sys/fcntl.h>
95 #else /* not HAVE_SYS_FCNTL_H */
98 # endif /* HAVE_FCNTL_H */
99 #endif /* not HAVE_SYS_FCNTL_H */
101 #if HAVE_SYS_SYSTEMINFO_H
102 # include <sys/systeminfo.h>
103 #endif /* HAVE_SYS_SYSTEMINFO_H */
105 #if TIME_WITH_SYS_TIME
106 # include <sys/time.h>
110 # include <sys/time.h>
121 # include <sys/wait.h>
126 # define NAMLEN(dirent) strlen((dirent)->d_name)
127 # define HAVE_DIR_STRUCT
129 # define dirent direct
130 # define NAMLEN(dirent) (dirent)->d_namlen
132 # include <sys/ndir.h>
133 # define HAVE_DIR_STRUCT
136 # include <sys/dir.h>
137 # define HAVE_DIR_STRUCT
141 # define HAVE_DIR_STRUCT
149 #include <X11/Intrinsic.h>
150 #include <X11/StringDefs.h>
151 #include <X11/Shell.h>
152 #include <X11/cursorfont.h>
153 #include <X11/Xatom.h>
154 #include <X11/Xmu/Atoms.h>
156 #include <X11/Xaw3d/Dialog.h>
157 #include <X11/Xaw3d/Form.h>
158 #include <X11/Xaw3d/List.h>
159 #include <X11/Xaw3d/Label.h>
160 #include <X11/Xaw3d/SimpleMenu.h>
161 #include <X11/Xaw3d/SmeBSB.h>
162 #include <X11/Xaw3d/SmeLine.h>
163 #include <X11/Xaw3d/Box.h>
164 #include <X11/Xaw3d/MenuButton.h>
165 #include <X11/Xaw3d/Text.h>
166 #include <X11/Xaw3d/AsciiText.h>
168 #include <X11/Xaw/Dialog.h>
169 #include <X11/Xaw/Form.h>
170 #include <X11/Xaw/List.h>
171 #include <X11/Xaw/Label.h>
172 #include <X11/Xaw/SimpleMenu.h>
173 #include <X11/Xaw/SmeBSB.h>
174 #include <X11/Xaw/SmeLine.h>
175 #include <X11/Xaw/Box.h>
176 #include <X11/Xaw/MenuButton.h>
177 #include <X11/Xaw/Text.h>
178 #include <X11/Xaw/AsciiText.h>
181 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
186 #include "pixmaps/pixmaps.h"
187 #define IMAGE_EXT "xpm"
189 #define IMAGE_EXT "xim"
190 #include "bitmaps/bitmaps.h"
193 #include "bitmaps/icon_white.bm"
194 #include "bitmaps/icon_black.bm"
195 #include "bitmaps/checkmark.bm"
197 #include "frontend.h"
199 #include "backendz.h"
203 #include "xgamelist.h"
204 #include "xhistory.h"
205 #include "xedittags.h"
208 // must be moved to xengineoutput.h
210 void EngineOutputProc P((Widget w, XEvent *event,
211 String *prms, Cardinal *nprms));
212 void EvalGraphProc P((Widget w, XEvent *event,
213 String *prms, Cardinal *nprms));
220 #define usleep(t) _sleep2(((t)+500)/1000)
224 # define _(s) gettext (s)
225 # define N_(s) gettext_noop (s)
243 int main P((int argc, char **argv));
244 FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
245 char *init_path, char *filter, char *mode, int (*show_entry)(), char **name_return));
246 RETSIGTYPE CmailSigHandler P((int sig));
247 RETSIGTYPE IntSigHandler P((int sig));
248 RETSIGTYPE TermSizeSigHandler P((int sig));
249 void CreateGCs P((int redo));
250 void CreateAnyPieces P((void));
251 void CreateXIMPieces P((void));
252 void CreateXPMPieces P((void));
253 void CreateXPMBoard P((char *s, int n));
254 void CreatePieces P((void));
255 void CreatePieceMenus P((void));
256 Widget CreateMenuBar P((Menu *mb));
257 Widget CreateButtonBar P ((MenuItem *mi));
259 char *InsertPxlSize P((char *pattern, int targetPxlSize));
260 XFontSet CreateFontSet P((char *base_fnt_lst));
262 char *FindFont P((char *pattern, int targetPxlSize));
264 void PieceMenuPopup P((Widget w, XEvent *event,
265 String *params, Cardinal *num_params));
266 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
267 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
268 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
269 u_int wreq, u_int hreq));
270 void CreateGrid P((void));
271 int EventToSquare P((int x, int limit));
272 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
273 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
274 void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
275 void HandleUserMove P((Widget w, XEvent *event,
276 String *prms, Cardinal *nprms));
277 void AnimateUserMove P((Widget w, XEvent * event,
278 String * params, Cardinal * nParams));
279 void HandlePV P((Widget w, XEvent * event,
280 String * params, Cardinal * nParams));
281 void SelectPV P((Widget w, XEvent * event,
282 String * params, Cardinal * nParams));
283 void StopPV P((Widget w, XEvent * event,
284 String * params, Cardinal * nParams));
285 void WhiteClock P((Widget w, XEvent *event,
286 String *prms, Cardinal *nprms));
287 void BlackClock P((Widget w, XEvent *event,
288 String *prms, Cardinal *nprms));
289 void DrawPositionProc P((Widget w, XEvent *event,
290 String *prms, Cardinal *nprms));
291 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
293 void CommentClick P((Widget w, XEvent * event,
294 String * params, Cardinal * nParams));
295 void CommentPopUp P((char *title, char *label));
296 void CommentPopDown P((void));
297 void ICSInputBoxPopUp P((void));
298 void ICSInputBoxPopDown P((void));
299 void FileNamePopUp P((char *label, char *def, char *filter,
300 FileProc proc, char *openMode));
301 void FileNamePopDown P((void));
302 void FileNameCallback P((Widget w, XtPointer client_data,
303 XtPointer call_data));
304 void FileNameAction P((Widget w, XEvent *event,
305 String *prms, Cardinal *nprms));
306 void AskQuestionReplyAction P((Widget w, XEvent *event,
307 String *prms, Cardinal *nprms));
308 void AskQuestionProc P((Widget w, XEvent *event,
309 String *prms, Cardinal *nprms));
310 void AskQuestionPopDown P((void));
311 void PromotionPopDown P((void));
312 void PromotionCallback P((Widget w, XtPointer client_data,
313 XtPointer call_data));
314 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
315 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
316 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
317 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
319 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
321 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
323 void LoadPositionProc P((Widget w, XEvent *event,
324 String *prms, Cardinal *nprms));
325 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
327 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
329 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
331 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
333 void PastePositionProc P((Widget w, XEvent *event, String *prms,
335 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
336 void CopyGameListProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
337 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
338 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
339 void SavePositionProc P((Widget w, XEvent *event,
340 String *prms, Cardinal *nprms));
341 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
342 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
344 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
345 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
346 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
348 void MachineWhiteProc P((Widget w, XEvent *event,
349 String *prms, Cardinal *nprms));
350 void AnalyzeModeProc P((Widget w, XEvent *event,
351 String *prms, Cardinal *nprms));
352 void AnalyzeFileProc P((Widget w, XEvent *event,
353 String *prms, Cardinal *nprms));
354 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
356 void MatchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
357 void MatchOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
358 void IcsClientProc P((Widget w, XEvent *event, String *prms,
360 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
361 void EditPositionProc P((Widget w, XEvent *event,
362 String *prms, Cardinal *nprms));
363 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
364 void EditCommentProc P((Widget w, XEvent *event,
365 String *prms, Cardinal *nprms));
366 void IcsInputBoxProc P((Widget w, XEvent *event,
367 String *prms, Cardinal *nprms));
368 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
369 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
370 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
371 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
372 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
373 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
374 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
375 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
376 void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
377 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
378 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
379 void TypeInProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
380 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
381 void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
382 void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
383 void StopObservingProc P((Widget w, XEvent *event, String *prms,
385 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
387 void UploadProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
388 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
389 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
390 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
391 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
392 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
393 void AnnotateProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
394 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
396 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
398 void MoveNowProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
399 void AlwaysQueenProc P((Widget w, XEvent *event, String *prms,
401 void AnimateDraggingProc P((Widget w, XEvent *event, String *prms,
403 void AnimateMovingProc P((Widget w, XEvent *event, String *prms,
405 void AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
406 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
407 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
408 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
410 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
411 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
413 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
415 void HighlightArrowProc P((Widget w, XEvent *event, String *prms,
417 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
418 //void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
419 void OneClickProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
420 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
422 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
424 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
426 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
428 //void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
429 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
431 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
433 void HideThinkingProc P((Widget w, XEvent *event, String *prms,
435 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
437 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
438 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
439 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
440 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
441 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
442 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
443 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
444 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
445 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
446 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
447 void Iconify P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
448 void DisplayMove P((int moveNumber));
449 void DisplayTitle P((char *title));
450 void ICSInitScript P((void));
451 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
452 void ErrorPopUp P((char *title, char *text, int modal));
453 void ErrorPopDown P((void));
454 static char *ExpandPathName P((char *path));
455 static void CreateAnimVars P((void));
456 static void DragPieceMove P((int x, int y));
457 static void DrawDragPiece P((void));
458 char *ModeToWidgetName P((GameMode mode));
459 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
460 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
461 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
462 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
463 void OptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
464 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
465 void IcsTextProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
466 void LoadEngineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
467 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
468 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
469 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
470 void IcsOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
471 void SoundOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
472 void BoardOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
473 void LoadOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
474 void SaveOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
475 void EditBookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
476 void GameListOptionsPopDown P(());
477 void ShufflePopDown P(());
478 void TimeControlPopDown P(());
479 void GenericPopDown P(());
480 void update_ics_width P(());
481 int get_term_width P(());
482 int CopyMemoProc P(());
483 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
484 Boolean IsDrawArrowEnabled P(());
487 * XBoard depends on Xt R4 or higher
489 int xtVersion = XtSpecificationRelease;
494 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
495 jailSquareColor, highlightSquareColor, premoveHighlightColor;
496 Pixel lowTimeWarningColor;
497 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
498 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
499 wjPieceGC, bjPieceGC, prelineGC, countGC;
500 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
501 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
502 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
503 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
504 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
505 ICSInputShell, fileNameShell, askQuestionShell;
506 Widget historyShell, evalGraphShell, gameListShell;
507 int hOffset; // [HGM] dual
508 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
509 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
510 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
512 XFontSet fontSet, clockFontSet;
515 XFontStruct *clockFontStruct;
517 Font coordFontID, countFontID;
518 XFontStruct *coordFontStruct, *countFontStruct;
519 XtAppContext appContext;
521 char *oldICSInteractionTitle;
525 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
527 Position commentX = -1, commentY = -1;
528 Dimension commentW, commentH;
529 typedef unsigned int BoardSize;
531 Boolean chessProgram;
533 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
534 int squareSize, smallLayout = 0, tinyLayout = 0,
535 marginW, marginH, // [HGM] for run-time resizing
536 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
537 ICSInputBoxUp = False, askQuestionUp = False,
538 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
539 errorUp = False, errorExitStatus = -1, lineGap, defaultLineGap;
540 Pixel timerForegroundPixel, timerBackgroundPixel;
541 Pixel buttonForegroundPixel, buttonBackgroundPixel;
542 char *chessDir, *programName, *programVersion,
543 *gameCopyFilename, *gamePasteFilename;
544 Boolean alwaysOnTop = False;
545 Boolean saveSettingsOnExit;
546 char *settingsFileName;
547 char *icsTextMenuString;
549 char *firstChessProgramNames;
550 char *secondChessProgramNames;
552 WindowPlacement wpMain;
553 WindowPlacement wpConsole;
554 WindowPlacement wpComment;
555 WindowPlacement wpMoveHistory;
556 WindowPlacement wpEvalGraph;
557 WindowPlacement wpEngineOutput;
558 WindowPlacement wpGameList;
559 WindowPlacement wpTags;
561 extern Widget shells[];
562 extern Boolean shellUp[];
566 Pixmap pieceBitmap[2][(int)BlackPawn];
567 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
568 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
569 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
570 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
571 Pixmap xpmBoardBitmap[2];
572 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
573 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
574 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
575 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
576 XImage *ximLightSquare, *ximDarkSquare;
579 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
580 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
582 #define White(piece) ((int)(piece) < (int)BlackPawn)
584 /* Variables for doing smooth animation. This whole thing
585 would be much easier if the board was double-buffered,
586 but that would require a fairly major rewrite. */
591 GC blitGC, pieceGC, outlineGC;
592 XPoint startSquare, prevFrame, mouseDelta;
596 int startBoardX, startBoardY;
599 /* There can be two pieces being animated at once: a player
600 can begin dragging a piece before the remote opponent has moved. */
602 static AnimState game, player;
604 /* Bitmaps for use as masks when drawing XPM pieces.
605 Need one for each black and white piece. */
606 static Pixmap xpmMask[BlackKing + 1];
608 /* This magic number is the number of intermediate frames used
609 in each half of the animation. For short moves it's reduced
610 by 1. The total number of frames will be factor * 2 + 1. */
613 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
615 MenuItem fileMenu[] = {
616 {N_("New Game Ctrl+N"), "New Game", ResetProc},
617 {N_("New Shuffle Game ..."), "New Shuffle Game", ShuffleMenuProc},
618 {N_("New Variant ... Alt+Shift+V"), "New Variant", NewVariantProc}, // [HGM] variant: not functional yet
619 {"----", NULL, NothingProc},
620 {N_("Load Game Ctrl+O"), "Load Game", LoadGameProc},
621 {N_("Load Position Ctrl+Shift+O"), "Load Position", LoadPositionProc},
622 // {N_("Load Next Game"), "Load Next Game", LoadNextGameProc},
623 // {N_("Load Previous Game"), "Load Previous Game", LoadPrevGameProc},
624 // {N_("Reload Same Game"), "Reload Same Game", ReloadGameProc},
625 {N_("Next Position Shift+PgDn"), "Load Next Position", LoadNextPositionProc},
626 {N_("Prev Position Shift+PgUp"), "Load Previous Position", LoadPrevPositionProc},
627 {"----", NULL, NothingProc},
628 // {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
629 {N_("Save Game Ctrl+S"), "Save Game", SaveGameProc},
630 {N_("Save Position Ctrl+Shift+S"), "Save Position", SavePositionProc},
631 {"----", NULL, NothingProc},
632 {N_("Mail Move"), "Mail Move", MailMoveProc},
633 {N_("Reload CMail Message"), "Reload CMail Message", ReloadCmailMsgProc},
634 {"----", NULL, NothingProc},
635 {N_("Quit Ctr+Q"), "Exit", QuitProc},
639 MenuItem editMenu[] = {
640 {N_("Copy Game Ctrl+C"), "Copy Game", CopyGameProc},
641 {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
642 {N_("Copy Game List"), "Copy Game List", CopyGameListProc},
643 {"----", NULL, NothingProc},
644 {N_("Paste Game Ctrl+V"), "Paste Game", PasteGameProc},
645 {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
646 {"----", NULL, NothingProc},
647 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
648 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
649 {N_("Edit Tags"), "Edit Tags", EditTagsProc},
650 {N_("Edit Comment"), "Edit Comment", EditCommentProc},
651 {N_("Edit Book"), "Edit Book", EditBookProc},
652 {"----", NULL, NothingProc},
653 {N_("Revert Home"), "Revert", RevertProc},
654 {N_("Annotate"), "Annotate", AnnotateProc},
655 {N_("Truncate Game End"), "Truncate Game", TruncateGameProc},
656 {"----", NULL, NothingProc},
657 {N_("Backward Alt+Left"), "Backward", BackwardProc},
658 {N_("Forward Alt+Right"), "Forward", ForwardProc},
659 {N_("Back to Start Alt+Home"), "Back to Start", ToStartProc},
660 {N_("Forward to End Alt+End"), "Forward to End", ToEndProc},
664 MenuItem viewMenu[] = {
665 {N_("Flip View F2"), "Flip View", FlipViewProc},
666 {"----", NULL, NothingProc},
667 {N_("Engine Output Alt+Shift+O"), "Show Engine Output", EngineOutputProc},
668 {N_("Move History Alt+Shift+H"), "Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
669 {N_("Evaluation Graph Alt+Shift+E"), "Show Evaluation Graph", EvalGraphProc},
670 {N_("Game List Alt+Shift+G"), "Show Game List", ShowGameListProc},
671 {N_("ICS text menu"), "ICStex", IcsTextProc},
672 {"----", NULL, NothingProc},
673 {N_("Tags"), "Show Tags", EditTagsProc},
674 {N_("Comments"), "Show Comments", EditCommentProc},
675 {N_("ICS Input Box"), "ICS Input Box", IcsInputBoxProc},
676 {"----", NULL, NothingProc},
677 {N_("Board..."), "Board Options", BoardOptionsProc},
678 {N_("Game List Tags..."), "Game List", GameListOptionsPopUp},
682 MenuItem modeMenu[] = {
683 {N_("Machine White Ctrl+W"), "Machine White", MachineWhiteProc},
684 {N_("Machine Black Ctrl+B"), "Machine Black", MachineBlackProc},
685 {N_("Two Machines Ctrl+T"), "Two Machines", TwoMachinesProc},
686 {N_("Analysis Mode Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
687 {N_("Analyze File Ctrl+F"), "Analyze File", AnalyzeFileProc },
688 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
689 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
690 {N_("Training"), "Training", TrainingProc},
691 {N_("ICS Client"), "ICS Client", IcsClientProc},
692 {"----", NULL, NothingProc},
693 {N_("Machine Match"), "Machine Match", MatchProc},
694 {N_("Pause Pause"), "Pause", PauseProc},
698 MenuItem actionMenu[] = {
699 {N_("Accept F3"), "Accept", AcceptProc},
700 {N_("Decline F4"), "Decline", DeclineProc},
701 {N_("Rematch F12"), "Rematch", RematchProc},
702 {"----", NULL, NothingProc},
703 {N_("Call Flag F5"), "Call Flag", CallFlagProc},
704 {N_("Draw F6"), "Draw", DrawProc},
705 {N_("Adjourn F7"), "Adjourn", AdjournProc},
706 {N_("Abort F8"),"Abort", AbortProc},
707 {N_("Resign F9"), "Resign", ResignProc},
708 {"----", NULL, NothingProc},
709 {N_("Stop Observing F10"), "Stop Observing", StopObservingProc},
710 {N_("Stop Examining F11"), "Stop Examining", StopExaminingProc},
711 {N_("Upload to Examine"), "Upload to Examine", UploadProc},
712 {"----", NULL, NothingProc},
713 {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
714 {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
715 {N_("Adjudicate Draw"), "Adjudicate Draw", AdjuDrawProc},
719 MenuItem engineMenu[] = {
720 {N_("Load New Engine ..."), "Load Engine", LoadEngineProc},
721 {"----", NULL, NothingProc},
722 {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
723 {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
724 {"----", NULL, NothingProc},
725 {N_("Hint"), "Hint", HintProc},
726 {N_("Book"), "Book", BookProc},
727 {"----", NULL, NothingProc},
728 {N_("Move Now Ctrl+M"), "Move Now", MoveNowProc},
729 {N_("Retract Move Ctrl+X"), "Retract Move", RetractMoveProc},
733 MenuItem optionsMenu[] = {
734 #define OPTIONSDIALOG
736 {N_("General ..."), "General", OptionsProc},
738 {N_("Time Control ... Alt+Shift+T"), "Time Control", TimeControlProc},
739 {N_("Common Engine ... Alt+Shift+U"), "Common Engine", UciMenuProc},
740 {N_("Adjudications ... Alt+Shift+J"), "Adjudications", EngineMenuProc},
741 {N_("ICS ..."), "ICS", IcsOptionsProc},
742 {N_("Match ..."), "Match", MatchOptionsProc},
743 {N_("Load Game ..."), "Load Game", LoadOptionsProc},
744 {N_("Save Game ..."), "Save Game", SaveOptionsProc},
745 // {N_(" ..."), "", OptionsProc},
746 {N_("Game List ..."), "Game List", GameListOptionsPopUp},
747 {N_("Sounds ..."), "Sounds", SoundOptionsProc},
748 {"----", NULL, NothingProc},
749 #ifndef OPTIONSDIALOG
750 {N_("Always Queen Ctrl+Shift+Q"), "Always Queen", AlwaysQueenProc},
751 {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
752 {N_("Animate Moving Ctrl+Shift+A"), "Animate Moving", AnimateMovingProc},
753 {N_("Auto Flag Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
754 {N_("Auto Flip View"), "Auto Flip View", AutoflipProc},
755 {N_("Blindfold"), "Blindfold", BlindfoldProc},
756 {N_("Flash Moves"), "Flash Moves", FlashMovesProc},
758 {N_("Highlight Dragging"), "Highlight Dragging", HighlightDraggingProc},
760 {N_("Highlight Last Move"), "Highlight Last Move", HighlightLastMoveProc},
761 {N_("Highlight With Arrow"), "Arrow", HighlightArrowProc},
762 {N_("Move Sound"), "Move Sound", MoveSoundProc},
763 // {N_("ICS Alarm"), "ICS Alarm", IcsAlarmProc},
764 {N_("One-Click Moving"), "OneClick", OneClickProc},
765 {N_("Periodic Updates"), "Periodic Updates", PeriodicUpdatesProc},
766 {N_("Ponder Next Move Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
767 {N_("Popup Exit Message"), "Popup Exit Message", PopupExitMessageProc},
768 {N_("Popup Move Errors"), "Popup Move Errors", PopupMoveErrorsProc},
769 // {N_("Premove"), "Premove", PremoveProc},
770 {N_("Show Coords"), "Show Coords", ShowCoordsProc},
771 {N_("Hide Thinking Ctrl+Shift+H"), "Hide Thinking", HideThinkingProc},
772 {N_("Test Legality Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
773 {"----", NULL, NothingProc},
775 {N_("Save Settings Now"), "Save Settings Now", SaveSettingsProc},
776 {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
780 MenuItem helpMenu[] = {
781 {N_("Info XBoard"), "Info XBoard", InfoProc},
782 {N_("Man XBoard F1"), "Man XBoard", ManProc},
783 {"----", NULL, NothingProc},
784 {N_("About XBoard"), "About XBoard", AboutProc},
789 {N_("File"), "File", fileMenu},
790 {N_("Edit"), "Edit", editMenu},
791 {N_("View"), "View", viewMenu},
792 {N_("Mode"), "Mode", modeMenu},
793 {N_("Action"), "Action", actionMenu},
794 {N_("Engine"), "Engine", engineMenu},
795 {N_("Options"), "Options", optionsMenu},
796 {N_("Help"), "Help", helpMenu},
800 #define PAUSE_BUTTON "P"
801 MenuItem buttonBar[] = {
802 {"<<", "<<", ToStartProc},
803 {"<", "<", BackwardProc},
804 {PAUSE_BUTTON, PAUSE_BUTTON, PauseProc},
805 {">", ">", ForwardProc},
806 {">>", ">>", ToEndProc},
810 #define PIECE_MENU_SIZE 18
811 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
812 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
813 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
814 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
815 N_("Empty square"), N_("Clear board") },
816 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
817 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
818 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
819 N_("Empty square"), N_("Clear board") }
821 /* must be in same order as pieceMenuStrings! */
822 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
823 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
824 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
825 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
826 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
827 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
828 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
829 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
830 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
833 #define DROP_MENU_SIZE 6
834 String dropMenuStrings[DROP_MENU_SIZE] = {
835 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
837 /* must be in same order as dropMenuStrings! */
838 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
839 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
840 WhiteRook, WhiteQueen
848 DropMenuEnables dmEnables[] = {
866 { XtNborderWidth, 0 },
867 { XtNdefaultDistance, 0 },
871 { XtNborderWidth, 0 },
872 { XtNresizable, (XtArgVal) True },
876 { XtNborderWidth, 0 },
882 { XtNjustify, (XtArgVal) XtJustifyRight },
883 { XtNlabel, (XtArgVal) "..." },
884 { XtNresizable, (XtArgVal) True },
885 { XtNresize, (XtArgVal) False }
888 Arg messageArgs[] = {
889 { XtNjustify, (XtArgVal) XtJustifyLeft },
890 { XtNlabel, (XtArgVal) "..." },
891 { XtNresizable, (XtArgVal) True },
892 { XtNresize, (XtArgVal) False }
896 { XtNborderWidth, 0 },
897 { XtNjustify, (XtArgVal) XtJustifyLeft }
900 XtResource clientResources[] = {
901 { "flashCount", "flashCount", XtRInt, sizeof(int),
902 XtOffset(AppDataPtr, flashCount), XtRImmediate,
903 (XtPointer) FLASH_COUNT },
906 XrmOptionDescRec shellOptions[] = {
907 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
908 { "-flash", "flashCount", XrmoptionNoArg, "3" },
909 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
912 XtActionsRec boardActions[] = {
913 { "DrawPosition", DrawPositionProc },
914 { "HandleUserMove", HandleUserMove },
915 { "AnimateUserMove", AnimateUserMove },
916 { "HandlePV", HandlePV },
917 { "SelectPV", SelectPV },
918 { "StopPV", StopPV },
919 { "FileNameAction", FileNameAction },
920 { "AskQuestionProc", AskQuestionProc },
921 { "AskQuestionReplyAction", AskQuestionReplyAction },
922 { "PieceMenuPopup", PieceMenuPopup },
923 { "WhiteClock", WhiteClock },
924 { "BlackClock", BlackClock },
925 { "Iconify", Iconify },
926 { "ResetProc", ResetProc },
927 { "NewVariantProc", NewVariantProc },
928 { "LoadGameProc", LoadGameProc },
929 { "LoadNextGameProc", LoadNextGameProc },
930 { "LoadPrevGameProc", LoadPrevGameProc },
931 { "LoadSelectedProc", LoadSelectedProc },
932 { "SetFilterProc", SetFilterProc },
933 { "ReloadGameProc", ReloadGameProc },
934 { "LoadPositionProc", LoadPositionProc },
935 { "LoadNextPositionProc", LoadNextPositionProc },
936 { "LoadPrevPositionProc", LoadPrevPositionProc },
937 { "ReloadPositionProc", ReloadPositionProc },
938 { "CopyPositionProc", CopyPositionProc },
939 { "PastePositionProc", PastePositionProc },
940 { "CopyGameProc", CopyGameProc },
941 { "CopyGameListProc", CopyGameListProc },
942 { "PasteGameProc", PasteGameProc },
943 { "SaveGameProc", SaveGameProc },
944 { "SavePositionProc", SavePositionProc },
945 { "MailMoveProc", MailMoveProc },
946 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
947 { "QuitProc", QuitProc },
948 { "MachineWhiteProc", MachineWhiteProc },
949 { "MachineBlackProc", MachineBlackProc },
950 { "AnalysisModeProc", AnalyzeModeProc },
951 { "AnalyzeFileProc", AnalyzeFileProc },
952 { "TwoMachinesProc", TwoMachinesProc },
953 { "IcsClientProc", IcsClientProc },
954 { "EditGameProc", EditGameProc },
955 { "EditPositionProc", EditPositionProc },
956 { "TrainingProc", EditPositionProc },
957 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
958 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
959 { "ShowGameListProc", ShowGameListProc },
960 { "ShowMoveListProc", HistoryShowProc},
961 { "EditTagsProc", EditCommentProc },
962 { "EditBookProc", EditBookProc },
963 { "EditCommentProc", EditCommentProc },
964 { "IcsInputBoxProc", IcsInputBoxProc },
965 { "PauseProc", PauseProc },
966 { "AcceptProc", AcceptProc },
967 { "DeclineProc", DeclineProc },
968 { "RematchProc", RematchProc },
969 { "CallFlagProc", CallFlagProc },
970 { "DrawProc", DrawProc },
971 { "AdjournProc", AdjournProc },
972 { "AbortProc", AbortProc },
973 { "ResignProc", ResignProc },
974 { "AdjuWhiteProc", AdjuWhiteProc },
975 { "AdjuBlackProc", AdjuBlackProc },
976 { "AdjuDrawProc", AdjuDrawProc },
977 { "TypeInProc", TypeInProc },
978 { "EnterKeyProc", EnterKeyProc },
979 { "UpKeyProc", UpKeyProc },
980 { "DownKeyProc", DownKeyProc },
981 { "StopObservingProc", StopObservingProc },
982 { "StopExaminingProc", StopExaminingProc },
983 { "UploadProc", UploadProc },
984 { "BackwardProc", BackwardProc },
985 { "ForwardProc", ForwardProc },
986 { "ToStartProc", ToStartProc },
987 { "ToEndProc", ToEndProc },
988 { "RevertProc", RevertProc },
989 { "AnnotateProc", AnnotateProc },
990 { "TruncateGameProc", TruncateGameProc },
991 { "MoveNowProc", MoveNowProc },
992 { "RetractMoveProc", RetractMoveProc },
993 { "EngineMenuProc", (XtActionProc) EngineMenuProc },
994 { "UciMenuProc", (XtActionProc) UciMenuProc },
995 { "TimeControlProc", (XtActionProc) TimeControlProc },
996 { "FlipViewProc", FlipViewProc },
997 { "PonderNextMoveProc", PonderNextMoveProc },
998 #ifndef OPTIONSDIALOG
999 { "AlwaysQueenProc", AlwaysQueenProc },
1000 { "AnimateDraggingProc", AnimateDraggingProc },
1001 { "AnimateMovingProc", AnimateMovingProc },
1002 { "AutoflagProc", AutoflagProc },
1003 { "AutoflipProc", AutoflipProc },
1004 { "BlindfoldProc", BlindfoldProc },
1005 { "FlashMovesProc", FlashMovesProc },
1007 { "HighlightDraggingProc", HighlightDraggingProc },
1009 { "HighlightLastMoveProc", HighlightLastMoveProc },
1010 // { "IcsAlarmProc", IcsAlarmProc },
1011 { "MoveSoundProc", MoveSoundProc },
1012 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
1013 { "PopupExitMessageProc", PopupExitMessageProc },
1014 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
1015 // { "PremoveProc", PremoveProc },
1016 { "ShowCoordsProc", ShowCoordsProc },
1017 { "ShowThinkingProc", ShowThinkingProc },
1018 { "HideThinkingProc", HideThinkingProc },
1019 { "TestLegalityProc", TestLegalityProc },
1021 { "SaveSettingsProc", SaveSettingsProc },
1022 { "SaveOnExitProc", SaveOnExitProc },
1023 { "InfoProc", InfoProc },
1024 { "ManProc", ManProc },
1025 { "HintProc", HintProc },
1026 { "BookProc", BookProc },
1027 { "AboutGameProc", AboutGameProc },
1028 { "AboutProc", AboutProc },
1029 { "DebugProc", DebugProc },
1030 { "NothingProc", NothingProc },
1031 { "CommentClick", (XtActionProc) CommentClick },
1032 { "CommentPopDown", (XtActionProc) CommentPopDown },
1033 { "TagsPopDown", (XtActionProc) TagsPopDown },
1034 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1035 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1036 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1037 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1038 { "GameListPopDown", (XtActionProc) GameListPopDown },
1039 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1040 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1041 { "HistoryPopDown", (XtActionProc) HistoryPopDown },
1042 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1043 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1044 { "ShufflePopDown", (XtActionProc) ShufflePopDown },
1045 { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
1046 { "GenericPopDown", (XtActionProc) GenericPopDown },
1047 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1050 char globalTranslations[] =
1051 ":<Key>F9: ResignProc() \n \
1052 :Ctrl<Key>n: ResetProc() \n \
1053 :Meta<Key>V: NewVariantProc() \n \
1054 :Ctrl<Key>o: LoadGameProc() \n \
1055 :Meta<Key>Next: LoadNextGameProc() \n \
1056 :Meta<Key>Prior: LoadPrevGameProc() \n \
1057 :Ctrl<Key>s: SaveGameProc() \n \
1058 :Ctrl<Key>c: CopyGameProc() \n \
1059 :Ctrl<Key>v: PasteGameProc() \n \
1060 :Ctrl<Key>O: LoadPositionProc() \n \
1061 :Shift<Key>Next: LoadNextPositionProc() \n \
1062 :Shift<Key>Prior: LoadPrevPositionProc() \n \
1063 :Ctrl<Key>S: SavePositionProc() \n \
1064 :Ctrl<Key>C: CopyPositionProc() \n \
1065 :Ctrl<Key>V: PastePositionProc() \n \
1066 :Ctrl<Key>q: QuitProc() \n \
1067 :Ctrl<Key>w: MachineWhiteProc() \n \
1068 :Ctrl<Key>b: MachineBlackProc() \n \
1069 :Ctrl<Key>t: TwoMachinesProc() \n \
1070 :Ctrl<Key>a: AnalysisModeProc() \n \
1071 :Ctrl<Key>f: AnalyzeFileProc() \n \
1072 :Ctrl<Key>e: EditGameProc() \n \
1073 :Ctrl<Key>E: EditPositionProc() \n \
1074 :Meta<Key>O: EngineOutputProc() \n \
1075 :Meta<Key>E: EvalGraphProc() \n \
1076 :Meta<Key>G: ShowGameListProc() \n \
1077 :Meta<Key>H: ShowMoveListProc() \n \
1078 :<Key>Pause: PauseProc() \n \
1079 :<Key>F3: AcceptProc() \n \
1080 :<Key>F4: DeclineProc() \n \
1081 :<Key>F12: RematchProc() \n \
1082 :<Key>F5: CallFlagProc() \n \
1083 :<Key>F6: DrawProc() \n \
1084 :<Key>F7: AdjournProc() \n \
1085 :<Key>F8: AbortProc() \n \
1086 :<Key>F10: StopObservingProc() \n \
1087 :<Key>F11: StopExaminingProc() \n \
1088 :Meta Ctrl<Key>F12: DebugProc() \n \
1089 :Meta<Key>End: ToEndProc() \n \
1090 :Meta<Key>Right: ForwardProc() \n \
1091 :Meta<Key>Home: ToStartProc() \n \
1092 :Meta<Key>Left: BackwardProc() \n \
1093 :<Key>Home: RevertProc() \n \
1094 :<Key>End: TruncateGameProc() \n \
1095 :Ctrl<Key>m: MoveNowProc() \n \
1096 :Ctrl<Key>x: RetractMoveProc() \n \
1097 :Meta<Key>J: EngineMenuProc() \n \
1098 :Meta<Key>U: UciMenuProc() \n \
1099 :Meta<Key>T: TimeControlProc() \n \
1100 :Ctrl<Key>P: PonderNextMoveProc() \n "
1101 #ifndef OPTIONSDIALOG
1103 :Ctrl<Key>Q: AlwaysQueenProc() \n \
1104 :Ctrl<Key>F: AutoflagProc() \n \
1105 :Ctrl<Key>A: AnimateMovingProc() \n \
1106 :Ctrl<Key>L: TestLegalityProc() \n \
1107 :Ctrl<Key>H: HideThinkingProc() \n "
1110 :<Key>-: Iconify() \n \
1111 :<Key>F1: ManProc() \n \
1112 :<Key>F2: FlipViewProc() \n \
1113 <KeyDown>.: BackwardProc() \n \
1114 <KeyUp>.: ForwardProc() \n \
1115 Shift<Key>1: AskQuestionProc(\"Direct command\",\
1116 \"Send to chess program:\",,1) \n \
1117 Shift<Key>2: AskQuestionProc(\"Direct command\",\
1118 \"Send to second chess program:\",,2) \n";
1120 char boardTranslations[] =
1121 "<Btn1Down>: HandleUserMove(0) \n \
1122 Shift<Btn1Up>: HandleUserMove(1) \n \
1123 <Btn1Up>: HandleUserMove(0) \n \
1124 <Btn1Motion>: AnimateUserMove() \n \
1125 <Btn3Motion>: HandlePV() \n \
1126 <Btn3Up>: PieceMenuPopup(menuB) \n \
1127 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1128 PieceMenuPopup(menuB) \n \
1129 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1130 PieceMenuPopup(menuW) \n \
1131 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1132 PieceMenuPopup(menuW) \n \
1133 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1134 PieceMenuPopup(menuB) \n";
1136 char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
1137 char blackTranslations[] = "<BtnDown>: BlackClock()\n";
1139 char ICSInputTranslations[] =
1140 "<Key>Up: UpKeyProc() \n "
1141 "<Key>Down: DownKeyProc() \n "
1142 "<Key>Return: EnterKeyProc() \n";
1144 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1145 // as the widget is destroyed before the up-click can call extend-end
1146 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1148 String xboardResources[] = {
1149 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1150 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1151 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1156 /* Max possible square size */
1157 #define MAXSQSIZE 256
1159 static int xpm_avail[MAXSQSIZE];
1161 #ifdef HAVE_DIR_STRUCT
1163 /* Extract piece size from filename */
1165 xpm_getsize(name, len, ext)
1176 if ((p=strchr(name, '.')) == NULL ||
1177 StrCaseCmp(p+1, ext) != 0)
1183 while (*p && isdigit(*p))
1190 /* Setup xpm_avail */
1192 xpm_getavail(dirname, ext)
1200 for (i=0; i<MAXSQSIZE; ++i)
1203 if (appData.debugMode)
1204 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1206 dir = opendir(dirname);
1209 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1210 programName, dirname);
1214 while ((ent=readdir(dir)) != NULL) {
1215 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1216 if (i > 0 && i < MAXSQSIZE)
1226 xpm_print_avail(fp, ext)
1232 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1233 for (i=1; i<MAXSQSIZE; ++i) {
1239 /* Return XPM piecesize closest to size */
1241 xpm_closest_to(dirname, size, ext)
1247 int sm_diff = MAXSQSIZE;
1251 xpm_getavail(dirname, ext);
1253 if (appData.debugMode)
1254 xpm_print_avail(stderr, ext);
1256 for (i=1; i<MAXSQSIZE; ++i) {
1259 diff = (diff<0) ? -diff : diff;
1260 if (diff < sm_diff) {
1268 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1274 #else /* !HAVE_DIR_STRUCT */
1275 /* If we are on a system without a DIR struct, we can't
1276 read the directory, so we can't collect a list of
1277 filenames, etc., so we can't do any size-fitting. */
1279 xpm_closest_to(dirname, size, ext)
1284 fprintf(stderr, _("\
1285 Warning: No DIR structure found on this system --\n\
1286 Unable to autosize for XPM/XIM pieces.\n\
1287 Please report this error to %s.\n\
1288 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
1291 #endif /* HAVE_DIR_STRUCT */
1293 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1294 "magenta", "cyan", "white" };
1298 TextColors textColors[(int)NColorClasses];
1300 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1302 parse_color(str, which)
1306 char *p, buf[100], *d;
1309 if (strlen(str) > 99) /* watch bounds on buf */
1314 for (i=0; i<which; ++i) {
1321 /* Could be looking at something like:
1323 .. in which case we want to stop on a comma also */
1324 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1328 return -1; /* Use default for empty field */
1331 if (which == 2 || isdigit(*p))
1334 while (*p && isalpha(*p))
1339 for (i=0; i<8; ++i) {
1340 if (!StrCaseCmp(buf, cnames[i]))
1341 return which? (i+40) : (i+30);
1343 if (!StrCaseCmp(buf, "default")) return -1;
1345 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1350 parse_cpair(cc, str)
1354 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1355 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1360 /* bg and attr are optional */
1361 textColors[(int)cc].bg = parse_color(str, 1);
1362 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1363 textColors[(int)cc].attr = 0;
1369 /* Arrange to catch delete-window events */
1370 Atom wm_delete_window;
1372 CatchDeleteWindow(Widget w, String procname)
1375 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1376 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1377 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1384 XtSetArg(args[0], XtNiconic, False);
1385 XtSetValues(shellWidget, args, 1);
1387 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1390 //---------------------------------------------------------------------------------------------------------
1391 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1394 #define CW_USEDEFAULT (1<<31)
1395 #define ICS_TEXT_MENU_SIZE 90
1396 #define DEBUG_FILE "xboard.debug"
1397 #define SetCurrentDirectory chdir
1398 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1402 // these two must some day move to frontend.h, when they are implemented
1403 Boolean GameListIsUp();
1405 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1408 // front-end part of option handling
1410 // [HGM] This platform-dependent table provides the location for storing the color info
1411 extern char *crWhite, * crBlack;
1415 &appData.whitePieceColor,
1416 &appData.blackPieceColor,
1417 &appData.lightSquareColor,
1418 &appData.darkSquareColor,
1419 &appData.highlightSquareColor,
1420 &appData.premoveHighlightColor,
1421 &appData.lowTimeWarningColor,
1432 // [HGM] font: keep a font for each square size, even non-stndard ones
1433 #define NUM_SIZES 18
1434 #define MAX_SIZE 130
1435 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1436 char *fontTable[NUM_FONTS][MAX_SIZE];
1439 ParseFont(char *name, int number)
1440 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1442 if(sscanf(name, "size%d:", &size)) {
1443 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1444 // defer processing it until we know if it matches our board size
1445 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1446 fontTable[number][size] = strdup(strchr(name, ':')+1);
1447 fontValid[number][size] = True;
1452 case 0: // CLOCK_FONT
1453 appData.clockFont = strdup(name);
1455 case 1: // MESSAGE_FONT
1456 appData.font = strdup(name);
1458 case 2: // COORD_FONT
1459 appData.coordFont = strdup(name);
1464 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1469 { // only 2 fonts currently
1470 appData.clockFont = CLOCK_FONT_NAME;
1471 appData.coordFont = COORD_FONT_NAME;
1472 appData.font = DEFAULT_FONT_NAME;
1477 { // no-op, until we identify the code for this already in XBoard and move it here
1481 ParseColor(int n, char *name)
1482 { // in XBoard, just copy the color-name string
1483 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1487 ParseTextAttribs(ColorClass cc, char *s)
1489 (&appData.colorShout)[cc] = strdup(s);
1493 ParseBoardSize(void *addr, char *name)
1495 appData.boardSize = strdup(name);
1500 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1504 SetCommPortDefaults()
1505 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1508 // [HGM] args: these three cases taken out to stay in front-end
1510 SaveFontArg(FILE *f, ArgDescriptor *ad)
1513 int i, n = (int)(intptr_t)ad->argLoc;
1515 case 0: // CLOCK_FONT
1516 name = appData.clockFont;
1518 case 1: // MESSAGE_FONT
1519 name = appData.font;
1521 case 2: // COORD_FONT
1522 name = appData.coordFont;
1527 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1528 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1529 fontTable[n][squareSize] = strdup(name);
1530 fontValid[n][squareSize] = True;
1533 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1534 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1539 { // nothing to do, as the sounds are at all times represented by their text-string names already
1543 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1544 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1545 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1549 SaveColor(FILE *f, ArgDescriptor *ad)
1550 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1551 if(colorVariable[(int)(intptr_t)ad->argLoc])
1552 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1556 SaveBoardSize(FILE *f, char *name, void *addr)
1557 { // wrapper to shield back-end from BoardSize & sizeInfo
1558 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1562 ParseCommPortSettings(char *s)
1563 { // no such option in XBoard (yet)
1566 extern Widget engineOutputShell;
1569 GetActualPlacement(Widget wg, WindowPlacement *wp)
1579 XtSetArg(args[i], XtNx, &x); i++;
1580 XtSetArg(args[i], XtNy, &y); i++;
1581 XtSetArg(args[i], XtNwidth, &w); i++;
1582 XtSetArg(args[i], XtNheight, &h); i++;
1583 XtGetValues(wg, args, i);
1592 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1593 // In XBoard this will have to wait until awareness of window parameters is implemented
1594 GetActualPlacement(shellWidget, &wpMain);
1595 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1596 if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1597 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1598 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1599 if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1600 if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1604 PrintCommPortSettings(FILE *f, char *name)
1605 { // This option does not exist in XBoard
1609 MySearchPath(char *installDir, char *name, char *fullname)
1610 { // just append installDir and name. Perhaps ExpandPath should be used here?
1611 name = ExpandPathName(name);
1612 if(name && name[0] == '/')
1613 safeStrCpy(fullname, name, MSG_SIZ );
1615 sprintf(fullname, "%s%c%s", installDir, '/', name);
1621 MyGetFullPathName(char *name, char *fullname)
1622 { // should use ExpandPath?
1623 name = ExpandPathName(name);
1624 safeStrCpy(fullname, name, MSG_SIZ );
1629 EnsureOnScreen(int *x, int *y, int minX, int minY)
1636 { // [HGM] args: allows testing if main window is realized from back-end
1637 return xBoardWindow != 0;
1641 PopUpStartupDialog()
1642 { // start menu not implemented in XBoard
1646 ConvertToLine(int argc, char **argv)
1648 static char line[128*1024], buf[1024];
1652 for(i=1; i<argc; i++)
1654 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1655 && argv[i][0] != '{' )
1656 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1658 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1659 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1662 line[strlen(line)-1] = NULLCHAR;
1666 //--------------------------------------------------------------------------------------------
1668 extern Boolean twoBoards, partnerUp;
1671 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1673 #define BoardSize int
1674 void InitDrawingSizes(BoardSize boardSize, int flags)
1675 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1676 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1678 XtGeometryResult gres;
1681 if(!formWidget) return;
1684 * Enable shell resizing.
1686 shellArgs[0].value = (XtArgVal) &w;
1687 shellArgs[1].value = (XtArgVal) &h;
1688 XtGetValues(shellWidget, shellArgs, 2);
1690 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1691 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1692 XtSetValues(shellWidget, &shellArgs[2], 4);
1694 XtSetArg(args[0], XtNdefaultDistance, &sep);
1695 XtGetValues(formWidget, args, 1);
1697 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1698 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1699 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1701 hOffset = boardWidth + 10;
1702 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1703 secondSegments[i] = gridSegments[i];
1704 secondSegments[i].x1 += hOffset;
1705 secondSegments[i].x2 += hOffset;
1708 XtSetArg(args[0], XtNwidth, boardWidth);
1709 XtSetArg(args[1], XtNheight, boardHeight);
1710 XtSetValues(boardWidget, args, 2);
1712 timerWidth = (boardWidth - sep) / 2;
1713 XtSetArg(args[0], XtNwidth, timerWidth);
1714 XtSetValues(whiteTimerWidget, args, 1);
1715 XtSetValues(blackTimerWidget, args, 1);
1717 XawFormDoLayout(formWidget, False);
1719 if (appData.titleInWindow) {
1721 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1722 XtSetArg(args[i], XtNheight, &h); i++;
1723 XtGetValues(titleWidget, args, i);
1725 w = boardWidth - 2*bor;
1727 XtSetArg(args[0], XtNwidth, &w);
1728 XtGetValues(menuBarWidget, args, 1);
1729 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1732 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1733 if (gres != XtGeometryYes && appData.debugMode) {
1735 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1736 programName, gres, w, h, wr, hr);
1740 XawFormDoLayout(formWidget, True);
1743 * Inhibit shell resizing.
1745 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1746 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1747 shellArgs[4].value = shellArgs[2].value = w;
1748 shellArgs[5].value = shellArgs[3].value = h;
1749 XtSetValues(shellWidget, &shellArgs[0], 6);
1751 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1754 for(i=0; i<4; i++) {
1756 for(p=0; p<=(int)WhiteKing; p++)
1757 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1758 if(gameInfo.variant == VariantShogi) {
1759 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1760 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1761 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1762 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1763 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1766 if(gameInfo.variant == VariantGothic) {
1767 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1770 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1771 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1772 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1775 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1776 for(p=0; p<=(int)WhiteKing; p++)
1777 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1778 if(gameInfo.variant == VariantShogi) {
1779 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1780 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1781 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1782 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1783 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1786 if(gameInfo.variant == VariantGothic) {
1787 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1790 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1791 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1792 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1797 for(i=0; i<2; i++) {
1799 for(p=0; p<=(int)WhiteKing; p++)
1800 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1801 if(gameInfo.variant == VariantShogi) {
1802 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1803 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1804 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1805 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1806 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1809 if(gameInfo.variant == VariantGothic) {
1810 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1813 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1814 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1815 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1825 void ParseIcsTextColors()
1826 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1827 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1828 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1829 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1830 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1831 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1832 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1833 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1834 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1835 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1836 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1838 if (appData.colorize) {
1840 _("%s: can't parse color names; disabling colorization\n"),
1843 appData.colorize = FALSE;
1848 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1849 XrmValue vFrom, vTo;
1850 int forceMono = False;
1852 if (!appData.monoMode) {
1853 vFrom.addr = (caddr_t) appData.lightSquareColor;
1854 vFrom.size = strlen(appData.lightSquareColor);
1855 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1856 if (vTo.addr == NULL) {
1857 appData.monoMode = True;
1860 lightSquareColor = *(Pixel *) vTo.addr;
1863 if (!appData.monoMode) {
1864 vFrom.addr = (caddr_t) appData.darkSquareColor;
1865 vFrom.size = strlen(appData.darkSquareColor);
1866 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1867 if (vTo.addr == NULL) {
1868 appData.monoMode = True;
1871 darkSquareColor = *(Pixel *) vTo.addr;
1874 if (!appData.monoMode) {
1875 vFrom.addr = (caddr_t) appData.whitePieceColor;
1876 vFrom.size = strlen(appData.whitePieceColor);
1877 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1878 if (vTo.addr == NULL) {
1879 appData.monoMode = True;
1882 whitePieceColor = *(Pixel *) vTo.addr;
1885 if (!appData.monoMode) {
1886 vFrom.addr = (caddr_t) appData.blackPieceColor;
1887 vFrom.size = strlen(appData.blackPieceColor);
1888 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1889 if (vTo.addr == NULL) {
1890 appData.monoMode = True;
1893 blackPieceColor = *(Pixel *) vTo.addr;
1897 if (!appData.monoMode) {
1898 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1899 vFrom.size = strlen(appData.highlightSquareColor);
1900 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1901 if (vTo.addr == NULL) {
1902 appData.monoMode = True;
1905 highlightSquareColor = *(Pixel *) vTo.addr;
1909 if (!appData.monoMode) {
1910 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1911 vFrom.size = strlen(appData.premoveHighlightColor);
1912 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1913 if (vTo.addr == NULL) {
1914 appData.monoMode = True;
1917 premoveHighlightColor = *(Pixel *) vTo.addr;
1925 { // [HGM] taken out of main
1927 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1928 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1929 appData.bitmapDirectory = DEF_BITMAP_DIR;
1931 if (appData.bitmapDirectory[0] != NULLCHAR) {
1935 CreateXPMBoard(appData.liteBackTextureFile, 1);
1936 CreateXPMBoard(appData.darkBackTextureFile, 0);
1940 /* Create regular pieces */
1941 if (!useImages) CreatePieces();
1950 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1951 XSetWindowAttributes window_attributes;
1953 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1954 XrmValue vFrom, vTo;
1955 XtGeometryResult gres;
1958 int forceMono = False;
1960 srandom(time(0)); // [HGM] book: make random truly random
1962 setbuf(stdout, NULL);
1963 setbuf(stderr, NULL);
1966 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1967 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1971 programName = strrchr(argv[0], '/');
1972 if (programName == NULL)
1973 programName = argv[0];
1978 XtSetLanguageProc(NULL, NULL, NULL);
1979 bindtextdomain(PACKAGE, LOCALEDIR);
1980 textdomain(PACKAGE);
1984 XtAppInitialize(&appContext, "XBoard", shellOptions,
1985 XtNumber(shellOptions),
1986 &argc, argv, xboardResources, NULL, 0);
1987 appData.boardSize = "";
1988 InitAppData(ConvertToLine(argc, argv));
1990 if (p == NULL) p = "/tmp";
1991 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1992 gameCopyFilename = (char*) malloc(i);
1993 gamePasteFilename = (char*) malloc(i);
1994 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1995 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1997 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1998 clientResources, XtNumber(clientResources),
2001 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
2002 static char buf[MSG_SIZ];
2003 EscapeExpand(buf, appData.firstInitString);
2004 appData.firstInitString = strdup(buf);
2005 EscapeExpand(buf, appData.secondInitString);
2006 appData.secondInitString = strdup(buf);
2007 EscapeExpand(buf, appData.firstComputerString);
2008 appData.firstComputerString = strdup(buf);
2009 EscapeExpand(buf, appData.secondComputerString);
2010 appData.secondComputerString = strdup(buf);
2013 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
2016 if (chdir(chessDir) != 0) {
2017 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2023 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2024 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2025 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
2026 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2029 setbuf(debugFP, NULL);
2033 if (appData.debugMode) {
2034 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
2038 /* [HGM,HR] make sure board size is acceptable */
2039 if(appData.NrFiles > BOARD_FILES ||
2040 appData.NrRanks > BOARD_RANKS )
2041 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
2044 /* This feature does not work; animation needs a rewrite */
2045 appData.highlightDragging = FALSE;
2049 xDisplay = XtDisplay(shellWidget);
2050 xScreen = DefaultScreen(xDisplay);
2051 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2053 gameInfo.variant = StringToVariant(appData.variant);
2054 InitPosition(FALSE);
2057 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2059 if (isdigit(appData.boardSize[0])) {
2060 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2061 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2062 &fontPxlSize, &smallLayout, &tinyLayout);
2064 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2065 programName, appData.boardSize);
2069 /* Find some defaults; use the nearest known size */
2070 SizeDefaults *szd, *nearest;
2071 int distance = 99999;
2072 nearest = szd = sizeDefaults;
2073 while (szd->name != NULL) {
2074 if (abs(szd->squareSize - squareSize) < distance) {
2076 distance = abs(szd->squareSize - squareSize);
2077 if (distance == 0) break;
2081 if (i < 2) lineGap = nearest->lineGap;
2082 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2083 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2084 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2085 if (i < 6) smallLayout = nearest->smallLayout;
2086 if (i < 7) tinyLayout = nearest->tinyLayout;
2089 SizeDefaults *szd = sizeDefaults;
2090 if (*appData.boardSize == NULLCHAR) {
2091 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2092 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2095 if (szd->name == NULL) szd--;
2096 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2098 while (szd->name != NULL &&
2099 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2100 if (szd->name == NULL) {
2101 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2102 programName, appData.boardSize);
2106 squareSize = szd->squareSize;
2107 lineGap = szd->lineGap;
2108 clockFontPxlSize = szd->clockFontPxlSize;
2109 coordFontPxlSize = szd->coordFontPxlSize;
2110 fontPxlSize = szd->fontPxlSize;
2111 smallLayout = szd->smallLayout;
2112 tinyLayout = szd->tinyLayout;
2113 // [HGM] font: use defaults from settings file if available and not overruled
2115 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2116 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2117 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2118 appData.font = fontTable[MESSAGE_FONT][squareSize];
2119 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2120 appData.coordFont = fontTable[COORD_FONT][squareSize];
2122 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2123 if (strlen(appData.pixmapDirectory) > 0) {
2124 p = ExpandPathName(appData.pixmapDirectory);
2126 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2127 appData.pixmapDirectory);
2130 if (appData.debugMode) {
2131 fprintf(stderr, _("\
2132 XBoard square size (hint): %d\n\
2133 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2135 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2136 if (appData.debugMode) {
2137 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2140 defaultLineGap = lineGap;
2141 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2143 /* [HR] height treated separately (hacked) */
2144 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2145 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2146 if (appData.showJail == 1) {
2147 /* Jail on top and bottom */
2148 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2149 XtSetArg(boardArgs[2], XtNheight,
2150 boardHeight + 2*(lineGap + squareSize));
2151 } else if (appData.showJail == 2) {
2153 XtSetArg(boardArgs[1], XtNwidth,
2154 boardWidth + 2*(lineGap + squareSize));
2155 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2158 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2159 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2163 * Determine what fonts to use.
2166 appData.font = InsertPxlSize(appData.font, fontPxlSize);
2167 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
2168 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
2169 fontSet = CreateFontSet(appData.font);
2170 clockFontSet = CreateFontSet(appData.clockFont);
2172 /* For the coordFont, use the 0th font of the fontset. */
2173 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
2174 XFontStruct **font_struct_list;
2175 char **font_name_list;
2176 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
2177 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
2178 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2181 appData.font = FindFont(appData.font, fontPxlSize);
2182 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2183 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2184 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2185 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2186 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2187 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2189 countFontID = coordFontID; // [HGM] holdings
2190 countFontStruct = coordFontStruct;
2192 xdb = XtDatabase(xDisplay);
2194 XrmPutLineResource(&xdb, "*international: True");
2195 vTo.size = sizeof(XFontSet);
2196 vTo.addr = (XtPointer) &fontSet;
2197 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
2199 XrmPutStringResource(&xdb, "*font", appData.font);
2203 * Detect if there are not enough colors available and adapt.
2205 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2206 appData.monoMode = True;
2209 forceMono = MakeColors();
2212 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2214 appData.monoMode = True;
2217 if (appData.lowTimeWarning && !appData.monoMode) {
2218 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2219 vFrom.size = strlen(appData.lowTimeWarningColor);
2220 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2221 if (vTo.addr == NULL)
2222 appData.monoMode = True;
2224 lowTimeWarningColor = *(Pixel *) vTo.addr;
2227 if (appData.monoMode && appData.debugMode) {
2228 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2229 (unsigned long) XWhitePixel(xDisplay, xScreen),
2230 (unsigned long) XBlackPixel(xDisplay, xScreen));
2233 ParseIcsTextColors();
2234 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2235 textColors[ColorNone].attr = 0;
2237 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2243 layoutName = "tinyLayout";
2244 } else if (smallLayout) {
2245 layoutName = "smallLayout";
2247 layoutName = "normalLayout";
2249 /* Outer layoutWidget is there only to provide a name for use in
2250 resources that depend on the layout style */
2252 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2253 layoutArgs, XtNumber(layoutArgs));
2255 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2256 formArgs, XtNumber(formArgs));
2257 XtSetArg(args[0], XtNdefaultDistance, &sep);
2258 XtGetValues(formWidget, args, 1);
2261 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2262 XtSetArg(args[0], XtNtop, XtChainTop);
2263 XtSetArg(args[1], XtNbottom, XtChainTop);
2264 XtSetArg(args[2], XtNright, XtChainLeft);
2265 XtSetValues(menuBarWidget, args, 3);
2267 widgetList[j++] = whiteTimerWidget =
2268 XtCreateWidget("whiteTime", labelWidgetClass,
2269 formWidget, timerArgs, XtNumber(timerArgs));
2271 XtSetArg(args[0], XtNfontSet, clockFontSet);
2273 XtSetArg(args[0], XtNfont, clockFontStruct);
2275 XtSetArg(args[1], XtNtop, XtChainTop);
2276 XtSetArg(args[2], XtNbottom, XtChainTop);
2277 XtSetValues(whiteTimerWidget, args, 3);
2279 widgetList[j++] = blackTimerWidget =
2280 XtCreateWidget("blackTime", labelWidgetClass,
2281 formWidget, timerArgs, XtNumber(timerArgs));
2283 XtSetArg(args[0], XtNfontSet, clockFontSet);
2285 XtSetArg(args[0], XtNfont, clockFontStruct);
2287 XtSetArg(args[1], XtNtop, XtChainTop);
2288 XtSetArg(args[2], XtNbottom, XtChainTop);
2289 XtSetValues(blackTimerWidget, args, 3);
2291 if (appData.titleInWindow) {
2292 widgetList[j++] = titleWidget =
2293 XtCreateWidget("title", labelWidgetClass, formWidget,
2294 titleArgs, XtNumber(titleArgs));
2295 XtSetArg(args[0], XtNtop, XtChainTop);
2296 XtSetArg(args[1], XtNbottom, XtChainTop);
2297 XtSetValues(titleWidget, args, 2);
2300 if (appData.showButtonBar) {
2301 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2302 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2303 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2304 XtSetArg(args[2], XtNtop, XtChainTop);
2305 XtSetArg(args[3], XtNbottom, XtChainTop);
2306 XtSetValues(buttonBarWidget, args, 4);
2309 widgetList[j++] = messageWidget =
2310 XtCreateWidget("message", labelWidgetClass, formWidget,
2311 messageArgs, XtNumber(messageArgs));
2312 XtSetArg(args[0], XtNtop, XtChainTop);
2313 XtSetArg(args[1], XtNbottom, XtChainTop);
2314 XtSetValues(messageWidget, args, 2);
2316 widgetList[j++] = boardWidget =
2317 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2318 XtNumber(boardArgs));
2320 XtManageChildren(widgetList, j);
2322 timerWidth = (boardWidth - sep) / 2;
2323 XtSetArg(args[0], XtNwidth, timerWidth);
2324 XtSetValues(whiteTimerWidget, args, 1);
2325 XtSetValues(blackTimerWidget, args, 1);
2327 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2328 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2329 XtGetValues(whiteTimerWidget, args, 2);
2331 if (appData.showButtonBar) {
2332 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2333 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2334 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2338 * formWidget uses these constraints but they are stored
2342 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2343 XtSetValues(menuBarWidget, args, i);
2344 if (appData.titleInWindow) {
2347 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2348 XtSetValues(whiteTimerWidget, args, i);
2350 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2351 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2352 XtSetValues(blackTimerWidget, args, i);
2354 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2355 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2356 XtSetValues(titleWidget, args, i);
2358 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2359 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2360 XtSetValues(messageWidget, args, i);
2361 if (appData.showButtonBar) {
2363 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2364 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2365 XtSetValues(buttonBarWidget, args, i);
2369 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2370 XtSetValues(whiteTimerWidget, args, i);
2372 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2373 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2374 XtSetValues(blackTimerWidget, args, i);
2376 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2377 XtSetValues(titleWidget, args, i);
2379 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2380 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2381 XtSetValues(messageWidget, args, i);
2382 if (appData.showButtonBar) {
2384 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2385 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2386 XtSetValues(buttonBarWidget, args, i);
2391 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2392 XtSetValues(whiteTimerWidget, args, i);
2394 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2395 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2396 XtSetValues(blackTimerWidget, args, i);
2398 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2399 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2400 XtSetValues(messageWidget, args, i);
2401 if (appData.showButtonBar) {
2403 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2404 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2405 XtSetValues(buttonBarWidget, args, i);
2409 XtSetArg(args[0], XtNfromVert, messageWidget);
2410 XtSetArg(args[1], XtNtop, XtChainTop);
2411 XtSetArg(args[2], XtNbottom, XtChainBottom);
2412 XtSetArg(args[3], XtNleft, XtChainLeft);
2413 XtSetArg(args[4], XtNright, XtChainRight);
2414 XtSetValues(boardWidget, args, 5);
2416 XtRealizeWidget(shellWidget);
2419 XtSetArg(args[0], XtNx, wpMain.x);
2420 XtSetArg(args[1], XtNy, wpMain.y);
2421 XtSetValues(shellWidget, args, 2);
2425 * Correct the width of the message and title widgets.
2426 * It is not known why some systems need the extra fudge term.
2427 * The value "2" is probably larger than needed.
2429 XawFormDoLayout(formWidget, False);
2431 #define WIDTH_FUDGE 2
2433 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2434 XtSetArg(args[i], XtNheight, &h); i++;
2435 XtGetValues(messageWidget, args, i);
2436 if (appData.showButtonBar) {
2438 XtSetArg(args[i], XtNwidth, &w); i++;
2439 XtGetValues(buttonBarWidget, args, i);
2440 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2442 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2445 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2446 if (gres != XtGeometryYes && appData.debugMode) {
2447 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2448 programName, gres, w, h, wr, hr);
2451 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2452 /* The size used for the child widget in layout lags one resize behind
2453 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2455 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2456 if (gres != XtGeometryYes && appData.debugMode) {
2457 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2458 programName, gres, w, h, wr, hr);
2461 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2462 XtSetArg(args[1], XtNright, XtChainRight);
2463 XtSetValues(messageWidget, args, 2);
2465 if (appData.titleInWindow) {
2467 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2468 XtSetArg(args[i], XtNheight, &h); i++;
2469 XtGetValues(titleWidget, args, i);
2471 w = boardWidth - 2*bor;
2473 XtSetArg(args[0], XtNwidth, &w);
2474 XtGetValues(menuBarWidget, args, 1);
2475 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2478 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2479 if (gres != XtGeometryYes && appData.debugMode) {
2481 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2482 programName, gres, w, h, wr, hr);
2485 XawFormDoLayout(formWidget, True);
2487 xBoardWindow = XtWindow(boardWidget);
2489 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2490 // not need to go into InitDrawingSizes().
2494 * Create X checkmark bitmap and initialize option menu checks.
2496 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2497 checkmark_bits, checkmark_width, checkmark_height);
2498 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2499 #ifndef OPTIONSDIALOG
2500 if (appData.alwaysPromoteToQueen) {
2501 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2504 if (appData.animateDragging) {
2505 XtSetValues(XtNameToWidget(menuBarWidget,
2506 "menuOptions.Animate Dragging"),
2509 if (appData.animate) {
2510 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2513 if (appData.autoCallFlag) {
2514 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2517 if (appData.autoFlipView) {
2518 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2521 if (appData.blindfold) {
2522 XtSetValues(XtNameToWidget(menuBarWidget,
2523 "menuOptions.Blindfold"), args, 1);
2525 if (appData.flashCount > 0) {
2526 XtSetValues(XtNameToWidget(menuBarWidget,
2527 "menuOptions.Flash Moves"),
2531 if (appData.highlightDragging) {
2532 XtSetValues(XtNameToWidget(menuBarWidget,
2533 "menuOptions.Highlight Dragging"),
2537 if (appData.highlightLastMove) {
2538 XtSetValues(XtNameToWidget(menuBarWidget,
2539 "menuOptions.Highlight Last Move"),
2542 if (appData.highlightMoveWithArrow) {
2543 XtSetValues(XtNameToWidget(menuBarWidget,
2544 "menuOptions.Arrow"),
2547 // if (appData.icsAlarm) {
2548 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2551 if (appData.ringBellAfterMoves) {
2552 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2555 if (appData.oneClick) {
2556 XtSetValues(XtNameToWidget(menuBarWidget,
2557 "menuOptions.OneClick"), args, 1);
2559 if (appData.periodicUpdates) {
2560 XtSetValues(XtNameToWidget(menuBarWidget,
2561 "menuOptions.Periodic Updates"), args, 1);
2563 if (appData.ponderNextMove) {
2564 XtSetValues(XtNameToWidget(menuBarWidget,
2565 "menuOptions.Ponder Next Move"), args, 1);
2567 if (appData.popupExitMessage) {
2568 XtSetValues(XtNameToWidget(menuBarWidget,
2569 "menuOptions.Popup Exit Message"), args, 1);
2571 if (appData.popupMoveErrors) {
2572 XtSetValues(XtNameToWidget(menuBarWidget,
2573 "menuOptions.Popup Move Errors"), args, 1);
2575 // if (appData.premove) {
2576 // XtSetValues(XtNameToWidget(menuBarWidget,
2577 // "menuOptions.Premove"), args, 1);
2579 if (appData.showCoords) {
2580 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2583 if (appData.hideThinkingFromHuman) {
2584 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2587 if (appData.testLegality) {
2588 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2592 if (saveSettingsOnExit) {
2593 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2600 ReadBitmap(&wIconPixmap, "icon_white.bm",
2601 icon_white_bits, icon_white_width, icon_white_height);
2602 ReadBitmap(&bIconPixmap, "icon_black.bm",
2603 icon_black_bits, icon_black_width, icon_black_height);
2604 iconPixmap = wIconPixmap;
2606 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2607 XtSetValues(shellWidget, args, i);
2610 * Create a cursor for the board widget.
2612 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2613 XChangeWindowAttributes(xDisplay, xBoardWindow,
2614 CWCursor, &window_attributes);
2617 * Inhibit shell resizing.
2619 shellArgs[0].value = (XtArgVal) &w;
2620 shellArgs[1].value = (XtArgVal) &h;
2621 XtGetValues(shellWidget, shellArgs, 2);
2622 shellArgs[4].value = shellArgs[2].value = w;
2623 shellArgs[5].value = shellArgs[3].value = h;
2624 XtSetValues(shellWidget, &shellArgs[2], 4);
2625 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2626 marginH = h - boardHeight;
2628 CatchDeleteWindow(shellWidget, "QuitProc");
2636 if (appData.animate || appData.animateDragging)
2639 XtAugmentTranslations(formWidget,
2640 XtParseTranslationTable(globalTranslations));
2641 XtAugmentTranslations(boardWidget,
2642 XtParseTranslationTable(boardTranslations));
2643 XtAugmentTranslations(whiteTimerWidget,
2644 XtParseTranslationTable(whiteTranslations));
2645 XtAugmentTranslations(blackTimerWidget,
2646 XtParseTranslationTable(blackTranslations));
2648 /* Why is the following needed on some versions of X instead
2649 * of a translation? */
2650 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2651 (XtEventHandler) EventProc, NULL);
2653 XtAddEventHandler(formWidget, KeyPressMask, False,
2654 (XtEventHandler) MoveTypeInProc, NULL);
2656 /* [AS] Restore layout */
2657 if( wpMoveHistory.visible ) {
2661 if( wpEvalGraph.visible )
2666 if( wpEngineOutput.visible ) {
2667 EngineOutputPopUp();
2672 if (errorExitStatus == -1) {
2673 if (appData.icsActive) {
2674 /* We now wait until we see "login:" from the ICS before
2675 sending the logon script (problems with timestamp otherwise) */
2676 /*ICSInitScript();*/
2677 if (appData.icsInputBox) ICSInputBoxPopUp();
2681 signal(SIGWINCH, TermSizeSigHandler);
2683 signal(SIGINT, IntSigHandler);
2684 signal(SIGTERM, IntSigHandler);
2685 if (*appData.cmailGameName != NULLCHAR) {
2686 signal(SIGUSR1, CmailSigHandler);
2689 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2691 // XtSetKeyboardFocus(shellWidget, formWidget);
2692 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2694 XtAppMainLoop(appContext);
2695 if (appData.debugMode) fclose(debugFP); // [DM] debug
2702 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2703 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2705 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2706 unlink(gameCopyFilename);
2707 unlink(gamePasteFilename);
2710 RETSIGTYPE TermSizeSigHandler(int sig)
2723 CmailSigHandler(sig)
2729 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2731 /* Activate call-back function CmailSigHandlerCallBack() */
2732 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2734 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2738 CmailSigHandlerCallBack(isr, closure, message, count, error)
2746 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2748 /**** end signal code ****/
2754 /* try to open the icsLogon script, either in the location given
2755 * or in the users HOME directory
2762 f = fopen(appData.icsLogon, "r");
2765 homedir = getenv("HOME");
2766 if (homedir != NULL)
2768 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2769 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2770 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2771 f = fopen(buf, "r");
2776 ProcessICSInitScript(f);
2778 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2801 if (!menuBarWidget) return;
2802 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2804 DisplayError("menuEdit.Revert", 0);
2806 XtSetSensitive(w, !grey);
2808 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2810 DisplayError("menuEdit.Annotate", 0);
2812 XtSetSensitive(w, !grey);
2817 SetMenuEnables(enab)
2821 if (!menuBarWidget) return;
2822 while (enab->name != NULL) {
2823 w = XtNameToWidget(menuBarWidget, enab->name);
2825 DisplayError(enab->name, 0);
2827 XtSetSensitive(w, enab->value);
2833 Enables icsEnables[] = {
2834 { "menuFile.Mail Move", False },
2835 { "menuFile.Reload CMail Message", False },
2836 { "menuMode.Machine Black", False },
2837 { "menuMode.Machine White", False },
2838 { "menuMode.Analysis Mode", False },
2839 { "menuMode.Analyze File", False },
2840 { "menuMode.Two Machines", False },
2841 { "menuMode.Machine Match", False },
2843 { "menuEngine.Hint", False },
2844 { "menuEngine.Book", False },
2845 { "menuEngine.Move Now", False },
2846 #ifndef OPTIONSDIALOG
2847 { "menuOptions.Periodic Updates", False },
2848 { "menuOptions.Hide Thinking", False },
2849 { "menuOptions.Ponder Next Move", False },
2852 { "menuEngine.Engine #1 Settings", False },
2853 { "menuEngine.Engine #2 Settings", False },
2854 { "menuEngine.Load Engine", False },
2855 { "menuEdit.Annotate", False },
2856 { "menuOptions.Match", False },
2860 Enables ncpEnables[] = {
2861 { "menuFile.Mail Move", False },
2862 { "menuFile.Reload CMail Message", False },
2863 { "menuMode.Machine White", False },
2864 { "menuMode.Machine Black", False },
2865 { "menuMode.Analysis Mode", False },
2866 { "menuMode.Analyze File", False },
2867 { "menuMode.Two Machines", False },
2868 { "menuMode.Machine Match", False },
2869 { "menuMode.ICS Client", False },
2870 { "menuView.ICStex", False },
2871 { "menuView.ICS Input Box", False },
2872 { "Action", False },
2873 { "menuEdit.Revert", False },
2874 { "menuEdit.Annotate", False },
2875 { "menuEngine.Engine #1 Settings", False },
2876 { "menuEngine.Engine #2 Settings", False },
2877 { "menuEngine.Move Now", False },
2878 { "menuEngine.Retract Move", False },
2879 { "menuOptions.ICS", False },
2880 #ifndef OPTIONSDIALOG
2881 { "menuOptions.Auto Flag", False },
2882 { "menuOptions.Auto Flip View", False },
2883 // { "menuOptions.ICS Alarm", False },
2884 { "menuOptions.Move Sound", False },
2885 { "menuOptions.Hide Thinking", False },
2886 { "menuOptions.Periodic Updates", False },
2887 { "menuOptions.Ponder Next Move", False },
2889 { "menuEngine.Hint", False },
2890 { "menuEngine.Book", False },
2894 Enables gnuEnables[] = {
2895 { "menuMode.ICS Client", False },
2896 { "menuView.ICStex", False },
2897 { "menuView.ICS Input Box", False },
2898 { "menuAction.Accept", False },
2899 { "menuAction.Decline", False },
2900 { "menuAction.Rematch", False },
2901 { "menuAction.Adjourn", False },
2902 { "menuAction.Stop Examining", False },
2903 { "menuAction.Stop Observing", False },
2904 { "menuAction.Upload to Examine", False },
2905 { "menuEdit.Revert", False },
2906 { "menuEdit.Annotate", False },
2907 { "menuOptions.ICS", False },
2909 /* The next two options rely on SetCmailMode being called *after* */
2910 /* SetGNUMode so that when GNU is being used to give hints these */
2911 /* menu options are still available */
2913 { "menuFile.Mail Move", False },
2914 { "menuFile.Reload CMail Message", False },
2915 // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2916 { "menuMode.Machine White", True },
2917 { "menuMode.Machine Black", True },
2918 { "menuMode.Analysis Mode", True },
2919 { "menuMode.Analyze File", True },
2920 { "menuMode.Two Machines", True },
2921 { "menuMode.Machine Match", True },
2922 { "menuEngine.Engine #1 Settings", True },
2923 { "menuEngine.Engine #2 Settings", True },
2924 { "menuEngine.Hint", True },
2925 { "menuEngine.Book", True },
2926 { "menuEngine.Move Now", True },
2927 { "menuEngine.Retract Move", True },
2932 Enables cmailEnables[] = {
2934 { "menuAction.Call Flag", False },
2935 { "menuAction.Draw", True },
2936 { "menuAction.Adjourn", False },
2937 { "menuAction.Abort", False },
2938 { "menuAction.Stop Observing", False },
2939 { "menuAction.Stop Examining", False },
2940 { "menuFile.Mail Move", True },
2941 { "menuFile.Reload CMail Message", True },
2945 Enables trainingOnEnables[] = {
2946 { "menuMode.Edit Comment", False },
2947 { "menuMode.Pause", False },
2948 { "menuEdit.Forward", False },
2949 { "menuEdit.Backward", False },
2950 { "menuEdit.Forward to End", False },
2951 { "menuEdit.Back to Start", False },
2952 { "menuEngine.Move Now", False },
2953 { "menuEdit.Truncate Game", False },
2957 Enables trainingOffEnables[] = {
2958 { "menuMode.Edit Comment", True },
2959 { "menuMode.Pause", True },
2960 { "menuEdit.Forward", True },
2961 { "menuEdit.Backward", True },
2962 { "menuEdit.Forward to End", True },
2963 { "menuEdit.Back to Start", True },
2964 { "menuEngine.Move Now", True },
2965 { "menuEdit.Truncate Game", True },
2969 Enables machineThinkingEnables[] = {
2970 { "menuFile.Load Game", False },
2971 // { "menuFile.Load Next Game", False },
2972 // { "menuFile.Load Previous Game", False },
2973 // { "menuFile.Reload Same Game", False },
2974 { "menuEdit.Paste Game", False },
2975 { "menuFile.Load Position", False },
2976 // { "menuFile.Load Next Position", False },
2977 // { "menuFile.Load Previous Position", False },
2978 // { "menuFile.Reload Same Position", False },
2979 { "menuEdit.Paste Position", False },
2980 { "menuMode.Machine White", False },
2981 { "menuMode.Machine Black", False },
2982 { "menuMode.Two Machines", False },
2983 // { "menuMode.Machine Match", False },
2984 { "menuEngine.Retract Move", False },
2988 Enables userThinkingEnables[] = {
2989 { "menuFile.Load Game", True },
2990 // { "menuFile.Load Next Game", True },
2991 // { "menuFile.Load Previous Game", True },
2992 // { "menuFile.Reload Same Game", True },
2993 { "menuEdit.Paste Game", True },
2994 { "menuFile.Load Position", True },
2995 // { "menuFile.Load Next Position", True },
2996 // { "menuFile.Load Previous Position", True },
2997 // { "menuFile.Reload Same Position", True },
2998 { "menuEdit.Paste Position", True },
2999 { "menuMode.Machine White", True },
3000 { "menuMode.Machine Black", True },
3001 { "menuMode.Two Machines", True },
3002 // { "menuMode.Machine Match", True },
3003 { "menuEngine.Retract Move", True },
3009 SetMenuEnables(icsEnables);
3012 if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
3013 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
3014 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
3022 SetMenuEnables(ncpEnables);
3028 SetMenuEnables(gnuEnables);
3034 SetMenuEnables(cmailEnables);
3040 SetMenuEnables(trainingOnEnables);
3041 if (appData.showButtonBar) {
3042 XtSetSensitive(buttonBarWidget, False);
3048 SetTrainingModeOff()
3050 SetMenuEnables(trainingOffEnables);
3051 if (appData.showButtonBar) {
3052 XtSetSensitive(buttonBarWidget, True);
3057 SetUserThinkingEnables()
3059 if (appData.noChessProgram) return;
3060 SetMenuEnables(userThinkingEnables);
3064 SetMachineThinkingEnables()
3066 if (appData.noChessProgram) return;
3067 SetMenuEnables(machineThinkingEnables);
3069 case MachinePlaysBlack:
3070 case MachinePlaysWhite:
3071 case TwoMachinesPlay:
3072 XtSetSensitive(XtNameToWidget(menuBarWidget,
3073 ModeToWidgetName(gameMode)), True);
3080 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3081 #define HISTORY_SIZE 64
3082 static char *history[HISTORY_SIZE];
3083 int histIn = 0, histP = 0;
3086 SaveInHistory(char *cmd)
3088 if (history[histIn] != NULL) {
3089 free(history[histIn]);
3090 history[histIn] = NULL;
3092 if (*cmd == NULLCHAR) return;
3093 history[histIn] = StrSave(cmd);
3094 histIn = (histIn + 1) % HISTORY_SIZE;
3095 if (history[histIn] != NULL) {
3096 free(history[histIn]);
3097 history[histIn] = NULL;
3103 PrevInHistory(char *cmd)
3106 if (histP == histIn) {
3107 if (history[histIn] != NULL) free(history[histIn]);
3108 history[histIn] = StrSave(cmd);
3110 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3111 if (newhp == histIn || history[newhp] == NULL) return NULL;
3113 return history[histP];
3119 if (histP == histIn) return NULL;
3120 histP = (histP + 1) % HISTORY_SIZE;
3121 return history[histP];
3123 // end of borrowed code
3125 #define Abs(n) ((n)<0 ? -(n) : (n))
3129 InsertPxlSize(pattern, targetPxlSize)
3133 char *base_fnt_lst, strInt[12], *p, *q;
3134 int alternatives, i, len, strIntLen;
3137 * Replace the "*" (if present) in the pixel-size slot of each
3138 * alternative with the targetPxlSize.
3142 while ((p = strchr(p, ',')) != NULL) {
3146 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
3147 strIntLen = strlen(strInt);
3148 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
3152 while (alternatives--) {
3153 char *comma = strchr(p, ',');
3154 for (i=0; i<14; i++) {
3155 char *hyphen = strchr(p, '-');
3157 if (comma && hyphen > comma) break;
3158 len = hyphen + 1 - p;
3159 if (i == 7 && *p == '*' && len == 2) {
3161 memcpy(q, strInt, strIntLen);
3171 len = comma + 1 - p;
3178 return base_fnt_lst;
3182 CreateFontSet(base_fnt_lst)
3186 char **missing_list;
3190 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
3191 &missing_list, &missing_count, &def_string);
3192 if (appData.debugMode) {
3194 XFontStruct **font_struct_list;
3195 char **font_name_list;
3196 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
3198 fprintf(debugFP, " got list %s, locale %s\n",
3199 XBaseFontNameListOfFontSet(fntSet),
3200 XLocaleOfFontSet(fntSet));
3201 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
3202 for (i = 0; i < count; i++) {
3203 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
3206 for (i = 0; i < missing_count; i++) {
3207 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
3210 if (fntSet == NULL) {
3211 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
3216 #else // not ENABLE_NLS
3218 * Find a font that matches "pattern" that is as close as
3219 * possible to the targetPxlSize. Prefer fonts that are k
3220 * pixels smaller to fonts that are k pixels larger. The
3221 * pattern must be in the X Consortium standard format,
3222 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3223 * The return value should be freed with XtFree when no
3227 FindFont(pattern, targetPxlSize)
3231 char **fonts, *p, *best, *scalable, *scalableTail;
3232 int i, j, nfonts, minerr, err, pxlSize;
3234 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3236 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3237 programName, pattern);
3244 for (i=0; i<nfonts; i++) {
3247 if (*p != '-') continue;
3249 if (*p == NULLCHAR) break;
3250 if (*p++ == '-') j++;
3252 if (j < 7) continue;
3255 scalable = fonts[i];
3258 err = pxlSize - targetPxlSize;
3259 if (Abs(err) < Abs(minerr) ||
3260 (minerr > 0 && err < 0 && -err == minerr)) {
3266 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3267 /* If the error is too big and there is a scalable font,
3268 use the scalable font. */
3269 int headlen = scalableTail - scalable;
3270 p = (char *) XtMalloc(strlen(scalable) + 10);
3271 while (isdigit(*scalableTail)) scalableTail++;
3272 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3274 p = (char *) XtMalloc(strlen(best) + 2);
3275 safeStrCpy(p, best, strlen(best)+1 );
3277 if (appData.debugMode) {
3278 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3279 pattern, targetPxlSize, p);
3281 XFreeFontNames(fonts);
3287 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3288 // must be called before all non-first callse to CreateGCs()
3289 XtReleaseGC(shellWidget, highlineGC);
3290 XtReleaseGC(shellWidget, lightSquareGC);
3291 XtReleaseGC(shellWidget, darkSquareGC);
3292 XtReleaseGC(shellWidget, lineGC);
3293 if (appData.monoMode) {
3294 if (DefaultDepth(xDisplay, xScreen) == 1) {
3295 XtReleaseGC(shellWidget, wbPieceGC);
3297 XtReleaseGC(shellWidget, bwPieceGC);
3300 XtReleaseGC(shellWidget, prelineGC);
3301 XtReleaseGC(shellWidget, jailSquareGC);
3302 XtReleaseGC(shellWidget, wdPieceGC);
3303 XtReleaseGC(shellWidget, wlPieceGC);
3304 XtReleaseGC(shellWidget, wjPieceGC);
3305 XtReleaseGC(shellWidget, bdPieceGC);
3306 XtReleaseGC(shellWidget, blPieceGC);
3307 XtReleaseGC(shellWidget, bjPieceGC);
3311 void CreateGCs(int redo)
3313 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3314 | GCBackground | GCFunction | GCPlaneMask;
3315 XGCValues gc_values;
3318 gc_values.plane_mask = AllPlanes;
3319 gc_values.line_width = lineGap;
3320 gc_values.line_style = LineSolid;
3321 gc_values.function = GXcopy;
3324 DeleteGCs(); // called a second time; clean up old GCs first
3325 } else { // [HGM] grid and font GCs created on first call only
3326 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3327 gc_values.background = XWhitePixel(xDisplay, xScreen);
3328 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3329 XSetFont(xDisplay, coordGC, coordFontID);
3331 // [HGM] make font for holdings counts (white on black)
3332 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3333 gc_values.background = XBlackPixel(xDisplay, xScreen);
3334 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3335 XSetFont(xDisplay, countGC, countFontID);
3337 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3338 gc_values.background = XBlackPixel(xDisplay, xScreen);
3339 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3341 if (appData.monoMode) {
3342 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3343 gc_values.background = XWhitePixel(xDisplay, xScreen);
3344 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3346 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3347 gc_values.background = XBlackPixel(xDisplay, xScreen);
3348 lightSquareGC = wbPieceGC
3349 = XtGetGC(shellWidget, value_mask, &gc_values);
3351 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3352 gc_values.background = XWhitePixel(xDisplay, xScreen);
3353 darkSquareGC = bwPieceGC
3354 = XtGetGC(shellWidget, value_mask, &gc_values);
3356 if (DefaultDepth(xDisplay, xScreen) == 1) {
3357 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3358 gc_values.function = GXcopyInverted;
3359 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3360 gc_values.function = GXcopy;
3361 if (XBlackPixel(xDisplay, xScreen) == 1) {
3362 bwPieceGC = darkSquareGC;
3363 wbPieceGC = copyInvertedGC;
3365 bwPieceGC = copyInvertedGC;
3366 wbPieceGC = lightSquareGC;
3370 gc_values.foreground = highlightSquareColor;
3371 gc_values.background = highlightSquareColor;
3372 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3374 gc_values.foreground = premoveHighlightColor;
3375 gc_values.background = premoveHighlightColor;
3376 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3378 gc_values.foreground = lightSquareColor;
3379 gc_values.background = darkSquareColor;
3380 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3382 gc_values.foreground = darkSquareColor;
3383 gc_values.background = lightSquareColor;
3384 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3386 gc_values.foreground = jailSquareColor;
3387 gc_values.background = jailSquareColor;
3388 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3390 gc_values.foreground = whitePieceColor;
3391 gc_values.background = darkSquareColor;
3392 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3394 gc_values.foreground = whitePieceColor;
3395 gc_values.background = lightSquareColor;
3396 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3398 gc_values.foreground = whitePieceColor;
3399 gc_values.background = jailSquareColor;
3400 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3402 gc_values.foreground = blackPieceColor;
3403 gc_values.background = darkSquareColor;
3404 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3406 gc_values.foreground = blackPieceColor;
3407 gc_values.background = lightSquareColor;
3408 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3410 gc_values.foreground = blackPieceColor;
3411 gc_values.background = jailSquareColor;
3412 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3416 void loadXIM(xim, xmask, filename, dest, mask)
3429 fp = fopen(filename, "rb");
3431 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3438 for (y=0; y<h; ++y) {
3439 for (x=0; x<h; ++x) {
3444 XPutPixel(xim, x, y, blackPieceColor);
3446 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3449 XPutPixel(xim, x, y, darkSquareColor);
3451 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3454 XPutPixel(xim, x, y, whitePieceColor);
3456 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3459 XPutPixel(xim, x, y, lightSquareColor);
3461 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3469 /* create Pixmap of piece */
3470 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3472 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3475 /* create Pixmap of clipmask
3476 Note: We assume the white/black pieces have the same
3477 outline, so we make only 6 masks. This is okay
3478 since the XPM clipmask routines do the same. */
3480 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3482 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3485 /* now create the 1-bit version */
3486 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3489 values.foreground = 1;
3490 values.background = 0;
3492 /* Don't use XtGetGC, not read only */
3493 maskGC = XCreateGC(xDisplay, *mask,
3494 GCForeground | GCBackground, &values);
3495 XCopyPlane(xDisplay, temp, *mask, maskGC,
3496 0, 0, squareSize, squareSize, 0, 0, 1);
3497 XFreePixmap(xDisplay, temp);
3502 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3504 void CreateXIMPieces()
3509 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3514 /* The XSynchronize calls were copied from CreatePieces.
3515 Not sure if needed, but can't hurt */
3516 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3519 /* temp needed by loadXIM() */
3520 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3521 0, 0, ss, ss, AllPlanes, XYPixmap);
3523 if (strlen(appData.pixmapDirectory) == 0) {
3527 if (appData.monoMode) {
3528 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3532 fprintf(stderr, _("\nLoading XIMs...\n"));
3534 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3535 fprintf(stderr, "%d", piece+1);
3536 for (kind=0; kind<4; kind++) {
3537 fprintf(stderr, ".");
3538 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3539 ExpandPathName(appData.pixmapDirectory),
3540 piece <= (int) WhiteKing ? "" : "w",
3541 pieceBitmapNames[piece],
3543 ximPieceBitmap[kind][piece] =
3544 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3545 0, 0, ss, ss, AllPlanes, XYPixmap);
3546 if (appData.debugMode)
3547 fprintf(stderr, _("(File:%s:) "), buf);
3548 loadXIM(ximPieceBitmap[kind][piece],
3550 &(xpmPieceBitmap2[kind][piece]),
3551 &(ximMaskPm2[piece]));
3552 if(piece <= (int)WhiteKing)
3553 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3555 fprintf(stderr," ");
3557 /* Load light and dark squares */
3558 /* If the LSQ and DSQ pieces don't exist, we will
3559 draw them with solid squares. */
3560 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3561 if (access(buf, 0) != 0) {
3565 fprintf(stderr, _("light square "));
3567 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3568 0, 0, ss, ss, AllPlanes, XYPixmap);
3569 if (appData.debugMode)
3570 fprintf(stderr, _("(File:%s:) "), buf);
3572 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3573 fprintf(stderr, _("dark square "));
3574 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3575 ExpandPathName(appData.pixmapDirectory), ss);
3576 if (appData.debugMode)
3577 fprintf(stderr, _("(File:%s:) "), buf);
3579 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3580 0, 0, ss, ss, AllPlanes, XYPixmap);
3581 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3582 xpmJailSquare = xpmLightSquare;
3584 fprintf(stderr, _("Done.\n"));
3586 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3589 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3592 void CreateXPMBoard(char *s, int kind)
3596 if(s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3597 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3598 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3602 void FreeXPMPieces()
3603 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3604 // thisroutine has to be called t free the old piece pixmaps
3606 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3607 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3609 XFreePixmap(xDisplay, xpmLightSquare);
3610 XFreePixmap(xDisplay, xpmDarkSquare);
3614 void CreateXPMPieces()
3618 u_int ss = squareSize;
3620 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3621 XpmColorSymbol symbols[4];
3622 static int redo = False;
3624 if(redo) FreeXPMPieces(); else redo = 1;
3626 /* The XSynchronize calls were copied from CreatePieces.
3627 Not sure if needed, but can't hurt */
3628 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3630 /* Setup translations so piece colors match square colors */
3631 symbols[0].name = "light_piece";
3632 symbols[0].value = appData.whitePieceColor;
3633 symbols[1].name = "dark_piece";
3634 symbols[1].value = appData.blackPieceColor;
3635 symbols[2].name = "light_square";
3636 symbols[2].value = appData.lightSquareColor;
3637 symbols[3].name = "dark_square";
3638 symbols[3].value = appData.darkSquareColor;
3640 attr.valuemask = XpmColorSymbols;
3641 attr.colorsymbols = symbols;
3642 attr.numsymbols = 4;
3644 if (appData.monoMode) {
3645 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3649 if (strlen(appData.pixmapDirectory) == 0) {
3650 XpmPieces* pieces = builtInXpms;
3653 while (pieces->size != squareSize && pieces->size) pieces++;
3654 if (!pieces->size) {
3655 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3658 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3659 for (kind=0; kind<4; kind++) {
3661 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3662 pieces->xpm[piece][kind],
3663 &(xpmPieceBitmap2[kind][piece]),
3664 NULL, &attr)) != 0) {
3665 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3669 if(piece <= (int) WhiteKing)
3670 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3674 xpmJailSquare = xpmLightSquare;
3678 fprintf(stderr, _("\nLoading XPMs...\n"));
3681 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3682 fprintf(stderr, "%d ", piece+1);
3683 for (kind=0; kind<4; kind++) {
3684 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3685 ExpandPathName(appData.pixmapDirectory),
3686 piece > (int) WhiteKing ? "w" : "",
3687 pieceBitmapNames[piece],
3689 if (appData.debugMode) {
3690 fprintf(stderr, _("(File:%s:) "), buf);
3692 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3693 &(xpmPieceBitmap2[kind][piece]),
3694 NULL, &attr)) != 0) {
3695 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3696 // [HGM] missing: read of unorthodox piece failed; substitute King.
3697 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3698 ExpandPathName(appData.pixmapDirectory),
3700 if (appData.debugMode) {
3701 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3703 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3704 &(xpmPieceBitmap2[kind][piece]),
3708 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3713 if(piece <= (int) WhiteKing)
3714 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3717 /* Load light and dark squares */
3718 /* If the LSQ and DSQ pieces don't exist, we will
3719 draw them with solid squares. */
3720 fprintf(stderr, _("light square "));
3721 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3722 if (access(buf, 0) != 0) {
3726 if (appData.debugMode)
3727 fprintf(stderr, _("(File:%s:) "), buf);
3729 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3730 &xpmLightSquare, NULL, &attr)) != 0) {
3731 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3734 fprintf(stderr, _("dark square "));
3735 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3736 ExpandPathName(appData.pixmapDirectory), ss);
3737 if (appData.debugMode) {
3738 fprintf(stderr, _("(File:%s:) "), buf);
3740 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3741 &xpmDarkSquare, NULL, &attr)) != 0) {
3742 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3746 xpmJailSquare = xpmLightSquare;
3747 fprintf(stderr, _("Done.\n"));
3749 oldVariant = -1; // kludge to force re-makig of animation masks
3750 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3753 #endif /* HAVE_LIBXPM */
3756 /* No built-in bitmaps */
3761 u_int ss = squareSize;
3763 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3766 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3767 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3768 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3769 pieceBitmapNames[piece],
3770 ss, kind == SOLID ? 's' : 'o');
3771 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3772 if(piece <= (int)WhiteKing)
3773 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3777 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3781 /* With built-in bitmaps */
3784 BuiltInBits* bib = builtInBits;
3787 u_int ss = squareSize;
3789 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3792 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3794 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3795 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3796 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3797 pieceBitmapNames[piece],
3798 ss, kind == SOLID ? 's' : 'o');
3799 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3800 bib->bits[kind][piece], ss, ss);
3801 if(piece <= (int)WhiteKing)
3802 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3806 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3811 void ReadBitmap(pm, name, bits, wreq, hreq)
3814 unsigned char bits[];
3820 char msg[MSG_SIZ], fullname[MSG_SIZ];
3822 if (*appData.bitmapDirectory != NULLCHAR) {
3823 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3824 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3825 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3826 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3827 &w, &h, pm, &x_hot, &y_hot);
3828 fprintf(stderr, "load %s\n", name);
3829 if (errcode != BitmapSuccess) {
3831 case BitmapOpenFailed:
3832 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3834 case BitmapFileInvalid:
3835 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3837 case BitmapNoMemory:
3838 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3842 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3846 fprintf(stderr, _("%s: %s...using built-in\n"),
3848 } else if (w != wreq || h != hreq) {
3850 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3851 programName, fullname, w, h, wreq, hreq);
3857 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3866 if (lineGap == 0) return;
3868 /* [HR] Split this into 2 loops for non-square boards. */
3870 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3871 gridSegments[i].x1 = 0;
3872 gridSegments[i].x2 =
3873 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3874 gridSegments[i].y1 = gridSegments[i].y2
3875 = lineGap / 2 + (i * (squareSize + lineGap));
3878 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3879 gridSegments[j + i].y1 = 0;
3880 gridSegments[j + i].y2 =
3881 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3882 gridSegments[j + i].x1 = gridSegments[j + i].x2
3883 = lineGap / 2 + (j * (squareSize + lineGap));
3887 static void MenuBarSelect(w, addr, index)
3892 XtActionProc proc = (XtActionProc) addr;
3894 (proc)(NULL, NULL, NULL, NULL);
3897 void CreateMenuBarPopup(parent, name, mb)
3907 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3910 XtSetArg(args[j], XtNleftMargin, 20); j++;
3911 XtSetArg(args[j], XtNrightMargin, 20); j++;
3913 while (mi->string != NULL) {
3914 if (strcmp(mi->string, "----") == 0) {
3915 entry = XtCreateManagedWidget(_(mi->string), smeLineObjectClass,
3918 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3919 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3921 XtAddCallback(entry, XtNcallback,
3922 (XtCallbackProc) MenuBarSelect,
3923 (caddr_t) mi->proc);
3929 Widget CreateMenuBar(mb)
3933 Widget anchor, menuBar;
3935 char menuName[MSG_SIZ];
3938 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3939 XtSetArg(args[j], XtNvSpace, 0); j++;
3940 XtSetArg(args[j], XtNborderWidth, 0); j++;
3941 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3942 formWidget, args, j);
3944 while (mb->name != NULL) {
3945 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3946 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3948 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3951 shortName[0] = mb->name[0];
3952 shortName[1] = NULLCHAR;
3953 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3956 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3959 XtSetArg(args[j], XtNborderWidth, 0); j++;
3960 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3962 CreateMenuBarPopup(menuBar, menuName, mb);
3968 Widget CreateButtonBar(mi)
3972 Widget button, buttonBar;
3976 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3978 XtSetArg(args[j], XtNhSpace, 0); j++;
3980 XtSetArg(args[j], XtNborderWidth, 0); j++;
3981 XtSetArg(args[j], XtNvSpace, 0); j++;
3982 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3983 formWidget, args, j);
3985 while (mi->string != NULL) {
3988 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3989 XtSetArg(args[j], XtNborderWidth, 0); j++;
3991 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3992 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3993 buttonBar, args, j);
3994 XtAddCallback(button, XtNcallback,
3995 (XtCallbackProc) MenuBarSelect,
3996 (caddr_t) mi->proc);
4003 CreatePieceMenu(name, color)
4010 ChessSquare selection;
4012 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
4013 boardWidget, args, 0);
4015 for (i = 0; i < PIECE_MENU_SIZE; i++) {
4016 String item = pieceMenuStrings[color][i];
4018 if (strcmp(item, "----") == 0) {
4019 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4022 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4023 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4025 selection = pieceMenuTranslation[color][i];
4026 XtAddCallback(entry, XtNcallback,
4027 (XtCallbackProc) PieceMenuSelect,
4028 (caddr_t) selection);
4029 if (selection == WhitePawn || selection == BlackPawn) {
4030 XtSetArg(args[0], XtNpopupOnEntry, entry);
4031 XtSetValues(menu, args, 1);
4044 ChessSquare selection;
4046 whitePieceMenu = CreatePieceMenu("menuW", 0);
4047 blackPieceMenu = CreatePieceMenu("menuB", 1);
4049 XtRegisterGrabAction(PieceMenuPopup, True,
4050 (unsigned)(ButtonPressMask|ButtonReleaseMask),
4051 GrabModeAsync, GrabModeAsync);
4053 XtSetArg(args[0], XtNlabel, _("Drop"));
4054 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
4055 boardWidget, args, 1);
4056 for (i = 0; i < DROP_MENU_SIZE; i++) {
4057 String item = dropMenuStrings[i];
4059 if (strcmp(item, "----") == 0) {
4060 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4063 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4064 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4066 selection = dropMenuTranslation[i];
4067 XtAddCallback(entry, XtNcallback,
4068 (XtCallbackProc) DropMenuSelect,
4069 (caddr_t) selection);
4074 void SetupDropMenu()
4082 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
4083 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
4084 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
4085 dmEnables[i].piece);
4086 XtSetSensitive(entry, p != NULL || !appData.testLegality
4087 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
4088 && !appData.icsActive));
4090 while (p && *p++ == dmEnables[i].piece) count++;
4091 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
4093 XtSetArg(args[j], XtNlabel, label); j++;
4094 XtSetValues(entry, args, j);
4098 void PieceMenuPopup(w, event, params, num_params)
4102 Cardinal *num_params;
4104 String whichMenu; int menuNr = -2;
4105 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
4106 if (event->type == ButtonRelease)
4107 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4108 else if (event->type == ButtonPress)
4109 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4111 case 0: whichMenu = params[0]; break;
4112 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
4114 case -1: if (errorUp) ErrorPopDown();
4117 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
4120 static void PieceMenuSelect(w, piece, junk)
4125 if (pmFromX < 0 || pmFromY < 0) return;
4126 EditPositionMenuEvent(piece, pmFromX, pmFromY);
4129 static void DropMenuSelect(w, piece, junk)
4134 if (pmFromX < 0 || pmFromY < 0) return;
4135 DropMenuEvent(piece, pmFromX, pmFromY);
4138 void WhiteClock(w, event, prms, nprms)
4147 void BlackClock(w, event, prms, nprms)
4158 * If the user selects on a border boundary, return -1; if off the board,
4159 * return -2. Otherwise map the event coordinate to the square.
4161 int EventToSquare(x, limit)
4169 if ((x % (squareSize + lineGap)) >= squareSize)
4171 x /= (squareSize + lineGap);
4177 static void do_flash_delay(msec)
4183 static void drawHighlight(file, rank, gc)
4189 if (lineGap == 0) return;
4192 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4193 (squareSize + lineGap);
4194 y = lineGap/2 + rank * (squareSize + lineGap);
4196 x = lineGap/2 + file * (squareSize + lineGap);
4197 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4198 (squareSize + lineGap);
4201 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4202 squareSize+lineGap, squareSize+lineGap);
4205 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4206 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4209 SetHighlights(fromX, fromY, toX, toY)
4210 int fromX, fromY, toX, toY;
4212 if (hi1X != fromX || hi1Y != fromY) {
4213 if (hi1X >= 0 && hi1Y >= 0) {
4214 drawHighlight(hi1X, hi1Y, lineGC);
4216 } // [HGM] first erase both, then draw new!
4217 if (hi2X != toX || hi2Y != toY) {
4218 if (hi2X >= 0 && hi2Y >= 0) {
4219 drawHighlight(hi2X, hi2Y, lineGC);
4222 if (hi1X != fromX || hi1Y != fromY) {
4223 if (fromX >= 0 && fromY >= 0) {
4224 drawHighlight(fromX, fromY, highlineGC);
4227 if (hi2X != toX || hi2Y != toY) {
4228 if (toX >= 0 && toY >= 0) {
4229 drawHighlight(toX, toY, highlineGC);
4241 SetHighlights(-1, -1, -1, -1);
4246 SetPremoveHighlights(fromX, fromY, toX, toY)
4247 int fromX, fromY, toX, toY;
4249 if (pm1X != fromX || pm1Y != fromY) {
4250 if (pm1X >= 0 && pm1Y >= 0) {
4251 drawHighlight(pm1X, pm1Y, lineGC);
4253 if (fromX >= 0 && fromY >= 0) {
4254 drawHighlight(fromX, fromY, prelineGC);
4257 if (pm2X != toX || pm2Y != toY) {
4258 if (pm2X >= 0 && pm2Y >= 0) {
4259 drawHighlight(pm2X, pm2Y, lineGC);
4261 if (toX >= 0 && toY >= 0) {
4262 drawHighlight(toX, toY, prelineGC);
4272 ClearPremoveHighlights()
4274 SetPremoveHighlights(-1, -1, -1, -1);
4277 static int CutOutSquare(x, y, x0, y0, kind)
4278 int x, y, *x0, *y0, kind;
4280 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4281 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4283 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4284 if(textureW[kind] < W*squareSize)
4285 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4287 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4288 if(textureH[kind] < H*squareSize)
4289 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4291 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4295 static void BlankSquare(x, y, color, piece, dest, fac)
4296 int x, y, color, fac;
4299 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4301 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4302 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4303 squareSize, squareSize, x*fac, y*fac);
4305 if (useImages && useImageSqs) {
4309 pm = xpmLightSquare;
4314 case 2: /* neutral */
4319 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4320 squareSize, squareSize, x*fac, y*fac);
4330 case 2: /* neutral */
4335 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4340 I split out the routines to draw a piece so that I could
4341 make a generic flash routine.
4343 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4345 int square_color, x, y;
4348 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4349 switch (square_color) {
4351 case 2: /* neutral */
4353 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4354 ? *pieceToOutline(piece)
4355 : *pieceToSolid(piece),
4356 dest, bwPieceGC, 0, 0,
4357 squareSize, squareSize, x, y);
4360 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4361 ? *pieceToSolid(piece)
4362 : *pieceToOutline(piece),
4363 dest, wbPieceGC, 0, 0,
4364 squareSize, squareSize, x, y);
4369 static void monoDrawPiece(piece, square_color, x, y, dest)
4371 int square_color, x, y;
4374 switch (square_color) {
4376 case 2: /* neutral */
4378 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4379 ? *pieceToOutline(piece)
4380 : *pieceToSolid(piece),
4381 dest, bwPieceGC, 0, 0,
4382 squareSize, squareSize, x, y, 1);
4385 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4386 ? *pieceToSolid(piece)
4387 : *pieceToOutline(piece),
4388 dest, wbPieceGC, 0, 0,
4389 squareSize, squareSize, x, y, 1);
4394 static void colorDrawPiece(piece, square_color, x, y, dest)
4396 int square_color, x, y;
4399 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4400 switch (square_color) {
4402 XCopyPlane(xDisplay, *pieceToSolid(piece),
4403 dest, (int) piece < (int) BlackPawn
4404 ? wlPieceGC : blPieceGC, 0, 0,
4405 squareSize, squareSize, x, y, 1);
4408 XCopyPlane(xDisplay, *pieceToSolid(piece),
4409 dest, (int) piece < (int) BlackPawn
4410 ? wdPieceGC : bdPieceGC, 0, 0,
4411 squareSize, squareSize, x, y, 1);
4413 case 2: /* neutral */
4415 XCopyPlane(xDisplay, *pieceToSolid(piece),
4416 dest, (int) piece < (int) BlackPawn
4417 ? wjPieceGC : bjPieceGC, 0, 0,
4418 squareSize, squareSize, x, y, 1);
4423 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4425 int square_color, x, y;
4428 int kind, p = piece;
4430 switch (square_color) {
4432 case 2: /* neutral */
4434 if ((int)piece < (int) BlackPawn) {
4442 if ((int)piece < (int) BlackPawn) {
4450 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4451 if(useTexture & square_color+1) {
4452 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4453 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4454 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4455 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4456 XSetClipMask(xDisplay, wlPieceGC, None);
4457 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4459 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4460 dest, wlPieceGC, 0, 0,
4461 squareSize, squareSize, x, y);
4464 typedef void (*DrawFunc)();
4466 DrawFunc ChooseDrawFunc()
4468 if (appData.monoMode) {
4469 if (DefaultDepth(xDisplay, xScreen) == 1) {
4470 return monoDrawPiece_1bit;
4472 return monoDrawPiece;
4476 return colorDrawPieceImage;
4478 return colorDrawPiece;
4482 /* [HR] determine square color depending on chess variant. */
4483 static int SquareColor(row, column)
4488 if (gameInfo.variant == VariantXiangqi) {
4489 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4491 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4493 } else if (row <= 4) {
4499 square_color = ((column + row) % 2) == 1;
4502 /* [hgm] holdings: next line makes all holdings squares light */
4503 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4505 return square_color;
4508 void DrawSquare(row, column, piece, do_flash)
4509 int row, column, do_flash;
4512 int square_color, x, y, direction, font_ascent, font_descent;
4515 XCharStruct overall;
4519 /* Calculate delay in milliseconds (2-delays per complete flash) */
4520 flash_delay = 500 / appData.flashRate;
4523 x = lineGap + ((BOARD_WIDTH-1)-column) *
4524 (squareSize + lineGap);
4525 y = lineGap + row * (squareSize + lineGap);
4527 x = lineGap + column * (squareSize + lineGap);
4528 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4529 (squareSize + lineGap);
4532 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4534 square_color = SquareColor(row, column);
4536 if ( // [HGM] holdings: blank out area between board and holdings
4537 column == BOARD_LEFT-1 || column == BOARD_RGHT
4538 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4539 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4540 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4542 // [HGM] print piece counts next to holdings
4543 string[1] = NULLCHAR;
4544 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4545 string[0] = '0' + piece;
4546 XTextExtents(countFontStruct, string, 1, &direction,
4547 &font_ascent, &font_descent, &overall);
4548 if (appData.monoMode) {
4549 XDrawImageString(xDisplay, xBoardWindow, countGC,
4550 x + squareSize - overall.width - 2,
4551 y + font_ascent + 1, string, 1);
4553 XDrawString(xDisplay, xBoardWindow, countGC,
4554 x + squareSize - overall.width - 2,
4555 y + font_ascent + 1, string, 1);
4558 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4559 string[0] = '0' + piece;
4560 XTextExtents(countFontStruct, string, 1, &direction,
4561 &font_ascent, &font_descent, &overall);
4562 if (appData.monoMode) {
4563 XDrawImageString(xDisplay, xBoardWindow, countGC,
4564 x + 2, y + font_ascent + 1, string, 1);
4566 XDrawString(xDisplay, xBoardWindow, countGC,
4567 x + 2, y + font_ascent + 1, string, 1);
4571 if (piece == EmptySquare || appData.blindfold) {
4572 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4574 drawfunc = ChooseDrawFunc();
4576 if (do_flash && appData.flashCount > 0) {
4577 for (i=0; i<appData.flashCount; ++i) {
4578 drawfunc(piece, square_color, x, y, xBoardWindow);
4579 XSync(xDisplay, False);
4580 do_flash_delay(flash_delay);
4582 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4583 XSync(xDisplay, False);
4584 do_flash_delay(flash_delay);
4587 drawfunc(piece, square_color, x, y, xBoardWindow);
4591 string[1] = NULLCHAR;
4592 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4593 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4594 string[0] = 'a' + column - BOARD_LEFT;
4595 XTextExtents(coordFontStruct, string, 1, &direction,
4596 &font_ascent, &font_descent, &overall);
4597 if (appData.monoMode) {
4598 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4599 x + squareSize - overall.width - 2,
4600 y + squareSize - font_descent - 1, string, 1);
4602 XDrawString(xDisplay, xBoardWindow, coordGC,
4603 x + squareSize - overall.width - 2,
4604 y + squareSize - font_descent - 1, string, 1);
4607 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4608 string[0] = ONE + row;
4609 XTextExtents(coordFontStruct, string, 1, &direction,
4610 &font_ascent, &font_descent, &overall);
4611 if (appData.monoMode) {
4612 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4613 x + 2, y + font_ascent + 1, string, 1);
4615 XDrawString(xDisplay, xBoardWindow, coordGC,
4616 x + 2, y + font_ascent + 1, string, 1);
4619 if(!partnerUp && marker[row][column]) {
4620 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4621 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4626 /* Why is this needed on some versions of X? */
4627 void EventProc(widget, unused, event)
4632 if (!XtIsRealized(widget))
4635 switch (event->type) {
4637 if (event->xexpose.count > 0) return; /* no clipping is done */
4638 XDrawPosition(widget, True, NULL);
4639 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4640 flipView = !flipView; partnerUp = !partnerUp;
4641 XDrawPosition(widget, True, NULL);
4642 flipView = !flipView; partnerUp = !partnerUp;
4646 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4653 void DrawPosition(fullRedraw, board)
4654 /*Boolean*/int fullRedraw;
4657 XDrawPosition(boardWidget, fullRedraw, board);
4660 /* Returns 1 if there are "too many" differences between b1 and b2
4661 (i.e. more than 1 move was made) */
4662 static int too_many_diffs(b1, b2)
4668 for (i=0; i<BOARD_HEIGHT; ++i) {
4669 for (j=0; j<BOARD_WIDTH; ++j) {
4670 if (b1[i][j] != b2[i][j]) {
4671 if (++c > 4) /* Castling causes 4 diffs */
4679 /* Matrix describing castling maneuvers */
4680 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4681 static int castling_matrix[4][5] = {
4682 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4683 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4684 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4685 { 7, 7, 4, 5, 6 } /* 0-0, black */
4688 /* Checks whether castling occurred. If it did, *rrow and *rcol
4689 are set to the destination (row,col) of the rook that moved.
4691 Returns 1 if castling occurred, 0 if not.
4693 Note: Only handles a max of 1 castling move, so be sure
4694 to call too_many_diffs() first.
4696 static int check_castle_draw(newb, oldb, rrow, rcol)
4703 /* For each type of castling... */
4704 for (i=0; i<4; ++i) {
4705 r = castling_matrix[i];
4707 /* Check the 4 squares involved in the castling move */
4709 for (j=1; j<=4; ++j) {
4710 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4717 /* All 4 changed, so it must be a castling move */
4726 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4727 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4729 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4732 void DrawSeekBackground( int left, int top, int right, int bottom )
4734 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4737 void DrawSeekText(char *buf, int x, int y)
4739 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4742 void DrawSeekDot(int x, int y, int colorNr)
4744 int square = colorNr & 0x80;
4747 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4749 XFillRectangle(xDisplay, xBoardWindow, color,
4750 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4752 XFillArc(xDisplay, xBoardWindow, color,
4753 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4756 static int damage[2][BOARD_RANKS][BOARD_FILES];
4759 * event handler for redrawing the board
4761 void XDrawPosition(w, repaint, board)
4763 /*Boolean*/int repaint;
4767 static int lastFlipView = 0;
4768 static int lastBoardValid[2] = {0, 0};
4769 static Board lastBoard[2];
4772 int nr = twoBoards*partnerUp;
4774 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4776 if (board == NULL) {
4777 if (!lastBoardValid[nr]) return;
4778 board = lastBoard[nr];
4780 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4781 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4782 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4787 * It would be simpler to clear the window with XClearWindow()
4788 * but this causes a very distracting flicker.
4791 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4793 if ( lineGap && IsDrawArrowEnabled())
4794 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4795 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4797 /* If too much changes (begin observing new game, etc.), don't
4799 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4801 /* Special check for castling so we don't flash both the king
4802 and the rook (just flash the king). */
4804 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4805 /* Draw rook with NO flashing. King will be drawn flashing later */
4806 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4807 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4811 /* First pass -- Draw (newly) empty squares and repair damage.
4812 This prevents you from having a piece show up twice while it
4813 is flashing on its new square */
4814 for (i = 0; i < BOARD_HEIGHT; i++)
4815 for (j = 0; j < BOARD_WIDTH; j++)
4816 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4817 || damage[nr][i][j]) {
4818 DrawSquare(i, j, board[i][j], 0);
4819 damage[nr][i][j] = False;
4822 /* Second pass -- Draw piece(s) in new position and flash them */
4823 for (i = 0; i < BOARD_HEIGHT; i++)
4824 for (j = 0; j < BOARD_WIDTH; j++)
4825 if (board[i][j] != lastBoard[nr][i][j]) {
4826 DrawSquare(i, j, board[i][j], do_flash);
4830 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4831 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4832 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4834 for (i = 0; i < BOARD_HEIGHT; i++)
4835 for (j = 0; j < BOARD_WIDTH; j++) {
4836 DrawSquare(i, j, board[i][j], 0);
4837 damage[nr][i][j] = False;
4841 CopyBoard(lastBoard[nr], board);
4842 lastBoardValid[nr] = 1;
4843 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4844 lastFlipView = flipView;
4846 /* Draw highlights */
4847 if (pm1X >= 0 && pm1Y >= 0) {
4848 drawHighlight(pm1X, pm1Y, prelineGC);
4850 if (pm2X >= 0 && pm2Y >= 0) {
4851 drawHighlight(pm2X, pm2Y, prelineGC);
4853 if (hi1X >= 0 && hi1Y >= 0) {
4854 drawHighlight(hi1X, hi1Y, highlineGC);
4856 if (hi2X >= 0 && hi2Y >= 0) {
4857 drawHighlight(hi2X, hi2Y, highlineGC);
4859 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4861 /* If piece being dragged around board, must redraw that too */
4864 XSync(xDisplay, False);
4869 * event handler for redrawing the board
4871 void DrawPositionProc(w, event, prms, nprms)
4877 XDrawPosition(w, True, NULL);
4882 * event handler for parsing user moves
4884 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4885 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4886 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4887 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4888 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4889 // and at the end FinishMove() to perform the move after optional promotion popups.
4890 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4891 void HandleUserMove(w, event, prms, nprms)
4897 if (w != boardWidget || errorExitStatus != -1) return;
4898 if(nprms) shiftKey = !strcmp(prms[0], "1");
4901 if (event->type == ButtonPress) {
4902 XtPopdown(promotionShell);
4903 XtDestroyWidget(promotionShell);
4904 promotionUp = False;
4912 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4913 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4914 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4917 void AnimateUserMove (Widget w, XEvent * event,
4918 String * params, Cardinal * nParams)
4920 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4921 DragPieceMove(event->xmotion.x, event->xmotion.y);
4924 void HandlePV (Widget w, XEvent * event,
4925 String * params, Cardinal * nParams)
4926 { // [HGM] pv: walk PV
4927 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4930 static int savedIndex; /* gross that this is global */
4932 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4935 XawTextPosition index, dummy;
4938 XawTextGetSelectionPos(w, &index, &dummy);
4939 XtSetArg(arg, XtNstring, &val);
4940 XtGetValues(w, &arg, 1);
4941 ReplaceComment(savedIndex, val);
4942 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4943 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4946 void EditCommentPopUp(index, title, text)
4951 if (text == NULL) text = "";
4952 NewCommentPopup(title, text, index);
4955 void ICSInputBoxPopUp()
4960 extern Option boxOptions[];
4962 void ICSInputSendText()
4969 edit = boxOptions[0].handle;
4971 XtSetArg(args[j], XtNstring, &val); j++;
4972 XtGetValues(edit, args, j);
4974 SendMultiLineToICS(val);
4975 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4976 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4979 void ICSInputBoxPopDown()
4984 void CommentPopUp(title, text)
4987 savedIndex = currentMove; // [HGM] vari
4988 NewCommentPopup(title, text, currentMove);
4991 void CommentPopDown()
4996 void FileNamePopUp(label, def, filter, proc, openMode)
5003 fileProc = proc; /* I can't see a way not */
5004 fileOpenMode = openMode; /* to use globals here */
5005 { // [HGM] use file-selector dialog stolen from Ghostview
5007 int index; // this is not supported yet
5009 if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
5010 (def[0] ? def : NULL), filter, openMode, NULL, &name))
5011 (void) (*fileProc)(f, index=0, name);
5015 void FileNamePopDown()
5017 if (!filenameUp) return;
5018 XtPopdown(fileNameShell);
5019 XtDestroyWidget(fileNameShell);
5024 void FileNameCallback(w, client_data, call_data)
5026 XtPointer client_data, call_data;
5031 XtSetArg(args[0], XtNlabel, &name);
5032 XtGetValues(w, args, 1);
5034 if (strcmp(name, _("cancel")) == 0) {
5039 FileNameAction(w, NULL, NULL, NULL);
5042 void FileNameAction(w, event, prms, nprms)
5054 name = XawDialogGetValueString(w = XtParent(w));
5056 if ((name != NULL) && (*name != NULLCHAR)) {
5057 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5058 XtPopdown(w = XtParent(XtParent(w)));
5062 p = strrchr(buf, ' ');
5069 fullname = ExpandPathName(buf);
5071 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5074 f = fopen(fullname, fileOpenMode);
5076 DisplayError(_("Failed to open file"), errno);
5078 (void) (*fileProc)(f, index, buf);
5085 XtPopdown(w = XtParent(XtParent(w)));
5091 void PromotionPopUp()
5094 Widget dialog, layout;
5096 Dimension bw_width, pw_width;
5100 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5101 XtGetValues(boardWidget, args, j);
5104 XtSetArg(args[j], XtNresizable, True); j++;
5105 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5107 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5108 shellWidget, args, j);
5110 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5111 layoutArgs, XtNumber(layoutArgs));
5114 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5115 XtSetArg(args[j], XtNborderWidth, 0); j++;
5116 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5119 if(gameInfo.variant != VariantShogi) {
5120 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5121 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback,
5122 (XtPointer) dialog);
5123 XawDialogAddButton(dialog, _("General"), PromotionCallback,
5124 (XtPointer) dialog);
5125 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback,
5126 (XtPointer) dialog);
5127 XawDialogAddButton(dialog, _("Captain"), PromotionCallback,
5128 (XtPointer) dialog);
5130 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
5131 (XtPointer) dialog);
5132 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
5133 (XtPointer) dialog);
5134 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
5135 (XtPointer) dialog);
5136 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
5137 (XtPointer) dialog);
5139 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5140 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
5141 gameInfo.variant == VariantGiveaway) {
5142 XawDialogAddButton(dialog, _("King"), PromotionCallback,
5143 (XtPointer) dialog);
5145 if(gameInfo.variant == VariantCapablanca ||
5146 gameInfo.variant == VariantGothic ||
5147 gameInfo.variant == VariantCapaRandom) {
5148 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5149 (XtPointer) dialog);
5150 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5151 (XtPointer) dialog);
5153 } else // [HGM] shogi
5155 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5156 (XtPointer) dialog);
5157 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5158 (XtPointer) dialog);
5160 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5161 (XtPointer) dialog);
5163 XtRealizeWidget(promotionShell);
5164 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5167 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5168 XtGetValues(promotionShell, args, j);
5170 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5171 lineGap + squareSize/3 +
5172 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5173 0 : 6*(squareSize + lineGap)), &x, &y);
5176 XtSetArg(args[j], XtNx, x); j++;
5177 XtSetArg(args[j], XtNy, y); j++;
5178 XtSetValues(promotionShell, args, j);
5180 XtPopup(promotionShell, XtGrabNone);
5185 void PromotionPopDown()
5187 if (!promotionUp) return;
5188 XtPopdown(promotionShell);
5189 XtDestroyWidget(promotionShell);
5190 promotionUp = False;
5193 void PromotionCallback(w, client_data, call_data)
5195 XtPointer client_data, call_data;
5201 XtSetArg(args[0], XtNlabel, &name);
5202 XtGetValues(w, args, 1);
5206 if (fromX == -1) return;
5208 if (strcmp(name, _("cancel")) == 0) {
5212 } else if (strcmp(name, _("Knight")) == 0) {
5214 } else if (strcmp(name, _("Promote")) == 0) {
5216 } else if (strcmp(name, _("Defer")) == 0) {
5219 promoChar = ToLower(name[0]);
5222 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5224 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5225 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5230 void ErrorCallback(w, client_data, call_data)
5232 XtPointer client_data, call_data;
5235 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5237 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5243 if (!errorUp) return;
5245 XtPopdown(errorShell);
5246 XtDestroyWidget(errorShell);
5247 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5250 void ErrorPopUp(title, label, modal)
5251 char *title, *label;
5255 Widget dialog, layout;
5259 Dimension bw_width, pw_width;
5260 Dimension pw_height;
5264 XtSetArg(args[i], XtNresizable, True); i++;
5265 XtSetArg(args[i], XtNtitle, title); i++;
5267 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5268 shellWidget, args, i);
5270 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5271 layoutArgs, XtNumber(layoutArgs));
5274 XtSetArg(args[i], XtNlabel, label); i++;
5275 XtSetArg(args[i], XtNborderWidth, 0); i++;
5276 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5279 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5281 XtRealizeWidget(errorShell);
5282 CatchDeleteWindow(errorShell, "ErrorPopDown");
5285 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5286 XtGetValues(boardWidget, args, i);
5288 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5289 XtSetArg(args[i], XtNheight, &pw_height); i++;
5290 XtGetValues(errorShell, args, i);
5293 /* This code seems to tickle an X bug if it is executed too soon
5294 after xboard starts up. The coordinates get transformed as if
5295 the main window was positioned at (0, 0).
5297 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5298 0 - pw_height + squareSize / 3, &x, &y);
5300 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5301 RootWindowOfScreen(XtScreen(boardWidget)),
5302 (bw_width - pw_width) / 2,
5303 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5307 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5310 XtSetArg(args[i], XtNx, x); i++;
5311 XtSetArg(args[i], XtNy, y); i++;
5312 XtSetValues(errorShell, args, i);
5315 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5318 /* Disable all user input other than deleting the window */
5319 static int frozen = 0;
5323 /* Grab by a widget that doesn't accept input */
5324 XtAddGrab(messageWidget, TRUE, FALSE);
5328 /* Undo a FreezeUI */
5331 if (!frozen) return;
5332 XtRemoveGrab(messageWidget);
5336 char *ModeToWidgetName(mode)
5340 case BeginningOfGame:
5341 if (appData.icsActive)
5342 return "menuMode.ICS Client";
5343 else if (appData.noChessProgram ||
5344 *appData.cmailGameName != NULLCHAR)
5345 return "menuMode.Edit Game";
5347 return "menuMode.Machine Black";
5348 case MachinePlaysBlack:
5349 return "menuMode.Machine Black";
5350 case MachinePlaysWhite:
5351 return "menuMode.Machine White";
5353 return "menuMode.Analysis Mode";
5355 return "menuMode.Analyze File";
5356 case TwoMachinesPlay:
5357 return "menuMode.Two Machines";
5359 return "menuMode.Edit Game";
5360 case PlayFromGameFile:
5361 return "menuFile.Load Game";
5363 return "menuMode.Edit Position";
5365 return "menuMode.Training";
5366 case IcsPlayingWhite:
5367 case IcsPlayingBlack:
5371 return "menuMode.ICS Client";
5378 void ModeHighlight()
5381 static int oldPausing = FALSE;
5382 static GameMode oldmode = (GameMode) -1;
5385 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5387 if (pausing != oldPausing) {
5388 oldPausing = pausing;
5390 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5392 XtSetArg(args[0], XtNleftBitmap, None);
5394 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5397 if (appData.showButtonBar) {
5398 /* Always toggle, don't set. Previous code messes up when
5399 invoked while the button is pressed, as releasing it
5400 toggles the state again. */
5403 XtSetArg(args[0], XtNbackground, &oldbg);
5404 XtSetArg(args[1], XtNforeground, &oldfg);
5405 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5407 XtSetArg(args[0], XtNbackground, oldfg);
5408 XtSetArg(args[1], XtNforeground, oldbg);
5410 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5414 wname = ModeToWidgetName(oldmode);
5415 if (wname != NULL) {
5416 XtSetArg(args[0], XtNleftBitmap, None);
5417 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5419 wname = ModeToWidgetName(gameMode);
5420 if (wname != NULL) {
5421 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5422 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5425 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5426 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5428 /* Maybe all the enables should be handled here, not just this one */
5429 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5430 gameMode == Training || gameMode == PlayFromGameFile);
5435 * Button/menu procedures
5437 void ResetProc(w, event, prms, nprms)
5446 int LoadGamePopUp(f, gameNumber, title)
5451 cmailMsgLoaded = FALSE;
5452 if (gameNumber == 0) {
5453 int error = GameListBuild(f);
5455 DisplayError(_("Cannot build game list"), error);
5456 } else if (!ListEmpty(&gameList) &&
5457 ((ListGame *) gameList.tailPred)->number > 1) {
5458 GameListPopUp(f, title);
5464 return LoadGame(f, gameNumber, title, FALSE);
5467 void LoadGameProc(w, event, prms, nprms)
5473 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5476 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5479 void LoadNextGameProc(w, event, prms, nprms)
5488 void LoadPrevGameProc(w, event, prms, nprms)
5497 void ReloadGameProc(w, event, prms, nprms)
5506 void LoadNextPositionProc(w, event, prms, nprms)
5515 void LoadPrevPositionProc(w, event, prms, nprms)
5524 void ReloadPositionProc(w, event, prms, nprms)
5533 void LoadPositionProc(w, event, prms, nprms)
5539 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5542 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5545 void SaveGameProc(w, event, prms, nprms)
5551 FileNamePopUp(_("Save game file name?"),
5552 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5553 appData.oldSaveStyle ? ".game" : ".pgn",
5557 void SavePositionProc(w, event, prms, nprms)
5563 FileNamePopUp(_("Save position file name?"),
5564 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5565 appData.oldSaveStyle ? ".pos" : ".fen",
5569 void ReloadCmailMsgProc(w, event, prms, nprms)
5575 ReloadCmailMsgEvent(FALSE);
5578 void MailMoveProc(w, event, prms, nprms)
5587 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5588 char *selected_fen_position=NULL;
5591 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5592 Atom *type_return, XtPointer *value_return,
5593 unsigned long *length_return, int *format_return)
5595 char *selection_tmp;
5597 if (!selected_fen_position) return False; /* should never happen */
5598 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5599 /* note: since no XtSelectionDoneProc was registered, Xt will
5600 * automatically call XtFree on the value returned. So have to
5601 * make a copy of it allocated with XtMalloc */
5602 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5603 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5605 *value_return=selection_tmp;
5606 *length_return=strlen(selection_tmp);
5607 *type_return=*target;
5608 *format_return = 8; /* bits per byte */
5610 } else if (*target == XA_TARGETS(xDisplay)) {
5611 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5612 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5613 targets_tmp[1] = XA_STRING;
5614 *value_return = targets_tmp;
5615 *type_return = XA_ATOM;
5617 *format_return = 8 * sizeof(Atom);
5618 if (*format_return > 32) {
5619 *length_return *= *format_return / 32;
5620 *format_return = 32;
5628 /* note: when called from menu all parameters are NULL, so no clue what the
5629 * Widget which was clicked on was, or what the click event was
5631 void CopyPositionProc(w, event, prms, nprms)
5638 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5639 * have a notion of a position that is selected but not copied.
5640 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5642 if(gameMode == EditPosition) EditPositionDone(TRUE);
5643 if (selected_fen_position) free(selected_fen_position);
5644 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5645 if (!selected_fen_position) return;
5646 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5648 SendPositionSelection,
5649 NULL/* lose_ownership_proc */ ,
5650 NULL/* transfer_done_proc */);
5651 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5653 SendPositionSelection,
5654 NULL/* lose_ownership_proc */ ,
5655 NULL/* transfer_done_proc */);
5658 /* function called when the data to Paste is ready */
5660 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5661 Atom *type, XtPointer value, unsigned long *len, int *format)
5664 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5665 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5666 EditPositionPasteFEN(fenstr);
5670 /* called when Paste Position button is pressed,
5671 * all parameters will be NULL */
5672 void PastePositionProc(w, event, prms, nprms)
5678 XtGetSelectionValue(menuBarWidget,
5679 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5680 /* (XtSelectionCallbackProc) */ PastePositionCB,
5681 NULL, /* client_data passed to PastePositionCB */
5683 /* better to use the time field from the event that triggered the
5684 * call to this function, but that isn't trivial to get
5692 SendGameSelection(Widget w, Atom *selection, Atom *target,
5693 Atom *type_return, XtPointer *value_return,
5694 unsigned long *length_return, int *format_return)
5696 char *selection_tmp;
5698 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5699 FILE* f = fopen(gameCopyFilename, "r");
5702 if (f == NULL) return False;
5706 selection_tmp = XtMalloc(len + 1);
5707 count = fread(selection_tmp, 1, len, f);
5710 XtFree(selection_tmp);
5713 selection_tmp[len] = NULLCHAR;
5714 *value_return = selection_tmp;
5715 *length_return = len;
5716 *type_return = *target;
5717 *format_return = 8; /* bits per byte */
5719 } else if (*target == XA_TARGETS(xDisplay)) {
5720 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5721 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5722 targets_tmp[1] = XA_STRING;
5723 *value_return = targets_tmp;
5724 *type_return = XA_ATOM;
5726 *format_return = 8 * sizeof(Atom);
5727 if (*format_return > 32) {
5728 *length_return *= *format_return / 32;
5729 *format_return = 32;
5737 void CopySomething()
5742 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5743 * have a notion of a game that is selected but not copied.
5744 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5746 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5749 NULL/* lose_ownership_proc */ ,
5750 NULL/* transfer_done_proc */);
5751 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5754 NULL/* lose_ownership_proc */ ,
5755 NULL/* transfer_done_proc */);
5758 /* note: when called from menu all parameters are NULL, so no clue what the
5759 * Widget which was clicked on was, or what the click event was
5761 void CopyGameProc(w, event, prms, nprms)
5769 ret = SaveGameToFile(gameCopyFilename, FALSE);
5775 void CopyGameListProc(w, event, prms, nprms)
5781 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5785 /* function called when the data to Paste is ready */
5787 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5788 Atom *type, XtPointer value, unsigned long *len, int *format)
5791 if (value == NULL || *len == 0) {
5792 return; /* nothing had been selected to copy */
5794 f = fopen(gamePasteFilename, "w");
5796 DisplayError(_("Can't open temp file"), errno);
5799 fwrite(value, 1, *len, f);
5802 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5805 /* called when Paste Game button is pressed,
5806 * all parameters will be NULL */
5807 void PasteGameProc(w, event, prms, nprms)
5813 XtGetSelectionValue(menuBarWidget,
5814 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5815 /* (XtSelectionCallbackProc) */ PasteGameCB,
5816 NULL, /* client_data passed to PasteGameCB */
5818 /* better to use the time field from the event that triggered the
5819 * call to this function, but that isn't trivial to get
5829 SaveGameProc(NULL, NULL, NULL, NULL);
5833 void QuitProc(w, event, prms, nprms)
5842 void PauseProc(w, event, prms, nprms)
5852 void MachineBlackProc(w, event, prms, nprms)
5858 MachineBlackEvent();
5861 void MachineWhiteProc(w, event, prms, nprms)
5867 MachineWhiteEvent();
5870 void AnalyzeModeProc(w, event, prms, nprms)
5878 if (!first.analysisSupport) {
5879 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5880 DisplayError(buf, 0);
5883 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5884 if (appData.icsActive) {
5885 if (gameMode != IcsObserving) {
5886 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5887 DisplayError(buf, 0);
5889 if (appData.icsEngineAnalyze) {
5890 if (appData.debugMode)
5891 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5897 /* if enable, use want disable icsEngineAnalyze */
5898 if (appData.icsEngineAnalyze) {
5903 appData.icsEngineAnalyze = TRUE;
5904 if (appData.debugMode)
5905 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5907 #ifndef OPTIONSDIALOG
5908 if (!appData.showThinking)
5909 ShowThinkingProc(w,event,prms,nprms);
5915 void AnalyzeFileProc(w, event, prms, nprms)
5921 if (!first.analysisSupport) {
5923 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5924 DisplayError(buf, 0);
5928 #ifndef OPTIONSDIALOG
5929 if (!appData.showThinking)
5930 ShowThinkingProc(w,event,prms,nprms);
5933 FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5934 AnalysisPeriodicEvent(1);
5937 void TwoMachinesProc(w, event, prms, nprms)
5946 void MatchProc(w, event, prms, nprms)
5955 void IcsClientProc(w, event, prms, nprms)
5964 void EditGameProc(w, event, prms, nprms)
5973 void EditPositionProc(w, event, prms, nprms)
5979 EditPositionEvent();
5982 void TrainingProc(w, event, prms, nprms)
5991 void EditCommentProc(w, event, prms, nprms)
5999 if (PopDown(1)) { // popdown succesful
6001 XtSetArg(args[j], XtNleftBitmap, None); j++;
6002 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
6003 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
6004 } else // was not up
6008 void IcsInputBoxProc(w, event, prms, nprms)
6014 if (!PopDown(4)) ICSInputBoxPopUp();
6017 void AcceptProc(w, event, prms, nprms)
6026 void DeclineProc(w, event, prms, nprms)
6035 void RematchProc(w, event, prms, nprms)
6044 void CallFlagProc(w, event, prms, nprms)
6053 void DrawProc(w, event, prms, nprms)
6062 void AbortProc(w, event, prms, nprms)
6071 void AdjournProc(w, event, prms, nprms)
6080 void ResignProc(w, event, prms, nprms)
6089 void AdjuWhiteProc(w, event, prms, nprms)
6095 UserAdjudicationEvent(+1);
6098 void AdjuBlackProc(w, event, prms, nprms)
6104 UserAdjudicationEvent(-1);
6107 void AdjuDrawProc(w, event, prms, nprms)
6113 UserAdjudicationEvent(0);
6116 void EnterKeyProc(w, event, prms, nprms)
6122 if (shellUp[4] == True)
6126 void UpKeyProc(w, event, prms, nprms)
6131 { // [HGM] input: let up-arrow recall previous line from history
6138 if (!shellUp[4]) return;
6139 edit = boxOptions[0].handle;
6141 XtSetArg(args[j], XtNstring, &val); j++;
6142 XtGetValues(edit, args, j);
6143 val = PrevInHistory(val);
6144 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6145 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6147 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6148 XawTextReplace(edit, 0, 0, &t);
6149 XawTextSetInsertionPoint(edit, 9999);
6153 void DownKeyProc(w, event, prms, nprms)
6158 { // [HGM] input: let down-arrow recall next line from history
6163 if (!shellUp[4]) return;
6164 edit = boxOptions[0].handle;
6165 val = NextInHistory();
6166 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6167 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6169 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6170 XawTextReplace(edit, 0, 0, &t);
6171 XawTextSetInsertionPoint(edit, 9999);
6175 void StopObservingProc(w, event, prms, nprms)
6181 StopObservingEvent();
6184 void StopExaminingProc(w, event, prms, nprms)
6190 StopExaminingEvent();
6193 void UploadProc(w, event, prms, nprms)
6203 void ForwardProc(w, event, prms, nprms)
6213 void BackwardProc(w, event, prms, nprms)
6222 void ToStartProc(w, event, prms, nprms)
6231 void ToEndProc(w, event, prms, nprms)
6240 void RevertProc(w, event, prms, nprms)
6249 void AnnotateProc(w, event, prms, nprms)
6258 void TruncateGameProc(w, event, prms, nprms)
6264 TruncateGameEvent();
6266 void RetractMoveProc(w, event, prms, nprms)
6275 void MoveNowProc(w, event, prms, nprms)
6284 void FlipViewProc(w, event, prms, nprms)
6290 flipView = !flipView;
6291 DrawPosition(True, NULL);
6294 void PonderNextMoveProc(w, event, prms, nprms)
6302 PonderNextMoveEvent(!appData.ponderNextMove);
6303 #ifndef OPTIONSDIALOG
6304 if (appData.ponderNextMove) {
6305 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6307 XtSetArg(args[0], XtNleftBitmap, None);
6309 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6314 #ifndef OPTIONSDIALOG
6315 void AlwaysQueenProc(w, event, prms, nprms)
6323 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6325 if (appData.alwaysPromoteToQueen) {
6326 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6328 XtSetArg(args[0], XtNleftBitmap, None);
6330 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6334 void AnimateDraggingProc(w, event, prms, nprms)
6342 appData.animateDragging = !appData.animateDragging;
6344 if (appData.animateDragging) {
6345 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6348 XtSetArg(args[0], XtNleftBitmap, None);
6350 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6354 void AnimateMovingProc(w, event, prms, nprms)
6362 appData.animate = !appData.animate;
6364 if (appData.animate) {
6365 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6368 XtSetArg(args[0], XtNleftBitmap, None);
6370 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6374 void AutoflagProc(w, event, prms, nprms)
6382 appData.autoCallFlag = !appData.autoCallFlag;
6384 if (appData.autoCallFlag) {
6385 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6387 XtSetArg(args[0], XtNleftBitmap, None);
6389 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6393 void AutoflipProc(w, event, prms, nprms)
6401 appData.autoFlipView = !appData.autoFlipView;
6403 if (appData.autoFlipView) {
6404 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6406 XtSetArg(args[0], XtNleftBitmap, None);
6408 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6412 void BlindfoldProc(w, event, prms, nprms)
6420 appData.blindfold = !appData.blindfold;
6422 if (appData.blindfold) {
6423 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6425 XtSetArg(args[0], XtNleftBitmap, None);
6427 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6430 DrawPosition(True, NULL);
6433 void TestLegalityProc(w, event, prms, nprms)
6441 appData.testLegality = !appData.testLegality;
6443 if (appData.testLegality) {
6444 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6446 XtSetArg(args[0], XtNleftBitmap, None);
6448 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6453 void FlashMovesProc(w, event, prms, nprms)
6461 if (appData.flashCount == 0) {
6462 appData.flashCount = 3;
6464 appData.flashCount = -appData.flashCount;
6467 if (appData.flashCount > 0) {
6468 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6470 XtSetArg(args[0], XtNleftBitmap, None);
6472 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6477 void HighlightDraggingProc(w, event, prms, nprms)
6485 appData.highlightDragging = !appData.highlightDragging;
6487 if (appData.highlightDragging) {
6488 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6490 XtSetArg(args[0], XtNleftBitmap, None);
6492 XtSetValues(XtNameToWidget(menuBarWidget,
6493 "menuOptions.Highlight Dragging"), args, 1);
6497 void HighlightLastMoveProc(w, event, prms, nprms)
6505 appData.highlightLastMove = !appData.highlightLastMove;
6507 if (appData.highlightLastMove) {
6508 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6510 XtSetArg(args[0], XtNleftBitmap, None);
6512 XtSetValues(XtNameToWidget(menuBarWidget,
6513 "menuOptions.Highlight Last Move"), args, 1);
6516 void HighlightArrowProc(w, event, prms, nprms)
6524 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6526 if (appData.highlightMoveWithArrow) {
6527 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6529 XtSetArg(args[0], XtNleftBitmap, None);
6531 XtSetValues(XtNameToWidget(menuBarWidget,
6532 "menuOptions.Arrow"), args, 1);
6536 void IcsAlarmProc(w, event, prms, nprms)
6544 appData.icsAlarm = !appData.icsAlarm;
6546 if (appData.icsAlarm) {
6547 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6549 XtSetArg(args[0], XtNleftBitmap, None);
6551 XtSetValues(XtNameToWidget(menuBarWidget,
6552 "menuOptions.ICS Alarm"), args, 1);
6556 void MoveSoundProc(w, event, prms, nprms)
6564 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6566 if (appData.ringBellAfterMoves) {
6567 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6569 XtSetArg(args[0], XtNleftBitmap, None);
6571 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6575 void OneClickProc(w, event, prms, nprms)
6583 appData.oneClick = !appData.oneClick;
6585 if (appData.oneClick) {
6586 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6588 XtSetArg(args[0], XtNleftBitmap, None);
6590 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6594 void PeriodicUpdatesProc(w, event, prms, nprms)
6602 PeriodicUpdatesEvent(!appData.periodicUpdates);
6604 if (appData.periodicUpdates) {
6605 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6607 XtSetArg(args[0], XtNleftBitmap, None);
6609 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6613 void PopupExitMessageProc(w, event, prms, nprms)
6621 appData.popupExitMessage = !appData.popupExitMessage;
6623 if (appData.popupExitMessage) {
6624 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6626 XtSetArg(args[0], XtNleftBitmap, None);
6628 XtSetValues(XtNameToWidget(menuBarWidget,
6629 "menuOptions.Popup Exit Message"), args, 1);
6632 void PopupMoveErrorsProc(w, event, prms, nprms)
6640 appData.popupMoveErrors = !appData.popupMoveErrors;
6642 if (appData.popupMoveErrors) {
6643 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6645 XtSetArg(args[0], XtNleftBitmap, None);
6647 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6652 void PremoveProc(w, event, prms, nprms)
6660 appData.premove = !appData.premove;
6662 if (appData.premove) {
6663 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6665 XtSetArg(args[0], XtNleftBitmap, None);
6667 XtSetValues(XtNameToWidget(menuBarWidget,
6668 "menuOptions.Premove"), args, 1);
6672 void ShowCoordsProc(w, event, prms, nprms)
6680 appData.showCoords = !appData.showCoords;
6682 if (appData.showCoords) {
6683 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6685 XtSetArg(args[0], XtNleftBitmap, None);
6687 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6690 DrawPosition(True, NULL);
6693 void ShowThinkingProc(w, event, prms, nprms)
6699 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6700 ShowThinkingEvent();
6703 void HideThinkingProc(w, event, prms, nprms)
6711 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6712 ShowThinkingEvent();
6714 if (appData.hideThinkingFromHuman) {
6715 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6717 XtSetArg(args[0], XtNleftBitmap, None);
6719 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6724 void SaveOnExitProc(w, event, prms, nprms)
6732 saveSettingsOnExit = !saveSettingsOnExit;
6734 if (saveSettingsOnExit) {
6735 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6737 XtSetArg(args[0], XtNleftBitmap, None);
6739 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6743 void SaveSettingsProc(w, event, prms, nprms)
6749 SaveSettings(settingsFileName);
6752 void InfoProc(w, event, prms, nprms)
6759 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6764 void ManProc(w, event, prms, nprms)
6772 if (nprms && *nprms > 0)
6776 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6780 void HintProc(w, event, prms, nprms)
6789 void BookProc(w, event, prms, nprms)
6798 void AboutProc(w, event, prms, nprms)
6806 char *zippy = " (with Zippy code)";
6810 snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
6811 programVersion, zippy,
6812 "Copyright 1991 Digital Equipment Corporation",
6813 "Enhancements Copyright 1992-2009 Free Software Foundation",
6814 "Enhancements Copyright 2005 Alessandro Scotti",
6815 PACKAGE, " is free software and carries NO WARRANTY;",
6816 "see the file COPYING for more information.");
6817 ErrorPopUp(_("About XBoard"), buf, FALSE);
6820 void DebugProc(w, event, prms, nprms)
6826 appData.debugMode = !appData.debugMode;
6829 void AboutGameProc(w, event, prms, nprms)
6838 void NothingProc(w, event, prms, nprms)
6847 void Iconify(w, event, prms, nprms)
6856 XtSetArg(args[0], XtNiconic, True);
6857 XtSetValues(shellWidget, args, 1);
6860 void DisplayMessage(message, extMessage)
6861 char *message, *extMessage;
6863 /* display a message in the message widget */
6872 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6877 message = extMessage;
6881 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6883 /* need to test if messageWidget already exists, since this function
6884 can also be called during the startup, if for example a Xresource
6885 is not set up correctly */
6888 XtSetArg(arg, XtNlabel, message);
6889 XtSetValues(messageWidget, &arg, 1);
6895 void DisplayTitle(text)
6900 char title[MSG_SIZ];
6903 if (text == NULL) text = "";
6905 if (appData.titleInWindow) {
6907 XtSetArg(args[i], XtNlabel, text); i++;
6908 XtSetValues(titleWidget, args, i);
6911 if (*text != NULLCHAR) {
6912 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6913 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6914 } else if (appData.icsActive) {
6915 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6916 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6917 } else if (appData.cmailGameName[0] != NULLCHAR) {
6918 snprintf(icon, sizeof(icon), "%s", "CMail");
6919 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6921 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6922 } else if (gameInfo.variant == VariantGothic) {
6923 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6924 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6927 } else if (gameInfo.variant == VariantFalcon) {
6928 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6929 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6931 } else if (appData.noChessProgram) {
6932 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6933 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6935 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6936 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6939 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6940 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6941 XtSetValues(shellWidget, args, i);
6946 DisplayError(message, error)
6953 if (appData.debugMode || appData.matchMode) {
6954 fprintf(stderr, "%s: %s\n", programName, message);
6957 if (appData.debugMode || appData.matchMode) {
6958 fprintf(stderr, "%s: %s: %s\n",
6959 programName, message, strerror(error));
6961 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6964 ErrorPopUp(_("Error"), message, FALSE);
6968 void DisplayMoveError(message)
6973 DrawPosition(FALSE, NULL);
6974 if (appData.debugMode || appData.matchMode) {
6975 fprintf(stderr, "%s: %s\n", programName, message);
6977 if (appData.popupMoveErrors) {
6978 ErrorPopUp(_("Error"), message, FALSE);
6980 DisplayMessage(message, "");
6985 void DisplayFatalError(message, error, status)
6991 errorExitStatus = status;
6993 fprintf(stderr, "%s: %s\n", programName, message);
6995 fprintf(stderr, "%s: %s: %s\n",
6996 programName, message, strerror(error));
6997 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7000 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
7001 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
7007 void DisplayInformation(message)
7011 ErrorPopUp(_("Information"), message, TRUE);
7014 void DisplayNote(message)
7018 ErrorPopUp(_("Note"), message, FALSE);
7022 NullXErrorCheck(dpy, error_event)
7024 XErrorEvent *error_event;
7029 void DisplayIcsInteractionTitle(message)
7032 if (oldICSInteractionTitle == NULL) {
7033 /* Magic to find the old window title, adapted from vim */
7034 char *wina = getenv("WINDOWID");
7036 Window win = (Window) atoi(wina);
7037 Window root, parent, *children;
7038 unsigned int nchildren;
7039 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
7041 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
7042 if (!XQueryTree(xDisplay, win, &root, &parent,
7043 &children, &nchildren)) break;
7044 if (children) XFree((void *)children);
7045 if (parent == root || parent == 0) break;
7048 XSetErrorHandler(oldHandler);
7050 if (oldICSInteractionTitle == NULL) {
7051 oldICSInteractionTitle = "xterm";
7054 printf("\033]0;%s\007", message);
7058 char pendingReplyPrefix[MSG_SIZ];
7059 ProcRef pendingReplyPR;
7061 void AskQuestionProc(w, event, prms, nprms)
7068 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7072 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7075 void AskQuestionPopDown()
7077 if (!askQuestionUp) return;
7078 XtPopdown(askQuestionShell);
7079 XtDestroyWidget(askQuestionShell);
7080 askQuestionUp = False;
7083 void AskQuestionReplyAction(w, event, prms, nprms)
7093 reply = XawDialogGetValueString(w = XtParent(w));
7094 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
7095 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
7096 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
7097 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
7098 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7099 AskQuestionPopDown();
7101 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7104 void AskQuestionCallback(w, client_data, call_data)
7106 XtPointer client_data, call_data;
7111 XtSetArg(args[0], XtNlabel, &name);
7112 XtGetValues(w, args, 1);
7114 if (strcmp(name, _("cancel")) == 0) {
7115 AskQuestionPopDown();
7117 AskQuestionReplyAction(w, NULL, NULL, NULL);
7121 void AskQuestion(title, question, replyPrefix, pr)
7122 char *title, *question, *replyPrefix;
7126 Widget popup, layout, dialog, edit;
7132 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7133 pendingReplyPR = pr;
7136 XtSetArg(args[i], XtNresizable, True); i++;
7137 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7138 askQuestionShell = popup =
7139 XtCreatePopupShell(title, transientShellWidgetClass,
7140 shellWidget, args, i);
7143 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7144 layoutArgs, XtNumber(layoutArgs));
7147 XtSetArg(args[i], XtNlabel, question); i++;
7148 XtSetArg(args[i], XtNvalue, ""); i++;
7149 XtSetArg(args[i], XtNborderWidth, 0); i++;
7150 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7153 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7154 (XtPointer) dialog);
7155 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7156 (XtPointer) dialog);
7158 XtRealizeWidget(popup);
7159 CatchDeleteWindow(popup, "AskQuestionPopDown");
7161 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7162 &x, &y, &win_x, &win_y, &mask);
7164 XtSetArg(args[0], XtNx, x - 10);
7165 XtSetArg(args[1], XtNy, y - 30);
7166 XtSetValues(popup, args, 2);
7168 XtPopup(popup, XtGrabExclusive);
7169 askQuestionUp = True;
7171 edit = XtNameToWidget(dialog, "*value");
7172 XtSetKeyboardFocus(popup, edit);
7180 if (*name == NULLCHAR) {
7182 } else if (strcmp(name, "$") == 0) {
7183 putc(BELLCHAR, stderr);
7186 char *prefix = "", *sep = "";
7187 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7188 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7196 PlaySound(appData.soundMove);
7202 PlaySound(appData.soundIcsWin);
7208 PlaySound(appData.soundIcsLoss);
7214 PlaySound(appData.soundIcsDraw);
7218 PlayIcsUnfinishedSound()
7220 PlaySound(appData.soundIcsUnfinished);
7226 PlaySound(appData.soundIcsAlarm);
7232 system("stty echo");
7238 system("stty -echo");
7242 Colorize(cc, continuation)
7247 int count, outCount, error;
7249 if (textColors[(int)cc].bg > 0) {
7250 if (textColors[(int)cc].fg > 0) {
7251 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7252 textColors[(int)cc].fg, textColors[(int)cc].bg);
7254 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7255 textColors[(int)cc].bg);
7258 if (textColors[(int)cc].fg > 0) {
7259 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7260 textColors[(int)cc].fg);
7262 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7265 count = strlen(buf);
7266 outCount = OutputToProcess(NoProc, buf, count, &error);
7267 if (outCount < count) {
7268 DisplayFatalError(_("Error writing to display"), error, 1);
7271 if (continuation) return;
7274 PlaySound(appData.soundShout);
7277 PlaySound(appData.soundSShout);
7280 PlaySound(appData.soundChannel1);
7283 PlaySound(appData.soundChannel);
7286 PlaySound(appData.soundKibitz);
7289 PlaySound(appData.soundTell);
7291 case ColorChallenge:
7292 PlaySound(appData.soundChallenge);
7295 PlaySound(appData.soundRequest);
7298 PlaySound(appData.soundSeek);
7309 return getpwuid(getuid())->pw_name;
7313 ExpandPathName(path)
7316 static char static_buf[4*MSG_SIZ];
7317 char *d, *s, buf[4*MSG_SIZ];
7323 while (*s && isspace(*s))
7332 if (*(s+1) == '/') {
7333 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7337 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7338 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7339 pwd = getpwnam(buf);
7342 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7346 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7347 strcat(d, strchr(s+1, '/'));
7351 safeStrCpy(d, s, 4*MSG_SIZ );
7358 static char host_name[MSG_SIZ];
7360 #if HAVE_GETHOSTNAME
7361 gethostname(host_name, MSG_SIZ);
7363 #else /* not HAVE_GETHOSTNAME */
7364 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7365 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7367 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7369 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7370 #endif /* not HAVE_GETHOSTNAME */
7373 XtIntervalId delayedEventTimerXID = 0;
7374 DelayedEventCallback delayedEventCallback = 0;
7379 delayedEventTimerXID = 0;
7380 delayedEventCallback();
7384 ScheduleDelayedEvent(cb, millisec)
7385 DelayedEventCallback cb; long millisec;
7387 if(delayedEventTimerXID && delayedEventCallback == cb)
7388 // [HGM] alive: replace, rather than add or flush identical event
7389 XtRemoveTimeOut(delayedEventTimerXID);
7390 delayedEventCallback = cb;
7391 delayedEventTimerXID =
7392 XtAppAddTimeOut(appContext, millisec,
7393 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7396 DelayedEventCallback
7399 if (delayedEventTimerXID) {
7400 return delayedEventCallback;
7407 CancelDelayedEvent()
7409 if (delayedEventTimerXID) {
7410 XtRemoveTimeOut(delayedEventTimerXID);
7411 delayedEventTimerXID = 0;
7415 XtIntervalId loadGameTimerXID = 0;
7417 int LoadGameTimerRunning()
7419 return loadGameTimerXID != 0;
7422 int StopLoadGameTimer()
7424 if (loadGameTimerXID != 0) {
7425 XtRemoveTimeOut(loadGameTimerXID);
7426 loadGameTimerXID = 0;
7434 LoadGameTimerCallback(arg, id)
7438 loadGameTimerXID = 0;
7443 StartLoadGameTimer(millisec)
7447 XtAppAddTimeOut(appContext, millisec,
7448 (XtTimerCallbackProc) LoadGameTimerCallback,
7452 XtIntervalId analysisClockXID = 0;
7455 AnalysisClockCallback(arg, id)
7459 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7460 || appData.icsEngineAnalyze) { // [DM]
7461 AnalysisPeriodicEvent(0);
7462 StartAnalysisClock();
7467 StartAnalysisClock()
7470 XtAppAddTimeOut(appContext, 2000,
7471 (XtTimerCallbackProc) AnalysisClockCallback,
7475 XtIntervalId clockTimerXID = 0;
7477 int ClockTimerRunning()
7479 return clockTimerXID != 0;
7482 int StopClockTimer()
7484 if (clockTimerXID != 0) {
7485 XtRemoveTimeOut(clockTimerXID);
7494 ClockTimerCallback(arg, id)
7503 StartClockTimer(millisec)
7507 XtAppAddTimeOut(appContext, millisec,
7508 (XtTimerCallbackProc) ClockTimerCallback,
7513 DisplayTimerLabel(w, color, timer, highlight)
7522 /* check for low time warning */
7523 Pixel foregroundOrWarningColor = timerForegroundPixel;
7526 appData.lowTimeWarning &&
7527 (timer / 1000) < appData.icsAlarmTime)
7528 foregroundOrWarningColor = lowTimeWarningColor;
7530 if (appData.clockMode) {
7531 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7532 XtSetArg(args[0], XtNlabel, buf);
7534 snprintf(buf, MSG_SIZ, "%s ", color);
7535 XtSetArg(args[0], XtNlabel, buf);
7540 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7541 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7543 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7544 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7547 XtSetValues(w, args, 3);
7551 DisplayWhiteClock(timeRemaining, highlight)
7557 if(appData.noGUI) return;
7558 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7559 if (highlight && iconPixmap == bIconPixmap) {
7560 iconPixmap = wIconPixmap;
7561 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7562 XtSetValues(shellWidget, args, 1);
7567 DisplayBlackClock(timeRemaining, highlight)
7573 if(appData.noGUI) return;
7574 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7575 if (highlight && iconPixmap == wIconPixmap) {
7576 iconPixmap = bIconPixmap;
7577 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7578 XtSetValues(shellWidget, args, 1);
7596 int StartChildProcess(cmdLine, dir, pr)
7603 int to_prog[2], from_prog[2];
7607 if (appData.debugMode) {
7608 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7611 /* We do NOT feed the cmdLine to the shell; we just
7612 parse it into blank-separated arguments in the
7613 most simple-minded way possible.
7616 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7619 while(*p == ' ') p++;
7621 if(*p == '"' || *p == '\'')
7622 p = strchr(++argv[i-1], *p);
7623 else p = strchr(p, ' ');
7624 if (p == NULL) break;
7629 SetUpChildIO(to_prog, from_prog);
7631 if ((pid = fork()) == 0) {
7633 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7634 close(to_prog[1]); // first close the unused pipe ends
7635 close(from_prog[0]);
7636 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7637 dup2(from_prog[1], 1);
7638 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7639 close(from_prog[1]); // and closing again loses one of the pipes!
7640 if(fileno(stderr) >= 2) // better safe than sorry...
7641 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7643 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7648 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7650 execvp(argv[0], argv);
7652 /* If we get here, exec failed */
7657 /* Parent process */
7659 close(from_prog[1]);
7661 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7664 cp->fdFrom = from_prog[0];
7665 cp->fdTo = to_prog[1];
7670 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7671 static RETSIGTYPE AlarmCallBack(int n)
7677 DestroyChildProcess(pr, signalType)
7681 ChildProc *cp = (ChildProc *) pr;
7683 if (cp->kind != CPReal) return;
7685 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7686 signal(SIGALRM, AlarmCallBack);
7688 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7689 kill(cp->pid, SIGKILL); // kill it forcefully
7690 wait((int *) 0); // and wait again
7694 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7696 /* Process is exiting either because of the kill or because of
7697 a quit command sent by the backend; either way, wait for it to die.
7706 InterruptChildProcess(pr)
7709 ChildProc *cp = (ChildProc *) pr;
7711 if (cp->kind != CPReal) return;
7712 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7715 int OpenTelnet(host, port, pr)
7720 char cmdLine[MSG_SIZ];
7722 if (port[0] == NULLCHAR) {
7723 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7725 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7727 return StartChildProcess(cmdLine, "", pr);
7730 int OpenTCP(host, port, pr)
7736 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7737 #else /* !OMIT_SOCKETS */
7738 struct addrinfo hints;
7739 struct addrinfo *ais, *ai;
7744 memset(&hints, 0, sizeof(hints));
7745 hints.ai_family = AF_UNSPEC;
7746 hints.ai_socktype = SOCK_STREAM;
7748 error = getaddrinfo(host, port, &hints, &ais);
7750 /* a getaddrinfo error is not an errno, so can't return it */
7751 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7752 host, port, gai_strerror(error));
7756 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7757 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7761 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7774 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7780 #endif /* !OMIT_SOCKETS */
7785 int OpenCommPort(name, pr)
7792 fd = open(name, 2, 0);
7793 if (fd < 0) return errno;
7795 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7805 int OpenLoopback(pr)
7811 SetUpChildIO(to, from);
7813 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7816 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7823 int OpenRcmd(host, user, cmd, pr)
7824 char *host, *user, *cmd;
7827 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7831 #define INPUT_SOURCE_BUF_SIZE 8192
7840 char buf[INPUT_SOURCE_BUF_SIZE];
7845 DoInputCallback(closure, source, xid)
7850 InputSource *is = (InputSource *) closure;
7855 if (is->lineByLine) {
7856 count = read(is->fd, is->unused,
7857 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7859 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7862 is->unused += count;
7864 while (p < is->unused) {
7865 q = memchr(p, '\n', is->unused - p);
7866 if (q == NULL) break;
7868 (is->func)(is, is->closure, p, q - p, 0);
7872 while (p < is->unused) {
7877 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7882 (is->func)(is, is->closure, is->buf, count, error);
7886 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7893 ChildProc *cp = (ChildProc *) pr;
7895 is = (InputSource *) calloc(1, sizeof(InputSource));
7896 is->lineByLine = lineByLine;
7900 is->fd = fileno(stdin);
7902 is->kind = cp->kind;
7903 is->fd = cp->fdFrom;
7906 is->unused = is->buf;
7909 is->xid = XtAppAddInput(appContext, is->fd,
7910 (XtPointer) (XtInputReadMask),
7911 (XtInputCallbackProc) DoInputCallback,
7913 is->closure = closure;
7914 return (InputSourceRef) is;
7918 RemoveInputSource(isr)
7921 InputSource *is = (InputSource *) isr;
7923 if (is->xid == 0) return;
7924 XtRemoveInput(is->xid);
7928 int OutputToProcess(pr, message, count, outError)
7934 static int line = 0;
7935 ChildProc *cp = (ChildProc *) pr;
7940 if (appData.noJoin || !appData.useInternalWrap)
7941 outCount = fwrite(message, 1, count, stdout);
7944 int width = get_term_width();
7945 int len = wrap(NULL, message, count, width, &line);
7946 char *msg = malloc(len);
7950 outCount = fwrite(message, 1, count, stdout);
7953 dbgchk = wrap(msg, message, count, width, &line);
7954 if (dbgchk != len && appData.debugMode)
7955 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7956 outCount = fwrite(msg, 1, dbgchk, stdout);
7962 outCount = write(cp->fdTo, message, count);
7972 /* Output message to process, with "ms" milliseconds of delay
7973 between each character. This is needed when sending the logon
7974 script to ICC, which for some reason doesn't like the
7975 instantaneous send. */
7976 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
7983 ChildProc *cp = (ChildProc *) pr;
7988 r = write(cp->fdTo, message++, 1);
8001 /**** Animation code by Hugh Fisher, DCS, ANU.
8003 Known problem: if a window overlapping the board is
8004 moved away while a piece is being animated underneath,
8005 the newly exposed area won't be updated properly.
8006 I can live with this.
8008 Known problem: if you look carefully at the animation
8009 of pieces in mono mode, they are being drawn as solid
8010 shapes without interior detail while moving. Fixing
8011 this would be a major complication for minimal return.
8014 /* Masks for XPM pieces. Black and white pieces can have
8015 different shapes, but in the interest of retaining my
8016 sanity pieces must have the same outline on both light
8017 and dark squares, and all pieces must use the same
8018 background square colors/images. */
8020 static int xpmDone = 0;
8023 CreateAnimMasks (pieceDepth)
8030 unsigned long plane;
8033 /* Need a bitmap just to get a GC with right depth */
8034 buf = XCreatePixmap(xDisplay, xBoardWindow,
8036 values.foreground = 1;
8037 values.background = 0;
8038 /* Don't use XtGetGC, not read only */
8039 maskGC = XCreateGC(xDisplay, buf,
8040 GCForeground | GCBackground, &values);
8041 XFreePixmap(xDisplay, buf);
8043 buf = XCreatePixmap(xDisplay, xBoardWindow,
8044 squareSize, squareSize, pieceDepth);
8045 values.foreground = XBlackPixel(xDisplay, xScreen);
8046 values.background = XWhitePixel(xDisplay, xScreen);
8047 bufGC = XCreateGC(xDisplay, buf,
8048 GCForeground | GCBackground, &values);
8050 for (piece = WhitePawn; piece <= BlackKing; piece++) {
8051 /* Begin with empty mask */
8052 if(!xpmDone) // [HGM] pieces: keep using existing
8053 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8054 squareSize, squareSize, 1);
8055 XSetFunction(xDisplay, maskGC, GXclear);
8056 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8057 0, 0, squareSize, squareSize);
8059 /* Take a copy of the piece */
8064 XSetFunction(xDisplay, bufGC, GXcopy);
8065 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8067 0, 0, squareSize, squareSize, 0, 0);
8069 /* XOR the background (light) over the piece */
8070 XSetFunction(xDisplay, bufGC, GXxor);
8072 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8073 0, 0, squareSize, squareSize, 0, 0);
8075 XSetForeground(xDisplay, bufGC, lightSquareColor);
8076 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8079 /* We now have an inverted piece image with the background
8080 erased. Construct mask by just selecting all the non-zero
8081 pixels - no need to reconstruct the original image. */
8082 XSetFunction(xDisplay, maskGC, GXor);
8084 /* Might be quicker to download an XImage and create bitmap
8085 data from it rather than this N copies per piece, but it
8086 only takes a fraction of a second and there is a much
8087 longer delay for loading the pieces. */
8088 for (n = 0; n < pieceDepth; n ++) {
8089 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8090 0, 0, squareSize, squareSize,
8096 XFreePixmap(xDisplay, buf);
8097 XFreeGC(xDisplay, bufGC);
8098 XFreeGC(xDisplay, maskGC);
8102 InitAnimState (anim, info)
8104 XWindowAttributes * info;
8109 /* Each buffer is square size, same depth as window */
8110 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8111 squareSize, squareSize, info->depth);
8112 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8113 squareSize, squareSize, info->depth);
8115 /* Create a plain GC for blitting */
8116 mask = GCForeground | GCBackground | GCFunction |
8117 GCPlaneMask | GCGraphicsExposures;
8118 values.foreground = XBlackPixel(xDisplay, xScreen);
8119 values.background = XWhitePixel(xDisplay, xScreen);
8120 values.function = GXcopy;
8121 values.plane_mask = AllPlanes;
8122 values.graphics_exposures = False;
8123 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8125 /* Piece will be copied from an existing context at
8126 the start of each new animation/drag. */
8127 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8129 /* Outline will be a read-only copy of an existing */
8130 anim->outlineGC = None;
8136 XWindowAttributes info;
8138 if (xpmDone && gameInfo.variant == oldVariant) return;
8139 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
8140 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8142 InitAnimState(&game, &info);
8143 InitAnimState(&player, &info);
8145 /* For XPM pieces, we need bitmaps to use as masks. */
8147 CreateAnimMasks(info.depth), xpmDone = 1;
8152 static Boolean frameWaiting;
8154 static RETSIGTYPE FrameAlarm (sig)
8157 frameWaiting = False;
8158 /* In case System-V style signals. Needed?? */
8159 signal(SIGALRM, FrameAlarm);
8166 struct itimerval delay;
8168 XSync(xDisplay, False);
8171 frameWaiting = True;
8172 signal(SIGALRM, FrameAlarm);
8173 delay.it_interval.tv_sec =
8174 delay.it_value.tv_sec = time / 1000;
8175 delay.it_interval.tv_usec =
8176 delay.it_value.tv_usec = (time % 1000) * 1000;
8177 setitimer(ITIMER_REAL, &delay, NULL);
8178 while (frameWaiting) pause();
8179 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8180 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8181 setitimer(ITIMER_REAL, &delay, NULL);
8191 XSync(xDisplay, False);
8193 usleep(time * 1000);
8198 /* Convert board position to corner of screen rect and color */
8201 ScreenSquare(column, row, pt, color)
8202 int column; int row; XPoint * pt; int * color;
8205 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8206 pt->y = lineGap + row * (squareSize + lineGap);
8208 pt->x = lineGap + column * (squareSize + lineGap);
8209 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8211 *color = SquareColor(row, column);
8214 /* Convert window coords to square */
8217 BoardSquare(x, y, column, row)
8218 int x; int y; int * column; int * row;
8220 *column = EventToSquare(x, BOARD_WIDTH);
8221 if (flipView && *column >= 0)
8222 *column = BOARD_WIDTH - 1 - *column;
8223 *row = EventToSquare(y, BOARD_HEIGHT);
8224 if (!flipView && *row >= 0)
8225 *row = BOARD_HEIGHT - 1 - *row;
8230 #undef Max /* just in case */
8232 #define Max(a, b) ((a) > (b) ? (a) : (b))
8233 #define Min(a, b) ((a) < (b) ? (a) : (b))
8236 SetRect(rect, x, y, width, height)
8237 XRectangle * rect; int x; int y; int width; int height;
8241 rect->width = width;
8242 rect->height = height;
8245 /* Test if two frames overlap. If they do, return
8246 intersection rect within old and location of
8247 that rect within new. */
8250 Intersect(old, new, size, area, pt)
8251 XPoint * old; XPoint * new;
8252 int size; XRectangle * area; XPoint * pt;
8254 if (old->x > new->x + size || new->x > old->x + size ||
8255 old->y > new->y + size || new->y > old->y + size) {
8258 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8259 size - abs(old->x - new->x), size - abs(old->y - new->y));
8260 pt->x = Max(old->x - new->x, 0);
8261 pt->y = Max(old->y - new->y, 0);
8266 /* For two overlapping frames, return the rect(s)
8267 in the old that do not intersect with the new. */
8270 CalcUpdateRects(old, new, size, update, nUpdates)
8271 XPoint * old; XPoint * new; int size;
8272 XRectangle update[]; int * nUpdates;
8276 /* If old = new (shouldn't happen) then nothing to draw */
8277 if (old->x == new->x && old->y == new->y) {
8281 /* Work out what bits overlap. Since we know the rects
8282 are the same size we don't need a full intersect calc. */
8284 /* Top or bottom edge? */
8285 if (new->y > old->y) {
8286 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8288 } else if (old->y > new->y) {
8289 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8290 size, old->y - new->y);
8293 /* Left or right edge - don't overlap any update calculated above. */
8294 if (new->x > old->x) {
8295 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8296 new->x - old->x, size - abs(new->y - old->y));
8298 } else if (old->x > new->x) {
8299 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8300 old->x - new->x, size - abs(new->y - old->y));
8307 /* Generate a series of frame coords from start->mid->finish.
8308 The movement rate doubles until the half way point is
8309 reached, then halves back down to the final destination,
8310 which gives a nice slow in/out effect. The algorithmn
8311 may seem to generate too many intermediates for short
8312 moves, but remember that the purpose is to attract the
8313 viewers attention to the piece about to be moved and
8314 then to where it ends up. Too few frames would be less
8318 Tween(start, mid, finish, factor, frames, nFrames)
8319 XPoint * start; XPoint * mid;
8320 XPoint * finish; int factor;
8321 XPoint frames[]; int * nFrames;
8323 int fraction, n, count;
8327 /* Slow in, stepping 1/16th, then 1/8th, ... */
8329 for (n = 0; n < factor; n++)
8331 for (n = 0; n < factor; n++) {
8332 frames[count].x = start->x + (mid->x - start->x) / fraction;
8333 frames[count].y = start->y + (mid->y - start->y) / fraction;
8335 fraction = fraction / 2;
8339 frames[count] = *mid;
8342 /* Slow out, stepping 1/2, then 1/4, ... */
8344 for (n = 0; n < factor; n++) {
8345 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8346 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8348 fraction = fraction * 2;
8353 /* Draw a piece on the screen without disturbing what's there */
8356 SelectGCMask(piece, clip, outline, mask)
8357 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8361 /* Bitmap for piece being moved. */
8362 if (appData.monoMode) {
8363 *mask = *pieceToSolid(piece);
8364 } else if (useImages) {
8366 *mask = xpmMask[piece];
8368 *mask = ximMaskPm[piece];
8371 *mask = *pieceToSolid(piece);
8374 /* GC for piece being moved. Square color doesn't matter, but
8375 since it gets modified we make a copy of the original. */
8377 if (appData.monoMode)
8382 if (appData.monoMode)
8387 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8389 /* Outline only used in mono mode and is not modified */
8391 *outline = bwPieceGC;
8393 *outline = wbPieceGC;
8397 OverlayPiece(piece, clip, outline, dest)
8398 ChessSquare piece; GC clip; GC outline; Drawable dest;
8403 /* Draw solid rectangle which will be clipped to shape of piece */
8404 XFillRectangle(xDisplay, dest, clip,
8405 0, 0, squareSize, squareSize);
8406 if (appData.monoMode)
8407 /* Also draw outline in contrasting color for black
8408 on black / white on white cases */
8409 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8410 0, 0, squareSize, squareSize, 0, 0, 1);
8412 /* Copy the piece */
8417 if(appData.upsideDown && flipView) kind ^= 2;
8418 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8420 0, 0, squareSize, squareSize,
8425 /* Animate the movement of a single piece */
8428 BeginAnimation(anim, piece, startColor, start)
8436 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8437 /* The old buffer is initialised with the start square (empty) */
8438 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8439 anim->prevFrame = *start;
8441 /* The piece will be drawn using its own bitmap as a matte */
8442 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8443 XSetClipMask(xDisplay, anim->pieceGC, mask);
8447 AnimationFrame(anim, frame, piece)
8452 XRectangle updates[4];
8457 /* Save what we are about to draw into the new buffer */
8458 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8459 frame->x, frame->y, squareSize, squareSize,
8462 /* Erase bits of the previous frame */
8463 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8464 /* Where the new frame overlapped the previous,
8465 the contents in newBuf are wrong. */
8466 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8467 overlap.x, overlap.y,
8468 overlap.width, overlap.height,
8470 /* Repaint the areas in the old that don't overlap new */
8471 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8472 for (i = 0; i < count; i++)
8473 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8474 updates[i].x - anim->prevFrame.x,
8475 updates[i].y - anim->prevFrame.y,
8476 updates[i].width, updates[i].height,
8477 updates[i].x, updates[i].y);
8479 /* Easy when no overlap */
8480 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8481 0, 0, squareSize, squareSize,
8482 anim->prevFrame.x, anim->prevFrame.y);
8485 /* Save this frame for next time round */
8486 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8487 0, 0, squareSize, squareSize,
8489 anim->prevFrame = *frame;
8491 /* Draw piece over original screen contents, not current,
8492 and copy entire rect. Wipes out overlapping piece images. */
8493 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8494 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8495 0, 0, squareSize, squareSize,
8496 frame->x, frame->y);
8500 EndAnimation (anim, finish)
8504 XRectangle updates[4];
8509 /* The main code will redraw the final square, so we
8510 only need to erase the bits that don't overlap. */
8511 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8512 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8513 for (i = 0; i < count; i++)
8514 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8515 updates[i].x - anim->prevFrame.x,
8516 updates[i].y - anim->prevFrame.y,
8517 updates[i].width, updates[i].height,
8518 updates[i].x, updates[i].y);
8520 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8521 0, 0, squareSize, squareSize,
8522 anim->prevFrame.x, anim->prevFrame.y);
8527 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8529 ChessSquare piece; int startColor;
8530 XPoint * start; XPoint * finish;
8531 XPoint frames[]; int nFrames;
8535 BeginAnimation(anim, piece, startColor, start);
8536 for (n = 0; n < nFrames; n++) {
8537 AnimationFrame(anim, &(frames[n]), piece);
8538 FrameDelay(appData.animSpeed);
8540 EndAnimation(anim, finish);
8544 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8547 ChessSquare piece = board[fromY][toY];
8548 board[fromY][toY] = EmptySquare;
8549 DrawPosition(FALSE, board);
8551 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8552 y = lineGap + toY * (squareSize + lineGap);
8554 x = lineGap + toX * (squareSize + lineGap);
8555 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8557 for(i=1; i<4*kFactor; i++) {
8558 int r = squareSize * 9 * i/(20*kFactor - 5);
8559 XFillArc(xDisplay, xBoardWindow, highlineGC,
8560 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8561 FrameDelay(appData.animSpeed);
8563 board[fromY][toY] = piece;
8566 /* Main control logic for deciding what to animate and how */
8569 AnimateMove(board, fromX, fromY, toX, toY)
8578 XPoint start, finish, mid;
8579 XPoint frames[kFactor * 2 + 1];
8580 int nFrames, startColor, endColor;
8582 /* Are we animating? */
8583 if (!appData.animate || appData.blindfold)
8586 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8587 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8588 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8590 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8591 piece = board[fromY][fromX];
8592 if (piece >= EmptySquare) return;
8597 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8600 if (appData.debugMode) {
8601 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8602 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8603 piece, fromX, fromY, toX, toY); }
8605 ScreenSquare(fromX, fromY, &start, &startColor);
8606 ScreenSquare(toX, toY, &finish, &endColor);
8609 /* Knight: make straight movement then diagonal */
8610 if (abs(toY - fromY) < abs(toX - fromX)) {
8611 mid.x = start.x + (finish.x - start.x) / 2;
8615 mid.y = start.y + (finish.y - start.y) / 2;
8618 mid.x = start.x + (finish.x - start.x) / 2;
8619 mid.y = start.y + (finish.y - start.y) / 2;
8622 /* Don't use as many frames for very short moves */
8623 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8624 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8626 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8627 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8628 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8630 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8631 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8634 /* Be sure end square is redrawn */
8635 damage[0][toY][toX] = True;
8639 DragPieceBegin(x, y)
8642 int boardX, boardY, color;
8645 /* Are we animating? */
8646 if (!appData.animateDragging || appData.blindfold)
8649 /* Figure out which square we start in and the
8650 mouse position relative to top left corner. */
8651 BoardSquare(x, y, &boardX, &boardY);
8652 player.startBoardX = boardX;
8653 player.startBoardY = boardY;
8654 ScreenSquare(boardX, boardY, &corner, &color);
8655 player.startSquare = corner;
8656 player.startColor = color;
8657 /* As soon as we start dragging, the piece will jump slightly to
8658 be centered over the mouse pointer. */
8659 player.mouseDelta.x = squareSize/2;
8660 player.mouseDelta.y = squareSize/2;
8661 /* Initialise animation */
8662 player.dragPiece = PieceForSquare(boardX, boardY);
8664 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8665 player.dragActive = True;
8666 BeginAnimation(&player, player.dragPiece, color, &corner);
8667 /* Mark this square as needing to be redrawn. Note that
8668 we don't remove the piece though, since logically (ie
8669 as seen by opponent) the move hasn't been made yet. */
8670 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8671 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8672 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8673 corner.x, corner.y, squareSize, squareSize,
8674 0, 0); // [HGM] zh: unstack in stead of grab
8675 if(gatingPiece != EmptySquare) {
8676 /* Kludge alert: When gating we want the introduced
8677 piece to appear on the from square. To generate an
8678 image of it, we draw it on the board, copy the image,
8679 and draw the original piece again. */
8680 ChessSquare piece = boards[currentMove][boardY][boardX];
8681 DrawSquare(boardY, boardX, gatingPiece, 0);
8682 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8683 corner.x, corner.y, squareSize, squareSize, 0, 0);
8684 DrawSquare(boardY, boardX, piece, 0);
8686 damage[0][boardY][boardX] = True;
8688 player.dragActive = False;
8693 ChangeDragPiece(ChessSquare piece)
8696 player.dragPiece = piece;
8697 /* The piece will be drawn using its own bitmap as a matte */
8698 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8699 XSetClipMask(xDisplay, player.pieceGC, mask);
8708 /* Are we animating? */
8709 if (!appData.animateDragging || appData.blindfold)
8713 if (! player.dragActive)
8715 /* Move piece, maintaining same relative position
8716 of mouse within square */
8717 corner.x = x - player.mouseDelta.x;
8718 corner.y = y - player.mouseDelta.y;
8719 AnimationFrame(&player, &corner, player.dragPiece);
8721 if (appData.highlightDragging) {
8723 BoardSquare(x, y, &boardX, &boardY);
8724 SetHighlights(fromX, fromY, boardX, boardY);
8733 int boardX, boardY, color;
8736 /* Are we animating? */
8737 if (!appData.animateDragging || appData.blindfold)
8741 if (! player.dragActive)
8743 /* Last frame in sequence is square piece is
8744 placed on, which may not match mouse exactly. */
8745 BoardSquare(x, y, &boardX, &boardY);
8746 ScreenSquare(boardX, boardY, &corner, &color);
8747 EndAnimation(&player, &corner);
8749 /* Be sure end square is redrawn */
8750 damage[0][boardY][boardX] = True;
8752 /* This prevents weird things happening with fast successive
8753 clicks which on my Sun at least can cause motion events
8754 without corresponding press/release. */
8755 player.dragActive = False;
8758 /* Handle expose event while piece being dragged */
8763 if (!player.dragActive || appData.blindfold)
8766 /* What we're doing: logically, the move hasn't been made yet,
8767 so the piece is still in it's original square. But visually
8768 it's being dragged around the board. So we erase the square
8769 that the piece is on and draw it at the last known drag point. */
8770 BlankSquare(player.startSquare.x, player.startSquare.y,
8771 player.startColor, EmptySquare, xBoardWindow, 1);
8772 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8773 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8776 #include <sys/ioctl.h>
8777 int get_term_width()
8779 int fd, default_width;
8782 default_width = 79; // this is FICS default anyway...
8784 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8786 if (!ioctl(fd, TIOCGSIZE, &win))
8787 default_width = win.ts_cols;
8788 #elif defined(TIOCGWINSZ)
8790 if (!ioctl(fd, TIOCGWINSZ, &win))
8791 default_width = win.ws_col;
8793 return default_width;
8799 static int old_width = 0;
8800 int new_width = get_term_width();
8802 if (old_width != new_width)
8803 ics_printf("set width %d\n", new_width);
8804 old_width = new_width;
8807 void NotifyFrontendLogin()
8812 /* [AS] Arrow highlighting support */
8814 static double A_WIDTH = 5; /* Width of arrow body */
8816 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8817 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8819 static double Sqr( double x )
8824 static int Round( double x )
8826 return (int) (x + 0.5);
8829 void SquareToPos(int rank, int file, int *x, int *y)
8832 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8833 *y = lineGap + rank * (squareSize + lineGap);
8835 *x = lineGap + file * (squareSize + lineGap);
8836 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8840 /* Draw an arrow between two points using current settings */
8841 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8844 double dx, dy, j, k, x, y;
8847 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8849 arrow[0].x = s_x + A_WIDTH + 0.5;
8852 arrow[1].x = s_x + A_WIDTH + 0.5;
8853 arrow[1].y = d_y - h;
8855 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8856 arrow[2].y = d_y - h;
8861 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8862 arrow[5].y = d_y - h;
8864 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8865 arrow[4].y = d_y - h;
8867 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8870 else if( d_y == s_y ) {
8871 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8874 arrow[0].y = s_y + A_WIDTH + 0.5;
8876 arrow[1].x = d_x - w;
8877 arrow[1].y = s_y + A_WIDTH + 0.5;
8879 arrow[2].x = d_x - w;
8880 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8885 arrow[5].x = d_x - w;
8886 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8888 arrow[4].x = d_x - w;
8889 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8892 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8895 /* [AS] Needed a lot of paper for this! :-) */
8896 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8897 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8899 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8901 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8906 arrow[0].x = Round(x - j);
8907 arrow[0].y = Round(y + j*dx);
8909 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8910 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8913 x = (double) d_x - k;
8914 y = (double) d_y - k*dy;
8917 x = (double) d_x + k;
8918 y = (double) d_y + k*dy;
8921 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8923 arrow[6].x = Round(x - j);
8924 arrow[6].y = Round(y + j*dx);
8926 arrow[2].x = Round(arrow[6].x + 2*j);
8927 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8929 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8930 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8935 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8936 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8939 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8940 // Polygon( hdc, arrow, 7 );
8943 /* [AS] Draw an arrow between two squares */
8944 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
8946 int s_x, s_y, d_x, d_y, hor, vert, i;
8948 if( s_col == d_col && s_row == d_row ) {
8952 /* Get source and destination points */
8953 SquareToPos( s_row, s_col, &s_x, &s_y);
8954 SquareToPos( d_row, d_col, &d_x, &d_y);
8957 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8959 else if( d_y < s_y ) {
8960 d_y += squareSize / 2 + squareSize / 4;
8963 d_y += squareSize / 2;
8967 d_x += squareSize / 2 - squareSize / 4;
8969 else if( d_x < s_x ) {
8970 d_x += squareSize / 2 + squareSize / 4;
8973 d_x += squareSize / 2;
8976 s_x += squareSize / 2;
8977 s_y += squareSize / 2;
8980 A_WIDTH = squareSize / 14.; //[HGM] make float
8982 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8984 hor = 64*s_col + 32; vert = 64*s_row + 32;
8985 for(i=0; i<= 64; i++) {
8986 damage[0][vert+6>>6][hor+6>>6] = True;
8987 damage[0][vert-6>>6][hor+6>>6] = True;
8988 damage[0][vert+6>>6][hor-6>>6] = True;
8989 damage[0][vert-6>>6][hor-6>>6] = True;
8990 hor += d_col - s_col; vert += d_row - s_row;
8994 Boolean IsDrawArrowEnabled()
8996 return appData.highlightMoveWithArrow && squareSize >= 32;
8999 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
9001 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
9002 DrawArrowBetweenSquares(fromX, fromY, toX, toY);