2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
66 # if HAVE_SYS_SOCKET_H
67 # include <sys/socket.h>
68 # include <netinet/in.h>
70 # else /* not HAVE_SYS_SOCKET_H */
71 # if HAVE_LAN_SOCKET_H
72 # include <lan/socket.h>
74 # include <lan/netdb.h>
75 # else /* not HAVE_LAN_SOCKET_H */
76 # define OMIT_SOCKETS 1
77 # endif /* not HAVE_LAN_SOCKET_H */
78 # endif /* not HAVE_SYS_SOCKET_H */
79 #endif /* !OMIT_SOCKETS */
84 #else /* not STDC_HEADERS */
85 extern char *getenv();
88 # else /* not HAVE_STRING_H */
90 # endif /* not HAVE_STRING_H */
91 #endif /* not STDC_HEADERS */
94 # include <sys/fcntl.h>
95 #else /* not HAVE_SYS_FCNTL_H */
98 # endif /* HAVE_FCNTL_H */
99 #endif /* not HAVE_SYS_FCNTL_H */
101 #if HAVE_SYS_SYSTEMINFO_H
102 # include <sys/systeminfo.h>
103 #endif /* HAVE_SYS_SYSTEMINFO_H */
105 #if TIME_WITH_SYS_TIME
106 # include <sys/time.h>
110 # include <sys/time.h>
121 # include <sys/wait.h>
126 # define NAMLEN(dirent) strlen((dirent)->d_name)
127 # define HAVE_DIR_STRUCT
129 # define dirent direct
130 # define NAMLEN(dirent) (dirent)->d_namlen
132 # include <sys/ndir.h>
133 # define HAVE_DIR_STRUCT
136 # include <sys/dir.h>
137 # define HAVE_DIR_STRUCT
141 # define HAVE_DIR_STRUCT
149 #include <X11/Intrinsic.h>
150 #include <X11/StringDefs.h>
151 #include <X11/Shell.h>
152 #include <X11/cursorfont.h>
153 #include <X11/Xatom.h>
154 #include <X11/Xmu/Atoms.h>
156 #include <X11/Xaw3d/Dialog.h>
157 #include <X11/Xaw3d/Form.h>
158 #include <X11/Xaw3d/List.h>
159 #include <X11/Xaw3d/Label.h>
160 #include <X11/Xaw3d/SimpleMenu.h>
161 #include <X11/Xaw3d/SmeBSB.h>
162 #include <X11/Xaw3d/SmeLine.h>
163 #include <X11/Xaw3d/Box.h>
164 #include <X11/Xaw3d/MenuButton.h>
165 #include <X11/Xaw3d/Text.h>
166 #include <X11/Xaw3d/AsciiText.h>
168 #include <X11/Xaw/Dialog.h>
169 #include <X11/Xaw/Form.h>
170 #include <X11/Xaw/List.h>
171 #include <X11/Xaw/Label.h>
172 #include <X11/Xaw/SimpleMenu.h>
173 #include <X11/Xaw/SmeBSB.h>
174 #include <X11/Xaw/SmeLine.h>
175 #include <X11/Xaw/Box.h>
176 #include <X11/Xaw/MenuButton.h>
177 #include <X11/Xaw/Text.h>
178 #include <X11/Xaw/AsciiText.h>
181 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
186 #include "pixmaps/pixmaps.h"
187 #define IMAGE_EXT "xpm"
189 #define IMAGE_EXT "xim"
190 #include "bitmaps/bitmaps.h"
193 #include "bitmaps/icon_white.bm"
194 #include "bitmaps/icon_black.bm"
195 #include "bitmaps/checkmark.bm"
197 #include "frontend.h"
199 #include "backendz.h"
203 #include "xgamelist.h"
204 #include "xhistory.h"
205 #include "xedittags.h"
208 // must be moved to xengineoutput.h
210 void EngineOutputProc P((Widget w, XEvent *event,
211 String *prms, Cardinal *nprms));
212 void EvalGraphProc P((Widget w, XEvent *event,
213 String *prms, Cardinal *nprms));
220 #define usleep(t) _sleep2(((t)+500)/1000)
224 # define _(s) gettext (s)
225 # define N_(s) gettext_noop (s)
243 int main P((int argc, char **argv));
244 FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
245 char *init_path, char *filter, char *mode, int (*show_entry)(), char **name_return));
246 RETSIGTYPE CmailSigHandler P((int sig));
247 RETSIGTYPE IntSigHandler P((int sig));
248 RETSIGTYPE TermSizeSigHandler P((int sig));
249 void CreateGCs P((int redo));
250 void CreateAnyPieces P((void));
251 void CreateXIMPieces P((void));
252 void CreateXPMPieces P((void));
253 void CreateXPMBoard P((char *s, int n));
254 void CreatePieces P((void));
255 void CreatePieceMenus P((void));
256 Widget CreateMenuBar P((Menu *mb));
257 Widget CreateButtonBar P ((MenuItem *mi));
259 char *InsertPxlSize P((char *pattern, int targetPxlSize));
260 XFontSet CreateFontSet P((char *base_fnt_lst));
262 char *FindFont P((char *pattern, int targetPxlSize));
264 void PieceMenuPopup P((Widget w, XEvent *event,
265 String *params, Cardinal *num_params));
266 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
267 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
268 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
269 u_int wreq, u_int hreq));
270 void CreateGrid P((void));
271 int EventToSquare P((int x, int limit));
272 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
273 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
274 void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
275 void HandleUserMove P((Widget w, XEvent *event,
276 String *prms, Cardinal *nprms));
277 void AnimateUserMove P((Widget w, XEvent * event,
278 String * params, Cardinal * nParams));
279 void HandlePV P((Widget w, XEvent * event,
280 String * params, Cardinal * nParams));
281 void SelectPV P((Widget w, XEvent * event,
282 String * params, Cardinal * nParams));
283 void StopPV P((Widget w, XEvent * event,
284 String * params, Cardinal * nParams));
285 void WhiteClock P((Widget w, XEvent *event,
286 String *prms, Cardinal *nprms));
287 void BlackClock P((Widget w, XEvent *event,
288 String *prms, Cardinal *nprms));
289 void DrawPositionProc P((Widget w, XEvent *event,
290 String *prms, Cardinal *nprms));
291 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
293 void CommentClick P((Widget w, XEvent * event,
294 String * params, Cardinal * nParams));
295 void CommentPopUp P((char *title, char *label));
296 void CommentPopDown P((void));
297 void ICSInputBoxPopUp P((void));
298 void ICSInputBoxPopDown P((void));
299 void FileNamePopUp P((char *label, char *def, char *filter,
300 FileProc proc, char *openMode));
301 void FileNamePopDown P((void));
302 void FileNameCallback P((Widget w, XtPointer client_data,
303 XtPointer call_data));
304 void FileNameAction P((Widget w, XEvent *event,
305 String *prms, Cardinal *nprms));
306 void AskQuestionReplyAction P((Widget w, XEvent *event,
307 String *prms, Cardinal *nprms));
308 void AskQuestionProc P((Widget w, XEvent *event,
309 String *prms, Cardinal *nprms));
310 void AskQuestionPopDown P((void));
311 void PromotionPopDown P((void));
312 void PromotionCallback P((Widget w, XtPointer client_data,
313 XtPointer call_data));
314 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
315 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
316 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
317 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
319 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
321 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
323 void LoadPositionProc P((Widget w, XEvent *event,
324 String *prms, Cardinal *nprms));
325 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
327 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
329 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
331 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
333 void PastePositionProc P((Widget w, XEvent *event, String *prms,
335 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
336 void CopyGameListProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
337 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
338 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
339 void SavePositionProc P((Widget w, XEvent *event,
340 String *prms, Cardinal *nprms));
341 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
342 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
344 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
345 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
346 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
348 void MachineWhiteProc P((Widget w, XEvent *event,
349 String *prms, Cardinal *nprms));
350 void AnalyzeModeProc P((Widget w, XEvent *event,
351 String *prms, Cardinal *nprms));
352 void AnalyzeFileProc P((Widget w, XEvent *event,
353 String *prms, Cardinal *nprms));
354 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
356 void MatchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
357 void MatchOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
358 void IcsClientProc P((Widget w, XEvent *event, String *prms,
360 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
361 void EditPositionProc P((Widget w, XEvent *event,
362 String *prms, Cardinal *nprms));
363 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
364 void EditCommentProc P((Widget w, XEvent *event,
365 String *prms, Cardinal *nprms));
366 void IcsInputBoxProc P((Widget w, XEvent *event,
367 String *prms, Cardinal *nprms));
368 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
369 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
370 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
371 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
372 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
373 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
374 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
375 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
376 void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
377 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
378 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
379 void TypeInProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
380 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
381 void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
382 void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
383 void StopObservingProc P((Widget w, XEvent *event, String *prms,
385 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
387 void UploadProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
388 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
389 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
390 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
391 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
392 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
393 void AnnotateProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
394 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
396 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
398 void MoveNowProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
399 void AlwaysQueenProc P((Widget w, XEvent *event, String *prms,
401 void AnimateDraggingProc P((Widget w, XEvent *event, String *prms,
403 void AnimateMovingProc P((Widget w, XEvent *event, String *prms,
405 void AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
406 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
407 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
408 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
410 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
411 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
413 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
415 void HighlightArrowProc P((Widget w, XEvent *event, String *prms,
417 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
418 //void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
419 void OneClickProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
420 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
422 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
424 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
426 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
428 //void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
429 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
431 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
433 void HideThinkingProc P((Widget w, XEvent *event, String *prms,
435 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
437 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
438 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
439 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
440 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
441 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
442 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
443 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
444 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
445 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
446 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
447 void Iconify P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
448 void DisplayMove P((int moveNumber));
449 void DisplayTitle P((char *title));
450 void ICSInitScript P((void));
451 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
452 void ErrorPopUp P((char *title, char *text, int modal));
453 void ErrorPopDown P((void));
454 static char *ExpandPathName P((char *path));
455 static void CreateAnimVars P((void));
456 static void DragPieceMove P((int x, int y));
457 static void DrawDragPiece P((void));
458 char *ModeToWidgetName P((GameMode mode));
459 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
460 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
461 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
462 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
463 void OptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
464 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
465 void IcsTextProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
466 void LoadEngineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
467 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
468 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
469 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
470 void IcsOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
471 void SoundOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
472 void BoardOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
473 void LoadOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
474 void SaveOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
475 void EditBookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
476 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
477 void GameListOptionsPopDown P(());
478 void GenericPopDown P(());
479 void update_ics_width P(());
480 int get_term_width P(());
481 int CopyMemoProc P(());
482 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
483 Boolean IsDrawArrowEnabled P(());
486 * XBoard depends on Xt R4 or higher
488 int xtVersion = XtSpecificationRelease;
493 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
494 jailSquareColor, highlightSquareColor, premoveHighlightColor;
495 Pixel lowTimeWarningColor;
496 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
497 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
498 wjPieceGC, bjPieceGC, prelineGC, countGC;
499 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
500 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
501 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
502 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
503 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
504 ICSInputShell, fileNameShell, askQuestionShell;
505 Widget historyShell, evalGraphShell, gameListShell;
506 int hOffset; // [HGM] dual
507 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
508 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
509 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
511 XFontSet fontSet, clockFontSet;
514 XFontStruct *clockFontStruct;
516 Font coordFontID, countFontID;
517 XFontStruct *coordFontStruct, *countFontStruct;
518 XtAppContext appContext;
520 char *oldICSInteractionTitle;
524 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
526 Position commentX = -1, commentY = -1;
527 Dimension commentW, commentH;
528 typedef unsigned int BoardSize;
530 Boolean chessProgram;
532 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
533 int squareSize, smallLayout = 0, tinyLayout = 0,
534 marginW, marginH, // [HGM] for run-time resizing
535 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
536 ICSInputBoxUp = False, askQuestionUp = False,
537 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
538 errorUp = False, errorExitStatus = -1, lineGap, defaultLineGap;
539 Pixel timerForegroundPixel, timerBackgroundPixel;
540 Pixel buttonForegroundPixel, buttonBackgroundPixel;
541 char *chessDir, *programName, *programVersion,
542 *gameCopyFilename, *gamePasteFilename;
543 Boolean alwaysOnTop = False;
544 Boolean saveSettingsOnExit;
545 char *settingsFileName;
546 char *icsTextMenuString;
548 char *firstChessProgramNames;
549 char *secondChessProgramNames;
551 WindowPlacement wpMain;
552 WindowPlacement wpConsole;
553 WindowPlacement wpComment;
554 WindowPlacement wpMoveHistory;
555 WindowPlacement wpEvalGraph;
556 WindowPlacement wpEngineOutput;
557 WindowPlacement wpGameList;
558 WindowPlacement wpTags;
560 extern Widget shells[];
561 extern Boolean shellUp[];
565 Pixmap pieceBitmap[2][(int)BlackPawn];
566 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
567 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
568 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
569 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
570 Pixmap xpmBoardBitmap[2];
571 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
572 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
573 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
574 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
575 XImage *ximLightSquare, *ximDarkSquare;
578 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
579 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
581 #define White(piece) ((int)(piece) < (int)BlackPawn)
583 /* Variables for doing smooth animation. This whole thing
584 would be much easier if the board was double-buffered,
585 but that would require a fairly major rewrite. */
590 GC blitGC, pieceGC, outlineGC;
591 XPoint startSquare, prevFrame, mouseDelta;
595 int startBoardX, startBoardY;
598 /* There can be two pieces being animated at once: a player
599 can begin dragging a piece before the remote opponent has moved. */
601 static AnimState game, player;
603 /* Bitmaps for use as masks when drawing XPM pieces.
604 Need one for each black and white piece. */
605 static Pixmap xpmMask[BlackKing + 1];
607 /* This magic number is the number of intermediate frames used
608 in each half of the animation. For short moves it's reduced
609 by 1. The total number of frames will be factor * 2 + 1. */
612 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
614 MenuItem fileMenu[] = {
615 {N_("New Game Ctrl+N"), "New Game", ResetProc},
616 {N_("New Shuffle Game ..."), "New Shuffle Game", ShuffleMenuProc},
617 {N_("New Variant ... Alt+Shift+V"), "New Variant", NewVariantProc}, // [HGM] variant: not functional yet
618 {"----", NULL, NothingProc},
619 {N_("Load Game Ctrl+O"), "Load Game", LoadGameProc},
620 {N_("Load Position Ctrl+Shift+O"), "Load Position", LoadPositionProc},
621 // {N_("Load Next Game"), "Load Next Game", LoadNextGameProc},
622 // {N_("Load Previous Game"), "Load Previous Game", LoadPrevGameProc},
623 // {N_("Reload Same Game"), "Reload Same Game", ReloadGameProc},
624 {N_("Next Position Shift+PgDn"), "Load Next Position", LoadNextPositionProc},
625 {N_("Prev Position Shift+PgUp"), "Load Previous Position", LoadPrevPositionProc},
626 {"----", NULL, NothingProc},
627 // {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
628 {N_("Save Game Ctrl+S"), "Save Game", SaveGameProc},
629 {N_("Save Position Ctrl+Shift+S"), "Save Position", SavePositionProc},
630 {"----", NULL, NothingProc},
631 {N_("Mail Move"), "Mail Move", MailMoveProc},
632 {N_("Reload CMail Message"), "Reload CMail Message", ReloadCmailMsgProc},
633 {"----", NULL, NothingProc},
634 {N_("Quit Ctr+Q"), "Exit", QuitProc},
638 MenuItem editMenu[] = {
639 {N_("Copy Game Ctrl+C"), "Copy Game", CopyGameProc},
640 {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
641 {N_("Copy Game List"), "Copy Game List", CopyGameListProc},
642 {"----", NULL, NothingProc},
643 {N_("Paste Game Ctrl+V"), "Paste Game", PasteGameProc},
644 {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
645 {"----", NULL, NothingProc},
646 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
647 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
648 {N_("Edit Tags"), "Edit Tags", EditTagsProc},
649 {N_("Edit Comment"), "Edit Comment", EditCommentProc},
650 {N_("Edit Book"), "Edit Book", EditBookProc},
651 {"----", NULL, NothingProc},
652 {N_("Revert Home"), "Revert", RevertProc},
653 {N_("Annotate"), "Annotate", AnnotateProc},
654 {N_("Truncate Game End"), "Truncate Game", TruncateGameProc},
655 {"----", NULL, NothingProc},
656 {N_("Backward Alt+Left"), "Backward", BackwardProc},
657 {N_("Forward Alt+Right"), "Forward", ForwardProc},
658 {N_("Back to Start Alt+Home"), "Back to Start", ToStartProc},
659 {N_("Forward to End Alt+End"), "Forward to End", ToEndProc},
663 MenuItem viewMenu[] = {
664 {N_("Flip View F2"), "Flip View", FlipViewProc},
665 {"----", NULL, NothingProc},
666 {N_("Engine Output Alt+Shift+O"), "Show Engine Output", EngineOutputProc},
667 {N_("Move History Alt+Shift+H"), "Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
668 {N_("Evaluation Graph Alt+Shift+E"), "Show Evaluation Graph", EvalGraphProc},
669 {N_("Game List Alt+Shift+G"), "Show Game List", ShowGameListProc},
670 {N_("ICS text menu"), "ICStex", IcsTextProc},
671 {"----", NULL, NothingProc},
672 {N_("Tags"), "Show Tags", EditTagsProc},
673 {N_("Comments"), "Show Comments", EditCommentProc},
674 {N_("ICS Input Box"), "ICS Input Box", IcsInputBoxProc},
675 {"----", NULL, NothingProc},
676 {N_("Board..."), "Board Options", BoardOptionsProc},
677 {N_("Game List Tags..."), "Game List", GameListOptionsPopUp},
681 MenuItem modeMenu[] = {
682 {N_("Machine White Ctrl+W"), "Machine White", MachineWhiteProc},
683 {N_("Machine Black Ctrl+B"), "Machine Black", MachineBlackProc},
684 {N_("Two Machines Ctrl+T"), "Two Machines", TwoMachinesProc},
685 {N_("Analysis Mode Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
686 {N_("Analyze Game Ctrl+G"), "Analyze File", AnalyzeFileProc },
687 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
688 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
689 {N_("Training"), "Training", TrainingProc},
690 {N_("ICS Client"), "ICS Client", IcsClientProc},
691 {"----", NULL, NothingProc},
692 {N_("Machine Match"), "Machine Match", MatchProc},
693 {N_("Pause Pause"), "Pause", PauseProc},
697 MenuItem actionMenu[] = {
698 {N_("Accept F3"), "Accept", AcceptProc},
699 {N_("Decline F4"), "Decline", DeclineProc},
700 {N_("Rematch F12"), "Rematch", RematchProc},
701 {"----", NULL, NothingProc},
702 {N_("Call Flag F5"), "Call Flag", CallFlagProc},
703 {N_("Draw F6"), "Draw", DrawProc},
704 {N_("Adjourn F7"), "Adjourn", AdjournProc},
705 {N_("Abort F8"),"Abort", AbortProc},
706 {N_("Resign F9"), "Resign", ResignProc},
707 {"----", NULL, NothingProc},
708 {N_("Stop Observing F10"), "Stop Observing", StopObservingProc},
709 {N_("Stop Examining F11"), "Stop Examining", StopExaminingProc},
710 {N_("Upload to Examine"), "Upload to Examine", UploadProc},
711 {"----", NULL, NothingProc},
712 {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
713 {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
714 {N_("Adjudicate Draw"), "Adjudicate Draw", AdjuDrawProc},
718 MenuItem engineMenu[] = {
719 {N_("Load New Engine ..."), "Load Engine", LoadEngineProc},
720 {"----", NULL, NothingProc},
721 {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
722 {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
723 {"----", NULL, NothingProc},
724 {N_("Hint"), "Hint", HintProc},
725 {N_("Book"), "Book", BookProc},
726 {"----", NULL, NothingProc},
727 {N_("Move Now Ctrl+M"), "Move Now", MoveNowProc},
728 {N_("Retract Move Ctrl+X"), "Retract Move", RetractMoveProc},
732 MenuItem optionsMenu[] = {
733 #define OPTIONSDIALOG
735 {N_("General ..."), "General", OptionsProc},
737 {N_("Time Control ... Alt+Shift+T"), "Time Control", TimeControlProc},
738 {N_("Common Engine ... Alt+Shift+U"), "Common Engine", UciMenuProc},
739 {N_("Adjudications ... Alt+Shift+J"), "Adjudications", EngineMenuProc},
740 {N_("ICS ..."), "ICS", IcsOptionsProc},
741 {N_("Match ..."), "Match", MatchOptionsProc},
742 {N_("Load Game ..."), "Load Game", LoadOptionsProc},
743 {N_("Save Game ..."), "Save Game", SaveOptionsProc},
744 // {N_(" ..."), "", OptionsProc},
745 {N_("Game List ..."), "Game List", GameListOptionsPopUp},
746 {N_("Sounds ..."), "Sounds", SoundOptionsProc},
747 {"----", NULL, NothingProc},
748 #ifndef OPTIONSDIALOG
749 {N_("Always Queen Ctrl+Shift+Q"), "Always Queen", AlwaysQueenProc},
750 {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
751 {N_("Animate Moving Ctrl+Shift+A"), "Animate Moving", AnimateMovingProc},
752 {N_("Auto Flag Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
753 {N_("Auto Flip View"), "Auto Flip View", AutoflipProc},
754 {N_("Blindfold"), "Blindfold", BlindfoldProc},
755 {N_("Flash Moves"), "Flash Moves", FlashMovesProc},
757 {N_("Highlight Dragging"), "Highlight Dragging", HighlightDraggingProc},
759 {N_("Highlight Last Move"), "Highlight Last Move", HighlightLastMoveProc},
760 {N_("Highlight With Arrow"), "Arrow", HighlightArrowProc},
761 {N_("Move Sound"), "Move Sound", MoveSoundProc},
762 // {N_("ICS Alarm"), "ICS Alarm", IcsAlarmProc},
763 {N_("One-Click Moving"), "OneClick", OneClickProc},
764 {N_("Periodic Updates"), "Periodic Updates", PeriodicUpdatesProc},
765 {N_("Ponder Next Move Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
766 {N_("Popup Exit Message"), "Popup Exit Message", PopupExitMessageProc},
767 {N_("Popup Move Errors"), "Popup Move Errors", PopupMoveErrorsProc},
768 // {N_("Premove"), "Premove", PremoveProc},
769 {N_("Show Coords"), "Show Coords", ShowCoordsProc},
770 {N_("Hide Thinking Ctrl+Shift+H"), "Hide Thinking", HideThinkingProc},
771 {N_("Test Legality Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
772 {"----", NULL, NothingProc},
774 {N_("Save Settings Now"), "Save Settings Now", SaveSettingsProc},
775 {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
779 MenuItem helpMenu[] = {
780 {N_("Info XBoard"), "Info XBoard", InfoProc},
781 {N_("Man XBoard F1"), "Man XBoard", ManProc},
782 {"----", NULL, NothingProc},
783 {N_("About XBoard"), "About XBoard", AboutProc},
788 {N_("File"), "File", fileMenu},
789 {N_("Edit"), "Edit", editMenu},
790 {N_("View"), "View", viewMenu},
791 {N_("Mode"), "Mode", modeMenu},
792 {N_("Action"), "Action", actionMenu},
793 {N_("Engine"), "Engine", engineMenu},
794 {N_("Options"), "Options", optionsMenu},
795 {N_("Help"), "Help", helpMenu},
799 #define PAUSE_BUTTON "P"
800 MenuItem buttonBar[] = {
801 {"<<", "<<", ToStartProc},
802 {"<", "<", BackwardProc},
803 {PAUSE_BUTTON, PAUSE_BUTTON, PauseProc},
804 {">", ">", ForwardProc},
805 {">>", ">>", ToEndProc},
809 #define PIECE_MENU_SIZE 18
810 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
811 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
812 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
813 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
814 N_("Empty square"), N_("Clear board") },
815 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
816 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
817 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
818 N_("Empty square"), N_("Clear board") }
820 /* must be in same order as pieceMenuStrings! */
821 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
822 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
823 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
824 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
825 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
826 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
827 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
828 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
829 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
832 #define DROP_MENU_SIZE 6
833 String dropMenuStrings[DROP_MENU_SIZE] = {
834 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
836 /* must be in same order as dropMenuStrings! */
837 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
838 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
839 WhiteRook, WhiteQueen
847 DropMenuEnables dmEnables[] = {
865 { XtNborderWidth, 0 },
866 { XtNdefaultDistance, 0 },
870 { XtNborderWidth, 0 },
871 { XtNresizable, (XtArgVal) True },
875 { XtNborderWidth, 0 },
881 { XtNjustify, (XtArgVal) XtJustifyRight },
882 { XtNlabel, (XtArgVal) "..." },
883 { XtNresizable, (XtArgVal) True },
884 { XtNresize, (XtArgVal) False }
887 Arg messageArgs[] = {
888 { XtNjustify, (XtArgVal) XtJustifyLeft },
889 { XtNlabel, (XtArgVal) "..." },
890 { XtNresizable, (XtArgVal) True },
891 { XtNresize, (XtArgVal) False }
895 { XtNborderWidth, 0 },
896 { XtNjustify, (XtArgVal) XtJustifyLeft }
899 XtResource clientResources[] = {
900 { "flashCount", "flashCount", XtRInt, sizeof(int),
901 XtOffset(AppDataPtr, flashCount), XtRImmediate,
902 (XtPointer) FLASH_COUNT },
905 XrmOptionDescRec shellOptions[] = {
906 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
907 { "-flash", "flashCount", XrmoptionNoArg, "3" },
908 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
911 XtActionsRec boardActions[] = {
912 { "DrawPosition", DrawPositionProc },
913 { "HandleUserMove", HandleUserMove },
914 { "AnimateUserMove", AnimateUserMove },
915 { "HandlePV", HandlePV },
916 { "SelectPV", SelectPV },
917 { "StopPV", StopPV },
918 { "FileNameAction", FileNameAction },
919 { "AskQuestionProc", AskQuestionProc },
920 { "AskQuestionReplyAction", AskQuestionReplyAction },
921 { "PieceMenuPopup", PieceMenuPopup },
922 { "WhiteClock", WhiteClock },
923 { "BlackClock", BlackClock },
924 { "Iconify", Iconify },
925 { "ResetProc", ResetProc },
926 { "NewVariantProc", NewVariantProc },
927 { "LoadGameProc", LoadGameProc },
928 { "LoadNextGameProc", LoadNextGameProc },
929 { "LoadPrevGameProc", LoadPrevGameProc },
930 { "LoadSelectedProc", LoadSelectedProc },
931 { "SetFilterProc", SetFilterProc },
932 { "ReloadGameProc", ReloadGameProc },
933 { "LoadPositionProc", LoadPositionProc },
934 { "LoadNextPositionProc", LoadNextPositionProc },
935 { "LoadPrevPositionProc", LoadPrevPositionProc },
936 { "ReloadPositionProc", ReloadPositionProc },
937 { "CopyPositionProc", CopyPositionProc },
938 { "PastePositionProc", PastePositionProc },
939 { "CopyGameProc", CopyGameProc },
940 { "CopyGameListProc", CopyGameListProc },
941 { "PasteGameProc", PasteGameProc },
942 { "SaveGameProc", SaveGameProc },
943 { "SavePositionProc", SavePositionProc },
944 { "MailMoveProc", MailMoveProc },
945 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
946 { "QuitProc", QuitProc },
947 { "MachineWhiteProc", MachineWhiteProc },
948 { "MachineBlackProc", MachineBlackProc },
949 { "AnalysisModeProc", AnalyzeModeProc },
950 { "AnalyzeFileProc", AnalyzeFileProc },
951 { "TwoMachinesProc", TwoMachinesProc },
952 { "IcsClientProc", IcsClientProc },
953 { "EditGameProc", EditGameProc },
954 { "EditPositionProc", EditPositionProc },
955 { "TrainingProc", EditPositionProc },
956 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
957 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
958 { "ShowGameListProc", ShowGameListProc },
959 { "ShowMoveListProc", HistoryShowProc},
960 { "EditTagsProc", EditCommentProc },
961 { "EditBookProc", EditBookProc },
962 { "EditCommentProc", EditCommentProc },
963 { "IcsInputBoxProc", IcsInputBoxProc },
964 { "PauseProc", PauseProc },
965 { "AcceptProc", AcceptProc },
966 { "DeclineProc", DeclineProc },
967 { "RematchProc", RematchProc },
968 { "CallFlagProc", CallFlagProc },
969 { "DrawProc", DrawProc },
970 { "AdjournProc", AdjournProc },
971 { "AbortProc", AbortProc },
972 { "ResignProc", ResignProc },
973 { "AdjuWhiteProc", AdjuWhiteProc },
974 { "AdjuBlackProc", AdjuBlackProc },
975 { "AdjuDrawProc", AdjuDrawProc },
976 { "TypeInProc", TypeInProc },
977 { "EnterKeyProc", EnterKeyProc },
978 { "UpKeyProc", UpKeyProc },
979 { "DownKeyProc", DownKeyProc },
980 { "StopObservingProc", StopObservingProc },
981 { "StopExaminingProc", StopExaminingProc },
982 { "UploadProc", UploadProc },
983 { "BackwardProc", BackwardProc },
984 { "ForwardProc", ForwardProc },
985 { "ToStartProc", ToStartProc },
986 { "ToEndProc", ToEndProc },
987 { "RevertProc", RevertProc },
988 { "AnnotateProc", AnnotateProc },
989 { "TruncateGameProc", TruncateGameProc },
990 { "MoveNowProc", MoveNowProc },
991 { "RetractMoveProc", RetractMoveProc },
992 { "EngineMenuProc", (XtActionProc) EngineMenuProc },
993 { "UciMenuProc", (XtActionProc) UciMenuProc },
994 { "TimeControlProc", (XtActionProc) TimeControlProc },
995 { "FlipViewProc", FlipViewProc },
996 { "PonderNextMoveProc", PonderNextMoveProc },
997 #ifndef OPTIONSDIALOG
998 { "AlwaysQueenProc", AlwaysQueenProc },
999 { "AnimateDraggingProc", AnimateDraggingProc },
1000 { "AnimateMovingProc", AnimateMovingProc },
1001 { "AutoflagProc", AutoflagProc },
1002 { "AutoflipProc", AutoflipProc },
1003 { "BlindfoldProc", BlindfoldProc },
1004 { "FlashMovesProc", FlashMovesProc },
1006 { "HighlightDraggingProc", HighlightDraggingProc },
1008 { "HighlightLastMoveProc", HighlightLastMoveProc },
1009 // { "IcsAlarmProc", IcsAlarmProc },
1010 { "MoveSoundProc", MoveSoundProc },
1011 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
1012 { "PopupExitMessageProc", PopupExitMessageProc },
1013 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
1014 // { "PremoveProc", PremoveProc },
1015 { "ShowCoordsProc", ShowCoordsProc },
1016 { "ShowThinkingProc", ShowThinkingProc },
1017 { "HideThinkingProc", HideThinkingProc },
1018 { "TestLegalityProc", TestLegalityProc },
1020 { "SaveSettingsProc", SaveSettingsProc },
1021 { "SaveOnExitProc", SaveOnExitProc },
1022 { "InfoProc", InfoProc },
1023 { "ManProc", ManProc },
1024 { "HintProc", HintProc },
1025 { "BookProc", BookProc },
1026 { "AboutGameProc", AboutGameProc },
1027 { "AboutProc", AboutProc },
1028 { "DebugProc", DebugProc },
1029 { "NothingProc", NothingProc },
1030 { "CommentClick", (XtActionProc) CommentClick },
1031 { "CommentPopDown", (XtActionProc) CommentPopDown },
1032 { "TagsPopDown", (XtActionProc) TagsPopDown },
1033 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1034 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1035 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1036 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1037 { "GameListPopDown", (XtActionProc) GameListPopDown },
1038 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1039 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1040 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1041 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1042 { "GenericPopDown", (XtActionProc) GenericPopDown },
1043 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1044 { "SelectMove", (XtActionProc) SelectMove },
1047 char globalTranslations[] =
1048 ":<Key>F9: ResignProc() \n \
1049 :Ctrl<Key>n: ResetProc() \n \
1050 :Meta<Key>V: NewVariantProc() \n \
1051 :Ctrl<Key>o: LoadGameProc() \n \
1052 :Meta<Key>Next: LoadNextGameProc() \n \
1053 :Meta<Key>Prior: LoadPrevGameProc() \n \
1054 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
1055 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
1056 :Ctrl<Key>s: SaveGameProc() \n \
1057 :Ctrl<Key>c: CopyGameProc() \n \
1058 :Ctrl<Key>v: PasteGameProc() \n \
1059 :Ctrl<Key>O: LoadPositionProc() \n \
1060 :Shift<Key>Next: LoadNextPositionProc() \n \
1061 :Shift<Key>Prior: LoadPrevPositionProc() \n \
1062 :Ctrl<Key>S: SavePositionProc() \n \
1063 :Ctrl<Key>C: CopyPositionProc() \n \
1064 :Ctrl<Key>V: PastePositionProc() \n \
1065 :Ctrl<Key>q: QuitProc() \n \
1066 :Ctrl<Key>w: MachineWhiteProc() \n \
1067 :Ctrl<Key>b: MachineBlackProc() \n \
1068 :Ctrl<Key>t: TwoMachinesProc() \n \
1069 :Ctrl<Key>a: AnalysisModeProc() \n \
1070 :Ctrl<Key>g: AnalyzeFileProc() \n \
1071 :Ctrl<Key>e: EditGameProc() \n \
1072 :Ctrl<Key>E: EditPositionProc() \n \
1073 :Meta<Key>O: EngineOutputProc() \n \
1074 :Meta<Key>E: EvalGraphProc() \n \
1075 :Meta<Key>G: ShowGameListProc() \n \
1076 :Meta<Key>H: ShowMoveListProc() \n \
1077 :<Key>Pause: PauseProc() \n \
1078 :<Key>F3: AcceptProc() \n \
1079 :<Key>F4: DeclineProc() \n \
1080 :<Key>F12: RematchProc() \n \
1081 :<Key>F5: CallFlagProc() \n \
1082 :<Key>F6: DrawProc() \n \
1083 :<Key>F7: AdjournProc() \n \
1084 :<Key>F8: AbortProc() \n \
1085 :<Key>F10: StopObservingProc() \n \
1086 :<Key>F11: StopExaminingProc() \n \
1087 :Meta Ctrl<Key>F12: DebugProc() \n \
1088 :Meta<Key>End: ToEndProc() \n \
1089 :Meta<Key>Right: ForwardProc() \n \
1090 :Meta<Key>Home: ToStartProc() \n \
1091 :Meta<Key>Left: BackwardProc() \n \
1092 :<Key>Left: BackwardProc() \n \
1093 :<Key>Right: ForwardProc() \n \
1094 :<Key>Home: RevertProc() \n \
1095 :<Key>End: TruncateGameProc() \n \
1096 :Ctrl<Key>m: MoveNowProc() \n \
1097 :Ctrl<Key>x: RetractMoveProc() \n \
1098 :Meta<Key>J: EngineMenuProc() \n \
1099 :Meta<Key>U: UciMenuProc() \n \
1100 :Meta<Key>T: TimeControlProc() \n \
1101 :Ctrl<Key>P: PonderNextMoveProc() \n "
1102 #ifndef OPTIONSDIALOG
1104 :Ctrl<Key>Q: AlwaysQueenProc() \n \
1105 :Ctrl<Key>F: AutoflagProc() \n \
1106 :Ctrl<Key>A: AnimateMovingProc() \n \
1107 :Ctrl<Key>L: TestLegalityProc() \n \
1108 :Ctrl<Key>H: HideThinkingProc() \n "
1111 :<Key>-: Iconify() \n \
1112 :<Key>F1: ManProc() \n \
1113 :<Key>F2: FlipViewProc() \n \
1114 <KeyDown>.: BackwardProc() \n \
1115 <KeyUp>.: ForwardProc() \n \
1116 Shift<Key>1: AskQuestionProc(\"Direct command\",\
1117 \"Send to chess program:\",,1) \n \
1118 Shift<Key>2: AskQuestionProc(\"Direct command\",\
1119 \"Send to second chess program:\",,2) \n";
1121 char boardTranslations[] =
1122 "<Btn1Down>: HandleUserMove(0) \n \
1123 Shift<Btn1Up>: HandleUserMove(1) \n \
1124 <Btn1Up>: HandleUserMove(0) \n \
1125 <Btn1Motion>: AnimateUserMove() \n \
1126 <Btn3Motion>: HandlePV() \n \
1127 <Btn3Up>: PieceMenuPopup(menuB) \n \
1128 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1129 PieceMenuPopup(menuB) \n \
1130 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1131 PieceMenuPopup(menuW) \n \
1132 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1133 PieceMenuPopup(menuW) \n \
1134 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1135 PieceMenuPopup(menuB) \n";
1137 char whiteTranslations[] =
1138 "Shift<BtnDown>: WhiteClock(1)\n \
1139 <BtnDown>: WhiteClock(0)\n";
1140 char blackTranslations[] =
1141 "Shift<BtnDown>: BlackClock(1)\n \
1142 <BtnDown>: BlackClock(0)\n";
1144 char ICSInputTranslations[] =
1145 "<Key>Up: UpKeyProc() \n "
1146 "<Key>Down: DownKeyProc() \n "
1147 "<Key>Return: EnterKeyProc() \n";
1149 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1150 // as the widget is destroyed before the up-click can call extend-end
1151 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1153 String xboardResources[] = {
1154 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1155 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1156 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1161 /* Max possible square size */
1162 #define MAXSQSIZE 256
1164 static int xpm_avail[MAXSQSIZE];
1166 #ifdef HAVE_DIR_STRUCT
1168 /* Extract piece size from filename */
1170 xpm_getsize(name, len, ext)
1181 if ((p=strchr(name, '.')) == NULL ||
1182 StrCaseCmp(p+1, ext) != 0)
1188 while (*p && isdigit(*p))
1195 /* Setup xpm_avail */
1197 xpm_getavail(dirname, ext)
1205 for (i=0; i<MAXSQSIZE; ++i)
1208 if (appData.debugMode)
1209 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1211 dir = opendir(dirname);
1214 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1215 programName, dirname);
1219 while ((ent=readdir(dir)) != NULL) {
1220 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1221 if (i > 0 && i < MAXSQSIZE)
1231 xpm_print_avail(fp, ext)
1237 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1238 for (i=1; i<MAXSQSIZE; ++i) {
1244 /* Return XPM piecesize closest to size */
1246 xpm_closest_to(dirname, size, ext)
1252 int sm_diff = MAXSQSIZE;
1256 xpm_getavail(dirname, ext);
1258 if (appData.debugMode)
1259 xpm_print_avail(stderr, ext);
1261 for (i=1; i<MAXSQSIZE; ++i) {
1264 diff = (diff<0) ? -diff : diff;
1265 if (diff < sm_diff) {
1273 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1279 #else /* !HAVE_DIR_STRUCT */
1280 /* If we are on a system without a DIR struct, we can't
1281 read the directory, so we can't collect a list of
1282 filenames, etc., so we can't do any size-fitting. */
1284 xpm_closest_to(dirname, size, ext)
1289 fprintf(stderr, _("\
1290 Warning: No DIR structure found on this system --\n\
1291 Unable to autosize for XPM/XIM pieces.\n\
1292 Please report this error to %s.\n\
1293 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
1296 #endif /* HAVE_DIR_STRUCT */
1298 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1299 "magenta", "cyan", "white" };
1303 TextColors textColors[(int)NColorClasses];
1305 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1307 parse_color(str, which)
1311 char *p, buf[100], *d;
1314 if (strlen(str) > 99) /* watch bounds on buf */
1319 for (i=0; i<which; ++i) {
1326 /* Could be looking at something like:
1328 .. in which case we want to stop on a comma also */
1329 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1333 return -1; /* Use default for empty field */
1336 if (which == 2 || isdigit(*p))
1339 while (*p && isalpha(*p))
1344 for (i=0; i<8; ++i) {
1345 if (!StrCaseCmp(buf, cnames[i]))
1346 return which? (i+40) : (i+30);
1348 if (!StrCaseCmp(buf, "default")) return -1;
1350 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1355 parse_cpair(cc, str)
1359 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1360 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1365 /* bg and attr are optional */
1366 textColors[(int)cc].bg = parse_color(str, 1);
1367 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1368 textColors[(int)cc].attr = 0;
1374 /* Arrange to catch delete-window events */
1375 Atom wm_delete_window;
1377 CatchDeleteWindow(Widget w, String procname)
1380 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1381 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1382 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1389 XtSetArg(args[0], XtNiconic, False);
1390 XtSetValues(shellWidget, args, 1);
1392 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1395 //---------------------------------------------------------------------------------------------------------
1396 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1399 #define CW_USEDEFAULT (1<<31)
1400 #define ICS_TEXT_MENU_SIZE 90
1401 #define DEBUG_FILE "xboard.debug"
1402 #define SetCurrentDirectory chdir
1403 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1407 // these two must some day move to frontend.h, when they are implemented
1408 Boolean GameListIsUp();
1410 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1413 // front-end part of option handling
1415 // [HGM] This platform-dependent table provides the location for storing the color info
1416 extern char *crWhite, * crBlack;
1420 &appData.whitePieceColor,
1421 &appData.blackPieceColor,
1422 &appData.lightSquareColor,
1423 &appData.darkSquareColor,
1424 &appData.highlightSquareColor,
1425 &appData.premoveHighlightColor,
1426 &appData.lowTimeWarningColor,
1437 // [HGM] font: keep a font for each square size, even non-stndard ones
1438 #define NUM_SIZES 18
1439 #define MAX_SIZE 130
1440 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1441 char *fontTable[NUM_FONTS][MAX_SIZE];
1444 ParseFont(char *name, int number)
1445 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1447 if(sscanf(name, "size%d:", &size)) {
1448 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1449 // defer processing it until we know if it matches our board size
1450 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1451 fontTable[number][size] = strdup(strchr(name, ':')+1);
1452 fontValid[number][size] = True;
1457 case 0: // CLOCK_FONT
1458 appData.clockFont = strdup(name);
1460 case 1: // MESSAGE_FONT
1461 appData.font = strdup(name);
1463 case 2: // COORD_FONT
1464 appData.coordFont = strdup(name);
1469 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1474 { // only 2 fonts currently
1475 appData.clockFont = CLOCK_FONT_NAME;
1476 appData.coordFont = COORD_FONT_NAME;
1477 appData.font = DEFAULT_FONT_NAME;
1482 { // no-op, until we identify the code for this already in XBoard and move it here
1486 ParseColor(int n, char *name)
1487 { // in XBoard, just copy the color-name string
1488 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1492 ParseTextAttribs(ColorClass cc, char *s)
1494 (&appData.colorShout)[cc] = strdup(s);
1498 ParseBoardSize(void *addr, char *name)
1500 appData.boardSize = strdup(name);
1505 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1509 SetCommPortDefaults()
1510 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1513 // [HGM] args: these three cases taken out to stay in front-end
1515 SaveFontArg(FILE *f, ArgDescriptor *ad)
1518 int i, n = (int)(intptr_t)ad->argLoc;
1520 case 0: // CLOCK_FONT
1521 name = appData.clockFont;
1523 case 1: // MESSAGE_FONT
1524 name = appData.font;
1526 case 2: // COORD_FONT
1527 name = appData.coordFont;
1532 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1533 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1534 fontTable[n][squareSize] = strdup(name);
1535 fontValid[n][squareSize] = True;
1538 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1539 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1544 { // nothing to do, as the sounds are at all times represented by their text-string names already
1548 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1549 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1550 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1554 SaveColor(FILE *f, ArgDescriptor *ad)
1555 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1556 if(colorVariable[(int)(intptr_t)ad->argLoc])
1557 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1561 SaveBoardSize(FILE *f, char *name, void *addr)
1562 { // wrapper to shield back-end from BoardSize & sizeInfo
1563 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1567 ParseCommPortSettings(char *s)
1568 { // no such option in XBoard (yet)
1571 extern Widget engineOutputShell;
1574 GetActualPlacement(Widget wg, WindowPlacement *wp)
1584 XtSetArg(args[i], XtNx, &x); i++;
1585 XtSetArg(args[i], XtNy, &y); i++;
1586 XtSetArg(args[i], XtNwidth, &w); i++;
1587 XtSetArg(args[i], XtNheight, &h); i++;
1588 XtGetValues(wg, args, i);
1597 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1598 // In XBoard this will have to wait until awareness of window parameters is implemented
1599 GetActualPlacement(shellWidget, &wpMain);
1600 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1601 if(MoveHistoryIsUp()) GetActualPlacement(shells[7], &wpMoveHistory);
1602 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1603 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1604 if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1605 if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1609 PrintCommPortSettings(FILE *f, char *name)
1610 { // This option does not exist in XBoard
1614 MySearchPath(char *installDir, char *name, char *fullname)
1615 { // just append installDir and name. Perhaps ExpandPath should be used here?
1616 name = ExpandPathName(name);
1617 if(name && name[0] == '/')
1618 safeStrCpy(fullname, name, MSG_SIZ );
1620 sprintf(fullname, "%s%c%s", installDir, '/', name);
1626 MyGetFullPathName(char *name, char *fullname)
1627 { // should use ExpandPath?
1628 name = ExpandPathName(name);
1629 safeStrCpy(fullname, name, MSG_SIZ );
1634 EnsureOnScreen(int *x, int *y, int minX, int minY)
1641 { // [HGM] args: allows testing if main window is realized from back-end
1642 return xBoardWindow != 0;
1646 PopUpStartupDialog()
1647 { // start menu not implemented in XBoard
1651 ConvertToLine(int argc, char **argv)
1653 static char line[128*1024], buf[1024];
1657 for(i=1; i<argc; i++)
1659 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1660 && argv[i][0] != '{' )
1661 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1663 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1664 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1667 line[strlen(line)-1] = NULLCHAR;
1671 //--------------------------------------------------------------------------------------------
1673 extern Boolean twoBoards, partnerUp;
1676 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1678 #define BoardSize int
1679 void InitDrawingSizes(BoardSize boardSize, int flags)
1680 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1681 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1683 XtGeometryResult gres;
1686 if(!formWidget) return;
1689 * Enable shell resizing.
1691 shellArgs[0].value = (XtArgVal) &w;
1692 shellArgs[1].value = (XtArgVal) &h;
1693 XtGetValues(shellWidget, shellArgs, 2);
1695 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1696 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1697 XtSetValues(shellWidget, &shellArgs[2], 4);
1699 XtSetArg(args[0], XtNdefaultDistance, &sep);
1700 XtGetValues(formWidget, args, 1);
1702 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1703 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1704 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1706 hOffset = boardWidth + 10;
1707 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1708 secondSegments[i] = gridSegments[i];
1709 secondSegments[i].x1 += hOffset;
1710 secondSegments[i].x2 += hOffset;
1713 XtSetArg(args[0], XtNwidth, boardWidth);
1714 XtSetArg(args[1], XtNheight, boardHeight);
1715 XtSetValues(boardWidget, args, 2);
1717 timerWidth = (boardWidth - sep) / 2;
1718 XtSetArg(args[0], XtNwidth, timerWidth);
1719 XtSetValues(whiteTimerWidget, args, 1);
1720 XtSetValues(blackTimerWidget, args, 1);
1722 XawFormDoLayout(formWidget, False);
1724 if (appData.titleInWindow) {
1726 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1727 XtSetArg(args[i], XtNheight, &h); i++;
1728 XtGetValues(titleWidget, args, i);
1730 w = boardWidth - 2*bor;
1732 XtSetArg(args[0], XtNwidth, &w);
1733 XtGetValues(menuBarWidget, args, 1);
1734 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1737 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1738 if (gres != XtGeometryYes && appData.debugMode) {
1740 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1741 programName, gres, w, h, wr, hr);
1745 XawFormDoLayout(formWidget, True);
1748 * Inhibit shell resizing.
1750 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1751 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1752 shellArgs[4].value = shellArgs[2].value = w;
1753 shellArgs[5].value = shellArgs[3].value = h;
1754 XtSetValues(shellWidget, &shellArgs[0], 6);
1756 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1759 for(i=0; i<4; i++) {
1761 for(p=0; p<=(int)WhiteKing; p++)
1762 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1763 if(gameInfo.variant == VariantShogi) {
1764 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1765 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1766 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1767 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1768 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1771 if(gameInfo.variant == VariantGothic) {
1772 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1775 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1776 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1777 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1780 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1781 for(p=0; p<=(int)WhiteKing; p++)
1782 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1783 if(gameInfo.variant == VariantShogi) {
1784 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1785 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1786 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1787 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1788 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1791 if(gameInfo.variant == VariantGothic) {
1792 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1795 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1796 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1797 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1802 for(i=0; i<2; i++) {
1804 for(p=0; p<=(int)WhiteKing; p++)
1805 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1806 if(gameInfo.variant == VariantShogi) {
1807 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1808 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1809 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1810 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1811 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1814 if(gameInfo.variant == VariantGothic) {
1815 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1818 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1819 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1820 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1830 void ParseIcsTextColors()
1831 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1832 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1833 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1834 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1835 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1836 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1837 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1838 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1839 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1840 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1841 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1843 if (appData.colorize) {
1845 _("%s: can't parse color names; disabling colorization\n"),
1848 appData.colorize = FALSE;
1853 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1854 XrmValue vFrom, vTo;
1855 int forceMono = False;
1857 if (!appData.monoMode) {
1858 vFrom.addr = (caddr_t) appData.lightSquareColor;
1859 vFrom.size = strlen(appData.lightSquareColor);
1860 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1861 if (vTo.addr == NULL) {
1862 appData.monoMode = True;
1865 lightSquareColor = *(Pixel *) vTo.addr;
1868 if (!appData.monoMode) {
1869 vFrom.addr = (caddr_t) appData.darkSquareColor;
1870 vFrom.size = strlen(appData.darkSquareColor);
1871 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1872 if (vTo.addr == NULL) {
1873 appData.monoMode = True;
1876 darkSquareColor = *(Pixel *) vTo.addr;
1879 if (!appData.monoMode) {
1880 vFrom.addr = (caddr_t) appData.whitePieceColor;
1881 vFrom.size = strlen(appData.whitePieceColor);
1882 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1883 if (vTo.addr == NULL) {
1884 appData.monoMode = True;
1887 whitePieceColor = *(Pixel *) vTo.addr;
1890 if (!appData.monoMode) {
1891 vFrom.addr = (caddr_t) appData.blackPieceColor;
1892 vFrom.size = strlen(appData.blackPieceColor);
1893 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1894 if (vTo.addr == NULL) {
1895 appData.monoMode = True;
1898 blackPieceColor = *(Pixel *) vTo.addr;
1902 if (!appData.monoMode) {
1903 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1904 vFrom.size = strlen(appData.highlightSquareColor);
1905 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1906 if (vTo.addr == NULL) {
1907 appData.monoMode = True;
1910 highlightSquareColor = *(Pixel *) vTo.addr;
1914 if (!appData.monoMode) {
1915 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1916 vFrom.size = strlen(appData.premoveHighlightColor);
1917 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1918 if (vTo.addr == NULL) {
1919 appData.monoMode = True;
1922 premoveHighlightColor = *(Pixel *) vTo.addr;
1930 { // [HGM] taken out of main
1932 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1933 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1934 appData.bitmapDirectory = DEF_BITMAP_DIR;
1936 if (appData.bitmapDirectory[0] != NULLCHAR) {
1940 CreateXPMBoard(appData.liteBackTextureFile, 1);
1941 CreateXPMBoard(appData.darkBackTextureFile, 0);
1945 /* Create regular pieces */
1946 if (!useImages) CreatePieces();
1955 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1956 XSetWindowAttributes window_attributes;
1958 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1959 XrmValue vFrom, vTo;
1960 XtGeometryResult gres;
1963 int forceMono = False;
1965 srandom(time(0)); // [HGM] book: make random truly random
1967 setbuf(stdout, NULL);
1968 setbuf(stderr, NULL);
1971 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1972 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1976 programName = strrchr(argv[0], '/');
1977 if (programName == NULL)
1978 programName = argv[0];
1983 XtSetLanguageProc(NULL, NULL, NULL);
1984 bindtextdomain(PACKAGE, LOCALEDIR);
1985 textdomain(PACKAGE);
1989 XtAppInitialize(&appContext, "XBoard", shellOptions,
1990 XtNumber(shellOptions),
1991 &argc, argv, xboardResources, NULL, 0);
1992 appData.boardSize = "";
1993 InitAppData(ConvertToLine(argc, argv));
1995 if (p == NULL) p = "/tmp";
1996 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1997 gameCopyFilename = (char*) malloc(i);
1998 gamePasteFilename = (char*) malloc(i);
1999 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
2000 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
2002 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
2003 clientResources, XtNumber(clientResources),
2006 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
2007 static char buf[MSG_SIZ];
2008 EscapeExpand(buf, appData.firstInitString);
2009 appData.firstInitString = strdup(buf);
2010 EscapeExpand(buf, appData.secondInitString);
2011 appData.secondInitString = strdup(buf);
2012 EscapeExpand(buf, appData.firstComputerString);
2013 appData.firstComputerString = strdup(buf);
2014 EscapeExpand(buf, appData.secondComputerString);
2015 appData.secondComputerString = strdup(buf);
2018 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
2021 if (chdir(chessDir) != 0) {
2022 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2028 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2029 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2030 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
2031 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2034 setbuf(debugFP, NULL);
2038 if (appData.debugMode) {
2039 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
2043 /* [HGM,HR] make sure board size is acceptable */
2044 if(appData.NrFiles > BOARD_FILES ||
2045 appData.NrRanks > BOARD_RANKS )
2046 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
2049 /* This feature does not work; animation needs a rewrite */
2050 appData.highlightDragging = FALSE;
2054 xDisplay = XtDisplay(shellWidget);
2055 xScreen = DefaultScreen(xDisplay);
2056 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2058 gameInfo.variant = StringToVariant(appData.variant);
2059 InitPosition(FALSE);
2062 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2064 if (isdigit(appData.boardSize[0])) {
2065 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2066 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2067 &fontPxlSize, &smallLayout, &tinyLayout);
2069 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2070 programName, appData.boardSize);
2074 /* Find some defaults; use the nearest known size */
2075 SizeDefaults *szd, *nearest;
2076 int distance = 99999;
2077 nearest = szd = sizeDefaults;
2078 while (szd->name != NULL) {
2079 if (abs(szd->squareSize - squareSize) < distance) {
2081 distance = abs(szd->squareSize - squareSize);
2082 if (distance == 0) break;
2086 if (i < 2) lineGap = nearest->lineGap;
2087 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2088 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2089 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2090 if (i < 6) smallLayout = nearest->smallLayout;
2091 if (i < 7) tinyLayout = nearest->tinyLayout;
2094 SizeDefaults *szd = sizeDefaults;
2095 if (*appData.boardSize == NULLCHAR) {
2096 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2097 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2100 if (szd->name == NULL) szd--;
2101 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2103 while (szd->name != NULL &&
2104 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2105 if (szd->name == NULL) {
2106 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2107 programName, appData.boardSize);
2111 squareSize = szd->squareSize;
2112 lineGap = szd->lineGap;
2113 clockFontPxlSize = szd->clockFontPxlSize;
2114 coordFontPxlSize = szd->coordFontPxlSize;
2115 fontPxlSize = szd->fontPxlSize;
2116 smallLayout = szd->smallLayout;
2117 tinyLayout = szd->tinyLayout;
2118 // [HGM] font: use defaults from settings file if available and not overruled
2120 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2121 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2122 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2123 appData.font = fontTable[MESSAGE_FONT][squareSize];
2124 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2125 appData.coordFont = fontTable[COORD_FONT][squareSize];
2127 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2128 if (strlen(appData.pixmapDirectory) > 0) {
2129 p = ExpandPathName(appData.pixmapDirectory);
2131 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2132 appData.pixmapDirectory);
2135 if (appData.debugMode) {
2136 fprintf(stderr, _("\
2137 XBoard square size (hint): %d\n\
2138 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2140 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2141 if (appData.debugMode) {
2142 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2145 defaultLineGap = lineGap;
2146 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2148 /* [HR] height treated separately (hacked) */
2149 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2150 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2151 if (appData.showJail == 1) {
2152 /* Jail on top and bottom */
2153 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2154 XtSetArg(boardArgs[2], XtNheight,
2155 boardHeight + 2*(lineGap + squareSize));
2156 } else if (appData.showJail == 2) {
2158 XtSetArg(boardArgs[1], XtNwidth,
2159 boardWidth + 2*(lineGap + squareSize));
2160 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2163 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2164 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2168 * Determine what fonts to use.
2171 appData.font = InsertPxlSize(appData.font, fontPxlSize);
2172 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
2173 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
2174 fontSet = CreateFontSet(appData.font);
2175 clockFontSet = CreateFontSet(appData.clockFont);
2177 /* For the coordFont, use the 0th font of the fontset. */
2178 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
2179 XFontStruct **font_struct_list;
2180 char **font_name_list;
2181 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
2182 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
2183 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2186 appData.font = FindFont(appData.font, fontPxlSize);
2187 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2188 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2189 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2190 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2191 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2192 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2194 countFontID = coordFontID; // [HGM] holdings
2195 countFontStruct = coordFontStruct;
2197 xdb = XtDatabase(xDisplay);
2199 XrmPutLineResource(&xdb, "*international: True");
2200 vTo.size = sizeof(XFontSet);
2201 vTo.addr = (XtPointer) &fontSet;
2202 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
2204 XrmPutStringResource(&xdb, "*font", appData.font);
2208 * Detect if there are not enough colors available and adapt.
2210 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2211 appData.monoMode = True;
2214 forceMono = MakeColors();
2217 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2219 appData.monoMode = True;
2222 if (appData.lowTimeWarning && !appData.monoMode) {
2223 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2224 vFrom.size = strlen(appData.lowTimeWarningColor);
2225 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2226 if (vTo.addr == NULL)
2227 appData.monoMode = True;
2229 lowTimeWarningColor = *(Pixel *) vTo.addr;
2232 if (appData.monoMode && appData.debugMode) {
2233 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2234 (unsigned long) XWhitePixel(xDisplay, xScreen),
2235 (unsigned long) XBlackPixel(xDisplay, xScreen));
2238 ParseIcsTextColors();
2239 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2240 textColors[ColorNone].attr = 0;
2242 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2248 layoutName = "tinyLayout";
2249 } else if (smallLayout) {
2250 layoutName = "smallLayout";
2252 layoutName = "normalLayout";
2254 /* Outer layoutWidget is there only to provide a name for use in
2255 resources that depend on the layout style */
2257 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2258 layoutArgs, XtNumber(layoutArgs));
2260 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2261 formArgs, XtNumber(formArgs));
2262 XtSetArg(args[0], XtNdefaultDistance, &sep);
2263 XtGetValues(formWidget, args, 1);
2266 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2267 XtSetArg(args[0], XtNtop, XtChainTop);
2268 XtSetArg(args[1], XtNbottom, XtChainTop);
2269 XtSetArg(args[2], XtNright, XtChainLeft);
2270 XtSetValues(menuBarWidget, args, 3);
2272 widgetList[j++] = whiteTimerWidget =
2273 XtCreateWidget("whiteTime", labelWidgetClass,
2274 formWidget, timerArgs, XtNumber(timerArgs));
2276 XtSetArg(args[0], XtNfontSet, clockFontSet);
2278 XtSetArg(args[0], XtNfont, clockFontStruct);
2280 XtSetArg(args[1], XtNtop, XtChainTop);
2281 XtSetArg(args[2], XtNbottom, XtChainTop);
2282 XtSetValues(whiteTimerWidget, args, 3);
2284 widgetList[j++] = blackTimerWidget =
2285 XtCreateWidget("blackTime", labelWidgetClass,
2286 formWidget, timerArgs, XtNumber(timerArgs));
2288 XtSetArg(args[0], XtNfontSet, clockFontSet);
2290 XtSetArg(args[0], XtNfont, clockFontStruct);
2292 XtSetArg(args[1], XtNtop, XtChainTop);
2293 XtSetArg(args[2], XtNbottom, XtChainTop);
2294 XtSetValues(blackTimerWidget, args, 3);
2296 if (appData.titleInWindow) {
2297 widgetList[j++] = titleWidget =
2298 XtCreateWidget("title", labelWidgetClass, formWidget,
2299 titleArgs, XtNumber(titleArgs));
2300 XtSetArg(args[0], XtNtop, XtChainTop);
2301 XtSetArg(args[1], XtNbottom, XtChainTop);
2302 XtSetValues(titleWidget, args, 2);
2305 if (appData.showButtonBar) {
2306 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2307 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2308 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2309 XtSetArg(args[2], XtNtop, XtChainTop);
2310 XtSetArg(args[3], XtNbottom, XtChainTop);
2311 XtSetValues(buttonBarWidget, args, 4);
2314 widgetList[j++] = messageWidget =
2315 XtCreateWidget("message", labelWidgetClass, formWidget,
2316 messageArgs, XtNumber(messageArgs));
2317 XtSetArg(args[0], XtNtop, XtChainTop);
2318 XtSetArg(args[1], XtNbottom, XtChainTop);
2319 XtSetValues(messageWidget, args, 2);
2321 widgetList[j++] = boardWidget =
2322 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2323 XtNumber(boardArgs));
2325 XtManageChildren(widgetList, j);
2327 timerWidth = (boardWidth - sep) / 2;
2328 XtSetArg(args[0], XtNwidth, timerWidth);
2329 XtSetValues(whiteTimerWidget, args, 1);
2330 XtSetValues(blackTimerWidget, args, 1);
2332 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2333 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2334 XtGetValues(whiteTimerWidget, args, 2);
2336 if (appData.showButtonBar) {
2337 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2338 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2339 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2343 * formWidget uses these constraints but they are stored
2347 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2348 XtSetValues(menuBarWidget, args, i);
2349 if (appData.titleInWindow) {
2352 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2353 XtSetValues(whiteTimerWidget, args, i);
2355 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2356 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2357 XtSetValues(blackTimerWidget, args, i);
2359 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2360 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2361 XtSetValues(titleWidget, args, i);
2363 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2364 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2365 XtSetValues(messageWidget, args, i);
2366 if (appData.showButtonBar) {
2368 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2369 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2370 XtSetValues(buttonBarWidget, args, i);
2374 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2375 XtSetValues(whiteTimerWidget, args, i);
2377 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2378 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2379 XtSetValues(blackTimerWidget, args, i);
2381 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2382 XtSetValues(titleWidget, args, i);
2384 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2385 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2386 XtSetValues(messageWidget, args, i);
2387 if (appData.showButtonBar) {
2389 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2390 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2391 XtSetValues(buttonBarWidget, args, i);
2396 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2397 XtSetValues(whiteTimerWidget, args, i);
2399 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2400 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2401 XtSetValues(blackTimerWidget, args, i);
2403 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2404 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2405 XtSetValues(messageWidget, args, i);
2406 if (appData.showButtonBar) {
2408 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2409 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2410 XtSetValues(buttonBarWidget, args, i);
2414 XtSetArg(args[0], XtNfromVert, messageWidget);
2415 XtSetArg(args[1], XtNtop, XtChainTop);
2416 XtSetArg(args[2], XtNbottom, XtChainBottom);
2417 XtSetArg(args[3], XtNleft, XtChainLeft);
2418 XtSetArg(args[4], XtNright, XtChainRight);
2419 XtSetValues(boardWidget, args, 5);
2421 XtRealizeWidget(shellWidget);
2424 XtSetArg(args[0], XtNx, wpMain.x);
2425 XtSetArg(args[1], XtNy, wpMain.y);
2426 XtSetValues(shellWidget, args, 2);
2430 * Correct the width of the message and title widgets.
2431 * It is not known why some systems need the extra fudge term.
2432 * The value "2" is probably larger than needed.
2434 XawFormDoLayout(formWidget, False);
2436 #define WIDTH_FUDGE 2
2438 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2439 XtSetArg(args[i], XtNheight, &h); i++;
2440 XtGetValues(messageWidget, args, i);
2441 if (appData.showButtonBar) {
2443 XtSetArg(args[i], XtNwidth, &w); i++;
2444 XtGetValues(buttonBarWidget, args, i);
2445 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2447 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2450 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2451 if (gres != XtGeometryYes && appData.debugMode) {
2452 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2453 programName, gres, w, h, wr, hr);
2456 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2457 /* The size used for the child widget in layout lags one resize behind
2458 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2460 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2461 if (gres != XtGeometryYes && appData.debugMode) {
2462 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2463 programName, gres, w, h, wr, hr);
2466 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2467 XtSetArg(args[1], XtNright, XtChainRight);
2468 XtSetValues(messageWidget, args, 2);
2470 if (appData.titleInWindow) {
2472 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2473 XtSetArg(args[i], XtNheight, &h); i++;
2474 XtGetValues(titleWidget, args, i);
2476 w = boardWidth - 2*bor;
2478 XtSetArg(args[0], XtNwidth, &w);
2479 XtGetValues(menuBarWidget, args, 1);
2480 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2483 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2484 if (gres != XtGeometryYes && appData.debugMode) {
2486 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2487 programName, gres, w, h, wr, hr);
2490 XawFormDoLayout(formWidget, True);
2492 xBoardWindow = XtWindow(boardWidget);
2494 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2495 // not need to go into InitDrawingSizes().
2499 * Create X checkmark bitmap and initialize option menu checks.
2501 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2502 checkmark_bits, checkmark_width, checkmark_height);
2503 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2504 #ifndef OPTIONSDIALOG
2505 if (appData.alwaysPromoteToQueen) {
2506 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2509 if (appData.animateDragging) {
2510 XtSetValues(XtNameToWidget(menuBarWidget,
2511 "menuOptions.Animate Dragging"),
2514 if (appData.animate) {
2515 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2518 if (appData.autoCallFlag) {
2519 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2522 if (appData.autoFlipView) {
2523 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2526 if (appData.blindfold) {
2527 XtSetValues(XtNameToWidget(menuBarWidget,
2528 "menuOptions.Blindfold"), args, 1);
2530 if (appData.flashCount > 0) {
2531 XtSetValues(XtNameToWidget(menuBarWidget,
2532 "menuOptions.Flash Moves"),
2536 if (appData.highlightDragging) {
2537 XtSetValues(XtNameToWidget(menuBarWidget,
2538 "menuOptions.Highlight Dragging"),
2542 if (appData.highlightLastMove) {
2543 XtSetValues(XtNameToWidget(menuBarWidget,
2544 "menuOptions.Highlight Last Move"),
2547 if (appData.highlightMoveWithArrow) {
2548 XtSetValues(XtNameToWidget(menuBarWidget,
2549 "menuOptions.Arrow"),
2552 // if (appData.icsAlarm) {
2553 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2556 if (appData.ringBellAfterMoves) {
2557 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2560 if (appData.oneClick) {
2561 XtSetValues(XtNameToWidget(menuBarWidget,
2562 "menuOptions.OneClick"), args, 1);
2564 if (appData.periodicUpdates) {
2565 XtSetValues(XtNameToWidget(menuBarWidget,
2566 "menuOptions.Periodic Updates"), args, 1);
2568 if (appData.ponderNextMove) {
2569 XtSetValues(XtNameToWidget(menuBarWidget,
2570 "menuOptions.Ponder Next Move"), args, 1);
2572 if (appData.popupExitMessage) {
2573 XtSetValues(XtNameToWidget(menuBarWidget,
2574 "menuOptions.Popup Exit Message"), args, 1);
2576 if (appData.popupMoveErrors) {
2577 XtSetValues(XtNameToWidget(menuBarWidget,
2578 "menuOptions.Popup Move Errors"), args, 1);
2580 // if (appData.premove) {
2581 // XtSetValues(XtNameToWidget(menuBarWidget,
2582 // "menuOptions.Premove"), args, 1);
2584 if (appData.showCoords) {
2585 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2588 if (appData.hideThinkingFromHuman) {
2589 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2592 if (appData.testLegality) {
2593 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2597 if (saveSettingsOnExit) {
2598 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2605 ReadBitmap(&wIconPixmap, "icon_white.bm",
2606 icon_white_bits, icon_white_width, icon_white_height);
2607 ReadBitmap(&bIconPixmap, "icon_black.bm",
2608 icon_black_bits, icon_black_width, icon_black_height);
2609 iconPixmap = wIconPixmap;
2611 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2612 XtSetValues(shellWidget, args, i);
2615 * Create a cursor for the board widget.
2617 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2618 XChangeWindowAttributes(xDisplay, xBoardWindow,
2619 CWCursor, &window_attributes);
2622 * Inhibit shell resizing.
2624 shellArgs[0].value = (XtArgVal) &w;
2625 shellArgs[1].value = (XtArgVal) &h;
2626 XtGetValues(shellWidget, shellArgs, 2);
2627 shellArgs[4].value = shellArgs[2].value = w;
2628 shellArgs[5].value = shellArgs[3].value = h;
2629 XtSetValues(shellWidget, &shellArgs[2], 4);
2630 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2631 marginH = h - boardHeight;
2633 CatchDeleteWindow(shellWidget, "QuitProc");
2641 if (appData.animate || appData.animateDragging)
2644 XtAugmentTranslations(formWidget,
2645 XtParseTranslationTable(globalTranslations));
2646 XtAugmentTranslations(boardWidget,
2647 XtParseTranslationTable(boardTranslations));
2648 XtAugmentTranslations(whiteTimerWidget,
2649 XtParseTranslationTable(whiteTranslations));
2650 XtAugmentTranslations(blackTimerWidget,
2651 XtParseTranslationTable(blackTranslations));
2653 /* Why is the following needed on some versions of X instead
2654 * of a translation? */
2655 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2656 (XtEventHandler) EventProc, NULL);
2658 XtAddEventHandler(formWidget, KeyPressMask, False,
2659 (XtEventHandler) MoveTypeInProc, NULL);
2661 /* [AS] Restore layout */
2662 if( wpMoveHistory.visible ) {
2666 if( wpEvalGraph.visible )
2671 if( wpEngineOutput.visible ) {
2672 EngineOutputPopUp();
2677 if (errorExitStatus == -1) {
2678 if (appData.icsActive) {
2679 /* We now wait until we see "login:" from the ICS before
2680 sending the logon script (problems with timestamp otherwise) */
2681 /*ICSInitScript();*/
2682 if (appData.icsInputBox) ICSInputBoxPopUp();
2686 signal(SIGWINCH, TermSizeSigHandler);
2688 signal(SIGINT, IntSigHandler);
2689 signal(SIGTERM, IntSigHandler);
2690 if (*appData.cmailGameName != NULLCHAR) {
2691 signal(SIGUSR1, CmailSigHandler);
2694 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2696 // XtSetKeyboardFocus(shellWidget, formWidget);
2697 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2699 XtAppMainLoop(appContext);
2700 if (appData.debugMode) fclose(debugFP); // [DM] debug
2704 static Boolean noEcho;
2709 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2710 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2712 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2713 unlink(gameCopyFilename);
2714 unlink(gamePasteFilename);
2715 if(noEcho) EchoOn();
2718 RETSIGTYPE TermSizeSigHandler(int sig)
2731 CmailSigHandler(sig)
2737 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2739 /* Activate call-back function CmailSigHandlerCallBack() */
2740 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2742 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2746 CmailSigHandlerCallBack(isr, closure, message, count, error)
2754 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2756 /**** end signal code ****/
2762 /* try to open the icsLogon script, either in the location given
2763 * or in the users HOME directory
2770 f = fopen(appData.icsLogon, "r");
2773 homedir = getenv("HOME");
2774 if (homedir != NULL)
2776 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2777 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2778 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2779 f = fopen(buf, "r");
2784 ProcessICSInitScript(f);
2786 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2809 if (!menuBarWidget) return;
2810 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2812 DisplayError("menuEdit.Revert", 0);
2814 XtSetSensitive(w, !grey);
2816 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2818 DisplayError("menuEdit.Annotate", 0);
2820 XtSetSensitive(w, !grey);
2825 SetMenuEnables(enab)
2829 if (!menuBarWidget) return;
2830 while (enab->name != NULL) {
2831 w = XtNameToWidget(menuBarWidget, enab->name);
2833 DisplayError(enab->name, 0);
2835 XtSetSensitive(w, enab->value);
2841 Enables icsEnables[] = {
2842 { "menuFile.Mail Move", False },
2843 { "menuFile.Reload CMail Message", False },
2844 { "menuMode.Machine Black", False },
2845 { "menuMode.Machine White", False },
2846 { "menuMode.Analysis Mode", False },
2847 { "menuMode.Analyze File", False },
2848 { "menuMode.Two Machines", False },
2849 { "menuMode.Machine Match", False },
2851 { "menuEngine.Hint", False },
2852 { "menuEngine.Book", False },
2853 { "menuEngine.Move Now", False },
2854 #ifndef OPTIONSDIALOG
2855 { "menuOptions.Periodic Updates", False },
2856 { "menuOptions.Hide Thinking", False },
2857 { "menuOptions.Ponder Next Move", False },
2860 { "menuEngine.Engine #1 Settings", False },
2861 { "menuEngine.Engine #2 Settings", False },
2862 { "menuEngine.Load Engine", False },
2863 { "menuEdit.Annotate", False },
2864 { "menuOptions.Match", False },
2868 Enables ncpEnables[] = {
2869 { "menuFile.Mail Move", False },
2870 { "menuFile.Reload CMail Message", False },
2871 { "menuMode.Machine White", False },
2872 { "menuMode.Machine Black", False },
2873 { "menuMode.Analysis Mode", False },
2874 { "menuMode.Analyze File", False },
2875 { "menuMode.Two Machines", False },
2876 { "menuMode.Machine Match", False },
2877 { "menuMode.ICS Client", False },
2878 { "menuView.ICStex", False },
2879 { "menuView.ICS Input Box", False },
2880 { "Action", False },
2881 { "menuEdit.Revert", False },
2882 { "menuEdit.Annotate", False },
2883 { "menuEngine.Engine #1 Settings", False },
2884 { "menuEngine.Engine #2 Settings", False },
2885 { "menuEngine.Move Now", False },
2886 { "menuEngine.Retract Move", False },
2887 { "menuOptions.ICS", False },
2888 #ifndef OPTIONSDIALOG
2889 { "menuOptions.Auto Flag", False },
2890 { "menuOptions.Auto Flip View", False },
2891 // { "menuOptions.ICS Alarm", False },
2892 { "menuOptions.Move Sound", False },
2893 { "menuOptions.Hide Thinking", False },
2894 { "menuOptions.Periodic Updates", False },
2895 { "menuOptions.Ponder Next Move", False },
2897 { "menuEngine.Hint", False },
2898 { "menuEngine.Book", False },
2902 Enables gnuEnables[] = {
2903 { "menuMode.ICS Client", False },
2904 { "menuView.ICStex", False },
2905 { "menuView.ICS Input Box", False },
2906 { "menuAction.Accept", False },
2907 { "menuAction.Decline", False },
2908 { "menuAction.Rematch", False },
2909 { "menuAction.Adjourn", False },
2910 { "menuAction.Stop Examining", False },
2911 { "menuAction.Stop Observing", False },
2912 { "menuAction.Upload to Examine", False },
2913 { "menuEdit.Revert", False },
2914 { "menuEdit.Annotate", False },
2915 { "menuOptions.ICS", False },
2917 /* The next two options rely on SetCmailMode being called *after* */
2918 /* SetGNUMode so that when GNU is being used to give hints these */
2919 /* menu options are still available */
2921 { "menuFile.Mail Move", False },
2922 { "menuFile.Reload CMail Message", False },
2923 // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2924 { "menuMode.Machine White", True },
2925 { "menuMode.Machine Black", True },
2926 { "menuMode.Analysis Mode", True },
2927 { "menuMode.Analyze File", True },
2928 { "menuMode.Two Machines", True },
2929 { "menuMode.Machine Match", True },
2930 { "menuEngine.Engine #1 Settings", True },
2931 { "menuEngine.Engine #2 Settings", True },
2932 { "menuEngine.Hint", True },
2933 { "menuEngine.Book", True },
2934 { "menuEngine.Move Now", True },
2935 { "menuEngine.Retract Move", True },
2940 Enables cmailEnables[] = {
2942 { "menuAction.Call Flag", False },
2943 { "menuAction.Draw", True },
2944 { "menuAction.Adjourn", False },
2945 { "menuAction.Abort", False },
2946 { "menuAction.Stop Observing", False },
2947 { "menuAction.Stop Examining", False },
2948 { "menuFile.Mail Move", True },
2949 { "menuFile.Reload CMail Message", True },
2953 Enables trainingOnEnables[] = {
2954 { "menuMode.Edit Comment", False },
2955 { "menuMode.Pause", False },
2956 { "menuEdit.Forward", False },
2957 { "menuEdit.Backward", False },
2958 { "menuEdit.Forward to End", False },
2959 { "menuEdit.Back to Start", False },
2960 { "menuEngine.Move Now", False },
2961 { "menuEdit.Truncate Game", False },
2965 Enables trainingOffEnables[] = {
2966 { "menuMode.Edit Comment", True },
2967 { "menuMode.Pause", True },
2968 { "menuEdit.Forward", True },
2969 { "menuEdit.Backward", True },
2970 { "menuEdit.Forward to End", True },
2971 { "menuEdit.Back to Start", True },
2972 { "menuEngine.Move Now", True },
2973 { "menuEdit.Truncate Game", True },
2977 Enables machineThinkingEnables[] = {
2978 { "menuFile.Load Game", False },
2979 // { "menuFile.Load Next Game", False },
2980 // { "menuFile.Load Previous Game", False },
2981 // { "menuFile.Reload Same Game", False },
2982 { "menuEdit.Paste Game", False },
2983 { "menuFile.Load Position", False },
2984 // { "menuFile.Load Next Position", False },
2985 // { "menuFile.Load Previous Position", False },
2986 // { "menuFile.Reload Same Position", False },
2987 { "menuEdit.Paste Position", False },
2988 { "menuMode.Machine White", False },
2989 { "menuMode.Machine Black", False },
2990 { "menuMode.Two Machines", False },
2991 // { "menuMode.Machine Match", False },
2992 { "menuEngine.Retract Move", False },
2996 Enables userThinkingEnables[] = {
2997 { "menuFile.Load Game", True },
2998 // { "menuFile.Load Next Game", True },
2999 // { "menuFile.Load Previous Game", True },
3000 // { "menuFile.Reload Same Game", True },
3001 { "menuEdit.Paste Game", True },
3002 { "menuFile.Load Position", True },
3003 // { "menuFile.Load Next Position", True },
3004 // { "menuFile.Load Previous Position", True },
3005 // { "menuFile.Reload Same Position", True },
3006 { "menuEdit.Paste Position", True },
3007 { "menuMode.Machine White", True },
3008 { "menuMode.Machine Black", True },
3009 { "menuMode.Two Machines", True },
3010 // { "menuMode.Machine Match", True },
3011 { "menuEngine.Retract Move", True },
3017 SetMenuEnables(icsEnables);
3020 if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
3021 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
3022 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
3030 SetMenuEnables(ncpEnables);
3036 SetMenuEnables(gnuEnables);
3042 SetMenuEnables(cmailEnables);
3048 SetMenuEnables(trainingOnEnables);
3049 if (appData.showButtonBar) {
3050 XtSetSensitive(buttonBarWidget, False);
3056 SetTrainingModeOff()
3058 SetMenuEnables(trainingOffEnables);
3059 if (appData.showButtonBar) {
3060 XtSetSensitive(buttonBarWidget, True);
3065 SetUserThinkingEnables()
3067 if (appData.noChessProgram) return;
3068 SetMenuEnables(userThinkingEnables);
3072 SetMachineThinkingEnables()
3074 if (appData.noChessProgram) return;
3075 SetMenuEnables(machineThinkingEnables);
3077 case MachinePlaysBlack:
3078 case MachinePlaysWhite:
3079 case TwoMachinesPlay:
3080 XtSetSensitive(XtNameToWidget(menuBarWidget,
3081 ModeToWidgetName(gameMode)), True);
3088 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3089 #define HISTORY_SIZE 64
3090 static char *history[HISTORY_SIZE];
3091 int histIn = 0, histP = 0;
3094 SaveInHistory(char *cmd)
3096 if (history[histIn] != NULL) {
3097 free(history[histIn]);
3098 history[histIn] = NULL;
3100 if (*cmd == NULLCHAR) return;
3101 history[histIn] = StrSave(cmd);
3102 histIn = (histIn + 1) % HISTORY_SIZE;
3103 if (history[histIn] != NULL) {
3104 free(history[histIn]);
3105 history[histIn] = NULL;
3111 PrevInHistory(char *cmd)
3114 if (histP == histIn) {
3115 if (history[histIn] != NULL) free(history[histIn]);
3116 history[histIn] = StrSave(cmd);
3118 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3119 if (newhp == histIn || history[newhp] == NULL) return NULL;
3121 return history[histP];
3127 if (histP == histIn) return NULL;
3128 histP = (histP + 1) % HISTORY_SIZE;
3129 return history[histP];
3131 // end of borrowed code
3133 #define Abs(n) ((n)<0 ? -(n) : (n))
3137 InsertPxlSize(pattern, targetPxlSize)
3141 char *base_fnt_lst, strInt[12], *p, *q;
3142 int alternatives, i, len, strIntLen;
3145 * Replace the "*" (if present) in the pixel-size slot of each
3146 * alternative with the targetPxlSize.
3150 while ((p = strchr(p, ',')) != NULL) {
3154 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
3155 strIntLen = strlen(strInt);
3156 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
3160 while (alternatives--) {
3161 char *comma = strchr(p, ',');
3162 for (i=0; i<14; i++) {
3163 char *hyphen = strchr(p, '-');
3165 if (comma && hyphen > comma) break;
3166 len = hyphen + 1 - p;
3167 if (i == 7 && *p == '*' && len == 2) {
3169 memcpy(q, strInt, strIntLen);
3179 len = comma + 1 - p;
3186 return base_fnt_lst;
3190 CreateFontSet(base_fnt_lst)
3194 char **missing_list;
3198 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
3199 &missing_list, &missing_count, &def_string);
3200 if (appData.debugMode) {
3202 XFontStruct **font_struct_list;
3203 char **font_name_list;
3204 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
3206 fprintf(debugFP, " got list %s, locale %s\n",
3207 XBaseFontNameListOfFontSet(fntSet),
3208 XLocaleOfFontSet(fntSet));
3209 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
3210 for (i = 0; i < count; i++) {
3211 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
3214 for (i = 0; i < missing_count; i++) {
3215 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
3218 if (fntSet == NULL) {
3219 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
3224 #else // not ENABLE_NLS
3226 * Find a font that matches "pattern" that is as close as
3227 * possible to the targetPxlSize. Prefer fonts that are k
3228 * pixels smaller to fonts that are k pixels larger. The
3229 * pattern must be in the X Consortium standard format,
3230 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3231 * The return value should be freed with XtFree when no
3235 FindFont(pattern, targetPxlSize)
3239 char **fonts, *p, *best, *scalable, *scalableTail;
3240 int i, j, nfonts, minerr, err, pxlSize;
3242 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3244 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3245 programName, pattern);
3252 for (i=0; i<nfonts; i++) {
3255 if (*p != '-') continue;
3257 if (*p == NULLCHAR) break;
3258 if (*p++ == '-') j++;
3260 if (j < 7) continue;
3263 scalable = fonts[i];
3266 err = pxlSize - targetPxlSize;
3267 if (Abs(err) < Abs(minerr) ||
3268 (minerr > 0 && err < 0 && -err == minerr)) {
3274 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3275 /* If the error is too big and there is a scalable font,
3276 use the scalable font. */
3277 int headlen = scalableTail - scalable;
3278 p = (char *) XtMalloc(strlen(scalable) + 10);
3279 while (isdigit(*scalableTail)) scalableTail++;
3280 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3282 p = (char *) XtMalloc(strlen(best) + 2);
3283 safeStrCpy(p, best, strlen(best)+1 );
3285 if (appData.debugMode) {
3286 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3287 pattern, targetPxlSize, p);
3289 XFreeFontNames(fonts);
3295 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3296 // must be called before all non-first callse to CreateGCs()
3297 XtReleaseGC(shellWidget, highlineGC);
3298 XtReleaseGC(shellWidget, lightSquareGC);
3299 XtReleaseGC(shellWidget, darkSquareGC);
3300 XtReleaseGC(shellWidget, lineGC);
3301 if (appData.monoMode) {
3302 if (DefaultDepth(xDisplay, xScreen) == 1) {
3303 XtReleaseGC(shellWidget, wbPieceGC);
3305 XtReleaseGC(shellWidget, bwPieceGC);
3308 XtReleaseGC(shellWidget, prelineGC);
3309 XtReleaseGC(shellWidget, jailSquareGC);
3310 XtReleaseGC(shellWidget, wdPieceGC);
3311 XtReleaseGC(shellWidget, wlPieceGC);
3312 XtReleaseGC(shellWidget, wjPieceGC);
3313 XtReleaseGC(shellWidget, bdPieceGC);
3314 XtReleaseGC(shellWidget, blPieceGC);
3315 XtReleaseGC(shellWidget, bjPieceGC);
3319 void CreateGCs(int redo)
3321 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3322 | GCBackground | GCFunction | GCPlaneMask;
3323 XGCValues gc_values;
3326 gc_values.plane_mask = AllPlanes;
3327 gc_values.line_width = lineGap;
3328 gc_values.line_style = LineSolid;
3329 gc_values.function = GXcopy;
3332 DeleteGCs(); // called a second time; clean up old GCs first
3333 } else { // [HGM] grid and font GCs created on first call only
3334 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3335 gc_values.background = XWhitePixel(xDisplay, xScreen);
3336 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3337 XSetFont(xDisplay, coordGC, coordFontID);
3339 // [HGM] make font for holdings counts (white on black)
3340 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3341 gc_values.background = XBlackPixel(xDisplay, xScreen);
3342 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3343 XSetFont(xDisplay, countGC, countFontID);
3345 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3346 gc_values.background = XBlackPixel(xDisplay, xScreen);
3347 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3349 if (appData.monoMode) {
3350 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3351 gc_values.background = XWhitePixel(xDisplay, xScreen);
3352 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3354 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3355 gc_values.background = XBlackPixel(xDisplay, xScreen);
3356 lightSquareGC = wbPieceGC
3357 = XtGetGC(shellWidget, value_mask, &gc_values);
3359 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3360 gc_values.background = XWhitePixel(xDisplay, xScreen);
3361 darkSquareGC = bwPieceGC
3362 = XtGetGC(shellWidget, value_mask, &gc_values);
3364 if (DefaultDepth(xDisplay, xScreen) == 1) {
3365 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3366 gc_values.function = GXcopyInverted;
3367 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3368 gc_values.function = GXcopy;
3369 if (XBlackPixel(xDisplay, xScreen) == 1) {
3370 bwPieceGC = darkSquareGC;
3371 wbPieceGC = copyInvertedGC;
3373 bwPieceGC = copyInvertedGC;
3374 wbPieceGC = lightSquareGC;
3378 gc_values.foreground = highlightSquareColor;
3379 gc_values.background = highlightSquareColor;
3380 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3382 gc_values.foreground = premoveHighlightColor;
3383 gc_values.background = premoveHighlightColor;
3384 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3386 gc_values.foreground = lightSquareColor;
3387 gc_values.background = darkSquareColor;
3388 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3390 gc_values.foreground = darkSquareColor;
3391 gc_values.background = lightSquareColor;
3392 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3394 gc_values.foreground = jailSquareColor;
3395 gc_values.background = jailSquareColor;
3396 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3398 gc_values.foreground = whitePieceColor;
3399 gc_values.background = darkSquareColor;
3400 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3402 gc_values.foreground = whitePieceColor;
3403 gc_values.background = lightSquareColor;
3404 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3406 gc_values.foreground = whitePieceColor;
3407 gc_values.background = jailSquareColor;
3408 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3410 gc_values.foreground = blackPieceColor;
3411 gc_values.background = darkSquareColor;
3412 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3414 gc_values.foreground = blackPieceColor;
3415 gc_values.background = lightSquareColor;
3416 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3418 gc_values.foreground = blackPieceColor;
3419 gc_values.background = jailSquareColor;
3420 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3424 void loadXIM(xim, xmask, filename, dest, mask)
3437 fp = fopen(filename, "rb");
3439 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3446 for (y=0; y<h; ++y) {
3447 for (x=0; x<h; ++x) {
3452 XPutPixel(xim, x, y, blackPieceColor);
3454 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3457 XPutPixel(xim, x, y, darkSquareColor);
3459 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3462 XPutPixel(xim, x, y, whitePieceColor);
3464 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3467 XPutPixel(xim, x, y, lightSquareColor);
3469 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3477 /* create Pixmap of piece */
3478 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3480 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3483 /* create Pixmap of clipmask
3484 Note: We assume the white/black pieces have the same
3485 outline, so we make only 6 masks. This is okay
3486 since the XPM clipmask routines do the same. */
3488 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3490 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3493 /* now create the 1-bit version */
3494 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3497 values.foreground = 1;
3498 values.background = 0;
3500 /* Don't use XtGetGC, not read only */
3501 maskGC = XCreateGC(xDisplay, *mask,
3502 GCForeground | GCBackground, &values);
3503 XCopyPlane(xDisplay, temp, *mask, maskGC,
3504 0, 0, squareSize, squareSize, 0, 0, 1);
3505 XFreePixmap(xDisplay, temp);
3510 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3512 void CreateXIMPieces()
3517 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3522 /* The XSynchronize calls were copied from CreatePieces.
3523 Not sure if needed, but can't hurt */
3524 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3527 /* temp needed by loadXIM() */
3528 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3529 0, 0, ss, ss, AllPlanes, XYPixmap);
3531 if (strlen(appData.pixmapDirectory) == 0) {
3535 if (appData.monoMode) {
3536 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3540 fprintf(stderr, _("\nLoading XIMs...\n"));
3542 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3543 fprintf(stderr, "%d", piece+1);
3544 for (kind=0; kind<4; kind++) {
3545 fprintf(stderr, ".");
3546 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3547 ExpandPathName(appData.pixmapDirectory),
3548 piece <= (int) WhiteKing ? "" : "w",
3549 pieceBitmapNames[piece],
3551 ximPieceBitmap[kind][piece] =
3552 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3553 0, 0, ss, ss, AllPlanes, XYPixmap);
3554 if (appData.debugMode)
3555 fprintf(stderr, _("(File:%s:) "), buf);
3556 loadXIM(ximPieceBitmap[kind][piece],
3558 &(xpmPieceBitmap2[kind][piece]),
3559 &(ximMaskPm2[piece]));
3560 if(piece <= (int)WhiteKing)
3561 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3563 fprintf(stderr," ");
3565 /* Load light and dark squares */
3566 /* If the LSQ and DSQ pieces don't exist, we will
3567 draw them with solid squares. */
3568 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3569 if (access(buf, 0) != 0) {
3573 fprintf(stderr, _("light square "));
3575 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3576 0, 0, ss, ss, AllPlanes, XYPixmap);
3577 if (appData.debugMode)
3578 fprintf(stderr, _("(File:%s:) "), buf);
3580 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3581 fprintf(stderr, _("dark square "));
3582 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3583 ExpandPathName(appData.pixmapDirectory), ss);
3584 if (appData.debugMode)
3585 fprintf(stderr, _("(File:%s:) "), buf);
3587 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3588 0, 0, ss, ss, AllPlanes, XYPixmap);
3589 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3590 xpmJailSquare = xpmLightSquare;
3592 fprintf(stderr, _("Done.\n"));
3594 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3597 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3600 void CreateXPMBoard(char *s, int kind)
3604 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3605 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3606 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3610 void FreeXPMPieces()
3611 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3612 // thisroutine has to be called t free the old piece pixmaps
3614 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3615 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3617 XFreePixmap(xDisplay, xpmLightSquare);
3618 XFreePixmap(xDisplay, xpmDarkSquare);
3622 void CreateXPMPieces()
3626 u_int ss = squareSize;
3628 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3629 XpmColorSymbol symbols[4];
3630 static int redo = False;
3632 if(redo) FreeXPMPieces(); else redo = 1;
3634 /* The XSynchronize calls were copied from CreatePieces.
3635 Not sure if needed, but can't hurt */
3636 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3638 /* Setup translations so piece colors match square colors */
3639 symbols[0].name = "light_piece";
3640 symbols[0].value = appData.whitePieceColor;
3641 symbols[1].name = "dark_piece";
3642 symbols[1].value = appData.blackPieceColor;
3643 symbols[2].name = "light_square";
3644 symbols[2].value = appData.lightSquareColor;
3645 symbols[3].name = "dark_square";
3646 symbols[3].value = appData.darkSquareColor;
3648 attr.valuemask = XpmColorSymbols;
3649 attr.colorsymbols = symbols;
3650 attr.numsymbols = 4;
3652 if (appData.monoMode) {
3653 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3657 if (strlen(appData.pixmapDirectory) == 0) {
3658 XpmPieces* pieces = builtInXpms;
3661 while (pieces->size != squareSize && pieces->size) pieces++;
3662 if (!pieces->size) {
3663 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3666 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3667 for (kind=0; kind<4; kind++) {
3669 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3670 pieces->xpm[piece][kind],
3671 &(xpmPieceBitmap2[kind][piece]),
3672 NULL, &attr)) != 0) {
3673 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3677 if(piece <= (int) WhiteKing)
3678 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3682 xpmJailSquare = xpmLightSquare;
3686 fprintf(stderr, _("\nLoading XPMs...\n"));
3689 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3690 fprintf(stderr, "%d ", piece+1);
3691 for (kind=0; kind<4; kind++) {
3692 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3693 ExpandPathName(appData.pixmapDirectory),
3694 piece > (int) WhiteKing ? "w" : "",
3695 pieceBitmapNames[piece],
3697 if (appData.debugMode) {
3698 fprintf(stderr, _("(File:%s:) "), buf);
3700 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3701 &(xpmPieceBitmap2[kind][piece]),
3702 NULL, &attr)) != 0) {
3703 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3704 // [HGM] missing: read of unorthodox piece failed; substitute King.
3705 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3706 ExpandPathName(appData.pixmapDirectory),
3708 if (appData.debugMode) {
3709 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3711 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3712 &(xpmPieceBitmap2[kind][piece]),
3716 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3721 if(piece <= (int) WhiteKing)
3722 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3725 /* Load light and dark squares */
3726 /* If the LSQ and DSQ pieces don't exist, we will
3727 draw them with solid squares. */
3728 fprintf(stderr, _("light square "));
3729 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3730 if (access(buf, 0) != 0) {
3734 if (appData.debugMode)
3735 fprintf(stderr, _("(File:%s:) "), buf);
3737 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3738 &xpmLightSquare, NULL, &attr)) != 0) {
3739 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3742 fprintf(stderr, _("dark square "));
3743 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3744 ExpandPathName(appData.pixmapDirectory), ss);
3745 if (appData.debugMode) {
3746 fprintf(stderr, _("(File:%s:) "), buf);
3748 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3749 &xpmDarkSquare, NULL, &attr)) != 0) {
3750 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3754 xpmJailSquare = xpmLightSquare;
3755 fprintf(stderr, _("Done.\n"));
3757 oldVariant = -1; // kludge to force re-makig of animation masks
3758 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3761 #endif /* HAVE_LIBXPM */
3764 /* No built-in bitmaps */
3769 u_int ss = squareSize;
3771 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3774 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3775 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3776 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3777 pieceBitmapNames[piece],
3778 ss, kind == SOLID ? 's' : 'o');
3779 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3780 if(piece <= (int)WhiteKing)
3781 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3785 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3789 /* With built-in bitmaps */
3792 BuiltInBits* bib = builtInBits;
3795 u_int ss = squareSize;
3797 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3800 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3802 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3803 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3804 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3805 pieceBitmapNames[piece],
3806 ss, kind == SOLID ? 's' : 'o');
3807 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3808 bib->bits[kind][piece], ss, ss);
3809 if(piece <= (int)WhiteKing)
3810 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3814 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3819 void ReadBitmap(pm, name, bits, wreq, hreq)
3822 unsigned char bits[];
3828 char msg[MSG_SIZ], fullname[MSG_SIZ];
3830 if (*appData.bitmapDirectory != NULLCHAR) {
3831 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3832 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3833 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3834 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3835 &w, &h, pm, &x_hot, &y_hot);
3836 fprintf(stderr, "load %s\n", name);
3837 if (errcode != BitmapSuccess) {
3839 case BitmapOpenFailed:
3840 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3842 case BitmapFileInvalid:
3843 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3845 case BitmapNoMemory:
3846 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3850 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3854 fprintf(stderr, _("%s: %s...using built-in\n"),
3856 } else if (w != wreq || h != hreq) {
3858 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3859 programName, fullname, w, h, wreq, hreq);
3865 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3874 if (lineGap == 0) return;
3876 /* [HR] Split this into 2 loops for non-square boards. */
3878 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3879 gridSegments[i].x1 = 0;
3880 gridSegments[i].x2 =
3881 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3882 gridSegments[i].y1 = gridSegments[i].y2
3883 = lineGap / 2 + (i * (squareSize + lineGap));
3886 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3887 gridSegments[j + i].y1 = 0;
3888 gridSegments[j + i].y2 =
3889 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3890 gridSegments[j + i].x1 = gridSegments[j + i].x2
3891 = lineGap / 2 + (j * (squareSize + lineGap));
3895 static void MenuBarSelect(w, addr, index)
3900 XtActionProc proc = (XtActionProc) addr;
3902 (proc)(NULL, NULL, NULL, NULL);
3905 void CreateMenuBarPopup(parent, name, mb)
3915 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3918 XtSetArg(args[j], XtNleftMargin, 20); j++;
3919 XtSetArg(args[j], XtNrightMargin, 20); j++;
3921 while (mi->string != NULL) {
3922 if (strcmp(mi->string, "----") == 0) {
3923 entry = XtCreateManagedWidget(_(mi->string), smeLineObjectClass,
3926 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3927 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3929 XtAddCallback(entry, XtNcallback,
3930 (XtCallbackProc) MenuBarSelect,
3931 (caddr_t) mi->proc);
3937 Widget CreateMenuBar(mb)
3941 Widget anchor, menuBar;
3943 char menuName[MSG_SIZ];
3946 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3947 XtSetArg(args[j], XtNvSpace, 0); j++;
3948 XtSetArg(args[j], XtNborderWidth, 0); j++;
3949 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3950 formWidget, args, j);
3952 while (mb->name != NULL) {
3953 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3954 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3956 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3959 shortName[0] = mb->name[0];
3960 shortName[1] = NULLCHAR;
3961 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3964 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3967 XtSetArg(args[j], XtNborderWidth, 0); j++;
3968 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3970 CreateMenuBarPopup(menuBar, menuName, mb);
3976 Widget CreateButtonBar(mi)
3980 Widget button, buttonBar;
3984 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3986 XtSetArg(args[j], XtNhSpace, 0); j++;
3988 XtSetArg(args[j], XtNborderWidth, 0); j++;
3989 XtSetArg(args[j], XtNvSpace, 0); j++;
3990 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3991 formWidget, args, j);
3993 while (mi->string != NULL) {
3996 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3997 XtSetArg(args[j], XtNborderWidth, 0); j++;
3999 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
4000 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
4001 buttonBar, args, j);
4002 XtAddCallback(button, XtNcallback,
4003 (XtCallbackProc) MenuBarSelect,
4004 (caddr_t) mi->proc);
4011 CreatePieceMenu(name, color)
4018 ChessSquare selection;
4020 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
4021 boardWidget, args, 0);
4023 for (i = 0; i < PIECE_MENU_SIZE; i++) {
4024 String item = pieceMenuStrings[color][i];
4026 if (strcmp(item, "----") == 0) {
4027 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4030 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4031 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4033 selection = pieceMenuTranslation[color][i];
4034 XtAddCallback(entry, XtNcallback,
4035 (XtCallbackProc) PieceMenuSelect,
4036 (caddr_t) selection);
4037 if (selection == WhitePawn || selection == BlackPawn) {
4038 XtSetArg(args[0], XtNpopupOnEntry, entry);
4039 XtSetValues(menu, args, 1);
4052 ChessSquare selection;
4054 whitePieceMenu = CreatePieceMenu("menuW", 0);
4055 blackPieceMenu = CreatePieceMenu("menuB", 1);
4057 XtRegisterGrabAction(PieceMenuPopup, True,
4058 (unsigned)(ButtonPressMask|ButtonReleaseMask),
4059 GrabModeAsync, GrabModeAsync);
4061 XtSetArg(args[0], XtNlabel, _("Drop"));
4062 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
4063 boardWidget, args, 1);
4064 for (i = 0; i < DROP_MENU_SIZE; i++) {
4065 String item = dropMenuStrings[i];
4067 if (strcmp(item, "----") == 0) {
4068 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4071 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4072 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4074 selection = dropMenuTranslation[i];
4075 XtAddCallback(entry, XtNcallback,
4076 (XtCallbackProc) DropMenuSelect,
4077 (caddr_t) selection);
4082 void SetupDropMenu()
4090 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
4091 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
4092 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
4093 dmEnables[i].piece);
4094 XtSetSensitive(entry, p != NULL || !appData.testLegality
4095 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
4096 && !appData.icsActive));
4098 while (p && *p++ == dmEnables[i].piece) count++;
4099 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
4101 XtSetArg(args[j], XtNlabel, label); j++;
4102 XtSetValues(entry, args, j);
4106 void PieceMenuPopup(w, event, params, num_params)
4110 Cardinal *num_params;
4112 String whichMenu; int menuNr = -2;
4113 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
4114 if (event->type == ButtonRelease)
4115 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4116 else if (event->type == ButtonPress)
4117 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4119 case 0: whichMenu = params[0]; break;
4120 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
4122 case -1: if (errorUp) ErrorPopDown();
4125 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
4128 static void PieceMenuSelect(w, piece, junk)
4133 if (pmFromX < 0 || pmFromY < 0) return;
4134 EditPositionMenuEvent(piece, pmFromX, pmFromY);
4137 static void DropMenuSelect(w, piece, junk)
4142 if (pmFromX < 0 || pmFromY < 0) return;
4143 DropMenuEvent(piece, pmFromX, pmFromY);
4146 void WhiteClock(w, event, prms, nprms)
4152 shiftKey = prms[0][0] & 1;
4156 void BlackClock(w, event, prms, nprms)
4162 shiftKey = prms[0][0] & 1;
4168 * If the user selects on a border boundary, return -1; if off the board,
4169 * return -2. Otherwise map the event coordinate to the square.
4171 int EventToSquare(x, limit)
4179 if ((x % (squareSize + lineGap)) >= squareSize)
4181 x /= (squareSize + lineGap);
4187 static void do_flash_delay(msec)
4193 static void drawHighlight(file, rank, gc)
4199 if (lineGap == 0) return;
4202 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4203 (squareSize + lineGap);
4204 y = lineGap/2 + rank * (squareSize + lineGap);
4206 x = lineGap/2 + file * (squareSize + lineGap);
4207 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4208 (squareSize + lineGap);
4211 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4212 squareSize+lineGap, squareSize+lineGap);
4215 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4216 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4219 SetHighlights(fromX, fromY, toX, toY)
4220 int fromX, fromY, toX, toY;
4222 if (hi1X != fromX || hi1Y != fromY) {
4223 if (hi1X >= 0 && hi1Y >= 0) {
4224 drawHighlight(hi1X, hi1Y, lineGC);
4226 } // [HGM] first erase both, then draw new!
4227 if (hi2X != toX || hi2Y != toY) {
4228 if (hi2X >= 0 && hi2Y >= 0) {
4229 drawHighlight(hi2X, hi2Y, lineGC);
4232 if (hi1X != fromX || hi1Y != fromY) {
4233 if (fromX >= 0 && fromY >= 0) {
4234 drawHighlight(fromX, fromY, highlineGC);
4237 if (hi2X != toX || hi2Y != toY) {
4238 if (toX >= 0 && toY >= 0) {
4239 drawHighlight(toX, toY, highlineGC);
4251 SetHighlights(-1, -1, -1, -1);
4256 SetPremoveHighlights(fromX, fromY, toX, toY)
4257 int fromX, fromY, toX, toY;
4259 if (pm1X != fromX || pm1Y != fromY) {
4260 if (pm1X >= 0 && pm1Y >= 0) {
4261 drawHighlight(pm1X, pm1Y, lineGC);
4263 if (fromX >= 0 && fromY >= 0) {
4264 drawHighlight(fromX, fromY, prelineGC);
4267 if (pm2X != toX || pm2Y != toY) {
4268 if (pm2X >= 0 && pm2Y >= 0) {
4269 drawHighlight(pm2X, pm2Y, lineGC);
4271 if (toX >= 0 && toY >= 0) {
4272 drawHighlight(toX, toY, prelineGC);
4282 ClearPremoveHighlights()
4284 SetPremoveHighlights(-1, -1, -1, -1);
4287 static int CutOutSquare(x, y, x0, y0, kind)
4288 int x, y, *x0, *y0, kind;
4290 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4291 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4293 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4294 if(textureW[kind] < W*squareSize)
4295 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4297 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4298 if(textureH[kind] < H*squareSize)
4299 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4301 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4305 static void BlankSquare(x, y, color, piece, dest, fac)
4306 int x, y, color, fac;
4309 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4311 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4312 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4313 squareSize, squareSize, x*fac, y*fac);
4315 if (useImages && useImageSqs) {
4319 pm = xpmLightSquare;
4324 case 2: /* neutral */
4329 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4330 squareSize, squareSize, x*fac, y*fac);
4340 case 2: /* neutral */
4345 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4350 I split out the routines to draw a piece so that I could
4351 make a generic flash routine.
4353 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4355 int square_color, x, y;
4358 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4359 switch (square_color) {
4361 case 2: /* neutral */
4363 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4364 ? *pieceToOutline(piece)
4365 : *pieceToSolid(piece),
4366 dest, bwPieceGC, 0, 0,
4367 squareSize, squareSize, x, y);
4370 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4371 ? *pieceToSolid(piece)
4372 : *pieceToOutline(piece),
4373 dest, wbPieceGC, 0, 0,
4374 squareSize, squareSize, x, y);
4379 static void monoDrawPiece(piece, square_color, x, y, dest)
4381 int square_color, x, y;
4384 switch (square_color) {
4386 case 2: /* neutral */
4388 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4389 ? *pieceToOutline(piece)
4390 : *pieceToSolid(piece),
4391 dest, bwPieceGC, 0, 0,
4392 squareSize, squareSize, x, y, 1);
4395 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4396 ? *pieceToSolid(piece)
4397 : *pieceToOutline(piece),
4398 dest, wbPieceGC, 0, 0,
4399 squareSize, squareSize, x, y, 1);
4404 static void colorDrawPiece(piece, square_color, x, y, dest)
4406 int square_color, x, y;
4409 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4410 switch (square_color) {
4412 XCopyPlane(xDisplay, *pieceToSolid(piece),
4413 dest, (int) piece < (int) BlackPawn
4414 ? wlPieceGC : blPieceGC, 0, 0,
4415 squareSize, squareSize, x, y, 1);
4418 XCopyPlane(xDisplay, *pieceToSolid(piece),
4419 dest, (int) piece < (int) BlackPawn
4420 ? wdPieceGC : bdPieceGC, 0, 0,
4421 squareSize, squareSize, x, y, 1);
4423 case 2: /* neutral */
4425 XCopyPlane(xDisplay, *pieceToSolid(piece),
4426 dest, (int) piece < (int) BlackPawn
4427 ? wjPieceGC : bjPieceGC, 0, 0,
4428 squareSize, squareSize, x, y, 1);
4433 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4435 int square_color, x, y;
4438 int kind, p = piece;
4440 switch (square_color) {
4442 case 2: /* neutral */
4444 if ((int)piece < (int) BlackPawn) {
4452 if ((int)piece < (int) BlackPawn) {
4460 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4461 if(useTexture & square_color+1) {
4462 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4463 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4464 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4465 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4466 XSetClipMask(xDisplay, wlPieceGC, None);
4467 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4469 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4470 dest, wlPieceGC, 0, 0,
4471 squareSize, squareSize, x, y);
4474 typedef void (*DrawFunc)();
4476 DrawFunc ChooseDrawFunc()
4478 if (appData.monoMode) {
4479 if (DefaultDepth(xDisplay, xScreen) == 1) {
4480 return monoDrawPiece_1bit;
4482 return monoDrawPiece;
4486 return colorDrawPieceImage;
4488 return colorDrawPiece;
4492 /* [HR] determine square color depending on chess variant. */
4493 static int SquareColor(row, column)
4498 if (gameInfo.variant == VariantXiangqi) {
4499 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4501 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4503 } else if (row <= 4) {
4509 square_color = ((column + row) % 2) == 1;
4512 /* [hgm] holdings: next line makes all holdings squares light */
4513 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4515 return square_color;
4518 void DrawSquare(row, column, piece, do_flash)
4519 int row, column, do_flash;
4522 int square_color, x, y, direction, font_ascent, font_descent;
4525 XCharStruct overall;
4529 /* Calculate delay in milliseconds (2-delays per complete flash) */
4530 flash_delay = 500 / appData.flashRate;
4533 x = lineGap + ((BOARD_WIDTH-1)-column) *
4534 (squareSize + lineGap);
4535 y = lineGap + row * (squareSize + lineGap);
4537 x = lineGap + column * (squareSize + lineGap);
4538 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4539 (squareSize + lineGap);
4542 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4544 square_color = SquareColor(row, column);
4546 if ( // [HGM] holdings: blank out area between board and holdings
4547 column == BOARD_LEFT-1 || column == BOARD_RGHT
4548 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4549 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4550 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4552 // [HGM] print piece counts next to holdings
4553 string[1] = NULLCHAR;
4554 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4555 string[0] = '0' + piece;
4556 XTextExtents(countFontStruct, string, 1, &direction,
4557 &font_ascent, &font_descent, &overall);
4558 if (appData.monoMode) {
4559 XDrawImageString(xDisplay, xBoardWindow, countGC,
4560 x + squareSize - overall.width - 2,
4561 y + font_ascent + 1, string, 1);
4563 XDrawString(xDisplay, xBoardWindow, countGC,
4564 x + squareSize - overall.width - 2,
4565 y + font_ascent + 1, string, 1);
4568 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4569 string[0] = '0' + piece;
4570 XTextExtents(countFontStruct, string, 1, &direction,
4571 &font_ascent, &font_descent, &overall);
4572 if (appData.monoMode) {
4573 XDrawImageString(xDisplay, xBoardWindow, countGC,
4574 x + 2, y + font_ascent + 1, string, 1);
4576 XDrawString(xDisplay, xBoardWindow, countGC,
4577 x + 2, y + font_ascent + 1, string, 1);
4581 if (piece == EmptySquare || appData.blindfold) {
4582 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4584 drawfunc = ChooseDrawFunc();
4586 if (do_flash && appData.flashCount > 0) {
4587 for (i=0; i<appData.flashCount; ++i) {
4588 drawfunc(piece, square_color, x, y, xBoardWindow);
4589 XSync(xDisplay, False);
4590 do_flash_delay(flash_delay);
4592 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4593 XSync(xDisplay, False);
4594 do_flash_delay(flash_delay);
4597 drawfunc(piece, square_color, x, y, xBoardWindow);
4601 string[1] = NULLCHAR;
4602 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4603 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4604 string[0] = 'a' + column - BOARD_LEFT;
4605 XTextExtents(coordFontStruct, string, 1, &direction,
4606 &font_ascent, &font_descent, &overall);
4607 if (appData.monoMode) {
4608 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4609 x + squareSize - overall.width - 2,
4610 y + squareSize - font_descent - 1, string, 1);
4612 XDrawString(xDisplay, xBoardWindow, coordGC,
4613 x + squareSize - overall.width - 2,
4614 y + squareSize - font_descent - 1, string, 1);
4617 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4618 string[0] = ONE + row;
4619 XTextExtents(coordFontStruct, string, 1, &direction,
4620 &font_ascent, &font_descent, &overall);
4621 if (appData.monoMode) {
4622 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4623 x + 2, y + font_ascent + 1, string, 1);
4625 XDrawString(xDisplay, xBoardWindow, coordGC,
4626 x + 2, y + font_ascent + 1, string, 1);
4629 if(!partnerUp && marker[row][column]) {
4630 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4631 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4636 /* Why is this needed on some versions of X? */
4637 void EventProc(widget, unused, event)
4642 if (!XtIsRealized(widget))
4645 switch (event->type) {
4647 if (event->xexpose.count > 0) return; /* no clipping is done */
4648 XDrawPosition(widget, True, NULL);
4649 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4650 flipView = !flipView; partnerUp = !partnerUp;
4651 XDrawPosition(widget, True, NULL);
4652 flipView = !flipView; partnerUp = !partnerUp;
4656 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4663 void DrawPosition(fullRedraw, board)
4664 /*Boolean*/int fullRedraw;
4667 XDrawPosition(boardWidget, fullRedraw, board);
4670 /* Returns 1 if there are "too many" differences between b1 and b2
4671 (i.e. more than 1 move was made) */
4672 static int too_many_diffs(b1, b2)
4678 for (i=0; i<BOARD_HEIGHT; ++i) {
4679 for (j=0; j<BOARD_WIDTH; ++j) {
4680 if (b1[i][j] != b2[i][j]) {
4681 if (++c > 4) /* Castling causes 4 diffs */
4689 /* Matrix describing castling maneuvers */
4690 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4691 static int castling_matrix[4][5] = {
4692 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4693 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4694 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4695 { 7, 7, 4, 5, 6 } /* 0-0, black */
4698 /* Checks whether castling occurred. If it did, *rrow and *rcol
4699 are set to the destination (row,col) of the rook that moved.
4701 Returns 1 if castling occurred, 0 if not.
4703 Note: Only handles a max of 1 castling move, so be sure
4704 to call too_many_diffs() first.
4706 static int check_castle_draw(newb, oldb, rrow, rcol)
4713 /* For each type of castling... */
4714 for (i=0; i<4; ++i) {
4715 r = castling_matrix[i];
4717 /* Check the 4 squares involved in the castling move */
4719 for (j=1; j<=4; ++j) {
4720 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4727 /* All 4 changed, so it must be a castling move */
4736 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4737 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4739 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4742 void DrawSeekBackground( int left, int top, int right, int bottom )
4744 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4747 void DrawSeekText(char *buf, int x, int y)
4749 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4752 void DrawSeekDot(int x, int y, int colorNr)
4754 int square = colorNr & 0x80;
4757 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4759 XFillRectangle(xDisplay, xBoardWindow, color,
4760 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4762 XFillArc(xDisplay, xBoardWindow, color,
4763 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4766 static int damage[2][BOARD_RANKS][BOARD_FILES];
4769 * event handler for redrawing the board
4771 void XDrawPosition(w, repaint, board)
4773 /*Boolean*/int repaint;
4777 static int lastFlipView = 0;
4778 static int lastBoardValid[2] = {0, 0};
4779 static Board lastBoard[2];
4782 int nr = twoBoards*partnerUp;
4784 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4786 if (board == NULL) {
4787 if (!lastBoardValid[nr]) return;
4788 board = lastBoard[nr];
4790 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4791 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4792 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4797 * It would be simpler to clear the window with XClearWindow()
4798 * but this causes a very distracting flicker.
4801 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4803 if ( lineGap && IsDrawArrowEnabled())
4804 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4805 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4807 /* If too much changes (begin observing new game, etc.), don't
4809 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4811 /* Special check for castling so we don't flash both the king
4812 and the rook (just flash the king). */
4814 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4815 /* Draw rook with NO flashing. King will be drawn flashing later */
4816 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4817 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4821 /* First pass -- Draw (newly) empty squares and repair damage.
4822 This prevents you from having a piece show up twice while it
4823 is flashing on its new square */
4824 for (i = 0; i < BOARD_HEIGHT; i++)
4825 for (j = 0; j < BOARD_WIDTH; j++)
4826 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4827 || damage[nr][i][j]) {
4828 DrawSquare(i, j, board[i][j], 0);
4829 damage[nr][i][j] = False;
4832 /* Second pass -- Draw piece(s) in new position and flash them */
4833 for (i = 0; i < BOARD_HEIGHT; i++)
4834 for (j = 0; j < BOARD_WIDTH; j++)
4835 if (board[i][j] != lastBoard[nr][i][j]) {
4836 DrawSquare(i, j, board[i][j], do_flash);
4840 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4841 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4842 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4844 for (i = 0; i < BOARD_HEIGHT; i++)
4845 for (j = 0; j < BOARD_WIDTH; j++) {
4846 DrawSquare(i, j, board[i][j], 0);
4847 damage[nr][i][j] = False;
4851 CopyBoard(lastBoard[nr], board);
4852 lastBoardValid[nr] = 1;
4853 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4854 lastFlipView = flipView;
4856 /* Draw highlights */
4857 if (pm1X >= 0 && pm1Y >= 0) {
4858 drawHighlight(pm1X, pm1Y, prelineGC);
4860 if (pm2X >= 0 && pm2Y >= 0) {
4861 drawHighlight(pm2X, pm2Y, prelineGC);
4863 if (hi1X >= 0 && hi1Y >= 0) {
4864 drawHighlight(hi1X, hi1Y, highlineGC);
4866 if (hi2X >= 0 && hi2Y >= 0) {
4867 drawHighlight(hi2X, hi2Y, highlineGC);
4869 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4871 /* If piece being dragged around board, must redraw that too */
4874 XSync(xDisplay, False);
4879 * event handler for redrawing the board
4881 void DrawPositionProc(w, event, prms, nprms)
4887 XDrawPosition(w, True, NULL);
4892 * event handler for parsing user moves
4894 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4895 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4896 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4897 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4898 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4899 // and at the end FinishMove() to perform the move after optional promotion popups.
4900 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4901 void HandleUserMove(w, event, prms, nprms)
4907 if (w != boardWidget || errorExitStatus != -1) return;
4908 if(nprms) shiftKey = !strcmp(prms[0], "1");
4911 if (event->type == ButtonPress) {
4912 XtPopdown(promotionShell);
4913 XtDestroyWidget(promotionShell);
4914 promotionUp = False;
4922 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4923 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4924 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4927 void AnimateUserMove (Widget w, XEvent * event,
4928 String * params, Cardinal * nParams)
4930 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4931 DragPieceMove(event->xmotion.x, event->xmotion.y);
4934 void HandlePV (Widget w, XEvent * event,
4935 String * params, Cardinal * nParams)
4936 { // [HGM] pv: walk PV
4937 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4940 static int savedIndex; /* gross that this is global */
4942 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4945 XawTextPosition index, dummy;
4948 XawTextGetSelectionPos(w, &index, &dummy);
4949 XtSetArg(arg, XtNstring, &val);
4950 XtGetValues(w, &arg, 1);
4951 ReplaceComment(savedIndex, val);
4952 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4953 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4956 void EditCommentPopUp(index, title, text)
4961 if (text == NULL) text = "";
4962 NewCommentPopup(title, text, index);
4965 void ICSInputBoxPopUp()
4970 extern Option boxOptions[];
4972 void ICSInputSendText()
4979 edit = boxOptions[0].handle;
4981 XtSetArg(args[j], XtNstring, &val); j++;
4982 XtGetValues(edit, args, j);
4984 SendMultiLineToICS(val);
4985 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4986 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4989 void ICSInputBoxPopDown()
4994 void CommentPopUp(title, text)
4997 savedIndex = currentMove; // [HGM] vari
4998 NewCommentPopup(title, text, currentMove);
5001 void CommentPopDown()
5006 static char *openName;
5011 (void) (*fileProc)(openFP, 0, openName);
5014 void FileNamePopUp(label, def, filter, proc, openMode)
5021 fileProc = proc; /* I can't see a way not */
5022 fileOpenMode = openMode; /* to use globals here */
5023 { // [HGM] use file-selector dialog stolen from Ghostview
5024 int index; // this is not supported yet
5025 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
5026 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
5027 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
5028 ScheduleDelayedEvent(&DelayedLoad, 50);
5032 void FileNamePopDown()
5034 if (!filenameUp) return;
5035 XtPopdown(fileNameShell);
5036 XtDestroyWidget(fileNameShell);
5041 void FileNameCallback(w, client_data, call_data)
5043 XtPointer client_data, call_data;
5048 XtSetArg(args[0], XtNlabel, &name);
5049 XtGetValues(w, args, 1);
5051 if (strcmp(name, _("cancel")) == 0) {
5056 FileNameAction(w, NULL, NULL, NULL);
5059 void FileNameAction(w, event, prms, nprms)
5071 name = XawDialogGetValueString(w = XtParent(w));
5073 if ((name != NULL) && (*name != NULLCHAR)) {
5074 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5075 XtPopdown(w = XtParent(XtParent(w)));
5079 p = strrchr(buf, ' ');
5086 fullname = ExpandPathName(buf);
5088 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5091 f = fopen(fullname, fileOpenMode);
5093 DisplayError(_("Failed to open file"), errno);
5095 (void) (*fileProc)(f, index, buf);
5102 XtPopdown(w = XtParent(XtParent(w)));
5108 void PromotionPopUp()
5111 Widget dialog, layout;
5113 Dimension bw_width, pw_width;
5117 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5118 XtGetValues(boardWidget, args, j);
5121 XtSetArg(args[j], XtNresizable, True); j++;
5122 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5124 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5125 shellWidget, args, j);
5127 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5128 layoutArgs, XtNumber(layoutArgs));
5131 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5132 XtSetArg(args[j], XtNborderWidth, 0); j++;
5133 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5136 if(gameInfo.variant != VariantShogi) {
5137 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5138 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback,
5139 (XtPointer) dialog);
5140 XawDialogAddButton(dialog, _("General"), PromotionCallback,
5141 (XtPointer) dialog);
5142 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback,
5143 (XtPointer) dialog);
5144 XawDialogAddButton(dialog, _("Captain"), PromotionCallback,
5145 (XtPointer) dialog);
5147 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
5148 (XtPointer) dialog);
5149 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
5150 (XtPointer) dialog);
5151 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
5152 (XtPointer) dialog);
5153 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
5154 (XtPointer) dialog);
5156 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5157 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
5158 gameInfo.variant == VariantGiveaway) {
5159 XawDialogAddButton(dialog, _("King"), PromotionCallback,
5160 (XtPointer) dialog);
5162 if(gameInfo.variant == VariantCapablanca ||
5163 gameInfo.variant == VariantGothic ||
5164 gameInfo.variant == VariantCapaRandom) {
5165 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5166 (XtPointer) dialog);
5167 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5168 (XtPointer) dialog);
5170 } else // [HGM] shogi
5172 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5173 (XtPointer) dialog);
5174 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5175 (XtPointer) dialog);
5177 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5178 (XtPointer) dialog);
5180 XtRealizeWidget(promotionShell);
5181 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5184 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5185 XtGetValues(promotionShell, args, j);
5187 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5188 lineGap + squareSize/3 +
5189 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5190 0 : 6*(squareSize + lineGap)), &x, &y);
5193 XtSetArg(args[j], XtNx, x); j++;
5194 XtSetArg(args[j], XtNy, y); j++;
5195 XtSetValues(promotionShell, args, j);
5197 XtPopup(promotionShell, XtGrabNone);
5202 void PromotionPopDown()
5204 if (!promotionUp) return;
5205 XtPopdown(promotionShell);
5206 XtDestroyWidget(promotionShell);
5207 promotionUp = False;
5210 void PromotionCallback(w, client_data, call_data)
5212 XtPointer client_data, call_data;
5218 XtSetArg(args[0], XtNlabel, &name);
5219 XtGetValues(w, args, 1);
5223 if (fromX == -1) return;
5225 if (strcmp(name, _("cancel")) == 0) {
5229 } else if (strcmp(name, _("Knight")) == 0) {
5231 } else if (strcmp(name, _("Promote")) == 0) {
5233 } else if (strcmp(name, _("Defer")) == 0) {
5236 promoChar = ToLower(name[0]);
5239 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5241 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5242 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5247 void ErrorCallback(w, client_data, call_data)
5249 XtPointer client_data, call_data;
5252 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5254 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5260 if (!errorUp) return;
5262 XtPopdown(errorShell);
5263 XtDestroyWidget(errorShell);
5264 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5267 void ErrorPopUp(title, label, modal)
5268 char *title, *label;
5272 Widget dialog, layout;
5276 Dimension bw_width, pw_width;
5277 Dimension pw_height;
5281 XtSetArg(args[i], XtNresizable, True); i++;
5282 XtSetArg(args[i], XtNtitle, title); i++;
5284 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5285 shellWidget, args, i);
5287 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5288 layoutArgs, XtNumber(layoutArgs));
5291 XtSetArg(args[i], XtNlabel, label); i++;
5292 XtSetArg(args[i], XtNborderWidth, 0); i++;
5293 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5296 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5298 XtRealizeWidget(errorShell);
5299 CatchDeleteWindow(errorShell, "ErrorPopDown");
5302 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5303 XtGetValues(boardWidget, args, i);
5305 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5306 XtSetArg(args[i], XtNheight, &pw_height); i++;
5307 XtGetValues(errorShell, args, i);
5310 /* This code seems to tickle an X bug if it is executed too soon
5311 after xboard starts up. The coordinates get transformed as if
5312 the main window was positioned at (0, 0).
5314 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5315 0 - pw_height + squareSize / 3, &x, &y);
5317 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5318 RootWindowOfScreen(XtScreen(boardWidget)),
5319 (bw_width - pw_width) / 2,
5320 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5324 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5327 XtSetArg(args[i], XtNx, x); i++;
5328 XtSetArg(args[i], XtNy, y); i++;
5329 XtSetValues(errorShell, args, i);
5332 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5335 /* Disable all user input other than deleting the window */
5336 static int frozen = 0;
5340 /* Grab by a widget that doesn't accept input */
5341 XtAddGrab(messageWidget, TRUE, FALSE);
5345 /* Undo a FreezeUI */
5348 if (!frozen) return;
5349 XtRemoveGrab(messageWidget);
5353 char *ModeToWidgetName(mode)
5357 case BeginningOfGame:
5358 if (appData.icsActive)
5359 return "menuMode.ICS Client";
5360 else if (appData.noChessProgram ||
5361 *appData.cmailGameName != NULLCHAR)
5362 return "menuMode.Edit Game";
5364 return "menuMode.Machine Black";
5365 case MachinePlaysBlack:
5366 return "menuMode.Machine Black";
5367 case MachinePlaysWhite:
5368 return "menuMode.Machine White";
5370 return "menuMode.Analysis Mode";
5372 return "menuMode.Analyze File";
5373 case TwoMachinesPlay:
5374 return "menuMode.Two Machines";
5376 return "menuMode.Edit Game";
5377 case PlayFromGameFile:
5378 return "menuFile.Load Game";
5380 return "menuMode.Edit Position";
5382 return "menuMode.Training";
5383 case IcsPlayingWhite:
5384 case IcsPlayingBlack:
5388 return "menuMode.ICS Client";
5395 void ModeHighlight()
5398 static int oldPausing = FALSE;
5399 static GameMode oldmode = (GameMode) -1;
5402 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5404 if (pausing != oldPausing) {
5405 oldPausing = pausing;
5407 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5409 XtSetArg(args[0], XtNleftBitmap, None);
5411 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5414 if (appData.showButtonBar) {
5415 /* Always toggle, don't set. Previous code messes up when
5416 invoked while the button is pressed, as releasing it
5417 toggles the state again. */
5420 XtSetArg(args[0], XtNbackground, &oldbg);
5421 XtSetArg(args[1], XtNforeground, &oldfg);
5422 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5424 XtSetArg(args[0], XtNbackground, oldfg);
5425 XtSetArg(args[1], XtNforeground, oldbg);
5427 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5431 wname = ModeToWidgetName(oldmode);
5432 if (wname != NULL) {
5433 XtSetArg(args[0], XtNleftBitmap, None);
5434 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5436 wname = ModeToWidgetName(gameMode);
5437 if (wname != NULL) {
5438 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5439 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5442 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5443 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5445 /* Maybe all the enables should be handled here, not just this one */
5446 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5447 gameMode == Training || gameMode == PlayFromGameFile);
5452 * Button/menu procedures
5454 void ResetProc(w, event, prms, nprms)
5463 int LoadGamePopUp(f, gameNumber, title)
5468 cmailMsgLoaded = FALSE;
5469 if (gameNumber == 0) {
5470 int error = GameListBuild(f);
5472 DisplayError(_("Cannot build game list"), error);
5473 } else if (!ListEmpty(&gameList) &&
5474 ((ListGame *) gameList.tailPred)->number > 1) {
5475 GameListPopUp(f, title);
5481 return LoadGame(f, gameNumber, title, FALSE);
5484 void LoadGameProc(w, event, prms, nprms)
5490 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5493 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5496 void LoadNextGameProc(w, event, prms, nprms)
5505 void LoadPrevGameProc(w, event, prms, nprms)
5514 void ReloadGameProc(w, event, prms, nprms)
5523 void LoadNextPositionProc(w, event, prms, nprms)
5532 void LoadPrevPositionProc(w, event, prms, nprms)
5541 void ReloadPositionProc(w, event, prms, nprms)
5550 void LoadPositionProc(w, event, prms, nprms)
5556 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5559 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5562 void SaveGameProc(w, event, prms, nprms)
5568 FileNamePopUp(_("Save game file name?"),
5569 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5570 appData.oldSaveStyle ? ".game" : ".pgn",
5574 void SavePositionProc(w, event, prms, nprms)
5580 FileNamePopUp(_("Save position file name?"),
5581 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5582 appData.oldSaveStyle ? ".pos" : ".fen",
5586 void ReloadCmailMsgProc(w, event, prms, nprms)
5592 ReloadCmailMsgEvent(FALSE);
5595 void MailMoveProc(w, event, prms, nprms)
5604 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5605 char *selected_fen_position=NULL;
5608 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5609 Atom *type_return, XtPointer *value_return,
5610 unsigned long *length_return, int *format_return)
5612 char *selection_tmp;
5614 if (!selected_fen_position) return False; /* should never happen */
5615 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5616 /* note: since no XtSelectionDoneProc was registered, Xt will
5617 * automatically call XtFree on the value returned. So have to
5618 * make a copy of it allocated with XtMalloc */
5619 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5620 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5622 *value_return=selection_tmp;
5623 *length_return=strlen(selection_tmp);
5624 *type_return=*target;
5625 *format_return = 8; /* bits per byte */
5627 } else if (*target == XA_TARGETS(xDisplay)) {
5628 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5629 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5630 targets_tmp[1] = XA_STRING;
5631 *value_return = targets_tmp;
5632 *type_return = XA_ATOM;
5634 *format_return = 8 * sizeof(Atom);
5635 if (*format_return > 32) {
5636 *length_return *= *format_return / 32;
5637 *format_return = 32;
5645 /* note: when called from menu all parameters are NULL, so no clue what the
5646 * Widget which was clicked on was, or what the click event was
5648 void CopyPositionProc(w, event, prms, nprms)
5655 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5656 * have a notion of a position that is selected but not copied.
5657 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5659 if(gameMode == EditPosition) EditPositionDone(TRUE);
5660 if (selected_fen_position) free(selected_fen_position);
5661 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5662 if (!selected_fen_position) return;
5663 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5665 SendPositionSelection,
5666 NULL/* lose_ownership_proc */ ,
5667 NULL/* transfer_done_proc */);
5668 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5670 SendPositionSelection,
5671 NULL/* lose_ownership_proc */ ,
5672 NULL/* transfer_done_proc */);
5675 /* function called when the data to Paste is ready */
5677 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5678 Atom *type, XtPointer value, unsigned long *len, int *format)
5681 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5682 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5683 EditPositionPasteFEN(fenstr);
5687 /* called when Paste Position button is pressed,
5688 * all parameters will be NULL */
5689 void PastePositionProc(w, event, prms, nprms)
5695 XtGetSelectionValue(menuBarWidget,
5696 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5697 /* (XtSelectionCallbackProc) */ PastePositionCB,
5698 NULL, /* client_data passed to PastePositionCB */
5700 /* better to use the time field from the event that triggered the
5701 * call to this function, but that isn't trivial to get
5709 SendGameSelection(Widget w, Atom *selection, Atom *target,
5710 Atom *type_return, XtPointer *value_return,
5711 unsigned long *length_return, int *format_return)
5713 char *selection_tmp;
5715 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5716 FILE* f = fopen(gameCopyFilename, "r");
5719 if (f == NULL) return False;
5723 selection_tmp = XtMalloc(len + 1);
5724 count = fread(selection_tmp, 1, len, f);
5727 XtFree(selection_tmp);
5730 selection_tmp[len] = NULLCHAR;
5731 *value_return = selection_tmp;
5732 *length_return = len;
5733 *type_return = *target;
5734 *format_return = 8; /* bits per byte */
5736 } else if (*target == XA_TARGETS(xDisplay)) {
5737 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5738 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5739 targets_tmp[1] = XA_STRING;
5740 *value_return = targets_tmp;
5741 *type_return = XA_ATOM;
5743 *format_return = 8 * sizeof(Atom);
5744 if (*format_return > 32) {
5745 *length_return *= *format_return / 32;
5746 *format_return = 32;
5754 void CopySomething()
5757 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5758 * have a notion of a game that is selected but not copied.
5759 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5761 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5764 NULL/* lose_ownership_proc */ ,
5765 NULL/* transfer_done_proc */);
5766 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5769 NULL/* lose_ownership_proc */ ,
5770 NULL/* transfer_done_proc */);
5773 /* note: when called from menu all parameters are NULL, so no clue what the
5774 * Widget which was clicked on was, or what the click event was
5776 void CopyGameProc(w, event, prms, nprms)
5784 ret = SaveGameToFile(gameCopyFilename, FALSE);
5790 void CopyGameListProc(w, event, prms, nprms)
5796 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5800 /* function called when the data to Paste is ready */
5802 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5803 Atom *type, XtPointer value, unsigned long *len, int *format)
5806 if (value == NULL || *len == 0) {
5807 return; /* nothing had been selected to copy */
5809 f = fopen(gamePasteFilename, "w");
5811 DisplayError(_("Can't open temp file"), errno);
5814 fwrite(value, 1, *len, f);
5817 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5820 /* called when Paste Game button is pressed,
5821 * all parameters will be NULL */
5822 void PasteGameProc(w, event, prms, nprms)
5828 XtGetSelectionValue(menuBarWidget,
5829 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5830 /* (XtSelectionCallbackProc) */ PasteGameCB,
5831 NULL, /* client_data passed to PasteGameCB */
5833 /* better to use the time field from the event that triggered the
5834 * call to this function, but that isn't trivial to get
5844 SaveGameProc(NULL, NULL, NULL, NULL);
5848 void QuitProc(w, event, prms, nprms)
5857 void PauseProc(w, event, prms, nprms)
5867 void MachineBlackProc(w, event, prms, nprms)
5873 MachineBlackEvent();
5876 void MachineWhiteProc(w, event, prms, nprms)
5882 MachineWhiteEvent();
5885 void AnalyzeModeProc(w, event, prms, nprms)
5893 if (!first.analysisSupport) {
5894 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5895 DisplayError(buf, 0);
5898 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5899 if (appData.icsActive) {
5900 if (gameMode != IcsObserving) {
5901 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5902 DisplayError(buf, 0);
5904 if (appData.icsEngineAnalyze) {
5905 if (appData.debugMode)
5906 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5912 /* if enable, use want disable icsEngineAnalyze */
5913 if (appData.icsEngineAnalyze) {
5918 appData.icsEngineAnalyze = TRUE;
5919 if (appData.debugMode)
5920 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5922 #ifndef OPTIONSDIALOG
5923 if (!appData.showThinking)
5924 ShowThinkingProc(w,event,prms,nprms);
5930 void AnalyzeFileProc(w, event, prms, nprms)
5936 if (!first.analysisSupport) {
5938 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5939 DisplayError(buf, 0);
5942 // Reset(FALSE, TRUE);
5943 #ifndef OPTIONSDIALOG
5944 if (!appData.showThinking)
5945 ShowThinkingProc(w,event,prms,nprms);
5948 // FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5949 AnalysisPeriodicEvent(1);
5952 void TwoMachinesProc(w, event, prms, nprms)
5961 void MatchProc(w, event, prms, nprms)
5970 void IcsClientProc(w, event, prms, nprms)
5979 void EditGameProc(w, event, prms, nprms)
5988 void EditPositionProc(w, event, prms, nprms)
5994 EditPositionEvent();
5997 void TrainingProc(w, event, prms, nprms)
6006 void EditCommentProc(w, event, prms, nprms)
6014 if (PopDown(1)) { // popdown succesful
6016 XtSetArg(args[j], XtNleftBitmap, None); j++;
6017 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
6018 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
6019 } else // was not up
6023 void IcsInputBoxProc(w, event, prms, nprms)
6029 if (!PopDown(4)) ICSInputBoxPopUp();
6032 void AcceptProc(w, event, prms, nprms)
6041 void DeclineProc(w, event, prms, nprms)
6050 void RematchProc(w, event, prms, nprms)
6059 void CallFlagProc(w, event, prms, nprms)
6068 void DrawProc(w, event, prms, nprms)
6077 void AbortProc(w, event, prms, nprms)
6086 void AdjournProc(w, event, prms, nprms)
6095 void ResignProc(w, event, prms, nprms)
6104 void AdjuWhiteProc(w, event, prms, nprms)
6110 UserAdjudicationEvent(+1);
6113 void AdjuBlackProc(w, event, prms, nprms)
6119 UserAdjudicationEvent(-1);
6122 void AdjuDrawProc(w, event, prms, nprms)
6128 UserAdjudicationEvent(0);
6131 void EnterKeyProc(w, event, prms, nprms)
6137 if (shellUp[4] == True)
6141 void UpKeyProc(w, event, prms, nprms)
6146 { // [HGM] input: let up-arrow recall previous line from history
6153 if (!shellUp[4]) return;
6154 edit = boxOptions[0].handle;
6156 XtSetArg(args[j], XtNstring, &val); j++;
6157 XtGetValues(edit, args, j);
6158 val = PrevInHistory(val);
6159 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6160 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6162 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6163 XawTextReplace(edit, 0, 0, &t);
6164 XawTextSetInsertionPoint(edit, 9999);
6168 void DownKeyProc(w, event, prms, nprms)
6173 { // [HGM] input: let down-arrow recall next line from history
6178 if (!shellUp[4]) return;
6179 edit = boxOptions[0].handle;
6180 val = NextInHistory();
6181 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6182 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6184 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6185 XawTextReplace(edit, 0, 0, &t);
6186 XawTextSetInsertionPoint(edit, 9999);
6190 void StopObservingProc(w, event, prms, nprms)
6196 StopObservingEvent();
6199 void StopExaminingProc(w, event, prms, nprms)
6205 StopExaminingEvent();
6208 void UploadProc(w, event, prms, nprms)
6218 void ForwardProc(w, event, prms, nprms)
6228 void BackwardProc(w, event, prms, nprms)
6237 void ToStartProc(w, event, prms, nprms)
6246 void ToEndProc(w, event, prms, nprms)
6255 void RevertProc(w, event, prms, nprms)
6264 void AnnotateProc(w, event, prms, nprms)
6273 void TruncateGameProc(w, event, prms, nprms)
6279 TruncateGameEvent();
6281 void RetractMoveProc(w, event, prms, nprms)
6290 void MoveNowProc(w, event, prms, nprms)
6299 void FlipViewProc(w, event, prms, nprms)
6305 flipView = !flipView;
6306 DrawPosition(True, NULL);
6309 void PonderNextMoveProc(w, event, prms, nprms)
6317 PonderNextMoveEvent(!appData.ponderNextMove);
6318 #ifndef OPTIONSDIALOG
6319 if (appData.ponderNextMove) {
6320 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6322 XtSetArg(args[0], XtNleftBitmap, None);
6324 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6329 #ifndef OPTIONSDIALOG
6330 void AlwaysQueenProc(w, event, prms, nprms)
6338 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6340 if (appData.alwaysPromoteToQueen) {
6341 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6343 XtSetArg(args[0], XtNleftBitmap, None);
6345 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6349 void AnimateDraggingProc(w, event, prms, nprms)
6357 appData.animateDragging = !appData.animateDragging;
6359 if (appData.animateDragging) {
6360 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6363 XtSetArg(args[0], XtNleftBitmap, None);
6365 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6369 void AnimateMovingProc(w, event, prms, nprms)
6377 appData.animate = !appData.animate;
6379 if (appData.animate) {
6380 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6383 XtSetArg(args[0], XtNleftBitmap, None);
6385 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6389 void AutoflagProc(w, event, prms, nprms)
6397 appData.autoCallFlag = !appData.autoCallFlag;
6399 if (appData.autoCallFlag) {
6400 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6402 XtSetArg(args[0], XtNleftBitmap, None);
6404 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6408 void AutoflipProc(w, event, prms, nprms)
6416 appData.autoFlipView = !appData.autoFlipView;
6418 if (appData.autoFlipView) {
6419 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6421 XtSetArg(args[0], XtNleftBitmap, None);
6423 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6427 void BlindfoldProc(w, event, prms, nprms)
6435 appData.blindfold = !appData.blindfold;
6437 if (appData.blindfold) {
6438 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6440 XtSetArg(args[0], XtNleftBitmap, None);
6442 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6445 DrawPosition(True, NULL);
6448 void TestLegalityProc(w, event, prms, nprms)
6456 appData.testLegality = !appData.testLegality;
6458 if (appData.testLegality) {
6459 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6461 XtSetArg(args[0], XtNleftBitmap, None);
6463 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6468 void FlashMovesProc(w, event, prms, nprms)
6476 if (appData.flashCount == 0) {
6477 appData.flashCount = 3;
6479 appData.flashCount = -appData.flashCount;
6482 if (appData.flashCount > 0) {
6483 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6485 XtSetArg(args[0], XtNleftBitmap, None);
6487 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6492 void HighlightDraggingProc(w, event, prms, nprms)
6500 appData.highlightDragging = !appData.highlightDragging;
6502 if (appData.highlightDragging) {
6503 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6505 XtSetArg(args[0], XtNleftBitmap, None);
6507 XtSetValues(XtNameToWidget(menuBarWidget,
6508 "menuOptions.Highlight Dragging"), args, 1);
6512 void HighlightLastMoveProc(w, event, prms, nprms)
6520 appData.highlightLastMove = !appData.highlightLastMove;
6522 if (appData.highlightLastMove) {
6523 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6525 XtSetArg(args[0], XtNleftBitmap, None);
6527 XtSetValues(XtNameToWidget(menuBarWidget,
6528 "menuOptions.Highlight Last Move"), args, 1);
6531 void HighlightArrowProc(w, event, prms, nprms)
6539 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6541 if (appData.highlightMoveWithArrow) {
6542 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6544 XtSetArg(args[0], XtNleftBitmap, None);
6546 XtSetValues(XtNameToWidget(menuBarWidget,
6547 "menuOptions.Arrow"), args, 1);
6551 void IcsAlarmProc(w, event, prms, nprms)
6559 appData.icsAlarm = !appData.icsAlarm;
6561 if (appData.icsAlarm) {
6562 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6564 XtSetArg(args[0], XtNleftBitmap, None);
6566 XtSetValues(XtNameToWidget(menuBarWidget,
6567 "menuOptions.ICS Alarm"), args, 1);
6571 void MoveSoundProc(w, event, prms, nprms)
6579 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6581 if (appData.ringBellAfterMoves) {
6582 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6584 XtSetArg(args[0], XtNleftBitmap, None);
6586 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6590 void OneClickProc(w, event, prms, nprms)
6598 appData.oneClick = !appData.oneClick;
6600 if (appData.oneClick) {
6601 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6603 XtSetArg(args[0], XtNleftBitmap, None);
6605 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6609 void PeriodicUpdatesProc(w, event, prms, nprms)
6617 PeriodicUpdatesEvent(!appData.periodicUpdates);
6619 if (appData.periodicUpdates) {
6620 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6622 XtSetArg(args[0], XtNleftBitmap, None);
6624 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6628 void PopupExitMessageProc(w, event, prms, nprms)
6636 appData.popupExitMessage = !appData.popupExitMessage;
6638 if (appData.popupExitMessage) {
6639 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6641 XtSetArg(args[0], XtNleftBitmap, None);
6643 XtSetValues(XtNameToWidget(menuBarWidget,
6644 "menuOptions.Popup Exit Message"), args, 1);
6647 void PopupMoveErrorsProc(w, event, prms, nprms)
6655 appData.popupMoveErrors = !appData.popupMoveErrors;
6657 if (appData.popupMoveErrors) {
6658 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6660 XtSetArg(args[0], XtNleftBitmap, None);
6662 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6667 void PremoveProc(w, event, prms, nprms)
6675 appData.premove = !appData.premove;
6677 if (appData.premove) {
6678 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6680 XtSetArg(args[0], XtNleftBitmap, None);
6682 XtSetValues(XtNameToWidget(menuBarWidget,
6683 "menuOptions.Premove"), args, 1);
6687 void ShowCoordsProc(w, event, prms, nprms)
6695 appData.showCoords = !appData.showCoords;
6697 if (appData.showCoords) {
6698 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6700 XtSetArg(args[0], XtNleftBitmap, None);
6702 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6705 DrawPosition(True, NULL);
6708 void ShowThinkingProc(w, event, prms, nprms)
6714 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6715 ShowThinkingEvent();
6718 void HideThinkingProc(w, event, prms, nprms)
6726 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6727 ShowThinkingEvent();
6729 if (appData.hideThinkingFromHuman) {
6730 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6732 XtSetArg(args[0], XtNleftBitmap, None);
6734 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6739 void SaveOnExitProc(w, event, prms, nprms)
6747 saveSettingsOnExit = !saveSettingsOnExit;
6749 if (saveSettingsOnExit) {
6750 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6752 XtSetArg(args[0], XtNleftBitmap, None);
6754 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6758 void SaveSettingsProc(w, event, prms, nprms)
6764 SaveSettings(settingsFileName);
6767 void InfoProc(w, event, prms, nprms)
6774 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6779 void ManProc(w, event, prms, nprms)
6787 if (nprms && *nprms > 0)
6791 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6795 void HintProc(w, event, prms, nprms)
6804 void BookProc(w, event, prms, nprms)
6813 void AboutProc(w, event, prms, nprms)
6821 char *zippy = " (with Zippy code)";
6825 snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
6826 programVersion, zippy,
6827 "Copyright 1991 Digital Equipment Corporation",
6828 "Enhancements Copyright 1992-2009 Free Software Foundation",
6829 "Enhancements Copyright 2005 Alessandro Scotti",
6830 PACKAGE, " is free software and carries NO WARRANTY;",
6831 "see the file COPYING for more information.");
6832 ErrorPopUp(_("About XBoard"), buf, FALSE);
6835 void DebugProc(w, event, prms, nprms)
6841 appData.debugMode = !appData.debugMode;
6844 void AboutGameProc(w, event, prms, nprms)
6853 void NothingProc(w, event, prms, nprms)
6862 void Iconify(w, event, prms, nprms)
6871 XtSetArg(args[0], XtNiconic, True);
6872 XtSetValues(shellWidget, args, 1);
6875 void DisplayMessage(message, extMessage)
6876 char *message, *extMessage;
6878 /* display a message in the message widget */
6887 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6892 message = extMessage;
6896 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6898 /* need to test if messageWidget already exists, since this function
6899 can also be called during the startup, if for example a Xresource
6900 is not set up correctly */
6903 XtSetArg(arg, XtNlabel, message);
6904 XtSetValues(messageWidget, &arg, 1);
6910 void DisplayTitle(text)
6915 char title[MSG_SIZ];
6918 if (text == NULL) text = "";
6920 if (appData.titleInWindow) {
6922 XtSetArg(args[i], XtNlabel, text); i++;
6923 XtSetValues(titleWidget, args, i);
6926 if (*text != NULLCHAR) {
6927 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6928 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6929 } else if (appData.icsActive) {
6930 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6931 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6932 } else if (appData.cmailGameName[0] != NULLCHAR) {
6933 snprintf(icon, sizeof(icon), "%s", "CMail");
6934 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6936 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6937 } else if (gameInfo.variant == VariantGothic) {
6938 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6939 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6942 } else if (gameInfo.variant == VariantFalcon) {
6943 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6944 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6946 } else if (appData.noChessProgram) {
6947 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6948 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6950 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6951 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6954 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6955 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6956 XtSetValues(shellWidget, args, i);
6957 XSync(xDisplay, False);
6962 DisplayError(message, error)
6969 if (appData.debugMode || appData.matchMode) {
6970 fprintf(stderr, "%s: %s\n", programName, message);
6973 if (appData.debugMode || appData.matchMode) {
6974 fprintf(stderr, "%s: %s: %s\n",
6975 programName, message, strerror(error));
6977 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6980 ErrorPopUp(_("Error"), message, FALSE);
6984 void DisplayMoveError(message)
6989 DrawPosition(FALSE, NULL);
6990 if (appData.debugMode || appData.matchMode) {
6991 fprintf(stderr, "%s: %s\n", programName, message);
6993 if (appData.popupMoveErrors) {
6994 ErrorPopUp(_("Error"), message, FALSE);
6996 DisplayMessage(message, "");
7001 void DisplayFatalError(message, error, status)
7007 errorExitStatus = status;
7009 fprintf(stderr, "%s: %s\n", programName, message);
7011 fprintf(stderr, "%s: %s: %s\n",
7012 programName, message, strerror(error));
7013 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7016 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
7017 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
7023 void DisplayInformation(message)
7027 ErrorPopUp(_("Information"), message, TRUE);
7030 void DisplayNote(message)
7034 ErrorPopUp(_("Note"), message, FALSE);
7038 NullXErrorCheck(dpy, error_event)
7040 XErrorEvent *error_event;
7045 void DisplayIcsInteractionTitle(message)
7048 if (oldICSInteractionTitle == NULL) {
7049 /* Magic to find the old window title, adapted from vim */
7050 char *wina = getenv("WINDOWID");
7052 Window win = (Window) atoi(wina);
7053 Window root, parent, *children;
7054 unsigned int nchildren;
7055 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
7057 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
7058 if (!XQueryTree(xDisplay, win, &root, &parent,
7059 &children, &nchildren)) break;
7060 if (children) XFree((void *)children);
7061 if (parent == root || parent == 0) break;
7064 XSetErrorHandler(oldHandler);
7066 if (oldICSInteractionTitle == NULL) {
7067 oldICSInteractionTitle = "xterm";
7070 printf("\033]0;%s\007", message);
7074 char pendingReplyPrefix[MSG_SIZ];
7075 ProcRef pendingReplyPR;
7077 void AskQuestionProc(w, event, prms, nprms)
7084 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7088 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7091 void AskQuestionPopDown()
7093 if (!askQuestionUp) return;
7094 XtPopdown(askQuestionShell);
7095 XtDestroyWidget(askQuestionShell);
7096 askQuestionUp = False;
7099 void AskQuestionReplyAction(w, event, prms, nprms)
7109 reply = XawDialogGetValueString(w = XtParent(w));
7110 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
7111 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
7112 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
7113 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
7114 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7115 AskQuestionPopDown();
7117 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7120 void AskQuestionCallback(w, client_data, call_data)
7122 XtPointer client_data, call_data;
7127 XtSetArg(args[0], XtNlabel, &name);
7128 XtGetValues(w, args, 1);
7130 if (strcmp(name, _("cancel")) == 0) {
7131 AskQuestionPopDown();
7133 AskQuestionReplyAction(w, NULL, NULL, NULL);
7137 void AskQuestion(title, question, replyPrefix, pr)
7138 char *title, *question, *replyPrefix;
7142 Widget popup, layout, dialog, edit;
7148 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7149 pendingReplyPR = pr;
7152 XtSetArg(args[i], XtNresizable, True); i++;
7153 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7154 askQuestionShell = popup =
7155 XtCreatePopupShell(title, transientShellWidgetClass,
7156 shellWidget, args, i);
7159 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7160 layoutArgs, XtNumber(layoutArgs));
7163 XtSetArg(args[i], XtNlabel, question); i++;
7164 XtSetArg(args[i], XtNvalue, ""); i++;
7165 XtSetArg(args[i], XtNborderWidth, 0); i++;
7166 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7169 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7170 (XtPointer) dialog);
7171 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7172 (XtPointer) dialog);
7174 XtRealizeWidget(popup);
7175 CatchDeleteWindow(popup, "AskQuestionPopDown");
7177 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7178 &x, &y, &win_x, &win_y, &mask);
7180 XtSetArg(args[0], XtNx, x - 10);
7181 XtSetArg(args[1], XtNy, y - 30);
7182 XtSetValues(popup, args, 2);
7184 XtPopup(popup, XtGrabExclusive);
7185 askQuestionUp = True;
7187 edit = XtNameToWidget(dialog, "*value");
7188 XtSetKeyboardFocus(popup, edit);
7196 if (*name == NULLCHAR) {
7198 } else if (strcmp(name, "$") == 0) {
7199 putc(BELLCHAR, stderr);
7202 char *prefix = "", *sep = "";
7203 if(appData.soundProgram[0] == NULLCHAR) return;
7204 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7205 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7213 PlaySound(appData.soundMove);
7219 PlaySound(appData.soundIcsWin);
7225 PlaySound(appData.soundIcsLoss);
7231 PlaySound(appData.soundIcsDraw);
7235 PlayIcsUnfinishedSound()
7237 PlaySound(appData.soundIcsUnfinished);
7243 PlaySound(appData.soundIcsAlarm);
7249 PlaySound(appData.soundTell);
7255 system("stty echo");
7262 system("stty -echo");
7267 RunCommand(char *buf)
7273 Colorize(cc, continuation)
7278 int count, outCount, error;
7280 if (textColors[(int)cc].bg > 0) {
7281 if (textColors[(int)cc].fg > 0) {
7282 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7283 textColors[(int)cc].fg, textColors[(int)cc].bg);
7285 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7286 textColors[(int)cc].bg);
7289 if (textColors[(int)cc].fg > 0) {
7290 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7291 textColors[(int)cc].fg);
7293 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7296 count = strlen(buf);
7297 outCount = OutputToProcess(NoProc, buf, count, &error);
7298 if (outCount < count) {
7299 DisplayFatalError(_("Error writing to display"), error, 1);
7302 if (continuation) return;
7305 PlaySound(appData.soundShout);
7308 PlaySound(appData.soundSShout);
7311 PlaySound(appData.soundChannel1);
7314 PlaySound(appData.soundChannel);
7317 PlaySound(appData.soundKibitz);
7320 PlaySound(appData.soundTell);
7322 case ColorChallenge:
7323 PlaySound(appData.soundChallenge);
7326 PlaySound(appData.soundRequest);
7329 PlaySound(appData.soundSeek);
7340 return getpwuid(getuid())->pw_name;
7344 ExpandPathName(path)
7347 static char static_buf[4*MSG_SIZ];
7348 char *d, *s, buf[4*MSG_SIZ];
7354 while (*s && isspace(*s))
7363 if (*(s+1) == '/') {
7364 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7368 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7369 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7370 pwd = getpwnam(buf);
7373 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7377 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7378 strcat(d, strchr(s+1, '/'));
7382 safeStrCpy(d, s, 4*MSG_SIZ );
7389 static char host_name[MSG_SIZ];
7391 #if HAVE_GETHOSTNAME
7392 gethostname(host_name, MSG_SIZ);
7394 #else /* not HAVE_GETHOSTNAME */
7395 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7396 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7398 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7400 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7401 #endif /* not HAVE_GETHOSTNAME */
7404 XtIntervalId delayedEventTimerXID = 0;
7405 DelayedEventCallback delayedEventCallback = 0;
7410 delayedEventTimerXID = 0;
7411 delayedEventCallback();
7415 ScheduleDelayedEvent(cb, millisec)
7416 DelayedEventCallback cb; long millisec;
7418 if(delayedEventTimerXID && delayedEventCallback == cb)
7419 // [HGM] alive: replace, rather than add or flush identical event
7420 XtRemoveTimeOut(delayedEventTimerXID);
7421 delayedEventCallback = cb;
7422 delayedEventTimerXID =
7423 XtAppAddTimeOut(appContext, millisec,
7424 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7427 DelayedEventCallback
7430 if (delayedEventTimerXID) {
7431 return delayedEventCallback;
7438 CancelDelayedEvent()
7440 if (delayedEventTimerXID) {
7441 XtRemoveTimeOut(delayedEventTimerXID);
7442 delayedEventTimerXID = 0;
7446 XtIntervalId loadGameTimerXID = 0;
7448 int LoadGameTimerRunning()
7450 return loadGameTimerXID != 0;
7453 int StopLoadGameTimer()
7455 if (loadGameTimerXID != 0) {
7456 XtRemoveTimeOut(loadGameTimerXID);
7457 loadGameTimerXID = 0;
7465 LoadGameTimerCallback(arg, id)
7469 loadGameTimerXID = 0;
7474 StartLoadGameTimer(millisec)
7478 XtAppAddTimeOut(appContext, millisec,
7479 (XtTimerCallbackProc) LoadGameTimerCallback,
7483 XtIntervalId analysisClockXID = 0;
7486 AnalysisClockCallback(arg, id)
7490 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7491 || appData.icsEngineAnalyze) { // [DM]
7492 AnalysisPeriodicEvent(0);
7493 StartAnalysisClock();
7498 StartAnalysisClock()
7501 XtAppAddTimeOut(appContext, 2000,
7502 (XtTimerCallbackProc) AnalysisClockCallback,
7506 XtIntervalId clockTimerXID = 0;
7508 int ClockTimerRunning()
7510 return clockTimerXID != 0;
7513 int StopClockTimer()
7515 if (clockTimerXID != 0) {
7516 XtRemoveTimeOut(clockTimerXID);
7525 ClockTimerCallback(arg, id)
7534 StartClockTimer(millisec)
7538 XtAppAddTimeOut(appContext, millisec,
7539 (XtTimerCallbackProc) ClockTimerCallback,
7544 DisplayTimerLabel(w, color, timer, highlight)
7553 /* check for low time warning */
7554 Pixel foregroundOrWarningColor = timerForegroundPixel;
7557 appData.lowTimeWarning &&
7558 (timer / 1000) < appData.icsAlarmTime)
7559 foregroundOrWarningColor = lowTimeWarningColor;
7561 if (appData.clockMode) {
7562 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7563 XtSetArg(args[0], XtNlabel, buf);
7565 snprintf(buf, MSG_SIZ, "%s ", color);
7566 XtSetArg(args[0], XtNlabel, buf);
7571 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7572 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7574 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7575 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7578 XtSetValues(w, args, 3);
7582 DisplayWhiteClock(timeRemaining, highlight)
7588 if(appData.noGUI) return;
7589 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7590 if (highlight && iconPixmap == bIconPixmap) {
7591 iconPixmap = wIconPixmap;
7592 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7593 XtSetValues(shellWidget, args, 1);
7598 DisplayBlackClock(timeRemaining, highlight)
7604 if(appData.noGUI) return;
7605 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7606 if (highlight && iconPixmap == wIconPixmap) {
7607 iconPixmap = bIconPixmap;
7608 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7609 XtSetValues(shellWidget, args, 1);
7627 int StartChildProcess(cmdLine, dir, pr)
7634 int to_prog[2], from_prog[2];
7638 if (appData.debugMode) {
7639 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7642 /* We do NOT feed the cmdLine to the shell; we just
7643 parse it into blank-separated arguments in the
7644 most simple-minded way possible.
7647 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7650 while(*p == ' ') p++;
7652 if(*p == '"' || *p == '\'')
7653 p = strchr(++argv[i-1], *p);
7654 else p = strchr(p, ' ');
7655 if (p == NULL) break;
7660 SetUpChildIO(to_prog, from_prog);
7662 if ((pid = fork()) == 0) {
7664 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7665 close(to_prog[1]); // first close the unused pipe ends
7666 close(from_prog[0]);
7667 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7668 dup2(from_prog[1], 1);
7669 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7670 close(from_prog[1]); // and closing again loses one of the pipes!
7671 if(fileno(stderr) >= 2) // better safe than sorry...
7672 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7674 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7679 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7681 execvp(argv[0], argv);
7683 /* If we get here, exec failed */
7688 /* Parent process */
7690 close(from_prog[1]);
7692 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7695 cp->fdFrom = from_prog[0];
7696 cp->fdTo = to_prog[1];
7701 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7702 static RETSIGTYPE AlarmCallBack(int n)
7708 DestroyChildProcess(pr, signalType)
7712 ChildProc *cp = (ChildProc *) pr;
7714 if (cp->kind != CPReal) return;
7716 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7717 signal(SIGALRM, AlarmCallBack);
7719 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7720 kill(cp->pid, SIGKILL); // kill it forcefully
7721 wait((int *) 0); // and wait again
7725 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7727 /* Process is exiting either because of the kill or because of
7728 a quit command sent by the backend; either way, wait for it to die.
7737 InterruptChildProcess(pr)
7740 ChildProc *cp = (ChildProc *) pr;
7742 if (cp->kind != CPReal) return;
7743 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7746 int OpenTelnet(host, port, pr)
7751 char cmdLine[MSG_SIZ];
7753 if (port[0] == NULLCHAR) {
7754 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7756 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7758 return StartChildProcess(cmdLine, "", pr);
7761 int OpenTCP(host, port, pr)
7767 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7768 #else /* !OMIT_SOCKETS */
7769 struct addrinfo hints;
7770 struct addrinfo *ais, *ai;
7775 memset(&hints, 0, sizeof(hints));
7776 hints.ai_family = AF_UNSPEC;
7777 hints.ai_socktype = SOCK_STREAM;
7779 error = getaddrinfo(host, port, &hints, &ais);
7781 /* a getaddrinfo error is not an errno, so can't return it */
7782 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7783 host, port, gai_strerror(error));
7787 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7788 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7792 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7805 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7811 #endif /* !OMIT_SOCKETS */
7816 int OpenCommPort(name, pr)
7823 fd = open(name, 2, 0);
7824 if (fd < 0) return errno;
7826 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7836 int OpenLoopback(pr)
7842 SetUpChildIO(to, from);
7844 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7847 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7854 int OpenRcmd(host, user, cmd, pr)
7855 char *host, *user, *cmd;
7858 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7862 #define INPUT_SOURCE_BUF_SIZE 8192
7871 char buf[INPUT_SOURCE_BUF_SIZE];
7876 DoInputCallback(closure, source, xid)
7881 InputSource *is = (InputSource *) closure;
7886 if (is->lineByLine) {
7887 count = read(is->fd, is->unused,
7888 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7890 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7893 is->unused += count;
7895 while (p < is->unused) {
7896 q = memchr(p, '\n', is->unused - p);
7897 if (q == NULL) break;
7899 (is->func)(is, is->closure, p, q - p, 0);
7903 while (p < is->unused) {
7908 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7913 (is->func)(is, is->closure, is->buf, count, error);
7917 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7924 ChildProc *cp = (ChildProc *) pr;
7926 is = (InputSource *) calloc(1, sizeof(InputSource));
7927 is->lineByLine = lineByLine;
7931 is->fd = fileno(stdin);
7933 is->kind = cp->kind;
7934 is->fd = cp->fdFrom;
7937 is->unused = is->buf;
7940 is->xid = XtAppAddInput(appContext, is->fd,
7941 (XtPointer) (XtInputReadMask),
7942 (XtInputCallbackProc) DoInputCallback,
7944 is->closure = closure;
7945 return (InputSourceRef) is;
7949 RemoveInputSource(isr)
7952 InputSource *is = (InputSource *) isr;
7954 if (is->xid == 0) return;
7955 XtRemoveInput(is->xid);
7959 int OutputToProcess(pr, message, count, outError)
7965 static int line = 0;
7966 ChildProc *cp = (ChildProc *) pr;
7971 if (appData.noJoin || !appData.useInternalWrap)
7972 outCount = fwrite(message, 1, count, stdout);
7975 int width = get_term_width();
7976 int len = wrap(NULL, message, count, width, &line);
7977 char *msg = malloc(len);
7981 outCount = fwrite(message, 1, count, stdout);
7984 dbgchk = wrap(msg, message, count, width, &line);
7985 if (dbgchk != len && appData.debugMode)
7986 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7987 outCount = fwrite(msg, 1, dbgchk, stdout);
7993 outCount = write(cp->fdTo, message, count);
8003 /* Output message to process, with "ms" milliseconds of delay
8004 between each character. This is needed when sending the logon
8005 script to ICC, which for some reason doesn't like the
8006 instantaneous send. */
8007 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
8014 ChildProc *cp = (ChildProc *) pr;
8019 r = write(cp->fdTo, message++, 1);
8032 /**** Animation code by Hugh Fisher, DCS, ANU.
8034 Known problem: if a window overlapping the board is
8035 moved away while a piece is being animated underneath,
8036 the newly exposed area won't be updated properly.
8037 I can live with this.
8039 Known problem: if you look carefully at the animation
8040 of pieces in mono mode, they are being drawn as solid
8041 shapes without interior detail while moving. Fixing
8042 this would be a major complication for minimal return.
8045 /* Masks for XPM pieces. Black and white pieces can have
8046 different shapes, but in the interest of retaining my
8047 sanity pieces must have the same outline on both light
8048 and dark squares, and all pieces must use the same
8049 background square colors/images. */
8051 static int xpmDone = 0;
8054 CreateAnimMasks (pieceDepth)
8061 unsigned long plane;
8064 /* Need a bitmap just to get a GC with right depth */
8065 buf = XCreatePixmap(xDisplay, xBoardWindow,
8067 values.foreground = 1;
8068 values.background = 0;
8069 /* Don't use XtGetGC, not read only */
8070 maskGC = XCreateGC(xDisplay, buf,
8071 GCForeground | GCBackground, &values);
8072 XFreePixmap(xDisplay, buf);
8074 buf = XCreatePixmap(xDisplay, xBoardWindow,
8075 squareSize, squareSize, pieceDepth);
8076 values.foreground = XBlackPixel(xDisplay, xScreen);
8077 values.background = XWhitePixel(xDisplay, xScreen);
8078 bufGC = XCreateGC(xDisplay, buf,
8079 GCForeground | GCBackground, &values);
8081 for (piece = WhitePawn; piece <= BlackKing; piece++) {
8082 /* Begin with empty mask */
8083 if(!xpmDone) // [HGM] pieces: keep using existing
8084 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8085 squareSize, squareSize, 1);
8086 XSetFunction(xDisplay, maskGC, GXclear);
8087 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8088 0, 0, squareSize, squareSize);
8090 /* Take a copy of the piece */
8095 XSetFunction(xDisplay, bufGC, GXcopy);
8096 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8098 0, 0, squareSize, squareSize, 0, 0);
8100 /* XOR the background (light) over the piece */
8101 XSetFunction(xDisplay, bufGC, GXxor);
8103 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8104 0, 0, squareSize, squareSize, 0, 0);
8106 XSetForeground(xDisplay, bufGC, lightSquareColor);
8107 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8110 /* We now have an inverted piece image with the background
8111 erased. Construct mask by just selecting all the non-zero
8112 pixels - no need to reconstruct the original image. */
8113 XSetFunction(xDisplay, maskGC, GXor);
8115 /* Might be quicker to download an XImage and create bitmap
8116 data from it rather than this N copies per piece, but it
8117 only takes a fraction of a second and there is a much
8118 longer delay for loading the pieces. */
8119 for (n = 0; n < pieceDepth; n ++) {
8120 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8121 0, 0, squareSize, squareSize,
8127 XFreePixmap(xDisplay, buf);
8128 XFreeGC(xDisplay, bufGC);
8129 XFreeGC(xDisplay, maskGC);
8133 InitAnimState (anim, info)
8135 XWindowAttributes * info;
8140 /* Each buffer is square size, same depth as window */
8141 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8142 squareSize, squareSize, info->depth);
8143 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8144 squareSize, squareSize, info->depth);
8146 /* Create a plain GC for blitting */
8147 mask = GCForeground | GCBackground | GCFunction |
8148 GCPlaneMask | GCGraphicsExposures;
8149 values.foreground = XBlackPixel(xDisplay, xScreen);
8150 values.background = XWhitePixel(xDisplay, xScreen);
8151 values.function = GXcopy;
8152 values.plane_mask = AllPlanes;
8153 values.graphics_exposures = False;
8154 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8156 /* Piece will be copied from an existing context at
8157 the start of each new animation/drag. */
8158 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8160 /* Outline will be a read-only copy of an existing */
8161 anim->outlineGC = None;
8167 XWindowAttributes info;
8169 if (xpmDone && gameInfo.variant == oldVariant) return;
8170 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
8171 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8173 InitAnimState(&game, &info);
8174 InitAnimState(&player, &info);
8176 /* For XPM pieces, we need bitmaps to use as masks. */
8178 CreateAnimMasks(info.depth), xpmDone = 1;
8183 static Boolean frameWaiting;
8185 static RETSIGTYPE FrameAlarm (sig)
8188 frameWaiting = False;
8189 /* In case System-V style signals. Needed?? */
8190 signal(SIGALRM, FrameAlarm);
8197 struct itimerval delay;
8199 XSync(xDisplay, False);
8202 frameWaiting = True;
8203 signal(SIGALRM, FrameAlarm);
8204 delay.it_interval.tv_sec =
8205 delay.it_value.tv_sec = time / 1000;
8206 delay.it_interval.tv_usec =
8207 delay.it_value.tv_usec = (time % 1000) * 1000;
8208 setitimer(ITIMER_REAL, &delay, NULL);
8209 while (frameWaiting) pause();
8210 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8211 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8212 setitimer(ITIMER_REAL, &delay, NULL);
8222 XSync(xDisplay, False);
8224 usleep(time * 1000);
8235 /* Convert board position to corner of screen rect and color */
8238 ScreenSquare(column, row, pt, color)
8239 int column; int row; XPoint * pt; int * color;
8242 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8243 pt->y = lineGap + row * (squareSize + lineGap);
8245 pt->x = lineGap + column * (squareSize + lineGap);
8246 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8248 *color = SquareColor(row, column);
8251 /* Convert window coords to square */
8254 BoardSquare(x, y, column, row)
8255 int x; int y; int * column; int * row;
8257 *column = EventToSquare(x, BOARD_WIDTH);
8258 if (flipView && *column >= 0)
8259 *column = BOARD_WIDTH - 1 - *column;
8260 *row = EventToSquare(y, BOARD_HEIGHT);
8261 if (!flipView && *row >= 0)
8262 *row = BOARD_HEIGHT - 1 - *row;
8267 #undef Max /* just in case */
8269 #define Max(a, b) ((a) > (b) ? (a) : (b))
8270 #define Min(a, b) ((a) < (b) ? (a) : (b))
8273 SetRect(rect, x, y, width, height)
8274 XRectangle * rect; int x; int y; int width; int height;
8278 rect->width = width;
8279 rect->height = height;
8282 /* Test if two frames overlap. If they do, return
8283 intersection rect within old and location of
8284 that rect within new. */
8287 Intersect(old, new, size, area, pt)
8288 XPoint * old; XPoint * new;
8289 int size; XRectangle * area; XPoint * pt;
8291 if (old->x > new->x + size || new->x > old->x + size ||
8292 old->y > new->y + size || new->y > old->y + size) {
8295 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8296 size - abs(old->x - new->x), size - abs(old->y - new->y));
8297 pt->x = Max(old->x - new->x, 0);
8298 pt->y = Max(old->y - new->y, 0);
8303 /* For two overlapping frames, return the rect(s)
8304 in the old that do not intersect with the new. */
8307 CalcUpdateRects(old, new, size, update, nUpdates)
8308 XPoint * old; XPoint * new; int size;
8309 XRectangle update[]; int * nUpdates;
8313 /* If old = new (shouldn't happen) then nothing to draw */
8314 if (old->x == new->x && old->y == new->y) {
8318 /* Work out what bits overlap. Since we know the rects
8319 are the same size we don't need a full intersect calc. */
8321 /* Top or bottom edge? */
8322 if (new->y > old->y) {
8323 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8325 } else if (old->y > new->y) {
8326 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8327 size, old->y - new->y);
8330 /* Left or right edge - don't overlap any update calculated above. */
8331 if (new->x > old->x) {
8332 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8333 new->x - old->x, size - abs(new->y - old->y));
8335 } else if (old->x > new->x) {
8336 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8337 old->x - new->x, size - abs(new->y - old->y));
8344 /* Generate a series of frame coords from start->mid->finish.
8345 The movement rate doubles until the half way point is
8346 reached, then halves back down to the final destination,
8347 which gives a nice slow in/out effect. The algorithmn
8348 may seem to generate too many intermediates for short
8349 moves, but remember that the purpose is to attract the
8350 viewers attention to the piece about to be moved and
8351 then to where it ends up. Too few frames would be less
8355 Tween(start, mid, finish, factor, frames, nFrames)
8356 XPoint * start; XPoint * mid;
8357 XPoint * finish; int factor;
8358 XPoint frames[]; int * nFrames;
8360 int fraction, n, count;
8364 /* Slow in, stepping 1/16th, then 1/8th, ... */
8366 for (n = 0; n < factor; n++)
8368 for (n = 0; n < factor; n++) {
8369 frames[count].x = start->x + (mid->x - start->x) / fraction;
8370 frames[count].y = start->y + (mid->y - start->y) / fraction;
8372 fraction = fraction / 2;
8376 frames[count] = *mid;
8379 /* Slow out, stepping 1/2, then 1/4, ... */
8381 for (n = 0; n < factor; n++) {
8382 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8383 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8385 fraction = fraction * 2;
8390 /* Draw a piece on the screen without disturbing what's there */
8393 SelectGCMask(piece, clip, outline, mask)
8394 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8398 /* Bitmap for piece being moved. */
8399 if (appData.monoMode) {
8400 *mask = *pieceToSolid(piece);
8401 } else if (useImages) {
8403 *mask = xpmMask[piece];
8405 *mask = ximMaskPm[piece];
8408 *mask = *pieceToSolid(piece);
8411 /* GC for piece being moved. Square color doesn't matter, but
8412 since it gets modified we make a copy of the original. */
8414 if (appData.monoMode)
8419 if (appData.monoMode)
8424 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8426 /* Outline only used in mono mode and is not modified */
8428 *outline = bwPieceGC;
8430 *outline = wbPieceGC;
8434 OverlayPiece(piece, clip, outline, dest)
8435 ChessSquare piece; GC clip; GC outline; Drawable dest;
8440 /* Draw solid rectangle which will be clipped to shape of piece */
8441 XFillRectangle(xDisplay, dest, clip,
8442 0, 0, squareSize, squareSize);
8443 if (appData.monoMode)
8444 /* Also draw outline in contrasting color for black
8445 on black / white on white cases */
8446 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8447 0, 0, squareSize, squareSize, 0, 0, 1);
8449 /* Copy the piece */
8454 if(appData.upsideDown && flipView) kind ^= 2;
8455 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8457 0, 0, squareSize, squareSize,
8462 /* Animate the movement of a single piece */
8465 BeginAnimation(anim, piece, startColor, start)
8473 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8474 /* The old buffer is initialised with the start square (empty) */
8475 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8476 anim->prevFrame = *start;
8478 /* The piece will be drawn using its own bitmap as a matte */
8479 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8480 XSetClipMask(xDisplay, anim->pieceGC, mask);
8484 AnimationFrame(anim, frame, piece)
8489 XRectangle updates[4];
8494 /* Save what we are about to draw into the new buffer */
8495 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8496 frame->x, frame->y, squareSize, squareSize,
8499 /* Erase bits of the previous frame */
8500 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8501 /* Where the new frame overlapped the previous,
8502 the contents in newBuf are wrong. */
8503 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8504 overlap.x, overlap.y,
8505 overlap.width, overlap.height,
8507 /* Repaint the areas in the old that don't overlap new */
8508 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8509 for (i = 0; i < count; i++)
8510 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8511 updates[i].x - anim->prevFrame.x,
8512 updates[i].y - anim->prevFrame.y,
8513 updates[i].width, updates[i].height,
8514 updates[i].x, updates[i].y);
8516 /* Easy when no overlap */
8517 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8518 0, 0, squareSize, squareSize,
8519 anim->prevFrame.x, anim->prevFrame.y);
8522 /* Save this frame for next time round */
8523 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8524 0, 0, squareSize, squareSize,
8526 anim->prevFrame = *frame;
8528 /* Draw piece over original screen contents, not current,
8529 and copy entire rect. Wipes out overlapping piece images. */
8530 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8531 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8532 0, 0, squareSize, squareSize,
8533 frame->x, frame->y);
8537 EndAnimation (anim, finish)
8541 XRectangle updates[4];
8546 /* The main code will redraw the final square, so we
8547 only need to erase the bits that don't overlap. */
8548 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8549 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8550 for (i = 0; i < count; i++)
8551 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8552 updates[i].x - anim->prevFrame.x,
8553 updates[i].y - anim->prevFrame.y,
8554 updates[i].width, updates[i].height,
8555 updates[i].x, updates[i].y);
8557 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8558 0, 0, squareSize, squareSize,
8559 anim->prevFrame.x, anim->prevFrame.y);
8564 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8566 ChessSquare piece; int startColor;
8567 XPoint * start; XPoint * finish;
8568 XPoint frames[]; int nFrames;
8572 BeginAnimation(anim, piece, startColor, start);
8573 for (n = 0; n < nFrames; n++) {
8574 AnimationFrame(anim, &(frames[n]), piece);
8575 FrameDelay(appData.animSpeed);
8577 EndAnimation(anim, finish);
8581 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8584 ChessSquare piece = board[fromY][toY];
8585 board[fromY][toY] = EmptySquare;
8586 DrawPosition(FALSE, board);
8588 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8589 y = lineGap + toY * (squareSize + lineGap);
8591 x = lineGap + toX * (squareSize + lineGap);
8592 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8594 for(i=1; i<4*kFactor; i++) {
8595 int r = squareSize * 9 * i/(20*kFactor - 5);
8596 XFillArc(xDisplay, xBoardWindow, highlineGC,
8597 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8598 FrameDelay(appData.animSpeed);
8600 board[fromY][toY] = piece;
8603 /* Main control logic for deciding what to animate and how */
8606 AnimateMove(board, fromX, fromY, toX, toY)
8615 XPoint start, finish, mid;
8616 XPoint frames[kFactor * 2 + 1];
8617 int nFrames, startColor, endColor;
8619 /* Are we animating? */
8620 if (!appData.animate || appData.blindfold)
8623 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8624 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8625 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8627 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8628 piece = board[fromY][fromX];
8629 if (piece >= EmptySquare) return;
8634 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8637 if (appData.debugMode) {
8638 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8639 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8640 piece, fromX, fromY, toX, toY); }
8642 ScreenSquare(fromX, fromY, &start, &startColor);
8643 ScreenSquare(toX, toY, &finish, &endColor);
8646 /* Knight: make straight movement then diagonal */
8647 if (abs(toY - fromY) < abs(toX - fromX)) {
8648 mid.x = start.x + (finish.x - start.x) / 2;
8652 mid.y = start.y + (finish.y - start.y) / 2;
8655 mid.x = start.x + (finish.x - start.x) / 2;
8656 mid.y = start.y + (finish.y - start.y) / 2;
8659 /* Don't use as many frames for very short moves */
8660 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8661 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8663 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8664 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8665 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8667 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8668 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8671 /* Be sure end square is redrawn */
8672 damage[0][toY][toX] = True;
8676 DragPieceBegin(x, y, instantly)
8677 int x; int y; Boolean instantly;
8679 int boardX, boardY, color;
8682 /* Are we animating? */
8683 if (!appData.animateDragging || appData.blindfold)
8686 /* Figure out which square we start in and the
8687 mouse position relative to top left corner. */
8688 BoardSquare(x, y, &boardX, &boardY);
8689 player.startBoardX = boardX;
8690 player.startBoardY = boardY;
8691 ScreenSquare(boardX, boardY, &corner, &color);
8692 player.startSquare = corner;
8693 player.startColor = color;
8694 /* As soon as we start dragging, the piece will jump slightly to
8695 be centered over the mouse pointer. */
8696 player.mouseDelta.x = squareSize/2;
8697 player.mouseDelta.y = squareSize/2;
8698 /* Initialise animation */
8699 player.dragPiece = PieceForSquare(boardX, boardY);
8701 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8702 player.dragActive = True;
8703 BeginAnimation(&player, player.dragPiece, color, &corner);
8704 /* Mark this square as needing to be redrawn. Note that
8705 we don't remove the piece though, since logically (ie
8706 as seen by opponent) the move hasn't been made yet. */
8707 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8708 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8709 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8710 corner.x, corner.y, squareSize, squareSize,
8711 0, 0); // [HGM] zh: unstack in stead of grab
8712 if(gatingPiece != EmptySquare) {
8713 /* Kludge alert: When gating we want the introduced
8714 piece to appear on the from square. To generate an
8715 image of it, we draw it on the board, copy the image,
8716 and draw the original piece again. */
8717 ChessSquare piece = boards[currentMove][boardY][boardX];
8718 DrawSquare(boardY, boardX, gatingPiece, 0);
8719 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8720 corner.x, corner.y, squareSize, squareSize, 0, 0);
8721 DrawSquare(boardY, boardX, piece, 0);
8723 damage[0][boardY][boardX] = True;
8725 player.dragActive = False;
8730 ChangeDragPiece(ChessSquare piece)
8733 player.dragPiece = piece;
8734 /* The piece will be drawn using its own bitmap as a matte */
8735 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8736 XSetClipMask(xDisplay, player.pieceGC, mask);
8745 /* Are we animating? */
8746 if (!appData.animateDragging || appData.blindfold)
8750 if (! player.dragActive)
8752 /* Move piece, maintaining same relative position
8753 of mouse within square */
8754 corner.x = x - player.mouseDelta.x;
8755 corner.y = y - player.mouseDelta.y;
8756 AnimationFrame(&player, &corner, player.dragPiece);
8758 if (appData.highlightDragging) {
8760 BoardSquare(x, y, &boardX, &boardY);
8761 SetHighlights(fromX, fromY, boardX, boardY);
8770 int boardX, boardY, color;
8773 /* Are we animating? */
8774 if (!appData.animateDragging || appData.blindfold)
8778 if (! player.dragActive)
8780 /* Last frame in sequence is square piece is
8781 placed on, which may not match mouse exactly. */
8782 BoardSquare(x, y, &boardX, &boardY);
8783 ScreenSquare(boardX, boardY, &corner, &color);
8784 EndAnimation(&player, &corner);
8786 /* Be sure end square is redrawn */
8787 damage[0][boardY][boardX] = True;
8789 /* This prevents weird things happening with fast successive
8790 clicks which on my Sun at least can cause motion events
8791 without corresponding press/release. */
8792 player.dragActive = False;
8795 /* Handle expose event while piece being dragged */
8800 if (!player.dragActive || appData.blindfold)
8803 /* What we're doing: logically, the move hasn't been made yet,
8804 so the piece is still in it's original square. But visually
8805 it's being dragged around the board. So we erase the square
8806 that the piece is on and draw it at the last known drag point. */
8807 BlankSquare(player.startSquare.x, player.startSquare.y,
8808 player.startColor, EmptySquare, xBoardWindow, 1);
8809 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8810 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8813 #include <sys/ioctl.h>
8814 int get_term_width()
8816 int fd, default_width;
8819 default_width = 79; // this is FICS default anyway...
8821 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8823 if (!ioctl(fd, TIOCGSIZE, &win))
8824 default_width = win.ts_cols;
8825 #elif defined(TIOCGWINSZ)
8827 if (!ioctl(fd, TIOCGWINSZ, &win))
8828 default_width = win.ws_col;
8830 return default_width;
8836 static int old_width = 0;
8837 int new_width = get_term_width();
8839 if (old_width != new_width)
8840 ics_printf("set width %d\n", new_width);
8841 old_width = new_width;
8844 void NotifyFrontendLogin()
8849 /* [AS] Arrow highlighting support */
8851 static double A_WIDTH = 5; /* Width of arrow body */
8853 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8854 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8856 static double Sqr( double x )
8861 static int Round( double x )
8863 return (int) (x + 0.5);
8866 void SquareToPos(int rank, int file, int *x, int *y)
8869 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8870 *y = lineGap + rank * (squareSize + lineGap);
8872 *x = lineGap + file * (squareSize + lineGap);
8873 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8877 /* Draw an arrow between two points using current settings */
8878 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8881 double dx, dy, j, k, x, y;
8884 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8886 arrow[0].x = s_x + A_WIDTH + 0.5;
8889 arrow[1].x = s_x + A_WIDTH + 0.5;
8890 arrow[1].y = d_y - h;
8892 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8893 arrow[2].y = d_y - h;
8898 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8899 arrow[5].y = d_y - h;
8901 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8902 arrow[4].y = d_y - h;
8904 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8907 else if( d_y == s_y ) {
8908 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8911 arrow[0].y = s_y + A_WIDTH + 0.5;
8913 arrow[1].x = d_x - w;
8914 arrow[1].y = s_y + A_WIDTH + 0.5;
8916 arrow[2].x = d_x - w;
8917 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8922 arrow[5].x = d_x - w;
8923 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8925 arrow[4].x = d_x - w;
8926 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8929 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8932 /* [AS] Needed a lot of paper for this! :-) */
8933 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8934 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8936 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8938 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8943 arrow[0].x = Round(x - j);
8944 arrow[0].y = Round(y + j*dx);
8946 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8947 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8950 x = (double) d_x - k;
8951 y = (double) d_y - k*dy;
8954 x = (double) d_x + k;
8955 y = (double) d_y + k*dy;
8958 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8960 arrow[6].x = Round(x - j);
8961 arrow[6].y = Round(y + j*dx);
8963 arrow[2].x = Round(arrow[6].x + 2*j);
8964 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8966 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8967 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8972 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8973 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8976 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8977 // Polygon( hdc, arrow, 7 );
8980 /* [AS] Draw an arrow between two squares */
8981 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
8983 int s_x, s_y, d_x, d_y, hor, vert, i;
8985 if( s_col == d_col && s_row == d_row ) {
8989 /* Get source and destination points */
8990 SquareToPos( s_row, s_col, &s_x, &s_y);
8991 SquareToPos( d_row, d_col, &d_x, &d_y);
8994 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8996 else if( d_y < s_y ) {
8997 d_y += squareSize / 2 + squareSize / 4;
9000 d_y += squareSize / 2;
9004 d_x += squareSize / 2 - squareSize / 4;
9006 else if( d_x < s_x ) {
9007 d_x += squareSize / 2 + squareSize / 4;
9010 d_x += squareSize / 2;
9013 s_x += squareSize / 2;
9014 s_y += squareSize / 2;
9017 A_WIDTH = squareSize / 14.; //[HGM] make float
9019 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
9021 hor = 64*s_col + 32; vert = 64*s_row + 32;
9022 for(i=0; i<= 64; i++) {
9023 damage[0][vert+6>>6][hor+6>>6] = True;
9024 damage[0][vert-6>>6][hor+6>>6] = True;
9025 damage[0][vert+6>>6][hor-6>>6] = True;
9026 damage[0][vert-6>>6][hor-6>>6] = True;
9027 hor += d_col - s_col; vert += d_row - s_row;
9031 Boolean IsDrawArrowEnabled()
9033 return appData.highlightMoveWithArrow && squareSize >= 32;
9036 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
9038 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
9039 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
9042 void UpdateLogos(int displ)
9044 return; // no logos in XBoard yet