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(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7204 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7212 PlaySound(appData.soundMove);
7218 PlaySound(appData.soundIcsWin);
7224 PlaySound(appData.soundIcsLoss);
7230 PlaySound(appData.soundIcsDraw);
7234 PlayIcsUnfinishedSound()
7236 PlaySound(appData.soundIcsUnfinished);
7242 PlaySound(appData.soundIcsAlarm);
7248 PlaySound(appData.soundTell);
7254 system("stty echo");
7261 system("stty -echo");
7266 Colorize(cc, continuation)
7271 int count, outCount, error;
7273 if (textColors[(int)cc].bg > 0) {
7274 if (textColors[(int)cc].fg > 0) {
7275 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7276 textColors[(int)cc].fg, textColors[(int)cc].bg);
7278 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7279 textColors[(int)cc].bg);
7282 if (textColors[(int)cc].fg > 0) {
7283 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7284 textColors[(int)cc].fg);
7286 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7289 count = strlen(buf);
7290 outCount = OutputToProcess(NoProc, buf, count, &error);
7291 if (outCount < count) {
7292 DisplayFatalError(_("Error writing to display"), error, 1);
7295 if (continuation) return;
7298 PlaySound(appData.soundShout);
7301 PlaySound(appData.soundSShout);
7304 PlaySound(appData.soundChannel1);
7307 PlaySound(appData.soundChannel);
7310 PlaySound(appData.soundKibitz);
7313 PlaySound(appData.soundTell);
7315 case ColorChallenge:
7316 PlaySound(appData.soundChallenge);
7319 PlaySound(appData.soundRequest);
7322 PlaySound(appData.soundSeek);
7333 return getpwuid(getuid())->pw_name;
7337 ExpandPathName(path)
7340 static char static_buf[4*MSG_SIZ];
7341 char *d, *s, buf[4*MSG_SIZ];
7347 while (*s && isspace(*s))
7356 if (*(s+1) == '/') {
7357 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7361 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7362 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7363 pwd = getpwnam(buf);
7366 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7370 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7371 strcat(d, strchr(s+1, '/'));
7375 safeStrCpy(d, s, 4*MSG_SIZ );
7382 static char host_name[MSG_SIZ];
7384 #if HAVE_GETHOSTNAME
7385 gethostname(host_name, MSG_SIZ);
7387 #else /* not HAVE_GETHOSTNAME */
7388 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7389 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7391 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7393 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7394 #endif /* not HAVE_GETHOSTNAME */
7397 XtIntervalId delayedEventTimerXID = 0;
7398 DelayedEventCallback delayedEventCallback = 0;
7403 delayedEventTimerXID = 0;
7404 delayedEventCallback();
7408 ScheduleDelayedEvent(cb, millisec)
7409 DelayedEventCallback cb; long millisec;
7411 if(delayedEventTimerXID && delayedEventCallback == cb)
7412 // [HGM] alive: replace, rather than add or flush identical event
7413 XtRemoveTimeOut(delayedEventTimerXID);
7414 delayedEventCallback = cb;
7415 delayedEventTimerXID =
7416 XtAppAddTimeOut(appContext, millisec,
7417 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7420 DelayedEventCallback
7423 if (delayedEventTimerXID) {
7424 return delayedEventCallback;
7431 CancelDelayedEvent()
7433 if (delayedEventTimerXID) {
7434 XtRemoveTimeOut(delayedEventTimerXID);
7435 delayedEventTimerXID = 0;
7439 XtIntervalId loadGameTimerXID = 0;
7441 int LoadGameTimerRunning()
7443 return loadGameTimerXID != 0;
7446 int StopLoadGameTimer()
7448 if (loadGameTimerXID != 0) {
7449 XtRemoveTimeOut(loadGameTimerXID);
7450 loadGameTimerXID = 0;
7458 LoadGameTimerCallback(arg, id)
7462 loadGameTimerXID = 0;
7467 StartLoadGameTimer(millisec)
7471 XtAppAddTimeOut(appContext, millisec,
7472 (XtTimerCallbackProc) LoadGameTimerCallback,
7476 XtIntervalId analysisClockXID = 0;
7479 AnalysisClockCallback(arg, id)
7483 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7484 || appData.icsEngineAnalyze) { // [DM]
7485 AnalysisPeriodicEvent(0);
7486 StartAnalysisClock();
7491 StartAnalysisClock()
7494 XtAppAddTimeOut(appContext, 2000,
7495 (XtTimerCallbackProc) AnalysisClockCallback,
7499 XtIntervalId clockTimerXID = 0;
7501 int ClockTimerRunning()
7503 return clockTimerXID != 0;
7506 int StopClockTimer()
7508 if (clockTimerXID != 0) {
7509 XtRemoveTimeOut(clockTimerXID);
7518 ClockTimerCallback(arg, id)
7527 StartClockTimer(millisec)
7531 XtAppAddTimeOut(appContext, millisec,
7532 (XtTimerCallbackProc) ClockTimerCallback,
7537 DisplayTimerLabel(w, color, timer, highlight)
7546 /* check for low time warning */
7547 Pixel foregroundOrWarningColor = timerForegroundPixel;
7550 appData.lowTimeWarning &&
7551 (timer / 1000) < appData.icsAlarmTime)
7552 foregroundOrWarningColor = lowTimeWarningColor;
7554 if (appData.clockMode) {
7555 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7556 XtSetArg(args[0], XtNlabel, buf);
7558 snprintf(buf, MSG_SIZ, "%s ", color);
7559 XtSetArg(args[0], XtNlabel, buf);
7564 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7565 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7567 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7568 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7571 XtSetValues(w, args, 3);
7575 DisplayWhiteClock(timeRemaining, highlight)
7581 if(appData.noGUI) return;
7582 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7583 if (highlight && iconPixmap == bIconPixmap) {
7584 iconPixmap = wIconPixmap;
7585 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7586 XtSetValues(shellWidget, args, 1);
7591 DisplayBlackClock(timeRemaining, highlight)
7597 if(appData.noGUI) return;
7598 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7599 if (highlight && iconPixmap == wIconPixmap) {
7600 iconPixmap = bIconPixmap;
7601 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7602 XtSetValues(shellWidget, args, 1);
7620 int StartChildProcess(cmdLine, dir, pr)
7627 int to_prog[2], from_prog[2];
7631 if (appData.debugMode) {
7632 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7635 /* We do NOT feed the cmdLine to the shell; we just
7636 parse it into blank-separated arguments in the
7637 most simple-minded way possible.
7640 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7643 while(*p == ' ') p++;
7645 if(*p == '"' || *p == '\'')
7646 p = strchr(++argv[i-1], *p);
7647 else p = strchr(p, ' ');
7648 if (p == NULL) break;
7653 SetUpChildIO(to_prog, from_prog);
7655 if ((pid = fork()) == 0) {
7657 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7658 close(to_prog[1]); // first close the unused pipe ends
7659 close(from_prog[0]);
7660 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7661 dup2(from_prog[1], 1);
7662 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7663 close(from_prog[1]); // and closing again loses one of the pipes!
7664 if(fileno(stderr) >= 2) // better safe than sorry...
7665 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7667 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7672 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7674 execvp(argv[0], argv);
7676 /* If we get here, exec failed */
7681 /* Parent process */
7683 close(from_prog[1]);
7685 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7688 cp->fdFrom = from_prog[0];
7689 cp->fdTo = to_prog[1];
7694 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7695 static RETSIGTYPE AlarmCallBack(int n)
7701 DestroyChildProcess(pr, signalType)
7705 ChildProc *cp = (ChildProc *) pr;
7707 if (cp->kind != CPReal) return;
7709 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7710 signal(SIGALRM, AlarmCallBack);
7712 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7713 kill(cp->pid, SIGKILL); // kill it forcefully
7714 wait((int *) 0); // and wait again
7718 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7720 /* Process is exiting either because of the kill or because of
7721 a quit command sent by the backend; either way, wait for it to die.
7730 InterruptChildProcess(pr)
7733 ChildProc *cp = (ChildProc *) pr;
7735 if (cp->kind != CPReal) return;
7736 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7739 int OpenTelnet(host, port, pr)
7744 char cmdLine[MSG_SIZ];
7746 if (port[0] == NULLCHAR) {
7747 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7749 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7751 return StartChildProcess(cmdLine, "", pr);
7754 int OpenTCP(host, port, pr)
7760 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7761 #else /* !OMIT_SOCKETS */
7762 struct addrinfo hints;
7763 struct addrinfo *ais, *ai;
7768 memset(&hints, 0, sizeof(hints));
7769 hints.ai_family = AF_UNSPEC;
7770 hints.ai_socktype = SOCK_STREAM;
7772 error = getaddrinfo(host, port, &hints, &ais);
7774 /* a getaddrinfo error is not an errno, so can't return it */
7775 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7776 host, port, gai_strerror(error));
7780 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7781 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7785 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7798 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7804 #endif /* !OMIT_SOCKETS */
7809 int OpenCommPort(name, pr)
7816 fd = open(name, 2, 0);
7817 if (fd < 0) return errno;
7819 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7829 int OpenLoopback(pr)
7835 SetUpChildIO(to, from);
7837 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7840 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7847 int OpenRcmd(host, user, cmd, pr)
7848 char *host, *user, *cmd;
7851 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7855 #define INPUT_SOURCE_BUF_SIZE 8192
7864 char buf[INPUT_SOURCE_BUF_SIZE];
7869 DoInputCallback(closure, source, xid)
7874 InputSource *is = (InputSource *) closure;
7879 if (is->lineByLine) {
7880 count = read(is->fd, is->unused,
7881 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7883 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7886 is->unused += count;
7888 while (p < is->unused) {
7889 q = memchr(p, '\n', is->unused - p);
7890 if (q == NULL) break;
7892 (is->func)(is, is->closure, p, q - p, 0);
7896 while (p < is->unused) {
7901 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7906 (is->func)(is, is->closure, is->buf, count, error);
7910 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7917 ChildProc *cp = (ChildProc *) pr;
7919 is = (InputSource *) calloc(1, sizeof(InputSource));
7920 is->lineByLine = lineByLine;
7924 is->fd = fileno(stdin);
7926 is->kind = cp->kind;
7927 is->fd = cp->fdFrom;
7930 is->unused = is->buf;
7933 is->xid = XtAppAddInput(appContext, is->fd,
7934 (XtPointer) (XtInputReadMask),
7935 (XtInputCallbackProc) DoInputCallback,
7937 is->closure = closure;
7938 return (InputSourceRef) is;
7942 RemoveInputSource(isr)
7945 InputSource *is = (InputSource *) isr;
7947 if (is->xid == 0) return;
7948 XtRemoveInput(is->xid);
7952 int OutputToProcess(pr, message, count, outError)
7958 static int line = 0;
7959 ChildProc *cp = (ChildProc *) pr;
7964 if (appData.noJoin || !appData.useInternalWrap)
7965 outCount = fwrite(message, 1, count, stdout);
7968 int width = get_term_width();
7969 int len = wrap(NULL, message, count, width, &line);
7970 char *msg = malloc(len);
7974 outCount = fwrite(message, 1, count, stdout);
7977 dbgchk = wrap(msg, message, count, width, &line);
7978 if (dbgchk != len && appData.debugMode)
7979 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7980 outCount = fwrite(msg, 1, dbgchk, stdout);
7986 outCount = write(cp->fdTo, message, count);
7996 /* Output message to process, with "ms" milliseconds of delay
7997 between each character. This is needed when sending the logon
7998 script to ICC, which for some reason doesn't like the
7999 instantaneous send. */
8000 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
8007 ChildProc *cp = (ChildProc *) pr;
8012 r = write(cp->fdTo, message++, 1);
8025 /**** Animation code by Hugh Fisher, DCS, ANU.
8027 Known problem: if a window overlapping the board is
8028 moved away while a piece is being animated underneath,
8029 the newly exposed area won't be updated properly.
8030 I can live with this.
8032 Known problem: if you look carefully at the animation
8033 of pieces in mono mode, they are being drawn as solid
8034 shapes without interior detail while moving. Fixing
8035 this would be a major complication for minimal return.
8038 /* Masks for XPM pieces. Black and white pieces can have
8039 different shapes, but in the interest of retaining my
8040 sanity pieces must have the same outline on both light
8041 and dark squares, and all pieces must use the same
8042 background square colors/images. */
8044 static int xpmDone = 0;
8047 CreateAnimMasks (pieceDepth)
8054 unsigned long plane;
8057 /* Need a bitmap just to get a GC with right depth */
8058 buf = XCreatePixmap(xDisplay, xBoardWindow,
8060 values.foreground = 1;
8061 values.background = 0;
8062 /* Don't use XtGetGC, not read only */
8063 maskGC = XCreateGC(xDisplay, buf,
8064 GCForeground | GCBackground, &values);
8065 XFreePixmap(xDisplay, buf);
8067 buf = XCreatePixmap(xDisplay, xBoardWindow,
8068 squareSize, squareSize, pieceDepth);
8069 values.foreground = XBlackPixel(xDisplay, xScreen);
8070 values.background = XWhitePixel(xDisplay, xScreen);
8071 bufGC = XCreateGC(xDisplay, buf,
8072 GCForeground | GCBackground, &values);
8074 for (piece = WhitePawn; piece <= BlackKing; piece++) {
8075 /* Begin with empty mask */
8076 if(!xpmDone) // [HGM] pieces: keep using existing
8077 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8078 squareSize, squareSize, 1);
8079 XSetFunction(xDisplay, maskGC, GXclear);
8080 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8081 0, 0, squareSize, squareSize);
8083 /* Take a copy of the piece */
8088 XSetFunction(xDisplay, bufGC, GXcopy);
8089 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8091 0, 0, squareSize, squareSize, 0, 0);
8093 /* XOR the background (light) over the piece */
8094 XSetFunction(xDisplay, bufGC, GXxor);
8096 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8097 0, 0, squareSize, squareSize, 0, 0);
8099 XSetForeground(xDisplay, bufGC, lightSquareColor);
8100 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8103 /* We now have an inverted piece image with the background
8104 erased. Construct mask by just selecting all the non-zero
8105 pixels - no need to reconstruct the original image. */
8106 XSetFunction(xDisplay, maskGC, GXor);
8108 /* Might be quicker to download an XImage and create bitmap
8109 data from it rather than this N copies per piece, but it
8110 only takes a fraction of a second and there is a much
8111 longer delay for loading the pieces. */
8112 for (n = 0; n < pieceDepth; n ++) {
8113 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8114 0, 0, squareSize, squareSize,
8120 XFreePixmap(xDisplay, buf);
8121 XFreeGC(xDisplay, bufGC);
8122 XFreeGC(xDisplay, maskGC);
8126 InitAnimState (anim, info)
8128 XWindowAttributes * info;
8133 /* Each buffer is square size, same depth as window */
8134 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8135 squareSize, squareSize, info->depth);
8136 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8137 squareSize, squareSize, info->depth);
8139 /* Create a plain GC for blitting */
8140 mask = GCForeground | GCBackground | GCFunction |
8141 GCPlaneMask | GCGraphicsExposures;
8142 values.foreground = XBlackPixel(xDisplay, xScreen);
8143 values.background = XWhitePixel(xDisplay, xScreen);
8144 values.function = GXcopy;
8145 values.plane_mask = AllPlanes;
8146 values.graphics_exposures = False;
8147 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8149 /* Piece will be copied from an existing context at
8150 the start of each new animation/drag. */
8151 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8153 /* Outline will be a read-only copy of an existing */
8154 anim->outlineGC = None;
8160 XWindowAttributes info;
8162 if (xpmDone && gameInfo.variant == oldVariant) return;
8163 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
8164 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8166 InitAnimState(&game, &info);
8167 InitAnimState(&player, &info);
8169 /* For XPM pieces, we need bitmaps to use as masks. */
8171 CreateAnimMasks(info.depth), xpmDone = 1;
8176 static Boolean frameWaiting;
8178 static RETSIGTYPE FrameAlarm (sig)
8181 frameWaiting = False;
8182 /* In case System-V style signals. Needed?? */
8183 signal(SIGALRM, FrameAlarm);
8190 struct itimerval delay;
8192 XSync(xDisplay, False);
8195 frameWaiting = True;
8196 signal(SIGALRM, FrameAlarm);
8197 delay.it_interval.tv_sec =
8198 delay.it_value.tv_sec = time / 1000;
8199 delay.it_interval.tv_usec =
8200 delay.it_value.tv_usec = (time % 1000) * 1000;
8201 setitimer(ITIMER_REAL, &delay, NULL);
8202 while (frameWaiting) pause();
8203 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8204 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8205 setitimer(ITIMER_REAL, &delay, NULL);
8215 XSync(xDisplay, False);
8217 usleep(time * 1000);
8222 /* Convert board position to corner of screen rect and color */
8225 ScreenSquare(column, row, pt, color)
8226 int column; int row; XPoint * pt; int * color;
8229 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8230 pt->y = lineGap + row * (squareSize + lineGap);
8232 pt->x = lineGap + column * (squareSize + lineGap);
8233 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8235 *color = SquareColor(row, column);
8238 /* Convert window coords to square */
8241 BoardSquare(x, y, column, row)
8242 int x; int y; int * column; int * row;
8244 *column = EventToSquare(x, BOARD_WIDTH);
8245 if (flipView && *column >= 0)
8246 *column = BOARD_WIDTH - 1 - *column;
8247 *row = EventToSquare(y, BOARD_HEIGHT);
8248 if (!flipView && *row >= 0)
8249 *row = BOARD_HEIGHT - 1 - *row;
8254 #undef Max /* just in case */
8256 #define Max(a, b) ((a) > (b) ? (a) : (b))
8257 #define Min(a, b) ((a) < (b) ? (a) : (b))
8260 SetRect(rect, x, y, width, height)
8261 XRectangle * rect; int x; int y; int width; int height;
8265 rect->width = width;
8266 rect->height = height;
8269 /* Test if two frames overlap. If they do, return
8270 intersection rect within old and location of
8271 that rect within new. */
8274 Intersect(old, new, size, area, pt)
8275 XPoint * old; XPoint * new;
8276 int size; XRectangle * area; XPoint * pt;
8278 if (old->x > new->x + size || new->x > old->x + size ||
8279 old->y > new->y + size || new->y > old->y + size) {
8282 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8283 size - abs(old->x - new->x), size - abs(old->y - new->y));
8284 pt->x = Max(old->x - new->x, 0);
8285 pt->y = Max(old->y - new->y, 0);
8290 /* For two overlapping frames, return the rect(s)
8291 in the old that do not intersect with the new. */
8294 CalcUpdateRects(old, new, size, update, nUpdates)
8295 XPoint * old; XPoint * new; int size;
8296 XRectangle update[]; int * nUpdates;
8300 /* If old = new (shouldn't happen) then nothing to draw */
8301 if (old->x == new->x && old->y == new->y) {
8305 /* Work out what bits overlap. Since we know the rects
8306 are the same size we don't need a full intersect calc. */
8308 /* Top or bottom edge? */
8309 if (new->y > old->y) {
8310 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8312 } else if (old->y > new->y) {
8313 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8314 size, old->y - new->y);
8317 /* Left or right edge - don't overlap any update calculated above. */
8318 if (new->x > old->x) {
8319 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8320 new->x - old->x, size - abs(new->y - old->y));
8322 } else if (old->x > new->x) {
8323 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8324 old->x - new->x, size - abs(new->y - old->y));
8331 /* Generate a series of frame coords from start->mid->finish.
8332 The movement rate doubles until the half way point is
8333 reached, then halves back down to the final destination,
8334 which gives a nice slow in/out effect. The algorithmn
8335 may seem to generate too many intermediates for short
8336 moves, but remember that the purpose is to attract the
8337 viewers attention to the piece about to be moved and
8338 then to where it ends up. Too few frames would be less
8342 Tween(start, mid, finish, factor, frames, nFrames)
8343 XPoint * start; XPoint * mid;
8344 XPoint * finish; int factor;
8345 XPoint frames[]; int * nFrames;
8347 int fraction, n, count;
8351 /* Slow in, stepping 1/16th, then 1/8th, ... */
8353 for (n = 0; n < factor; n++)
8355 for (n = 0; n < factor; n++) {
8356 frames[count].x = start->x + (mid->x - start->x) / fraction;
8357 frames[count].y = start->y + (mid->y - start->y) / fraction;
8359 fraction = fraction / 2;
8363 frames[count] = *mid;
8366 /* Slow out, stepping 1/2, then 1/4, ... */
8368 for (n = 0; n < factor; n++) {
8369 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8370 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8372 fraction = fraction * 2;
8377 /* Draw a piece on the screen without disturbing what's there */
8380 SelectGCMask(piece, clip, outline, mask)
8381 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8385 /* Bitmap for piece being moved. */
8386 if (appData.monoMode) {
8387 *mask = *pieceToSolid(piece);
8388 } else if (useImages) {
8390 *mask = xpmMask[piece];
8392 *mask = ximMaskPm[piece];
8395 *mask = *pieceToSolid(piece);
8398 /* GC for piece being moved. Square color doesn't matter, but
8399 since it gets modified we make a copy of the original. */
8401 if (appData.monoMode)
8406 if (appData.monoMode)
8411 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8413 /* Outline only used in mono mode and is not modified */
8415 *outline = bwPieceGC;
8417 *outline = wbPieceGC;
8421 OverlayPiece(piece, clip, outline, dest)
8422 ChessSquare piece; GC clip; GC outline; Drawable dest;
8427 /* Draw solid rectangle which will be clipped to shape of piece */
8428 XFillRectangle(xDisplay, dest, clip,
8429 0, 0, squareSize, squareSize);
8430 if (appData.monoMode)
8431 /* Also draw outline in contrasting color for black
8432 on black / white on white cases */
8433 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8434 0, 0, squareSize, squareSize, 0, 0, 1);
8436 /* Copy the piece */
8441 if(appData.upsideDown && flipView) kind ^= 2;
8442 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8444 0, 0, squareSize, squareSize,
8449 /* Animate the movement of a single piece */
8452 BeginAnimation(anim, piece, startColor, start)
8460 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8461 /* The old buffer is initialised with the start square (empty) */
8462 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8463 anim->prevFrame = *start;
8465 /* The piece will be drawn using its own bitmap as a matte */
8466 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8467 XSetClipMask(xDisplay, anim->pieceGC, mask);
8471 AnimationFrame(anim, frame, piece)
8476 XRectangle updates[4];
8481 /* Save what we are about to draw into the new buffer */
8482 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8483 frame->x, frame->y, squareSize, squareSize,
8486 /* Erase bits of the previous frame */
8487 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8488 /* Where the new frame overlapped the previous,
8489 the contents in newBuf are wrong. */
8490 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8491 overlap.x, overlap.y,
8492 overlap.width, overlap.height,
8494 /* Repaint the areas in the old that don't overlap new */
8495 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8496 for (i = 0; i < count; i++)
8497 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8498 updates[i].x - anim->prevFrame.x,
8499 updates[i].y - anim->prevFrame.y,
8500 updates[i].width, updates[i].height,
8501 updates[i].x, updates[i].y);
8503 /* Easy when no overlap */
8504 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8505 0, 0, squareSize, squareSize,
8506 anim->prevFrame.x, anim->prevFrame.y);
8509 /* Save this frame for next time round */
8510 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8511 0, 0, squareSize, squareSize,
8513 anim->prevFrame = *frame;
8515 /* Draw piece over original screen contents, not current,
8516 and copy entire rect. Wipes out overlapping piece images. */
8517 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8518 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8519 0, 0, squareSize, squareSize,
8520 frame->x, frame->y);
8524 EndAnimation (anim, finish)
8528 XRectangle updates[4];
8533 /* The main code will redraw the final square, so we
8534 only need to erase the bits that don't overlap. */
8535 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8536 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8537 for (i = 0; i < count; i++)
8538 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8539 updates[i].x - anim->prevFrame.x,
8540 updates[i].y - anim->prevFrame.y,
8541 updates[i].width, updates[i].height,
8542 updates[i].x, updates[i].y);
8544 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8545 0, 0, squareSize, squareSize,
8546 anim->prevFrame.x, anim->prevFrame.y);
8551 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8553 ChessSquare piece; int startColor;
8554 XPoint * start; XPoint * finish;
8555 XPoint frames[]; int nFrames;
8559 BeginAnimation(anim, piece, startColor, start);
8560 for (n = 0; n < nFrames; n++) {
8561 AnimationFrame(anim, &(frames[n]), piece);
8562 FrameDelay(appData.animSpeed);
8564 EndAnimation(anim, finish);
8568 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8571 ChessSquare piece = board[fromY][toY];
8572 board[fromY][toY] = EmptySquare;
8573 DrawPosition(FALSE, board);
8575 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8576 y = lineGap + toY * (squareSize + lineGap);
8578 x = lineGap + toX * (squareSize + lineGap);
8579 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8581 for(i=1; i<4*kFactor; i++) {
8582 int r = squareSize * 9 * i/(20*kFactor - 5);
8583 XFillArc(xDisplay, xBoardWindow, highlineGC,
8584 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8585 FrameDelay(appData.animSpeed);
8587 board[fromY][toY] = piece;
8590 /* Main control logic for deciding what to animate and how */
8593 AnimateMove(board, fromX, fromY, toX, toY)
8602 XPoint start, finish, mid;
8603 XPoint frames[kFactor * 2 + 1];
8604 int nFrames, startColor, endColor;
8606 /* Are we animating? */
8607 if (!appData.animate || appData.blindfold)
8610 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8611 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8612 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8614 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8615 piece = board[fromY][fromX];
8616 if (piece >= EmptySquare) return;
8621 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8624 if (appData.debugMode) {
8625 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8626 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8627 piece, fromX, fromY, toX, toY); }
8629 ScreenSquare(fromX, fromY, &start, &startColor);
8630 ScreenSquare(toX, toY, &finish, &endColor);
8633 /* Knight: make straight movement then diagonal */
8634 if (abs(toY - fromY) < abs(toX - fromX)) {
8635 mid.x = start.x + (finish.x - start.x) / 2;
8639 mid.y = start.y + (finish.y - start.y) / 2;
8642 mid.x = start.x + (finish.x - start.x) / 2;
8643 mid.y = start.y + (finish.y - start.y) / 2;
8646 /* Don't use as many frames for very short moves */
8647 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8648 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8650 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8651 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8652 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8654 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8655 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8658 /* Be sure end square is redrawn */
8659 damage[0][toY][toX] = True;
8663 DragPieceBegin(x, y, instantly)
8664 int x; int y; Boolean instantly;
8666 int boardX, boardY, color;
8669 /* Are we animating? */
8670 if (!appData.animateDragging || appData.blindfold)
8673 /* Figure out which square we start in and the
8674 mouse position relative to top left corner. */
8675 BoardSquare(x, y, &boardX, &boardY);
8676 player.startBoardX = boardX;
8677 player.startBoardY = boardY;
8678 ScreenSquare(boardX, boardY, &corner, &color);
8679 player.startSquare = corner;
8680 player.startColor = color;
8681 /* As soon as we start dragging, the piece will jump slightly to
8682 be centered over the mouse pointer. */
8683 player.mouseDelta.x = squareSize/2;
8684 player.mouseDelta.y = squareSize/2;
8685 /* Initialise animation */
8686 player.dragPiece = PieceForSquare(boardX, boardY);
8688 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8689 player.dragActive = True;
8690 BeginAnimation(&player, player.dragPiece, color, &corner);
8691 /* Mark this square as needing to be redrawn. Note that
8692 we don't remove the piece though, since logically (ie
8693 as seen by opponent) the move hasn't been made yet. */
8694 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8695 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8696 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8697 corner.x, corner.y, squareSize, squareSize,
8698 0, 0); // [HGM] zh: unstack in stead of grab
8699 if(gatingPiece != EmptySquare) {
8700 /* Kludge alert: When gating we want the introduced
8701 piece to appear on the from square. To generate an
8702 image of it, we draw it on the board, copy the image,
8703 and draw the original piece again. */
8704 ChessSquare piece = boards[currentMove][boardY][boardX];
8705 DrawSquare(boardY, boardX, gatingPiece, 0);
8706 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8707 corner.x, corner.y, squareSize, squareSize, 0, 0);
8708 DrawSquare(boardY, boardX, piece, 0);
8710 damage[0][boardY][boardX] = True;
8712 player.dragActive = False;
8717 ChangeDragPiece(ChessSquare piece)
8720 player.dragPiece = piece;
8721 /* The piece will be drawn using its own bitmap as a matte */
8722 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8723 XSetClipMask(xDisplay, player.pieceGC, mask);
8732 /* Are we animating? */
8733 if (!appData.animateDragging || appData.blindfold)
8737 if (! player.dragActive)
8739 /* Move piece, maintaining same relative position
8740 of mouse within square */
8741 corner.x = x - player.mouseDelta.x;
8742 corner.y = y - player.mouseDelta.y;
8743 AnimationFrame(&player, &corner, player.dragPiece);
8745 if (appData.highlightDragging) {
8747 BoardSquare(x, y, &boardX, &boardY);
8748 SetHighlights(fromX, fromY, boardX, boardY);
8757 int boardX, boardY, color;
8760 /* Are we animating? */
8761 if (!appData.animateDragging || appData.blindfold)
8765 if (! player.dragActive)
8767 /* Last frame in sequence is square piece is
8768 placed on, which may not match mouse exactly. */
8769 BoardSquare(x, y, &boardX, &boardY);
8770 ScreenSquare(boardX, boardY, &corner, &color);
8771 EndAnimation(&player, &corner);
8773 /* Be sure end square is redrawn */
8774 damage[0][boardY][boardX] = True;
8776 /* This prevents weird things happening with fast successive
8777 clicks which on my Sun at least can cause motion events
8778 without corresponding press/release. */
8779 player.dragActive = False;
8782 /* Handle expose event while piece being dragged */
8787 if (!player.dragActive || appData.blindfold)
8790 /* What we're doing: logically, the move hasn't been made yet,
8791 so the piece is still in it's original square. But visually
8792 it's being dragged around the board. So we erase the square
8793 that the piece is on and draw it at the last known drag point. */
8794 BlankSquare(player.startSquare.x, player.startSquare.y,
8795 player.startColor, EmptySquare, xBoardWindow, 1);
8796 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8797 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8800 #include <sys/ioctl.h>
8801 int get_term_width()
8803 int fd, default_width;
8806 default_width = 79; // this is FICS default anyway...
8808 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8810 if (!ioctl(fd, TIOCGSIZE, &win))
8811 default_width = win.ts_cols;
8812 #elif defined(TIOCGWINSZ)
8814 if (!ioctl(fd, TIOCGWINSZ, &win))
8815 default_width = win.ws_col;
8817 return default_width;
8823 static int old_width = 0;
8824 int new_width = get_term_width();
8826 if (old_width != new_width)
8827 ics_printf("set width %d\n", new_width);
8828 old_width = new_width;
8831 void NotifyFrontendLogin()
8836 /* [AS] Arrow highlighting support */
8838 static double A_WIDTH = 5; /* Width of arrow body */
8840 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8841 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8843 static double Sqr( double x )
8848 static int Round( double x )
8850 return (int) (x + 0.5);
8853 void SquareToPos(int rank, int file, int *x, int *y)
8856 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8857 *y = lineGap + rank * (squareSize + lineGap);
8859 *x = lineGap + file * (squareSize + lineGap);
8860 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8864 /* Draw an arrow between two points using current settings */
8865 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8868 double dx, dy, j, k, x, y;
8871 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8873 arrow[0].x = s_x + A_WIDTH + 0.5;
8876 arrow[1].x = s_x + A_WIDTH + 0.5;
8877 arrow[1].y = d_y - h;
8879 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8880 arrow[2].y = d_y - h;
8885 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8886 arrow[5].y = d_y - h;
8888 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8889 arrow[4].y = d_y - h;
8891 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8894 else if( d_y == s_y ) {
8895 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8898 arrow[0].y = s_y + A_WIDTH + 0.5;
8900 arrow[1].x = d_x - w;
8901 arrow[1].y = s_y + A_WIDTH + 0.5;
8903 arrow[2].x = d_x - w;
8904 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8909 arrow[5].x = d_x - w;
8910 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8912 arrow[4].x = d_x - w;
8913 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8916 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8919 /* [AS] Needed a lot of paper for this! :-) */
8920 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8921 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8923 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8925 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8930 arrow[0].x = Round(x - j);
8931 arrow[0].y = Round(y + j*dx);
8933 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8934 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8937 x = (double) d_x - k;
8938 y = (double) d_y - k*dy;
8941 x = (double) d_x + k;
8942 y = (double) d_y + k*dy;
8945 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8947 arrow[6].x = Round(x - j);
8948 arrow[6].y = Round(y + j*dx);
8950 arrow[2].x = Round(arrow[6].x + 2*j);
8951 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8953 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8954 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8959 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8960 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8963 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8964 // Polygon( hdc, arrow, 7 );
8967 /* [AS] Draw an arrow between two squares */
8968 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
8970 int s_x, s_y, d_x, d_y, hor, vert, i;
8972 if( s_col == d_col && s_row == d_row ) {
8976 /* Get source and destination points */
8977 SquareToPos( s_row, s_col, &s_x, &s_y);
8978 SquareToPos( d_row, d_col, &d_x, &d_y);
8981 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8983 else if( d_y < s_y ) {
8984 d_y += squareSize / 2 + squareSize / 4;
8987 d_y += squareSize / 2;
8991 d_x += squareSize / 2 - squareSize / 4;
8993 else if( d_x < s_x ) {
8994 d_x += squareSize / 2 + squareSize / 4;
8997 d_x += squareSize / 2;
9000 s_x += squareSize / 2;
9001 s_y += squareSize / 2;
9004 A_WIDTH = squareSize / 14.; //[HGM] make float
9006 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
9008 hor = 64*s_col + 32; vert = 64*s_row + 32;
9009 for(i=0; i<= 64; i++) {
9010 damage[0][vert+6>>6][hor+6>>6] = True;
9011 damage[0][vert-6>>6][hor+6>>6] = True;
9012 damage[0][vert+6>>6][hor-6>>6] = True;
9013 damage[0][vert-6>>6][hor-6>>6] = True;
9014 hor += d_col - s_col; vert += d_row - s_row;
9018 Boolean IsDrawArrowEnabled()
9020 return appData.highlightMoveWithArrow && squareSize >= 32;
9023 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
9025 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
9026 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
9029 void UpdateLogos(int displ)
9031 return; // no logos in XBoard yet